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()));
+    }
+}