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 = if cfg!(llpvm_changes) {
57         hidden_input_from_instance_id()?
58     } else {
59         instance_data.salt.clone().try_into().unwrap()
60     };
61     dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
62 }
63 
64 // Get the "Hidden input" for DICE derivation.
65 // This provides differentiation of secrets for different VM instances with same payload.
hidden_input_from_instance_id() -> Result<Hidden>66 fn hidden_input_from_instance_id() -> Result<Hidden> {
67     // For protected VM: this is all 0s, pvmfw ensures differentiation is added early in secrets.
68     // For non-protected VM: this is derived from instance_id of the VM instance.
69     let hidden_input = if !is_strict_boot() {
70         if let Some(id) = super::get_instance_id()? {
71             sha512(&id)
72         } else {
73             // TODO(b/325094712): Absence of instance_id occurs due to missing DT in some
74             // x86_64 test devices (such as Cuttlefish). From security perspective, this is
75             // acceptable for non-protected VM.
76             log::warn!(
77                 "Instance Id missing, this may lead to 2 non protected VMs having same secrets"
78             );
79             [0u8; HIDDEN_SIZE]
80         }
81     } else {
82         [0u8; HIDDEN_SIZE]
83     };
84     Ok(hidden_input)
85 }
86 
87 struct Subcomponent {
88     name: String,
89     version: u64,
90     code_hash: Vec<u8>,
91     authority_hash: Vec<u8>,
92 }
93 
94 impl Subcomponent {
into_value(self) -> Result<Value>95     fn into_value(self) -> Result<Value> {
96         Ok(cbor!({
97            1 => self.name,
98            2 => self.version,
99            3 => Value::Bytes(self.code_hash),
100            4 => Value::Bytes(self.authority_hash),
101         })?)
102     }
103 
for_apk(apk: &ApkData) -> Self104     fn for_apk(apk: &ApkData) -> Self {
105         Self {
106             name: format!("apk:{}", apk.package_name),
107             version: apk.version_code,
108             code_hash: apk.root_hash.clone(),
109             authority_hash: apk.cert_hash.clone(),
110         }
111     }
112 
for_apex(apex: &ApexData) -> Self113     fn for_apex(apex: &ApexData) -> Self {
114         // Note that this is only reachable if the dice_changes flag is on, in which case
115         // the manifest data will always be present.
116         Self {
117             name: format!("apex:{}", apex.manifest_name.as_ref().unwrap()),
118             version: apex.manifest_version.unwrap() as u64,
119             code_hash: apex.root_digest.clone(),
120             authority_hash: sha512(&apex.public_key).to_vec(),
121         }
122     }
123 }
124 
build_subcomponent_list(instance_data: &MicrodroidData) -> Vec<Subcomponent>125 fn build_subcomponent_list(instance_data: &MicrodroidData) -> Vec<Subcomponent> {
126     if !cfg!(dice_changes) {
127         return vec![];
128     }
129 
130     let apks = once(&instance_data.apk_data)
131         .chain(&instance_data.extra_apks_data)
132         .map(Subcomponent::for_apk);
133     let apexes = instance_data.apex_data.iter().map(Subcomponent::for_apex);
134     apks.chain(apexes).collect()
135 }
136 
137 // Returns a configuration descriptor of the given payload. See vm_config.cddl for the definition
138 // of the format.
format_payload_config_descriptor( payload: &PayloadMetadata, subcomponents: Vec<Subcomponent>, ) -> Result<Vec<u8>>139 fn format_payload_config_descriptor(
140     payload: &PayloadMetadata,
141     subcomponents: Vec<Subcomponent>,
142 ) -> Result<Vec<u8>> {
143     let mut map = Vec::new();
144     map.push((cbor!(-70002)?, cbor!("Microdroid payload")?));
145     map.push(match payload {
146         PayloadMetadata::ConfigPath(payload_config_path) => {
147             (cbor!(-71000)?, cbor!(payload_config_path)?)
148         }
149         PayloadMetadata::Config(payload_config) => {
150             (cbor!(-71001)?, cbor!({1 => payload_config.payload_binary_name})?)
151         }
152         _ => bail!("Failed to match the payload against a config type: {:?}", payload),
153     });
154 
155     if !subcomponents.is_empty() {
156         let values =
157             subcomponents.into_iter().map(Subcomponent::into_value).collect::<Result<Vec<_>>>()?;
158         map.push((cbor!(-71002)?, cbor!(values)?));
159     }
160 
161     Ok(Value::Map(map).to_vec()?)
162 }
163 
164 #[cfg(test)]
165 mod tests {
166     use super::*;
167     use microdroid_metadata::PayloadConfig;
168 
169     const NO_SUBCOMPONENTS: Vec<Subcomponent> = Vec::new();
170 
assert_eq_bytes(expected: &[u8], actual: &[u8])171     fn assert_eq_bytes(expected: &[u8], actual: &[u8]) {
172         assert_eq!(
173             expected,
174             actual,
175             "Expected {}, got {}",
176             hex::encode(expected),
177             hex::encode(actual)
178         )
179     }
180 
181     #[test]
payload_metadata_with_path_formats_correctly() -> Result<()>182     fn payload_metadata_with_path_formats_correctly() -> Result<()> {
183         let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
184         let config_descriptor =
185             format_payload_config_descriptor(&payload_metadata, NO_SUBCOMPONENTS)?;
186         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
187             0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
188             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
189             0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
190             0x68,
191         ];
192         assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
193         Ok(())
194     }
195 
196     #[test]
payload_metadata_with_config_formats_correctly() -> Result<()>197     fn payload_metadata_with_config_formats_correctly() -> Result<()> {
198         let payload_config = PayloadConfig {
199             payload_binary_name: "payload_binary".to_string(),
200             ..Default::default()
201         };
202         let payload_metadata = PayloadMetadata::Config(payload_config);
203         let config_descriptor =
204             format_payload_config_descriptor(&payload_metadata, NO_SUBCOMPONENTS)?;
205         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
206             0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
207             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
208             0x15, 0x58, 0xa1, 0x01, 0x6e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62,
209             0x69, 0x6e, 0x61, 0x72, 0x79,
210         ];
211         assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
212         Ok(())
213     }
214 
215     #[test]
payload_metadata_with_subcomponents_formats_correctly() -> Result<()>216     fn payload_metadata_with_subcomponents_formats_correctly() -> Result<()> {
217         let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
218         let subcomponents = vec![
219             Subcomponent {
220                 name: "apk1".to_string(),
221                 version: 1,
222                 code_hash: vec![42, 43],
223                 authority_hash: vec![17],
224             },
225             Subcomponent {
226                 name: "apk2".to_string(),
227                 version: 0x1000_0000_0001,
228                 code_hash: vec![43],
229                 authority_hash: vec![19, 20],
230             },
231         ];
232         let config_descriptor = format_payload_config_descriptor(&payload_metadata, subcomponents)?;
233         // Verified using cbor.me.
234         static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
235             0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
236             0x6f, 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01,
237             0x15, 0x57, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x61, 0x74,
238             0x68, 0x3a, 0x00, 0x01, 0x15, 0x59, 0x82, 0xa4, 0x01, 0x64, 0x61, 0x70, 0x6b, 0x31,
239             0x02, 0x01, 0x03, 0x42, 0x2a, 0x2b, 0x04, 0x41, 0x11, 0xa4, 0x01, 0x64, 0x61, 0x70,
240             0x6b, 0x32, 0x02, 0x1b, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x41,
241             0x2b, 0x04, 0x42, 0x13, 0x14,
242         ];
243         assert_eq_bytes(EXPECTED_CONFIG_DESCRIPTOR, &config_descriptor);
244         Ok(())
245     }
246 }
247