Add APK details to the DICE chain
Include the information from the manifest in ApkData (which also means
it is persisted in the instance image - which is not necessary but
harmless).
Add a CDDL file descibing what the VM config descriptor looks like.
Add information about the APKs to the Microdroid payload configuration
descriptor.
Also make some formatting fixes to open_dice error messages (since I
managed to trigger some).
Bug: 299591171
Test: composd_cmd test-compile; manually inspect BCC
Test: atest MicrodroidTests
Test: atest microdroid_manager_test
Change-Id: Iad7e8407cd9ad1d6715806aa4479641b9b9173cf
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index a576416..6b0775a 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -13,12 +13,15 @@
// limitations under the License.
use crate::dice_driver::DiceDriver;
+use crate::instance::ApkData;
use crate::{is_debuggable, MicrodroidData};
use anyhow::{bail, Context, Result};
-use ciborium::{cbor, ser};
+use ciborium::{cbor, Value};
+use coset::CborSerializable;
use diced_open_dice::OwnedDiceArtifacts;
use microdroid_metadata::PayloadMetadata;
-use openssl::sha::Sha512;
+use openssl::sha::{sha512, Sha512};
+use std::iter::once;
/// Perform an open DICE derivation for the payload.
pub fn dice_derivation(
@@ -26,6 +29,11 @@
verified_data: &MicrodroidData,
payload_metadata: &PayloadMetadata,
) -> Result<OwnedDiceArtifacts> {
+ let subcomponents = build_subcomponent_list(verified_data);
+
+ let config_descriptor = format_payload_config_descriptor(payload_metadata, &subcomponents)
+ .context("Building config descriptor")?;
+
// Calculate compound digests of code and authorities
let mut code_hash_ctx = Sha512::new();
let mut authority_hash_ctx = Sha512::new();
@@ -42,8 +50,6 @@
let code_hash = code_hash_ctx.finish();
let authority_hash = authority_hash_ctx.finish();
- let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
-
// Check debuggability, conservatively assuming it is debuggable
let debuggable = is_debuggable()?;
@@ -52,35 +58,71 @@
dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
}
-/// Returns a configuration descriptor of the given payload following the BCC's specification:
-/// https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
-/// {
-/// -70002: "Microdroid payload",
-/// ? -71000: tstr ; payload_config_path
-/// ? -71001: PayloadConfig
-/// }
-/// PayloadConfig = {
-/// 1: tstr ; payload_binary_name
-/// }
-fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
- const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
+struct Subcomponent<'a> {
+ name: String,
+ version: u64,
+ code_hash: &'a [u8],
+ authority_hash: Box<[u8]>,
+}
- let config_descriptor_cbor_value = match payload {
- PayloadMetadata::ConfigPath(payload_config_path) => cbor!({
- -70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
- -71000 => payload_config_path
- }),
- PayloadMetadata::Config(payload_config) => cbor!({
- -70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
- -71001 => {1 => payload_config.payload_binary_name}
- }),
- _ => bail!("Failed to match the payload against a config type: {:?}", payload),
+impl<'a> Subcomponent<'a> {
+ fn to_value(&self) -> Result<Value> {
+ Ok(cbor!({
+ 1 => self.name,
+ 2 => self.version,
+ 3 => self.code_hash,
+ 4 => self.authority_hash
+ })?)
}
- .context("Failed to build a CBOR Value from payload metadata")?;
- let mut config_descriptor = Vec::new();
- ser::into_writer(&config_descriptor_cbor_value, &mut config_descriptor)?;
- Ok(config_descriptor)
+ fn for_apk(apk: &'a ApkData) -> Self {
+ Self {
+ name: format!("apk:{}", apk.package_name),
+ version: apk.version_code,
+ code_hash: &apk.root_hash,
+ authority_hash:
+ // TODO(b/305925597): Hash the certificate not the pubkey
+ Box::new(sha512(&apk.pubkey)),
+ }
+ }
+}
+
+fn build_subcomponent_list(verified_data: &MicrodroidData) -> Vec<Subcomponent> {
+ if !cfg!(dice_changes) {
+ return vec![];
+ }
+
+ once(&verified_data.apk_data)
+ .chain(&verified_data.extra_apks_data)
+ .map(Subcomponent::for_apk)
+ .collect()
+}
+
+// Returns a configuration descriptor of the given payload. See vm_config.cddl for a definition
+// of the format.
+fn format_payload_config_descriptor(
+ payload: &PayloadMetadata,
+ subcomponents: &[Subcomponent],
+) -> Result<Vec<u8>> {
+ let mut map = Vec::new();
+ map.push((cbor!(-70002)?, cbor!("Microdroid payload")?));
+ map.push(match payload {
+ PayloadMetadata::ConfigPath(payload_config_path) => {
+ (cbor!(-71000)?, cbor!(payload_config_path)?)
+ }
+ PayloadMetadata::Config(payload_config) => {
+ (cbor!(-71001)?, cbor!({1 => payload_config.payload_binary_name})?)
+ }
+ _ => bail!("Failed to match the payload against a config type: {:?}", payload),
+ });
+
+ if !subcomponents.is_empty() {
+ let values =
+ subcomponents.iter().map(Subcomponent::to_value).collect::<Result<Vec<_>>>()?;
+ map.push((cbor!(-71002)?, cbor!(values)?));
+ }
+
+ Ok(Value::Map(map).to_vec()?)
}
#[cfg(test)]
@@ -88,17 +130,30 @@
use super::*;
use microdroid_metadata::PayloadConfig;
+ const NO_SUBCOMPONENTS: [Subcomponent; 0] = [];
+
+ fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
+ assert_eq!(
+ expected,
+ actual,
+ "Expected {}, got {}",
+ hex::encode(expected),
+ hex::encode(actual)
+ )
+ }
+
#[test]
fn payload_metadata_with_path_formats_correctly() -> Result<()> {
let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
- let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
+ let config_descriptor =
+ format_payload_config_descriptor(&payload_metadata, &NO_SUBCOMPONENTS)?;
static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
0x68,
];
- assert_eq!(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+ assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
Ok(())
}
@@ -109,14 +164,48 @@
..Default::default()
};
let payload_metadata = PayloadMetadata::Config(payload_config);
- let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
+ let config_descriptor =
+ format_payload_config_descriptor(&payload_metadata, &NO_SUBCOMPONENTS)?;
static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
0x15, 0x58, 0xa1, 0x01, 0x6e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62,
0x69, 0x6e, 0x61, 0x72, 0x79,
];
- assert_eq!(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+ assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
+ Ok(())
+ }
+
+ #[test]
+ fn payload_metadata_with_subcomponents_formats_correctly() -> Result<()> {
+ let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
+ let subcomponents = [
+ Subcomponent {
+ name: "apk1".to_string(),
+ version: 1,
+ code_hash: &[42u8],
+ authority_hash: Box::new([17u8]),
+ },
+ Subcomponent {
+ name: "apk2".to_string(),
+ version: 0x1000_0000_0001,
+ code_hash: &[43u8],
+ authority_hash: Box::new([19u8]),
+ },
+ ];
+ let config_descriptor =
+ format_payload_config_descriptor(&payload_metadata, &subcomponents)?;
+ // Verified using cbor.me.
+ static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
+ 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
+ 0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
+ 0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
+ 0x68, 0x3a, 0x00, 0x01, 0x15, 0x59, 0x82, 0xa4, 0x01, 0x64, 0x61, 0x70, 0x6b, 0x31,
+ 0x02, 0x01, 0x03, 0x81, 0x18, 0x2a, 0x04, 0x81, 0x11, 0xa4, 0x01, 0x64, 0x61, 0x70,
+ 0x6b, 0x32, 0x02, 0x1b, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x81,
+ 0x18, 0x2b, 0x04, 0x81, 0x13,
+ ];
+ assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
Ok(())
}
}