Merge "[apkverify] Skip DSA SHA256 during apk verification"
diff --git a/libs/apkverify/src/bytes_ext.rs b/libs/apkverify/src/bytes_ext.rs
index 8fb36ee..8a7badf 100644
--- a/libs/apkverify/src/bytes_ext.rs
+++ b/libs/apkverify/src/bytes_ext.rs
@@ -32,6 +32,13 @@
     }
 }
 
+impl<T> LengthPrefixed<T> {
+    /// Consumes the `LengthPrefixed` instance, returning the wrapped value.
+    pub fn into_inner(self) -> T {
+        self.inner
+    }
+}
+
 pub trait BytesExt {
     fn read<T: ReadFromBytes>(&mut self) -> Result<T>;
 }
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index 084a910..92de9b0 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -24,6 +24,5 @@
 mod v3;
 mod ziputil;
 
-// TODO(b/197052981) fallback to v2 when v3 not found
 pub use algorithms::SignatureAlgorithmID;
 pub use v3::{get_public_key_der, pick_v4_apk_digest, verify};
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 80ef687..5272834 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -65,6 +65,14 @@
     fn sdk_range(&self) -> Range<u32> {
         self.min_sdk..self.max_sdk
     }
+
+    fn find_digest_by_algorithm(&self, algorithm_id: SignatureAlgorithmID) -> Result<&Digest> {
+        Ok(self
+            .digests
+            .iter()
+            .find(|&dig| dig.signature_algorithm_id == Some(algorithm_id))
+            .context(format!("Digest not found for algorithm: {:?}", algorithm_id))?)
+    }
 }
 
 #[derive(Debug)]
@@ -86,52 +94,42 @@
 /// associated with the signer in DER format.
 pub fn verify<P: AsRef<Path>>(apk_path: P) -> Result<Box<[u8]>> {
     let apk = File::open(apk_path.as_ref())?;
-    let mut sections = ApkSections::new(apk)?;
-    find_signer_and_then(&mut sections, |(signer, sections)| signer.verify(sections))
-}
-
-/// Finds the supported signer and execute a function on it.
-fn find_signer_and_then<R, U, F>(sections: &mut ApkSections<R>, f: F) -> Result<U>
-where
-    R: Read + Seek,
-    F: FnOnce((&Signer, &mut ApkSections<R>)) -> Result<U>,
-{
-    let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
-    // parse v3 scheme block
-    let signers = block.read::<Signers>()?;
-
-    // find supported by platform
-    let supported = signers.iter().filter(|s| s.sdk_range().contains(&SDK_INT)).collect::<Vec<_>>();
-
-    // there should be exactly one
-    ensure!(
-        supported.len() == 1,
-        "APK Signature Scheme V3 only supports one signer: {} signers found.",
-        supported.len()
-    );
-
-    // Call the supplied function
-    f((supported[0], sections))
+    let (signer, mut sections) = extract_signer_and_apk_sections(apk)?;
+    signer.verify(&mut sections)
 }
 
 /// Gets the public key (in DER format) that was used to sign the given APK/APEX file
 pub fn get_public_key_der<P: AsRef<Path>>(apk_path: P) -> Result<Box<[u8]>> {
     let apk = File::open(apk_path.as_ref())?;
-    let mut sections = ApkSections::new(apk)?;
-    find_signer_and_then(&mut sections, |(signer, _)| {
-        Ok(signer.public_key.public_key_to_der()?.into_boxed_slice())
-    })
+    let (signer, _) = extract_signer_and_apk_sections(apk)?;
+    Ok(signer.public_key.public_key_to_der()?.into_boxed_slice())
 }
 
 /// 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<(SignatureAlgorithmID, Box<[u8]>)> {
+    let (signer, _) = extract_signer_and_apk_sections(apk)?;
+    signer.pick_v4_apk_digest()
+}
+
+fn extract_signer_and_apk_sections<R: Read + Seek>(apk: R) -> Result<(Signer, ApkSections<R>)> {
     let mut sections = ApkSections::new(apk)?;
-    let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID)?;
-    let signers = block.read::<Signers>()?;
-    ensure!(signers.len() == 1, "should only have one signer");
-    signers[0].pick_v4_apk_digest()
+    let mut block = sections.find_signature(APK_SIGNATURE_SCHEME_V3_BLOCK_ID).context(
+        "Fallback to v2 when v3 block not found is not yet implemented. See b/197052981.",
+    )?;
+    let mut supported = block
+        .read::<Signers>()?
+        .into_inner()
+        .into_iter()
+        .filter(|s| s.sdk_range().contains(&SDK_INT))
+        .collect::<Vec<_>>();
+    ensure!(
+        supported.len() == 1,
+        "APK Signature Scheme V3 only supports one signer: {} signers found.",
+        supported.len()
+    );
+    Ok((supported.pop().unwrap().into_inner(), sections))
 }
 
 impl Signer {
@@ -147,17 +145,13 @@
     }
 
     fn pick_v4_apk_digest(&self) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
-        let strongest = self.strongest_signature()?;
+        let strongest_algorithm_id = self
+            .strongest_signature()?
+            .signature_algorithm_id
+            .context("Strongest signature should contain a valid signature algorithm.")?;
         let signed_data: SignedData = self.signed_data.slice(..).read()?;
-        let digest = signed_data
-            .digests
-            .iter()
-            .find(|&dig| dig.signature_algorithm_id == strongest.signature_algorithm_id)
-            .context("Digest not found")?;
-        Ok((
-            digest.signature_algorithm_id.context("Unsupported algorithm")?,
-            digest.digest.as_ref().to_vec().into_boxed_slice(),
-        ))
+        let digest = signed_data.find_digest_by_algorithm(strongest_algorithm_id)?;
+        Ok((strongest_algorithm_id, digest.digest.as_ref().to_vec().into_boxed_slice()))
     }
 
     /// Verifies the strongest signature from signatures against signed data using public key.
@@ -200,13 +194,10 @@
 
         // 5. Compute the digest of APK contents using the same digest algorithm as the digest
         //    algorithm used by the signature algorithm.
-        let digest = verified_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.context("Unsupported algorithm")?)?;
+        let digest = verified_signed_data.find_digest_by_algorithm(
+            strongest.signature_algorithm_id.context("Unsupported algorithm")?,
+        )?;
+        let computed = sections.compute_digest(digest.signature_algorithm_id.unwrap())?;
 
         // 6. Verify that the computed digest is identical to the corresponding digest from digests.
         ensure!(