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