forked from brl/citadel-tools
calculate image shasum
This commit is contained in:
parent
dc9a49fd8a
commit
c580d784ff
@ -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(())
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user