apkverify: verify content digest
Bug: 190343842
Test: MicrodroidHostTestCases
Change-Id: I370aaf6bf88e7267f5a03b7a2da32c87d49552db
diff --git a/apkverify/src/v3.rs b/apkverify/src/v3.rs
index 91043ab..0ef035c 100644
--- a/apkverify/src/v3.rs
+++ b/apkverify/src/v3.rs
@@ -22,6 +22,7 @@
use anyhow::{anyhow, bail, Result};
use bytes::Bytes;
use std::fs::File;
+use std::io::{Read, Seek};
use std::ops::Range;
use std::path::Path;
@@ -85,16 +86,18 @@
/// associated with each signer.
pub fn verify<P: AsRef<Path>>(path: P) -> Result<()> {
let f = File::open(path.as_ref())?;
- let signature = find_signature(f, APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
- verify_signature(&signature.signature_block)?;
+ let mut sections = ApkSections::new(f)?;
+ verify_signature(&mut sections)?;
Ok(())
}
/// Verifies the contents of the provided APK file against the provided APK Signature Scheme v3
/// Block.
-fn verify_signature(block: &Bytes) -> Result<()> {
+fn verify_signature<R: Read + Seek>(sections: &mut ApkSections<R>) -> Result<()> {
+ let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
+
// parse v3 scheme block
- let signers = block.slice(..).read::<Signers>()?;
+ let signers = block.read::<Signers>()?;
// find supported by platform
let mut supported =
@@ -106,13 +109,13 @@
}
// and it should be verified
- supported.pop().unwrap().verify()?;
+ supported.pop().unwrap().verify(sections)?;
Ok(())
}
impl Signer {
- fn verify(&self) -> Result<()> {
+ fn verify<R: Read + Seek>(&self, sections: &mut ApkSections<R>) -> Result<()> {
// 1. Choose the strongest supported signature algorithm ID from signatures. The strength
// ordering is up to each implementation/platform version.
let strongest: &Signature = self
@@ -134,9 +137,36 @@
if self.sdk_range() != signed_data.sdk_range() {
bail!("SDK versions mismatch between signed and unsigned in v3 signer block.");
}
- // TODO(jooyung) 4. Verify that the ordered list of signature algorithm IDs in digests and signatures is identical. (This is to prevent signature stripping/addition.)
- // TODO(jooyung) 5. Compute the digest of APK contents using the same digest algorithm as the digest algorithm used by the signature algorithm.
- // TODO(jooyung) 6. Verify that the computed digest is identical to the corresponding digest from digests.
+
+ // 4. Verify that the ordered list of signature algorithm IDs in digests and signatures is
+ // identical. (This is to prevent signature stripping/addition.)
+ if !self
+ .signatures
+ .iter()
+ .map(|sig| sig.signature_algorithm_id)
+ .eq(signed_data.digests.iter().map(|dig| dig.signature_algorithm_id))
+ {
+ bail!("Signature algorithms don't match between digests and signatures records");
+ }
+
+ // 5. Compute the digest of APK contents using the same digest algorithm as the digest
+ // algorithm used by the signature algorithm.
+ let digest = signed_data
+ .digests
+ .iter()
+ .find(|&dig| dig.signature_algorithm_id == strongest.signature_algorithm_id)
+ .unwrap(); // ok to unwrap since we check if two lists are the same above
+ let computed = sections.compute_digest(digest.signature_algorithm_id)?;
+
+ // 6. Verify that the computed digest is identical to the corresponding digest from digests.
+ if computed != digest.digest.as_ref() {
+ bail!(
+ "digest mismatch: computed={:?} vs expected={:?}",
+ to_hex_string(&computed),
+ to_hex_string(&digest.digest),
+ );
+ }
+
// TODO(jooyung) 7. Verify that SubjectPublicKeyInfo of the first certificate of certificates is identical to public key.
// TODO(jooyung) 8. If the proof-of-rotation attribute exists for the signer verify that the struct is valid and this signer is the last certificate in the list.
Ok(())
@@ -215,3 +245,8 @@
Ok(Self { signature_algorithm_id: buf.read()?, digest: buf.read()? })
}
}
+
+#[inline]
+fn to_hex_string(buf: &[u8]) -> String {
+ buf.iter().map(|b| format!("{:02X}", b)).collect()
+}