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