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