blob: 8c804da6d84bcdb23ea4b4f4456814e640eb0e32 [file] [log] [blame]
Alice Wangd3a96402023-11-24 15:37:39 +00001// 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//! This module contains functions related to DICE.
16
Alice Wang1cc13502023-12-05 11:05:34 +000017use alloc::string::String;
Alice Wangd3a96402023-11-24 15:37:39 +000018use alloc::vec::Vec;
Alice Wangdd29c5d2023-12-07 09:56:23 +000019use cbor_util::{
20 cbor_value_type, value_to_array, value_to_byte_array, value_to_bytes, value_to_map,
21 value_to_num, value_to_text,
22};
23use ciborium::value::Value;
Alice Wangd3a96402023-11-24 15:37:39 +000024use core::cell::OnceCell;
25use core::result;
26use coset::{
27 self, iana, AsCborValue, CborSerializable, CoseError, CoseKey, CoseSign1, KeyOperation,
28};
29use diced_open_dice::{DiceMode, HASH_SIZE};
30use log::error;
Alice Wangdd29c5d2023-12-07 09:56:23 +000031use service_vm_comm::RequestProcessingError;
Alice Wangd3a96402023-11-24 15:37:39 +000032
33type Result<T> = result::Result<T, RequestProcessingError>;
34
35const CODE_HASH: i64 = -4670545;
36const CONFIG_DESC: i64 = -4670548;
37const AUTHORITY_HASH: i64 = -4670549;
38const MODE: i64 = -4670551;
39const SUBJECT_PUBLIC_KEY: i64 = -4670552;
40
Alice Wang1cc13502023-12-05 11:05:34 +000041const CONFIG_DESC_COMPONENT_NAME: i64 = -70002;
42const CONFIG_DESC_SUB_COMPONENTS: i64 = -71002;
43
44const SUB_COMPONENT_NAME: i64 = 1;
45const SUB_COMPONENT_VERSION: i64 = 2;
46const SUB_COMPONENT_CODE_HASH: i64 = 3;
47const SUB_COMPONENT_AUTHORITY_HASH: i64 = 4;
48
49const MICRODROID_KERNEL_COMPONENT_NAME: &str = "vm_entry";
50const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
51
Alice Wangd3a96402023-11-24 15:37:39 +000052/// Represents a partially decoded `DiceCertChain` from the client VM.
53/// The whole chain is defined as following:
54///
55/// DiceCertChain = [
56/// PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384, ; UDS_Pub
57/// + DiceChainEntry, ; First CDI_Certificate -> Last CDI_Certificate
58/// ]
59#[derive(Debug, Clone)]
60pub(crate) struct ClientVmDiceChain {
61 pub(crate) payloads: Vec<DiceChainEntryPayload>,
62}
63
64impl ClientVmDiceChain {
65 /// Validates the signatures of the entries in the `client_vm_dice_chain` as following:
66 ///
67 /// - The first entry of the `client_vm_dice_chain` must be signed with the root public key.
68 /// - After the first entry, each entry of the `client_vm_dice_chain` must be signed with the
69 /// subject public key of the previous entry.
70 ///
71 /// Returns a partially decoded client VM's DICE chain if the verification succeeds.
72 pub(crate) fn validate_signatures_and_parse_dice_chain(
73 mut client_vm_dice_chain: Vec<Value>,
74 ) -> Result<Self> {
75 let root_public_key =
76 CoseKey::from_cbor_value(client_vm_dice_chain.remove(0))?.try_into()?;
77
78 let mut payloads = Vec::with_capacity(client_vm_dice_chain.len());
79 let mut previous_public_key = &root_public_key;
80 for (i, value) in client_vm_dice_chain.into_iter().enumerate() {
81 let payload = DiceChainEntryPayload::validate_cose_signature_and_extract_payload(
82 value,
83 previous_public_key,
84 )
85 .map_err(|e| {
86 error!("Failed to verify the DICE chain entry {}: {:?}", i, e);
87 e
88 })?;
89 payloads.push(payload);
90 previous_public_key = &payloads.last().unwrap().subject_public_key;
91 }
92 // After successfully calling `validate_client_vm_dice_chain_prefix_match`, we can be
93 // certain that the client VM's DICE chain must contain at least three entries that
94 // describe:
95 // - pvmfw
96 // - Microdroid kernel
97 // - Apk/Apexes
98 assert!(
99 payloads.len() >= 3,
100 "The client VM DICE chain must contain at least three DiceChainEntryPayloads"
101 );
Alice Wang1cc13502023-12-05 11:05:34 +0000102 let chain = Self { payloads };
103 chain.validate_microdroid_components_names()?;
104 Ok(chain)
105 }
106
107 fn validate_microdroid_components_names(&self) -> Result<()> {
108 let microdroid_kernel_name = &self.microdroid_kernel().config_descriptor.component_name;
109 if MICRODROID_KERNEL_COMPONENT_NAME != microdroid_kernel_name {
110 error!(
111 "The second to last entry in the client VM DICE chain must describe the \
112 Microdroid kernel. Got {}",
113 microdroid_kernel_name
114 );
115 return Err(RequestProcessingError::InvalidDiceChain);
116 }
117 let microdroid_payload_name = &self.microdroid_payload().config_descriptor.component_name;
118 if MICRODROID_PAYLOAD_COMPONENT_NAME != microdroid_payload_name {
119 error!(
120 "The last entry in the client VM DICE chain must describe the Microdroid \
121 payload. Got {}",
122 microdroid_payload_name
123 );
124 return Err(RequestProcessingError::InvalidDiceChain);
125 }
126 Ok(())
127 }
128
Alice Wang4e093102023-12-13 09:16:29 +0000129 pub(crate) fn microdroid_kernel(&self) -> &DiceChainEntryPayload {
Alice Wang1cc13502023-12-05 11:05:34 +0000130 &self.payloads[self.payloads.len() - 2]
131 }
132
133 fn microdroid_payload(&self) -> &DiceChainEntryPayload {
134 &self.payloads[self.payloads.len() - 1]
135 }
136
137 pub(crate) fn microdroid_payload_components(&self) -> Option<&Vec<SubComponent>> {
138 self.microdroid_payload().config_descriptor.sub_components.as_ref()
Alice Wangd3a96402023-11-24 15:37:39 +0000139 }
140
141 /// Returns true if all payloads in the DICE chain are in normal mode.
142 pub(crate) fn all_entries_are_secure(&self) -> bool {
143 self.payloads.iter().all(|p| p.mode == DiceMode::kDiceModeNormal)
144 }
145}
146
147/// Validates that the `client_vm_dice_chain` matches the `service_vm_dice_chain` up to the pvmfw
148/// entry.
149///
Alice Wang4e093102023-12-13 09:16:29 +0000150/// Returns `Ok(())` if the verification succeeds.
Alice Wangd3a96402023-11-24 15:37:39 +0000151pub(crate) fn validate_client_vm_dice_chain_prefix_match(
Alice Wang4e093102023-12-13 09:16:29 +0000152 client_vm_dice_chain: &[Value],
153 service_vm_dice_chain: &[Value],
154) -> Result<()> {
Alice Wangd3a96402023-11-24 15:37:39 +0000155 if service_vm_dice_chain.len() < 3 {
156 // The service VM's DICE chain must contain the root key and at least two other entries
157 // that describe:
158 // - pvmfw
159 // - Service VM kernel
160 error!("The service VM DICE chain must contain at least three entries");
161 return Err(RequestProcessingError::InternalError);
162 }
163 // Ignores the last entry that describes service VM
164 let entries_up_to_pvmfw = &service_vm_dice_chain[0..(service_vm_dice_chain.len() - 1)];
165 if entries_up_to_pvmfw.len() + 2 != client_vm_dice_chain.len() {
166 // Client VM DICE chain = entries_up_to_pvmfw
167 // + Microdroid kernel entry (added in pvmfw)
168 // + Apk/Apexes entry (added in microdroid)
169 error!("The client VM's DICE chain must contain exactly two extra entries");
170 return Err(RequestProcessingError::InvalidDiceChain);
171 }
172 if entries_up_to_pvmfw != &client_vm_dice_chain[0..entries_up_to_pvmfw.len()] {
173 error!(
174 "The client VM's DICE chain does not match service VM's DICE chain up to \
175 the pvmfw entry"
176 );
177 return Err(RequestProcessingError::InvalidDiceChain);
178 }
Alice Wang4e093102023-12-13 09:16:29 +0000179 Ok(())
Alice Wangd3a96402023-11-24 15:37:39 +0000180}
181
182#[derive(Debug, Clone)]
183pub(crate) struct PublicKey(CoseKey);
184
185impl TryFrom<CoseKey> for PublicKey {
186 type Error = RequestProcessingError;
187
188 fn try_from(key: CoseKey) -> Result<Self> {
189 if !key.key_ops.contains(&KeyOperation::Assigned(iana::KeyOperation::Verify)) {
190 error!("Public key does not support verification");
191 return Err(RequestProcessingError::InvalidDiceChain);
192 }
193 Ok(Self(key))
194 }
195}
196
197/// Represents a partially decoded `DiceChainEntryPayload`. The whole payload is defined in:
198///
199/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
200/// generateCertificateRequestV2.cddl
201#[derive(Debug, Clone)]
202pub(crate) struct DiceChainEntryPayload {
203 /// TODO(b/310931749): Verify the DICE chain entry using the subject public key.
204 #[allow(dead_code)]
205 subject_public_key: PublicKey,
206 mode: DiceMode,
Alice Wang4e093102023-12-13 09:16:29 +0000207 pub(crate) code_hash: [u8; HASH_SIZE],
208 pub(crate) authority_hash: [u8; HASH_SIZE],
Alice Wang1cc13502023-12-05 11:05:34 +0000209 config_descriptor: ConfigDescriptor,
Alice Wangd3a96402023-11-24 15:37:39 +0000210}
211
212impl DiceChainEntryPayload {
213 /// Validates the signature of the provided CBOR value with the provided public key and
214 /// extracts payload from the value.
215 fn validate_cose_signature_and_extract_payload(
216 value: Value,
217 _authority_public_key: &PublicKey,
218 ) -> Result<Self> {
219 let cose_sign1 = CoseSign1::from_cbor_value(value)?;
220 // TODO(b/310931749): Verify the DICE chain entry using `authority_public_key`.
221
222 let payload = cose_sign1.payload.ok_or_else(|| {
223 error!("No payload found in the DICE chain entry");
224 RequestProcessingError::InvalidDiceChain
225 })?;
Alice Wang4e093102023-12-13 09:16:29 +0000226 Self::from_slice(&payload)
Alice Wangd3a96402023-11-24 15:37:39 +0000227 }
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000228
Alice Wang4e093102023-12-13 09:16:29 +0000229 pub(crate) fn from_slice(data: &[u8]) -> Result<Self> {
230 let entries = value_to_map(Value::from_slice(data)?, "DiceChainEntryPayload")?;
231 let mut builder = PayloadBuilder::default();
232 for (key, value) in entries.into_iter() {
233 let key: i64 = value_to_num(key, "DiceChainEntryPayload key")?;
234 match key {
235 SUBJECT_PUBLIC_KEY => {
236 let subject_public_key = value_to_bytes(value, "subject_public_key")?;
237 let subject_public_key =
238 CoseKey::from_slice(&subject_public_key)?.try_into()?;
239 builder.subject_public_key(subject_public_key)?;
240 }
241 MODE => builder.mode(to_mode(value)?)?,
242 CODE_HASH => {
243 let code_hash = value_to_byte_array(value, "DiceChainEntryPayload code_hash")?;
244 builder.code_hash(code_hash)?;
245 }
246 AUTHORITY_HASH => {
247 let authority_hash =
248 value_to_byte_array(value, "DiceChainEntryPayload authority_hash")?;
249 builder.authority_hash(authority_hash)?;
250 }
251 CONFIG_DESC => {
252 let config_descriptor = value_to_bytes(value, "config_descriptor")?;
253 let config_descriptor = ConfigDescriptor::from_slice(&config_descriptor)?;
254 builder.config_descriptor(config_descriptor)?;
255 }
256 _ => {}
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000257 }
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000258 }
Alice Wang4e093102023-12-13 09:16:29 +0000259 builder.build()
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000260 }
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000261}
Alice Wang1cc13502023-12-05 11:05:34 +0000262/// Represents a partially decoded `ConfigurationDescriptor`.
263///
264/// The whole `ConfigurationDescriptor` is defined in:
265///
266/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
267/// generateCertificateRequestV2.cddl
268#[derive(Debug, Clone)]
269pub(crate) struct ConfigDescriptor {
270 component_name: String,
271 sub_components: Option<Vec<SubComponent>>,
272}
273
274impl ConfigDescriptor {
275 fn from_slice(data: &[u8]) -> Result<Self> {
276 let value = Value::from_slice(data)?;
277 let entries = value_to_map(value, "ConfigDescriptor")?;
278 let mut builder = ConfigDescriptorBuilder::default();
279 for (key, value) in entries.into_iter() {
280 let key: i64 = value_to_num(key, "ConfigDescriptor key")?;
281 match key {
282 CONFIG_DESC_COMPONENT_NAME => {
283 let name = value_to_text(value, "ConfigDescriptor component_name")?;
284 builder.component_name(name)?;
285 }
286 CONFIG_DESC_SUB_COMPONENTS => {
287 let sub_components = value_to_array(value, "ConfigDescriptor sub_components")?;
288 let sub_components = sub_components
289 .into_iter()
290 .map(SubComponent::try_from)
291 .collect::<Result<Vec<_>>>()?;
292 builder.sub_components(sub_components)?
293 }
294 _ => {}
295 }
296 }
297 builder.build()
298 }
299}
300
301#[derive(Debug, Clone, Default)]
302struct ConfigDescriptorBuilder {
303 component_name: OnceCell<String>,
304 sub_components: OnceCell<Vec<SubComponent>>,
305}
306
307impl ConfigDescriptorBuilder {
308 fn component_name(&mut self, component_name: String) -> Result<()> {
309 set_once(&self.component_name, component_name, "ConfigDescriptor component_name")
310 }
311
312 fn sub_components(&mut self, sub_components: Vec<SubComponent>) -> Result<()> {
313 set_once(&self.sub_components, sub_components, "ConfigDescriptor sub_components")
314 }
315
316 fn build(mut self) -> Result<ConfigDescriptor> {
317 let component_name =
318 take_value(&mut self.component_name, "ConfigDescriptor component_name")?;
319 let sub_components = self.sub_components.take();
320 Ok(ConfigDescriptor { component_name, sub_components })
321 }
322}
323
324#[derive(Debug, Clone)]
325pub(crate) struct SubComponent {
326 pub(crate) name: String,
327 pub(crate) version: u64,
328 pub(crate) code_hash: Vec<u8>,
329 pub(crate) authority_hash: Vec<u8>,
330}
331
332impl TryFrom<Value> for SubComponent {
333 type Error = RequestProcessingError;
334
335 fn try_from(value: Value) -> Result<Self> {
336 let entries = value_to_map(value, "SubComponent")?;
337 let mut builder = SubComponentBuilder::default();
338 for (key, value) in entries.into_iter() {
339 let key: i64 = value_to_num(key, "SubComponent key")?;
340 match key {
341 SUB_COMPONENT_NAME => {
342 builder.name(value_to_text(value, "SubComponent component_name")?)?
343 }
344 SUB_COMPONENT_VERSION => {
345 builder.version(value_to_num(value, "SubComponent version")?)?
346 }
347 SUB_COMPONENT_CODE_HASH => {
348 builder.code_hash(value_to_bytes(value, "SubComponent code_hash")?)?
349 }
350 SUB_COMPONENT_AUTHORITY_HASH => {
351 builder.authority_hash(value_to_bytes(value, "SubComponent authority_hash")?)?
352 }
353 k => {
354 error!("Unknown key in SubComponent: {}", k);
355 return Err(RequestProcessingError::InvalidDiceChain);
356 }
357 }
358 }
359 builder.build()
360 }
361}
362
363#[derive(Debug, Clone, Default)]
364struct SubComponentBuilder {
365 name: OnceCell<String>,
366 version: OnceCell<u64>,
367 code_hash: OnceCell<Vec<u8>>,
368 authority_hash: OnceCell<Vec<u8>>,
369}
370
371impl SubComponentBuilder {
372 fn name(&mut self, name: String) -> Result<()> {
373 set_once(&self.name, name, "SubComponent name")
374 }
375
376 fn version(&mut self, version: u64) -> Result<()> {
377 set_once(&self.version, version, "SubComponent version")
378 }
379
380 fn code_hash(&mut self, code_hash: Vec<u8>) -> Result<()> {
381 set_once(&self.code_hash, code_hash, "SubComponent code_hash")
382 }
383
384 fn authority_hash(&mut self, authority_hash: Vec<u8>) -> Result<()> {
385 set_once(&self.authority_hash, authority_hash, "SubComponent authority_hash")
386 }
387
388 fn build(mut self) -> Result<SubComponent> {
389 let name = take_value(&mut self.name, "SubComponent name")?;
390 let version = take_value(&mut self.version, "SubComponent version")?;
391 let code_hash = take_value(&mut self.code_hash, "SubComponent code_hash")?;
392 let authority_hash = take_value(&mut self.authority_hash, "SubComponent authority_hash")?;
393 Ok(SubComponent { name, version, code_hash, authority_hash })
394 }
395}
396
Alice Wangd3a96402023-11-24 15:37:39 +0000397fn to_mode(value: Value) -> Result<DiceMode> {
398 let mode = match value {
399 // Mode is supposed to be encoded as a 1-byte bstr, but some implementations instead
400 // encode it as an integer. Accept either. See b/273552826.
401 // If Mode is omitted, it should be treated as if it was NotConfigured, according to
402 // the Open Profile for DICE spec.
403 Value::Bytes(bytes) => {
404 if bytes.len() != 1 {
405 error!("Bytes array with invalid length for mode: {:?}", bytes.len());
406 return Err(RequestProcessingError::InvalidDiceChain);
407 }
408 bytes[0].into()
409 }
410 Value::Integer(i) => i,
411 v => return Err(CoseError::UnexpectedItem(cbor_value_type(&v), "bstr or int").into()),
412 };
413 let mode = match mode {
414 x if x == (DiceMode::kDiceModeNormal as i64).into() => DiceMode::kDiceModeNormal,
415 x if x == (DiceMode::kDiceModeDebug as i64).into() => DiceMode::kDiceModeDebug,
416 x if x == (DiceMode::kDiceModeMaintenance as i64).into() => DiceMode::kDiceModeMaintenance,
417 // If Mode is invalid, it should be treated as if it was NotConfigured, according to
418 // the Open Profile for DICE spec.
419 _ => DiceMode::kDiceModeNotInitialized,
420 };
421 Ok(mode)
422}
423
424#[derive(Default, Debug, Clone)]
425struct PayloadBuilder {
426 subject_public_key: OnceCell<PublicKey>,
427 mode: OnceCell<DiceMode>,
428 code_hash: OnceCell<[u8; HASH_SIZE]>,
429 authority_hash: OnceCell<[u8; HASH_SIZE]>,
Alice Wang1cc13502023-12-05 11:05:34 +0000430 config_descriptor: OnceCell<ConfigDescriptor>,
Alice Wangd3a96402023-11-24 15:37:39 +0000431}
432
433fn set_once<T>(field: &OnceCell<T>, value: T, field_name: &str) -> Result<()> {
434 field.set(value).map_err(|_| {
435 error!("Field '{field_name}' is duplicated in the Payload");
436 RequestProcessingError::InvalidDiceChain
437 })
438}
439
440fn take_value<T>(field: &mut OnceCell<T>, field_name: &str) -> Result<T> {
441 field.take().ok_or_else(|| {
442 error!("Field '{field_name}' is missing in the Payload");
443 RequestProcessingError::InvalidDiceChain
444 })
445}
446
447impl PayloadBuilder {
448 fn subject_public_key(&mut self, key: PublicKey) -> Result<()> {
449 set_once(&self.subject_public_key, key, "subject_public_key")
450 }
451
452 fn mode(&mut self, mode: DiceMode) -> Result<()> {
453 set_once(&self.mode, mode, "mode")
454 }
455
456 fn code_hash(&mut self, code_hash: [u8; HASH_SIZE]) -> Result<()> {
457 set_once(&self.code_hash, code_hash, "code_hash")
458 }
459
460 fn authority_hash(&mut self, authority_hash: [u8; HASH_SIZE]) -> Result<()> {
461 set_once(&self.authority_hash, authority_hash, "authority_hash")
462 }
463
Alice Wang1cc13502023-12-05 11:05:34 +0000464 fn config_descriptor(&mut self, config_descriptor: ConfigDescriptor) -> Result<()> {
Alice Wangd3a96402023-11-24 15:37:39 +0000465 set_once(&self.config_descriptor, config_descriptor, "config_descriptor")
466 }
467
468 fn build(mut self) -> Result<DiceChainEntryPayload> {
469 let subject_public_key = take_value(&mut self.subject_public_key, "subject_public_key")?;
470 // If Mode is omitted, it should be treated as if it was NotConfigured, according to
471 // the Open Profile for DICE spec.
472 let mode = self.mode.take().unwrap_or(DiceMode::kDiceModeNotInitialized);
473 let code_hash = take_value(&mut self.code_hash, "code_hash")?;
474 let authority_hash = take_value(&mut self.authority_hash, "authority_hash")?;
475 let config_descriptor = take_value(&mut self.config_descriptor, "config_descriptor")?;
476 Ok(DiceChainEntryPayload {
477 subject_public_key,
478 mode,
479 code_hash,
480 authority_hash,
481 config_descriptor,
482 })
483 }
484}