• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::instance::{ApexData, ApkData};
16 use crate::{is_debuggable, is_strict_boot, MicrodroidData};
17 use anyhow::{bail, Context, Result};
18 use ciborium::{cbor, Value};
19 use coset::CborSerializable;
20 use dice_driver::DiceDriver;
21 use diced_open_dice::{Hidden, OwnedDiceArtifacts, HIDDEN_SIZE};
22 use microdroid_metadata::PayloadMetadata;
23 use openssl::sha::{sha512, Sha512};
24 use std::iter::once;
25 
26 /// Perform an open DICE derivation for the payload.
dice_derivation( dice: DiceDriver, instance_data: &MicrodroidData, payload_metadata: &PayloadMetadata, ) -> Result<OwnedDiceArtifacts>27 pub fn dice_derivation(
28     dice: DiceDriver,
29     instance_data: &MicrodroidData,
30     payload_metadata: &PayloadMetadata,
31 ) -> Result<OwnedDiceArtifacts> {
32     let subcomponents = build_subcomponent_list(instance_data);
33     let config_descriptor = format_payload_config_descriptor(payload_metadata, subcomponents)
34         .context("Building config descriptor")?;
35 
36     // Calculate compound digests of code and authorities
37     let mut code_hash_ctx = Sha512::new();
38     let mut authority_hash_ctx = Sha512::new();
39     code_hash_ctx.update(instance_data.apk_data.root_hash.as_ref());
40     authority_hash_ctx.update(instance_data.apk_data.cert_hash.as_ref());
41     for extra_apk in &instance_data.extra_apks_data {
42         code_hash_ctx.update(extra_apk.root_hash.as_ref());
43         authority_hash_ctx.update(extra_apk.cert_hash.as_ref());
44     }
45     for apex in &instance_data.apex_data {
46         code_hash_ctx.update(apex.root_digest.as_ref());
47         authority_hash_ctx.update(apex.public_key.as_ref());
48     }
49     let code_hash = code_hash_ctx.finish();
50     let authority_hash = authority_hash_ctx.finish();
51 
52     // Check debuggability, conservatively assuming it is debuggable
53     let debuggable = is_debuggable()?;
54 
55     // Send the details to diced
56     let hidden = hidden_input_from_instance_id()?;
57     dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
58 }
59 
60 // Get the "Hidden input" for DICE derivation.
61 // This provides differentiation of secrets for different VM instances with same payload.
hidden_input_from_instance_id() -> Result<Hidden>62 fn hidden_input_from_instance_id() -> Result<Hidden> {
63     // For protected VM: this is all 0s, pvmfw ensures differentiation is added early in secrets.
64     // For non-protected VM: this is derived from instance_id of the VM instance.
65     let hidden_input = if !is_strict_boot() {
66         if let Some(id) = super::get_instance_id()? {
67             sha512(&id)
68         } else {
69             // TODO(b/325094712): Absence of instance_id occurs due to missing DT in some
70             // x86_64 test devices (such as Cuttlefish). From security perspective, this is
71             // acceptable for non-protected VM.
72             log::warn!(
73                 "Instance Id missing, this may lead to 2 non protected VMs having same secrets"
74             );
75             [0u8; HIDDEN_SIZE]
76         }
77     } else {
78         [0u8; HIDDEN_SIZE]
79     };
80     Ok(hidden_input)
81 }
82 
83 struct Subcomponent {
84     name: String,
85     version: u64,
86     code_hash: Vec<u8>,
87     authority_hash: Vec<u8>,
88 }
89 
90 impl Subcomponent {
into_value(self) -> Result<Value>91     fn into_value(self) -> Result<Value> {
92         Ok(cbor!({
93            1 => self.name,
94            2 => self.version,
95            3 => Value::Bytes(self.code_hash),
96            4 => Value::Bytes(self.authority_hash),
97         })?)
98     }
99 
for_apk(apk: &ApkData) -> Self100     fn for_apk(apk: &ApkData) -> Self {
101         Self {
102             name: format!("apk:{}", apk.package_name),
103             // Ideally we would want to log both rollback_index and apk version code in dice. There
104             // is even a separate field called security_version_code, but it looks like it is not
105             // used in subcomponents, so for now log the rollback index as version code.
106             version: apk.rollback_index.map(u64::from).unwrap_or(apk.version_code),
107             code_hash: apk.root_hash.clone(),
108             authority_hash: apk.cert_hash.clone(),
109         }
110     }
111 
for_apex(apex: &ApexData) -> Self112     fn for_apex(apex: &ApexData) -> Self {
113         // Note that this is only reachable if the dice_changes flag is on, in which case
114         // the manifest data will always be present.
115         Self {
116             name: format!("apex:{}", apex.manifest_name.as_ref().unwrap()),
117             version: apex.manifest_version.unwrap() as u64,
118             code_hash: apex.root_digest.clone(),
119             authority_hash: sha512(&apex.public_key).to_vec(),
120         }
121     }
122 }
123 
build_subcomponent_list(instance_data: &MicrodroidData) -> Vec<Subcomponent>124 fn build_subcomponent_list(instance_data: &MicrodroidData) -> Vec<Subcomponent> {
125     if !cfg!(dice_changes) {
126         return vec![];
127     }
128 
129     let apks = once(&instance_data.apk_data)
130         .chain(&instance_data.extra_apks_data)
131         .map(Subcomponent::for_apk);
132     let apexes = instance_data.apex_data.iter().map(Subcomponent::for_apex);
133     apks.chain(apexes).collect()
134 }
135 
136 // Returns a configuration descriptor of the given payload. See dice_for_avf_guest.cddl for the
137 // definition of the format.
format_payload_config_descriptor( payload: &PayloadMetadata, subcomponents: Vec<Subcomponent>, ) -> Result<Vec<u8>>138 fn format_payload_config_descriptor(
139     payload: &PayloadMetadata,
140     subcomponents: Vec<Subcomponent>,
141 ) -> Result<Vec<u8>> {
142     let mut map = Vec::new();
143     map.push((cbor!(-70002)?, cbor!("Microdroid payload")?));
144     map.push(match payload {
145         PayloadMetadata::ConfigPath(payload_config_path) => {
146             (cbor!(-71000)?, cbor!(payload_config_path)?)
147         }
148         PayloadMetadata::Config(payload_config) => {
149             (cbor!(-71001)?, cbor!({1 => payload_config.payload_binary_name})?)
150         }
151         _ => bail!("Failed to match the payload against a config type: {:?}", payload),
152     });
153 
154     if !subcomponents.is_empty() {
155         let values =
156             subcomponents.into_iter().map(Subcomponent::into_value).collect::<Result<Vec<_>>>()?;
157         map.push((cbor!(-71002)?, cbor!(values)?));
158     }
159     // Add a placeholder security version as it is required by the open-dice profile "Android.16".
160     // Note: The DICE certificate derived in microdroid_manager primarily describes the APKs/APEXs
161     // loaded by microdroid_manager. Each APK/APEX is described separately with its own security
162     // version as a subcomponent within the certificate's config descriptor.
163     // Therefore, the global security version below (for the entire certificate) is unused.
164     map.push((cbor!(-70005)?, cbor!(0)?));
165     Ok(Value::Map(map).to_vec()?)
166 }
167 
168 #[cfg(test)]
169 mod tests {
170     use super::*;
171     use microdroid_metadata::PayloadConfig;
172 
173     const NO_SUBCOMPONENTS: Vec<Subcomponent> = Vec::new();
174 
assert_eq_bytes(expected: &[u8], actual: &[u8])175     fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
176         assert_eq!(
177             expected,
178             actual,
179             "Expected {}, got {}",
180             hex::encode(expected),
181             hex::encode(actual)
182         )
183     }
184 
185     #[test]
payload_metadata_with_path_formats_correctly() -> Result<()>186     fn payload_metadata_with_path_formats_correctly() -> Result<()> {
187         let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
188         let config_descriptor =
189             format_payload_config_descriptor(&payload_metadata, NO_SUBCOMPONENTS)?;
190         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
191             0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
192             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
193             0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
194             0x68, 0x3a, 0x00, 0x01, 0x11, 0x74, 0x00,
195         ];
196         assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
197         Ok(())
198     }
199 
200     #[test]
payload_metadata_with_config_formats_correctly() -> Result<()>201     fn payload_metadata_with_config_formats_correctly() -> Result<()> {
202         let payload_config = PayloadConfig {
203             payload_binary_name: "payload_binary".to_string(),
204             ..Default::default()
205         };
206         let payload_metadata = PayloadMetadata::Config(payload_config);
207         let config_descriptor =
208             format_payload_config_descriptor(&payload_metadata, NO_SUBCOMPONENTS)?;
209         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
210             0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
211             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
212             0x15, 0x58, 0xa1, 0x01, 0x6e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62,
213             0x69, 0x6e, 0x61, 0x72, 0x79, 0x3a, 0x00, 0x01, 0x11, 0x74, 0x00,
214         ];
215         assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
216         Ok(())
217     }
218 
219     #[test]
payload_metadata_with_subcomponents_formats_correctly() -> Result<()>220     fn payload_metadata_with_subcomponents_formats_correctly() -> Result<()> {
221         let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
222         let subcomponents = vec![
223             Subcomponent {
224                 name: "apk1".to_string(),
225                 version: 1,
226                 code_hash: vec![42, 43],
227                 authority_hash: vec![17],
228             },
229             Subcomponent {
230                 name: "apk2".to_string(),
231                 version: 0x1000_0000_0001,
232                 code_hash: vec![43],
233                 authority_hash: vec![19, 20],
234             },
235         ];
236         let config_descriptor = format_payload_config_descriptor(&payload_metadata, subcomponents)?;
237         // Verified using cbor.me.
238         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
239             0xa4, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
240             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
241             0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
242             0x68, 0x3a, 0x00, 0x01, 0x15, 0x59, 0x82, 0xa4, 0x01, 0x64, 0x61, 0x70, 0x6b, 0x31,
243             0x02, 0x01, 0x03, 0x42, 0x2a, 0x2b, 0x04, 0x41, 0x11, 0xa4, 0x01, 0x64, 0x61, 0x70,
244             0x6b, 0x32, 0x02, 0x1b, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x41,
245             0x2b, 0x04, 0x42, 0x13, 0x14, 0x3a, 0x00, 0x01, 0x11, 0x74, 0x00,
246         ];
247         assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
248         Ok(())
249     }
250 }
251