calculate image shasum

This commit is contained in:
Bruce Leidl 2019-01-05 20:39:17 -05:00
parent dc9a49fd8a
commit c580d784ff
2 changed files with 80 additions and 35 deletions

View File

@ -55,6 +55,12 @@ fn main() {
.subcommand(SubCommand::with_name("decompress") .subcommand(SubCommand::with_name("decompress")
.about("Decompress a compressed image file") .about("Decompress a compressed image file")
.arg(Arg::with_name("path")
.required(true)
.help("Path to image file")))
.subcommand(SubCommand::with_name("verify-shasum")
.about("Verify the sha256 sum of the image")
.arg(Arg::with_name("path") .arg(Arg::with_name("path")
.required(true) .required(true)
.help("Path to image file"))); .help("Path to image file")));
@ -70,6 +76,7 @@ fn main() {
("sign-image", Some(m)) => sign_image(m), ("sign-image", Some(m)) => sign_image(m),
("genkeys", Some(_)) => genkeys(), ("genkeys", Some(_)) => genkeys(),
("decompress", Some(m)) => decompress(m), ("decompress", Some(m)) => decompress(m),
("verify-shasum", Some(m)) => verify_shasum(m),
("install-rootfs", Some(m)) => install_rootfs(m), ("install-rootfs", Some(m)) => install_rootfs(m),
_ => Ok(()), _ => Ok(()),
}; };
@ -115,6 +122,19 @@ fn verify(arg_matches: &ArgMatches) -> Result<()> {
Ok(()) Ok(())
} }
fn verify_shasum(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?;
let shasum = img.generate_shasum()?;
if shasum == img.metainfo().shasum() {
info!("Image has correct sha256sum: {}", shasum);
} else {
info!("Image sha256 sum does not match metainfo:");
info!(" image: {}", shasum);
info!(" metainfo: {}", img.metainfo().shasum())
}
Ok(())
}
fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> { fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> {
let path = arg_matches.value_of("path").expect("path argument missing"); let path = arg_matches.value_of("path").expect("path argument missing");
if !Path::new(path).exists() { if !Path::new(path).exists() {
@ -129,6 +149,12 @@ fn load_image(arg_matches: &ArgMatches) -> Result<ResourceImage> {
fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> { fn install_rootfs(arg_matches: &ArgMatches) -> Result<()> {
let img = load_image(arg_matches)?; let img = load_image(arg_matches)?;
img.header().verify_signature()?;
let shasum = img.generate_shasum()?;
if shasum != img.metainfo().shasum() {
bail!("image file does not have expected sha256 value");
}
let partition = choose_install_partition()?; let partition = choose_install_partition()?;
img.write_to_partition(&partition)?; img.write_to_partition(&partition)?;
Ok(()) Ok(())
@ -142,7 +168,7 @@ fn sign_image(arg_matches: &ArgMatches) -> Result<()> {
fn genkeys() -> Result<()> { fn genkeys() -> Result<()> {
let keypair = KeyPair::generate()?; let keypair = KeyPair::generate()?;
println!("public-key = \"{}\"", keypair.public_key_hex()); println!("public-key = \"{}\"", keypair.public_key().to_hex());
println!("private-key = \"{}\"", keypair.private_key_hex()); println!("private-key = \"{}\"", keypair.private_key_hex());
Ok(()) Ok(())
} }

View File

@ -100,11 +100,11 @@ impl ResourceImage {
} }
} }
pub fn mount(&mut self, config: &Config) -> Result<()> { pub fn mount(&mut self) -> Result<()> {
if CommandLine::noverity() { if CommandLine::noverity() {
self.mount_noverity()?; self.mount_noverity()?;
} else { } else {
self.mount_verity(config)?; self.mount_verity()?;
} }
self.process_manifest_file() self.process_manifest_file()
@ -122,32 +122,32 @@ impl ResourceImage {
if !self.is_compressed() { if !self.is_compressed() {
return Ok(()) return Ok(())
} }
self.decompress_and_generate_hashtree(true)
}
// Avoid copying the body twice in the common case that image is both let tmpfile = self.extract_body_to_tmpfile()?;
// compressed and needs hashtree generated. let decompressed = self.decompress_tmpfile(tmpfile)?;
fn decompress_and_generate_hashtree(&self, decompress_only: bool) -> Result<()> {
assert!(self.is_compressed());
let mut tmpfile = self.extract_body_to_tmpfile(Some("xz"))?;
util::xz_decompress(&tmpfile)?;
tmpfile.set_extension("");
self.header.clear_flag(ImageHeader::FLAG_DATA_COMPRESSED); self.header.clear_flag(ImageHeader::FLAG_DATA_COMPRESSED);
self.write_image_from_tmpfile(&decompressed)?;
if !decompress_only && !self.has_verity_hashtree() { Ok(())
verity::generate_image_hashtree(&tmpfile, &self.metainfo)?;
self.header.set_flag(ImageHeader::FLAG_HASH_TREE);
}
self.write_image_from_tmpfile(&tmpfile)
} }
fn extract_body_to_tmpfile(&self, extension: Option<&str>) -> Result<PathBuf> { fn decompress_tmpfile(&self, tmpfile: PathBuf) -> Result<PathBuf> {
info!("Decompressing image contents");
if !self.is_compressed() {
return Ok(tmpfile);
}
util::xz_decompress(&tmpfile)?;
let mut new_tmpfile = PathBuf::from(tmpfile);
new_tmpfile.set_extension("");
Ok(new_tmpfile)
}
fn extract_body_to_tmpfile(&self) -> Result<PathBuf> {
let mut reader = File::open(&self.path)?; let mut reader = File::open(&self.path)?;
reader.seek(SeekFrom::Start(4096))?; reader.seek(SeekFrom::Start(4096))?;
fs::create_dir_all("/tmp/citadel-image-tmp")?; fs::create_dir_all("/tmp/citadel-image-tmp")?;
let mut path = Path::new("/tmp/citadel-image-tmp").join(format!("{}-tmp", &self.metainfo.image_type())); let mut path = Path::new("/tmp/citadel-image-tmp").join(format!("{}-tmp", &self.metainfo.image_type()));
if let Some(ext) = extension { if self.is_compressed() {
path.set_extension(ext); path.set_extension("xz");
} }
let mut out = File::create(&path)?; let mut out = File::create(&path)?;
io::copy(&mut reader, &mut out)?; io::copy(&mut reader, &mut out)?;
@ -166,7 +166,7 @@ impl ResourceImage {
info!("writing rootfs image to {}", partition.path().display()); info!("writing rootfs image to {}", partition.path().display());
let args = format!("if={} of={} bs=4096 skip=1", let args = format!("if={} of={} bs=4096 skip=1",
self.path.display(), partition.path().display()); self.path.display(), partition.path().display());
util::exec_cmdline("/bin/dd", args)?; util::exec_cmdline_quiet("/bin/dd", args)?;
self.header.set_status(ImageHeader::STATUS_NEW); self.header.set_status(ImageHeader::STATUS_NEW);
self.header.write_partition(partition.path())?; self.header.write_partition(partition.path())?;
@ -196,7 +196,8 @@ impl ResourceImage {
pub fn setup_verity_device(&self) -> Result<PathBuf> { pub fn setup_verity_device(&self) -> Result<PathBuf> {
if !CommandLine::nosignatures() { if !CommandLine::nosignatures() {
self.header.verify_signature(config)?; self.header.verify_signature()?;
info!("Image signature is valid for channel {}", self.metainfo.channel());
} }
info!("Setting up dm-verity device for image"); info!("Setting up dm-verity device for image");
if !self.has_verity_hashtree() { if !self.has_verity_hashtree() {
@ -210,26 +211,44 @@ impl ResourceImage {
return Ok(()) return Ok(())
} }
info!("Generating dm-verity hash tree for image {}", self.path.display()); info!("Generating dm-verity hash tree for image {}", self.path.display());
let mut tmp = self.extract_body_to_tmpfile()?;
if self.is_compressed() { if self.is_compressed() {
info!("Image is compressed, so need to decompress first"); tmp = self.decompress_tmpfile(tmp)?;
self.decompress_and_generate_hashtree(false) self.header.clear_flag(ImageHeader::FLAG_DATA_COMPRESSED);
} else {
let tmpfile = self.extract_body_to_tmpfile(None)?;
verity::generate_image_hashtree(&tmpfile, &self.metainfo)?;
self.header.set_flag(ImageHeader::FLAG_HASH_TREE);
self.write_image_from_tmpfile(&tmpfile)
} }
verity::generate_image_hashtree(&tmp, self.metainfo())?;
self.header.set_flag(ImageHeader::FLAG_HASH_TREE);
self.write_image_from_tmpfile(&tmp)?;
Ok(())
} }
pub fn verify_verity(&self) -> Result<bool> { pub fn verify_verity(&self) -> Result<bool> {
if !self.has_verity_hashtree() { if !self.has_verity_hashtree() {
self.generate_verity_hashtree()?; self.generate_verity_hashtree()?;
} }
verity::verify_image(self.path(), &self.metainfo) info!("Verifying dm-verity hash tree");
let tmp = self.extract_body_to_tmpfile()?;
let ok = verity::verify_image(&tmp, &self.metainfo)?;
fs::remove_file(tmp)?;
Ok(ok)
} }
pub fn verify_shasum(&self) -> Result<bool> { pub fn generate_shasum(&self) -> Result<String> {
unimplemented!(); let mut tmp = self.extract_body_to_tmpfile()?;
if self.is_compressed() {
tmp = self.decompress_tmpfile(tmp)?;
}
info!("Calculating sha256 of image");
if self.has_verity_hashtree() {
let args = format!("if={} of={}.shainput bs=4096 count={}", tmp.display(), tmp.display(), self.metainfo.nblocks());
util::exec_cmdline_quiet("/bin/dd", args)?;
fs::remove_file(&tmp)?;
tmp.set_extension("shainput");
}
let shasum = util::sha256(&tmp)?;
fs::remove_file(tmp)?;
Ok(shasum)
} }
// Mount the resource image but use a simple loop mount rather than setting up a dm-verity // Mount the resource image but use a simple loop mount rather than setting up a dm-verity