blob: 557b67860b8dbfa997152b7ec11337ed29b067de [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
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000129 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///
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000150/// Returns a CBOR value array of the client VM's DICE chain if the verification succeeds.
Alice Wangd3a96402023-11-24 15:37:39 +0000151pub(crate) fn validate_client_vm_dice_chain_prefix_match(
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000152 client_vm_dice_chain: &[u8],
153 service_vm_dice_chain: &[u8],
154) -> Result<Vec<Value>> {
155 let client_vm_dice_chain =
156 value_to_array(Value::from_slice(client_vm_dice_chain)?, "client_vm_dice_chain")?;
157 let service_vm_dice_chain =
158 value_to_array(Value::from_slice(service_vm_dice_chain)?, "service_vm_dice_chain")?;
Alice Wangd3a96402023-11-24 15:37:39 +0000159 if service_vm_dice_chain.len() < 3 {
160 // The service VM's DICE chain must contain the root key and at least two other entries
161 // that describe:
162 // - pvmfw
163 // - Service VM kernel
164 error!("The service VM DICE chain must contain at least three entries");
165 return Err(RequestProcessingError::InternalError);
166 }
167 // Ignores the last entry that describes service VM
168 let entries_up_to_pvmfw = &service_vm_dice_chain[0..(service_vm_dice_chain.len() - 1)];
169 if entries_up_to_pvmfw.len() + 2 != client_vm_dice_chain.len() {
170 // Client VM DICE chain = entries_up_to_pvmfw
171 // + Microdroid kernel entry (added in pvmfw)
172 // + Apk/Apexes entry (added in microdroid)
173 error!("The client VM's DICE chain must contain exactly two extra entries");
174 return Err(RequestProcessingError::InvalidDiceChain);
175 }
176 if entries_up_to_pvmfw != &client_vm_dice_chain[0..entries_up_to_pvmfw.len()] {
177 error!(
178 "The client VM's DICE chain does not match service VM's DICE chain up to \
179 the pvmfw entry"
180 );
181 return Err(RequestProcessingError::InvalidDiceChain);
182 }
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000183 Ok(client_vm_dice_chain)
Alice Wangd3a96402023-11-24 15:37:39 +0000184}
185
186#[derive(Debug, Clone)]
187pub(crate) struct PublicKey(CoseKey);
188
189impl TryFrom<CoseKey> for PublicKey {
190 type Error = RequestProcessingError;
191
192 fn try_from(key: CoseKey) -> Result<Self> {
193 if !key.key_ops.contains(&KeyOperation::Assigned(iana::KeyOperation::Verify)) {
194 error!("Public key does not support verification");
195 return Err(RequestProcessingError::InvalidDiceChain);
196 }
197 Ok(Self(key))
198 }
199}
200
201/// Represents a partially decoded `DiceChainEntryPayload`. The whole payload is defined in:
202///
203/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
204/// generateCertificateRequestV2.cddl
205#[derive(Debug, Clone)]
206pub(crate) struct DiceChainEntryPayload {
207 /// TODO(b/310931749): Verify the DICE chain entry using the subject public key.
208 #[allow(dead_code)]
209 subject_public_key: PublicKey,
210 mode: DiceMode,
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000211 /// TODO(b/271275206): Verify Microdroid kernel authority and code hashes.
212 #[allow(dead_code)]
213 code_hash: [u8; HASH_SIZE],
214 #[allow(dead_code)]
215 authority_hash: [u8; HASH_SIZE],
Alice Wang1cc13502023-12-05 11:05:34 +0000216 config_descriptor: ConfigDescriptor,
Alice Wangd3a96402023-11-24 15:37:39 +0000217}
218
219impl DiceChainEntryPayload {
220 /// Validates the signature of the provided CBOR value with the provided public key and
221 /// extracts payload from the value.
222 fn validate_cose_signature_and_extract_payload(
223 value: Value,
224 _authority_public_key: &PublicKey,
225 ) -> Result<Self> {
226 let cose_sign1 = CoseSign1::from_cbor_value(value)?;
227 // TODO(b/310931749): Verify the DICE chain entry using `authority_public_key`.
228
229 let payload = cose_sign1.payload.ok_or_else(|| {
230 error!("No payload found in the DICE chain entry");
231 RequestProcessingError::InvalidDiceChain
232 })?;
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000233 let entries = value_to_map(Value::from_slice(&payload)?, "DiceChainEntryPayload")?;
234 build_payload(entries)
Alice Wangd3a96402023-11-24 15:37:39 +0000235 }
Alice Wangd3a96402023-11-24 15:37:39 +0000236}
Ajinkya Chalkebd511762023-12-12 11:57:03 +0000237
238fn build_payload(entries: Vec<(Value, Value)>) -> Result<DiceChainEntryPayload> {
239 let mut builder = PayloadBuilder::default();
240 for (key, value) in entries.into_iter() {
241 let key: i64 = value_to_num(key, "DiceChainEntryPayload key")?;
242 match key {
243 SUBJECT_PUBLIC_KEY => {
244 let subject_public_key = value_to_bytes(value, "subject_public_key")?;
245 let subject_public_key = CoseKey::from_slice(&subject_public_key)?.try_into()?;
246 builder.subject_public_key(subject_public_key)?;
247 }
248 MODE => builder.mode(to_mode(value)?)?,
249 CODE_HASH => {
250 let code_hash = value_to_byte_array(value, "DiceChainEntryPayload code_hash")?;
251 builder.code_hash(code_hash)?;
252 }
253 AUTHORITY_HASH => {
254 let authority_hash =
255 value_to_byte_array(value, "DiceChainEntryPayload authority_hash")?;
256 builder.authority_hash(authority_hash)?;
257 }
258 CONFIG_DESC => {
259 let config_descriptor = value_to_bytes(value, "config_descriptor")?;
260 let config_descriptor = ConfigDescriptor::from_slice(&config_descriptor)?;
261 builder.config_descriptor(config_descriptor)?;
262 }
263 _ => {}
264 }
265 }
266 builder.build()
267}
268
Alice Wang1cc13502023-12-05 11:05:34 +0000269/// Represents a partially decoded `ConfigurationDescriptor`.
270///
271/// The whole `ConfigurationDescriptor` is defined in:
272///
273/// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
274/// generateCertificateRequestV2.cddl
275#[derive(Debug, Clone)]
276pub(crate) struct ConfigDescriptor {
277 component_name: String,
278 sub_components: Option<Vec<SubComponent>>,
279}
280
281impl ConfigDescriptor {
282 fn from_slice(data: &[u8]) -> Result<Self> {
283 let value = Value::from_slice(data)?;
284 let entries = value_to_map(value, "ConfigDescriptor")?;
285 let mut builder = ConfigDescriptorBuilder::default();
286 for (key, value) in entries.into_iter() {
287 let key: i64 = value_to_num(key, "ConfigDescriptor key")?;
288 match key {
289 CONFIG_DESC_COMPONENT_NAME => {
290 let name = value_to_text(value, "ConfigDescriptor component_name")?;
291 builder.component_name(name)?;
292 }
293 CONFIG_DESC_SUB_COMPONENTS => {
294 let sub_components = value_to_array(value, "ConfigDescriptor sub_components")?;
295 let sub_components = sub_components
296 .into_iter()
297 .map(SubComponent::try_from)
298 .collect::<Result<Vec<_>>>()?;
299 builder.sub_components(sub_components)?
300 }
301 _ => {}
302 }
303 }
304 builder.build()
305 }
306}
307
308#[derive(Debug, Clone, Default)]
309struct ConfigDescriptorBuilder {
310 component_name: OnceCell<String>,
311 sub_components: OnceCell<Vec<SubComponent>>,
312}
313
314impl ConfigDescriptorBuilder {
315 fn component_name(&mut self, component_name: String) -> Result<()> {
316 set_once(&self.component_name, component_name, "ConfigDescriptor component_name")
317 }
318
319 fn sub_components(&mut self, sub_components: Vec<SubComponent>) -> Result<()> {
320 set_once(&self.sub_components, sub_components, "ConfigDescriptor sub_components")
321 }
322
323 fn build(mut self) -> Result<ConfigDescriptor> {
324 let component_name =
325 take_value(&mut self.component_name, "ConfigDescriptor component_name")?;
326 let sub_components = self.sub_components.take();
327 Ok(ConfigDescriptor { component_name, sub_components })
328 }
329}
330
331#[derive(Debug, Clone)]
332pub(crate) struct SubComponent {
333 pub(crate) name: String,
334 pub(crate) version: u64,
335 pub(crate) code_hash: Vec<u8>,
336 pub(crate) authority_hash: Vec<u8>,
337}
338
339impl TryFrom<Value> for SubComponent {
340 type Error = RequestProcessingError;
341
342 fn try_from(value: Value) -> Result<Self> {
343 let entries = value_to_map(value, "SubComponent")?;
344 let mut builder = SubComponentBuilder::default();
345 for (key, value) in entries.into_iter() {
346 let key: i64 = value_to_num(key, "SubComponent key")?;
347 match key {
348 SUB_COMPONENT_NAME => {
349 builder.name(value_to_text(value, "SubComponent component_name")?)?
350 }
351 SUB_COMPONENT_VERSION => {
352 builder.version(value_to_num(value, "SubComponent version")?)?
353 }
354 SUB_COMPONENT_CODE_HASH => {
355 builder.code_hash(value_to_bytes(value, "SubComponent code_hash")?)?
356 }
357 SUB_COMPONENT_AUTHORITY_HASH => {
358 builder.authority_hash(value_to_bytes(value, "SubComponent authority_hash")?)?
359 }
360 k => {
361 error!("Unknown key in SubComponent: {}", k);
362 return Err(RequestProcessingError::InvalidDiceChain);
363 }
364 }
365 }
366 builder.build()
367 }
368}
369
370#[derive(Debug, Clone, Default)]
371struct SubComponentBuilder {
372 name: OnceCell<String>,
373 version: OnceCell<u64>,
374 code_hash: OnceCell<Vec<u8>>,
375 authority_hash: OnceCell<Vec<u8>>,
376}
377
378impl SubComponentBuilder {
379 fn name(&mut self, name: String) -> Result<()> {
380 set_once(&self.name, name, "SubComponent name")
381 }
382
383 fn version(&mut self, version: u64) -> Result<()> {
384 set_once(&self.version, version, "SubComponent version")
385 }
386
387 fn code_hash(&mut self, code_hash: Vec<u8>) -> Result<()> {
388 set_once(&self.code_hash, code_hash, "SubComponent code_hash")
389 }
390
391 fn authority_hash(&mut self, authority_hash: Vec<u8>) -> Result<()> {
392 set_once(&self.authority_hash, authority_hash, "SubComponent authority_hash")
393 }
394
395 fn build(mut self) -> Result<SubComponent> {
396 let name = take_value(&mut self.name, "SubComponent name")?;
397 let version = take_value(&mut self.version, "SubComponent version")?;
398 let code_hash = take_value(&mut self.code_hash, "SubComponent code_hash")?;
399 let authority_hash = take_value(&mut self.authority_hash, "SubComponent authority_hash")?;
400 Ok(SubComponent { name, version, code_hash, authority_hash })
401 }
402}
403
Alice Wangd3a96402023-11-24 15:37:39 +0000404fn to_mode(value: Value) -> Result<DiceMode> {
405 let mode = match value {
406 // Mode is supposed to be encoded as a 1-byte bstr, but some implementations instead
407 // encode it as an integer. Accept either. See b/273552826.
408 // If Mode is omitted, it should be treated as if it was NotConfigured, according to
409 // the Open Profile for DICE spec.
410 Value::Bytes(bytes) => {
411 if bytes.len() != 1 {
412 error!("Bytes array with invalid length for mode: {:?}", bytes.len());
413 return Err(RequestProcessingError::InvalidDiceChain);
414 }
415 bytes[0].into()
416 }
417 Value::Integer(i) => i,
418 v => return Err(CoseError::UnexpectedItem(cbor_value_type(&v), "bstr or int").into()),
419 };
420 let mode = match mode {
421 x if x == (DiceMode::kDiceModeNormal as i64).into() => DiceMode::kDiceModeNormal,
422 x if x == (DiceMode::kDiceModeDebug as i64).into() => DiceMode::kDiceModeDebug,
423 x if x == (DiceMode::kDiceModeMaintenance as i64).into() => DiceMode::kDiceModeMaintenance,
424 // If Mode is invalid, it should be treated as if it was NotConfigured, according to
425 // the Open Profile for DICE spec.
426 _ => DiceMode::kDiceModeNotInitialized,
427 };
428 Ok(mode)
429}
430
431#[derive(Default, Debug, Clone)]
432struct PayloadBuilder {
433 subject_public_key: OnceCell<PublicKey>,
434 mode: OnceCell<DiceMode>,
435 code_hash: OnceCell<[u8; HASH_SIZE]>,
436 authority_hash: OnceCell<[u8; HASH_SIZE]>,
Alice Wang1cc13502023-12-05 11:05:34 +0000437 config_descriptor: OnceCell<ConfigDescriptor>,
Alice Wangd3a96402023-11-24 15:37:39 +0000438}
439
440fn set_once<T>(field: &OnceCell<T>, value: T, field_name: &str) -> Result<()> {
441 field.set(value).map_err(|_| {
442 error!("Field '{field_name}' is duplicated in the Payload");
443 RequestProcessingError::InvalidDiceChain
444 })
445}
446
447fn take_value<T>(field: &mut OnceCell<T>, field_name: &str) -> Result<T> {
448 field.take().ok_or_else(|| {
449 error!("Field '{field_name}' is missing in the Payload");
450 RequestProcessingError::InvalidDiceChain
451 })
452}
453
454impl PayloadBuilder {
455 fn subject_public_key(&mut self, key: PublicKey) -> Result<()> {
456 set_once(&self.subject_public_key, key, "subject_public_key")
457 }
458
459 fn mode(&mut self, mode: DiceMode) -> Result<()> {
460 set_once(&self.mode, mode, "mode")
461 }
462
463 fn code_hash(&mut self, code_hash: [u8; HASH_SIZE]) -> Result<()> {
464 set_once(&self.code_hash, code_hash, "code_hash")
465 }
466
467 fn authority_hash(&mut self, authority_hash: [u8; HASH_SIZE]) -> Result<()> {
468 set_once(&self.authority_hash, authority_hash, "authority_hash")
469 }
470
Alice Wang1cc13502023-12-05 11:05:34 +0000471 fn config_descriptor(&mut self, config_descriptor: ConfigDescriptor) -> Result<()> {
Alice Wangd3a96402023-11-24 15:37:39 +0000472 set_once(&self.config_descriptor, config_descriptor, "config_descriptor")
473 }
474
475 fn build(mut self) -> Result<DiceChainEntryPayload> {
476 let subject_public_key = take_value(&mut self.subject_public_key, "subject_public_key")?;
477 // If Mode is omitted, it should be treated as if it was NotConfigured, according to
478 // the Open Profile for DICE spec.
479 let mode = self.mode.take().unwrap_or(DiceMode::kDiceModeNotInitialized);
480 let code_hash = take_value(&mut self.code_hash, "code_hash")?;
481 let authority_hash = take_value(&mut self.authority_hash, "authority_hash")?;
482 let config_descriptor = take_value(&mut self.config_descriptor, "config_descriptor")?;
483 Ok(DiceChainEntryPayload {
484 subject_public_key,
485 mode,
486 code_hash,
487 authority_hash,
488 config_descriptor,
489 })
490 }
491}