Store public key of APK to instance disk
Once a VM is started with an APK, we shouldn't accept any update of the
APK if its signer has changed.
For now, this CL can be considered a no-op because we don't accept any
update by comparing the root hash. However, in the future, when rollback
protection is supported, this change will make it possible to accept APK
updates if the public key remains the same.
Bug: 199143508
Test: atest MicrodroidHostTestCases
Change-Id: I2448faf4a8b9637571ebcc4bc49d3619129496d5
diff --git a/apkverify/src/lib.rs b/apkverify/src/lib.rs
index f75913c..71ea857 100644
--- a/apkverify/src/lib.rs
+++ b/apkverify/src/lib.rs
@@ -26,8 +26,14 @@
use anyhow::Result;
use std::path::Path;
-/// Verifies APK/APEX signing with v2/v3 scheme
-pub fn verify<P: AsRef<Path>>(path: P) -> Result<()> {
+/// Verifies APK/APEX signing with v2/v3 scheme. On success, the public key (in DER format) is
+/// returned.
+pub fn verify<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>> {
// TODO(jooyung) fallback to v2 when v3 not found
v3::verify(path)
}
+
+/// 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>>(path: P) -> Result<Box<[u8]>> {
+ v3::get_public_key_der(path)
+}
diff --git a/apkverify/src/v3.rs b/apkverify/src/v3.rs
index 2f9ce94..797911b 100644
--- a/apkverify/src/v3.rs
+++ b/apkverify/src/v3.rs
@@ -86,40 +86,50 @@
type X509Certificate = Bytes;
type AdditionalAttributes = Bytes;
-/// Verifies APK Signature Scheme v3 signatures of the provided APK and returns the certificates
-/// associated with each signer.
-pub fn verify<P: AsRef<Path>>(path: P) -> Result<()> {
+/// Verifies APK Signature Scheme v3 signatures of the provided APK and returns the public key
+/// associated with the signer.
+pub fn verify<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>> {
let f = File::open(path.as_ref())?;
let mut sections = ApkSections::new(f)?;
- verify_signature(&mut sections)?;
- Ok(())
+ find_signer_and_then(&mut sections, |(signer, sections)| signer.verify(sections))
}
-/// Verifies the contents of the provided APK file against the provided APK Signature Scheme v3
-/// Block.
-fn verify_signature<R: Read + Seek>(sections: &mut ApkSections<R>) -> Result<()> {
+/// 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 mut supported =
- signers.iter().filter(|s| s.sdk_range().contains(&SDK_INT)).collect::<Vec<_>>();
+ let supported = signers.iter().filter(|s| s.sdk_range().contains(&SDK_INT)).collect::<Vec<_>>();
// there should be exactly one
if supported.len() != 1 {
- bail!("APK Signature Scheme V3 only supports one signer: {} signers found.", signers.len())
+ bail!(
+ "APK Signature Scheme V3 only supports one signer: {} signers found.",
+ supported.len()
+ )
}
- // and it should be verified
- supported.pop().unwrap().verify(sections)?;
+ // Call the supplied function
+ f((supported[0], sections))
+}
- Ok(())
+/// 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>>(path: P) -> Result<Box<[u8]>> {
+ let f = File::open(path.as_ref())?;
+ let mut sections = ApkSections::new(f)?;
+ find_signer_and_then(&mut sections, |(signer, _)| {
+ Ok(signer.public_key.to_vec().into_boxed_slice())
+ })
}
impl Signer {
- fn verify<R: Read + Seek>(&self, sections: &mut ApkSections<R>) -> Result<()> {
+ fn verify<R: Read + Seek>(&self, sections: &mut ApkSections<R>) -> Result<Box<[u8]>> {
// 1. Choose the strongest supported signature algorithm ID from signatures. The strength
// ordering is up to each implementation/platform version.
let strongest: &Signature = self
@@ -181,7 +191,7 @@
}
// 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(())
+ Ok(self.public_key.to_vec().into_boxed_slice())
}
}
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 73983a7..e431f51 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -320,7 +320,7 @@
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct ApkData {
pub root_hash: Box<RootHash>,
- // TODO(b/199143508) add cert
+ pub pubkey: Box<[u8]>,
}
pub type RootHash = [u8];
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 204feab..ac7adc9 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -20,7 +20,7 @@
use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
use anyhow::{anyhow, bail, ensure, Context, Result};
-use apkverify::verify;
+use apkverify::{get_public_key_der, verify};
use binder::unstable_api::{new_spibinder, AIBinder};
use binder::{FromIBinder, Strong};
use idsig::V4Signature;
@@ -187,16 +187,18 @@
// taken only when the root_hash is un-trustful which can be either when this is the first boot
// of the VM or APK was updated in the host.
// TODO(jooyung): consider multithreading to make this faster
- if !root_hash_trustful {
- verify(DM_MOUNTED_APK_PATH).context(format!("failed to verify {}", DM_MOUNTED_APK_PATH))?;
- }
+ let apk_pubkey = if !root_hash_trustful {
+ verify(DM_MOUNTED_APK_PATH).context(format!("failed to verify {}", DM_MOUNTED_APK_PATH))?
+ } else {
+ get_public_key_der(DM_MOUNTED_APK_PATH)?
+ };
info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
// At this point, we can ensure that the root_hash from the idsig file is trusted, either by
// fully verifying the APK or by comparing it with the saved root_hash.
Ok(MicrodroidData {
- apk_data: ApkData { root_hash: root_hash_from_idsig },
+ apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: apk_pubkey },
apex_data: apex_data_from_payload,
})
}