Test V4 apk_digest computation in apkverify
Bug: 239534874
Test: atest libapkverify.test
Change-Id: I73df9d67be0fdc922299968a56db7ed87dec171c
diff --git a/libs/apkverify/src/sigutil.rs b/libs/apkverify/src/sigutil.rs
index 7034527..72c5c1a 100644
--- a/libs/apkverify/src/sigutil.rs
+++ b/libs/apkverify/src/sigutil.rs
@@ -289,17 +289,20 @@
}
}
-/// Rank the signature algorithm according to the preferences of the v4 signing scheme.
-pub fn rank_signature_algorithm(algo: u32) -> Result<u32> {
- rank_content_digest_algorithm(to_content_digest_algorithm(algo)?)
-}
-
-fn rank_content_digest_algorithm(id: u32) -> Result<u32> {
- match id {
+/// This method is used to help pick v4 apk digest. According to APK Signature
+/// Scheme v4, apk digest is the first available content digest of the highest
+/// rank (rank N).
+///
+/// This rank was also used for step 3a of the v3 signature verification.
+///
+/// [v3 verification]: https://source.android.com/docs/security/apksigning/v3#v3-verification
+pub fn get_signature_algorithm_rank(algo: u32) -> Result<u32> {
+ let content_digest = to_content_digest_algorithm(algo)?;
+ match content_digest {
CONTENT_DIGEST_CHUNKED_SHA256 => Ok(0),
CONTENT_DIGEST_VERITY_CHUNKED_SHA256 => Ok(1),
CONTENT_DIGEST_CHUNKED_SHA512 => Ok(2),
- _ => bail!("Unknown digest algorithm: {}", id),
+ _ => bail!("Unknown digest algorithm: {}", content_digest),
}
}
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 0c20a2e..bad6bc2 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -128,14 +128,14 @@
})
}
-/// Gets the APK digest.
+/// Gets the v4 [apk_digest].
+///
+/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
pub fn pick_v4_apk_digest<R: Read + Seek>(apk: R) -> Result<(u32, Box<[u8]>)> {
let mut sections = ApkSections::new(apk)?;
let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
let signers = block.read::<Signers>()?;
- if signers.len() != 1 {
- bail!("should only have one signer");
- }
+ ensure!(signers.len() == 1, "should only have one signer");
signers[0].pick_v4_apk_digest()
}
@@ -147,7 +147,7 @@
.signatures
.iter()
.filter(|sig| is_supported_signature_algorithm(sig.signature_algorithm_id))
- .max_by_key(|sig| rank_signature_algorithm(sig.signature_algorithm_id).unwrap())
+ .max_by_key(|sig| get_signature_algorithm_rank(sig.signature_algorithm_id).unwrap())
.ok_or_else(|| anyhow!("No supported signatures found"))?)
}
@@ -304,3 +304,36 @@
fn to_hex_string(buf: &[u8]) -> String {
buf.iter().map(|b| format!("{:02X}", b)).collect()
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn test_pick_v4_apk_digest_only_with_v3_dsa_sha256() {
+ check_v4_apk_digest(
+ "tests/data/v3-only-with-dsa-sha256-1024.apk",
+ SIGNATURE_DSA_WITH_SHA256,
+ "0DF2426EA33AEDAF495D88E5BE0C6A1663FF0A81C5ED12D5B2929AE4B4300F2F",
+ );
+ }
+
+ #[test]
+ fn test_pick_v4_apk_digest_only_with_v3_pkcs1_sha512() {
+ check_v4_apk_digest(
+ "tests/data/v3-only-with-rsa-pkcs1-sha512-1024.apk",
+ SIGNATURE_RSA_PKCS1_V1_5_WITH_SHA512,
+ "9B9AE02DA60B18999BF541790F00D380006FDF0655C3C482AA0BB0AF17CF7A42\
+ ECF56B973518546C9080B2FEF83027E895ED2882BFC88EA19790BBAB29AF53B3",
+ );
+ }
+
+ fn check_v4_apk_digest(apk_filename: &str, expected_algorithm: u32, expected_digest: &str) {
+ let apk_file = File::open(apk_filename).unwrap();
+ let (signature_algorithm_id, apk_digest) = pick_v4_apk_digest(apk_file).unwrap();
+
+ assert_eq!(expected_algorithm, signature_algorithm_id);
+ assert_eq!(expected_digest, to_hex_string(apk_digest.as_ref()));
+ }
+}