[apkverify] Add API to compute v4 apk_digest

Bug: 247689066
Test: libapkverify.integration_test avmdtool_tests libidsig.test
Change-Id: Ic8f39641fa41b6202aea43b6c2ce6a9254198212
diff --git a/libs/apkverify/src/lib.rs b/libs/apkverify/src/lib.rs
index 92de9b0..1e0bd77 100644
--- a/libs/apkverify/src/lib.rs
+++ b/libs/apkverify/src/lib.rs
@@ -22,7 +22,9 @@
 #[allow(dead_code)]
 pub mod testing;
 mod v3;
+mod v4;
 mod ziputil;
 
 pub use algorithms::SignatureAlgorithmID;
-pub use v3::{get_public_key_der, pick_v4_apk_digest, verify};
+pub use v3::{get_public_key_der, verify};
+pub use v4::get_apk_digest;
diff --git a/libs/apkverify/src/v3.rs b/libs/apkverify/src/v3.rs
index 5272834..fac0a7f 100644
--- a/libs/apkverify/src/v3.rs
+++ b/libs/apkverify/src/v3.rs
@@ -38,7 +38,7 @@
 
 type Signers = LengthPrefixed<Vec<LengthPrefixed<Signer>>>;
 
-struct Signer {
+pub(crate) struct Signer {
     signed_data: LengthPrefixed<Bytes>, // not verified yet
     min_sdk: u32,
     max_sdk: u32,
@@ -105,15 +105,9 @@
     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>)> {
+pub(crate) 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).context(
         "Fallback to v2 when v3 block not found is not yet implemented. See b/197052981.",
@@ -144,7 +138,7 @@
             .context("No supported signatures found")?)
     }
 
-    fn pick_v4_apk_digest(&self) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
+    pub(crate) fn pick_v4_apk_digest(&self) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
         let strongest_algorithm_id = self
             .strongest_signature()?
             .signature_algorithm_id
diff --git a/libs/apkverify/src/v4.rs b/libs/apkverify/src/v4.rs
new file mode 100644
index 0000000..d0522a7
--- /dev/null
+++ b/libs/apkverify/src/v4.rs
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! API for APK Signature Scheme [v4].
+//!
+//! [v4]: https://source.android.com/security/apksigning/v4
+
+use anyhow::{ensure, Result};
+use std::io::{Read, Seek};
+
+use crate::algorithms::SignatureAlgorithmID;
+use crate::v3::extract_signer_and_apk_sections;
+
+/// Gets the v4 [apk_digest]. If `verify` is true, we verify that digest computed
+/// with the extracted algorithm is equal to the digest extracted directly from apk.
+/// Otherwise, the extracted digest will be returned directly.
+///
+/// [apk_digest]: https://source.android.com/docs/security/apksigning/v4#apk-digest
+pub fn get_apk_digest<R: Read + Seek>(
+    apk: R,
+    verify: bool,
+) -> Result<(SignatureAlgorithmID, Box<[u8]>)> {
+    let (signer, mut sections) = extract_signer_and_apk_sections(apk)?;
+    let (signature_algorithm_id, extracted_digest) = signer.pick_v4_apk_digest()?;
+    if verify {
+        let computed_digest = sections.compute_digest(signature_algorithm_id)?;
+        ensure!(
+            computed_digest == extracted_digest.as_ref(),
+            "Computed digest does not match the extracted digest."
+        );
+    }
+    Ok((signature_algorithm_id, extracted_digest))
+}