1 use super::cose_key_from_cbor_value;
2 use super::profile::{ComponentVersionType, ModeType, Profile};
3 use crate::cbor::{field_value::FieldValue, value_from_bytes};
4 use crate::dice::{
5 ComponentVersion, ConfigDesc, ConfigDescBuilder, DiceMode, Payload, PayloadBuilder,
6 ProfileVersion,
7 };
8 use crate::publickey::PublicKey;
9 use crate::session::Session;
10 use anyhow::{anyhow, bail, ensure, Context, Result};
11 use ciborium::value::Value;
12 use coset::{AsCborValue, CoseSign1};
13 use openssl::sha::{sha256, sha384, sha512};
14 use std::collections::hash_map::Entry::{Occupied, Vacant};
15 use std::collections::HashMap;
16 use std::str::FromStr;
17
18 const ISS: i64 = 1;
19 const SUB: i64 = 2;
20 const CODE_HASH: i64 = -4670545;
21 const CODE_DESC: i64 = -4670546;
22 const CONFIG_HASH: i64 = -4670547;
23 const CONFIG_DESC: i64 = -4670548;
24 const AUTHORITY_HASH: i64 = -4670549;
25 const AUTHORITY_DESC: i64 = -4670550;
26 const MODE: i64 = -4670551;
27 const SUBJECT_PUBLIC_KEY: i64 = -4670552;
28 const KEY_USAGE: i64 = -4670553;
29 const PROFILE_NAME: i64 = -4670554;
30
31 const CONFIG_DESC_RESERVED_MAX: i64 = -70000;
32 const CONFIG_DESC_RESERVED_MIN: i64 = -70999;
33 const COMPONENT_NAME: i64 = -70002;
34 const COMPONENT_VERSION: i64 = -70003;
35 const RESETTABLE: i64 = -70004;
36 const SECURITY_VERSION: i64 = -70005;
37 const RKP_VM_MARKER: i64 = -70006;
38
39 pub(super) struct Entry {
40 payload: Vec<u8>,
41 }
42
43 impl Entry {
verify_cbor_value(cbor: Value, key: &PublicKey) -> Result<Self>44 pub(super) fn verify_cbor_value(cbor: Value, key: &PublicKey) -> Result<Self> {
45 let sign1 = CoseSign1::from_cbor_value(cbor)
46 .context("Given CBOR does not appear to be a COSE_sign1")?;
47 key.verify_cose_sign1(&sign1, b"").context("cannot verify COSE_sign1")?;
48 match sign1.payload {
49 None => bail!("Missing payload"),
50 Some(payload) => Ok(Self { payload }),
51 }
52 }
53
payload(&self) -> &[u8]54 pub(super) fn payload(&self) -> &[u8] {
55 &self.payload
56 }
57 }
58
59 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
60 pub(super) enum ConfigFormat {
61 /// The configuration descriptor format specified by Android.
62 #[default]
63 Android,
64 /// The configuration descriptor format is either that specified by Android or is ignored.
65 AndroidOrIgnored,
66 }
67
68 impl Payload {
from_cbor( session: &Session, bytes: &[u8], config_format: ConfigFormat, ) -> Result<Self>69 pub(super) fn from_cbor(
70 session: &Session,
71 bytes: &[u8],
72 config_format: ConfigFormat,
73 ) -> Result<Self> {
74 let entries = cbor_map_from_slice(bytes)?;
75 let profile_version = PayloadFields::extract_profile_version(session, &entries)?;
76 Self::from_entries(&profile_version.into(), entries, config_format)
77 }
78
from_entries( profile: &Profile, entries: Vec<(Value, Value)>, config_format: ConfigFormat, ) -> Result<Self>79 fn from_entries(
80 profile: &Profile,
81 entries: Vec<(Value, Value)>,
82 config_format: ConfigFormat,
83 ) -> Result<Self> {
84 let f = PayloadFields::from_entries(profile, entries, config_format)?;
85 PayloadBuilder::with_subject_public_key(f.subject_public_key)
86 .issuer(f.issuer)
87 .subject(f.subject)
88 .mode(f.mode.ok_or_else(|| anyhow!("mode required"))?)
89 .code_desc(f.code_desc)
90 .code_hash(f.code_hash.ok_or_else(|| anyhow!("code hash required"))?)
91 .config_desc(f.config_desc.ok_or_else(|| anyhow!("config desc required"))?)
92 .config_hash(f.config_hash)
93 .authority_desc(f.authority_desc)
94 .authority_hash(f.authority_hash.ok_or_else(|| anyhow!("authority hash required"))?)
95 .build()
96 .context("building payload")
97 }
98 }
99
100 pub(super) struct PayloadFields {
101 pub(super) issuer: String,
102 pub(super) subject: String,
103 pub(super) subject_public_key: PublicKey,
104 mode: Option<DiceMode>,
105 code_desc: Option<Vec<u8>>,
106 code_hash: Option<Vec<u8>>,
107 config_desc: Option<ConfigDesc>,
108 config_hash: Option<Vec<u8>>,
109 authority_desc: Option<Vec<u8>>,
110 authority_hash: Option<Vec<u8>>,
111 }
112
113 impl PayloadFields {
from_cbor( session: &Session, bytes: &[u8], config_format: ConfigFormat, ) -> Result<Self>114 pub(super) fn from_cbor(
115 session: &Session,
116 bytes: &[u8],
117 config_format: ConfigFormat,
118 ) -> Result<Self> {
119 let entries = cbor_map_from_slice(bytes)?;
120 let profile_version = Self::extract_profile_version(session, &entries)?;
121 Self::from_entries(&profile_version.into(), entries, config_format)
122 }
123
extract_profile_version( session: &Session, entries: &[(Value, Value)], ) -> Result<ProfileVersion>124 fn extract_profile_version(
125 session: &Session,
126 entries: &[(Value, Value)],
127 ) -> Result<ProfileVersion> {
128 let mut profile_name = FieldValue::new("profile name");
129 for (key, value) in entries.iter() {
130 if key == &Value::from(PROFILE_NAME) {
131 profile_name.set_once(value.clone())?;
132 }
133 }
134
135 let profile_version = match profile_name.into_optional_string()? {
136 None => {
137 let version = session.options.dice_profile_range.start();
138 ensure!(version <= ProfileVersion::Android14, "profile name is required");
139 version
140 }
141 Some(profile_name) => {
142 ProfileVersion::from_str(&profile_name).with_context(|| profile_name.clone())?
143 }
144 };
145 ensure!(
146 session.options.dice_profile_range.contains(profile_version),
147 "profile version \"{profile_version}\" is less than \"{}\" or greater than \"{}\"",
148 session.options.dice_profile_range.start(),
149 session.options.dice_profile_range.end(),
150 );
151
152 Ok(profile_version)
153 }
154
from_entries( profile: &Profile, entries: Vec<(Value, Value)>, config_format: ConfigFormat, ) -> Result<Self>155 fn from_entries(
156 profile: &Profile,
157 entries: Vec<(Value, Value)>,
158 config_format: ConfigFormat,
159 ) -> Result<Self> {
160 let mut issuer = FieldValue::new("issuer");
161 let mut subject = FieldValue::new("subject");
162 let mut subject_public_key = FieldValue::new("subject public key");
163 let mut mode = FieldValue::new("mode");
164 let mut code_desc = FieldValue::new("code desc");
165 let mut code_hash = FieldValue::new("code hash");
166 let mut config_desc = FieldValue::new("config desc");
167 let mut config_hash = FieldValue::new("config hash");
168 let mut authority_desc = FieldValue::new("authority desc");
169 let mut authority_hash = FieldValue::new("authority hash");
170 let mut key_usage = FieldValue::new("key usage");
171 let mut profile_name = FieldValue::new("profile name");
172
173 for (key, value) in entries.into_iter() {
174 if let Some(Ok(key)) = key.as_integer().map(TryInto::try_into) {
175 let field = match key {
176 ISS => &mut issuer,
177 SUB => &mut subject,
178 SUBJECT_PUBLIC_KEY => &mut subject_public_key,
179 MODE => &mut mode,
180 CODE_DESC => &mut code_desc,
181 CODE_HASH => &mut code_hash,
182 CONFIG_DESC => &mut config_desc,
183 CONFIG_HASH => &mut config_hash,
184 AUTHORITY_DESC => &mut authority_desc,
185 AUTHORITY_HASH => &mut authority_hash,
186 KEY_USAGE => &mut key_usage,
187 PROFILE_NAME => &mut profile_name,
188 _ => bail!("Unknown key {}", key),
189 };
190 field.set_once(value)?
191 } else {
192 bail!("Invalid key: {:?}", key);
193 }
194 }
195
196 validate_key_usage(profile, key_usage)?;
197 let (config_desc, config_hash) =
198 validate_config(profile, config_desc, config_hash, config_format).context("config")?;
199
200 Ok(Self {
201 issuer: issuer.into_string()?,
202 subject: subject.into_string()?,
203 subject_public_key: validate_subject_public_key(profile, subject_public_key)?,
204 mode: validate_mode(profile, mode)?,
205 code_desc: code_desc.into_optional_bytes()?,
206 code_hash: code_hash.into_optional_bytes()?,
207 config_desc,
208 config_hash,
209 authority_desc: authority_desc.into_optional_bytes()?,
210 authority_hash: authority_hash.into_optional_bytes()?,
211 })
212 }
213 }
214
validate_key_usage(profile: &Profile, key_usage: FieldValue) -> Result<()>215 fn validate_key_usage(profile: &Profile, key_usage: FieldValue) -> Result<()> {
216 let key_usage = key_usage.into_bytes().context("key usage")?;
217 let key_cert_sign = 1 << 5;
218 if key_usage.len() > 1
219 && profile.allow_big_endian_key_usage
220 && key_usage[key_usage.len() - 1] == key_cert_sign
221 && key_usage.iter().take(key_usage.len() - 1).all(|&x| x == 0)
222 {
223 return Ok(());
224 }
225 if key_usage.is_empty()
226 || key_usage[0] != key_cert_sign
227 || !key_usage.iter().skip(1).all(|&x| x == 0)
228 {
229 bail!("key usage must only contain keyCertSign (bit 5)");
230 };
231 Ok(())
232 }
233
validate_subject_public_key( profile: &Profile, subject_public_key: FieldValue, ) -> Result<PublicKey>234 fn validate_subject_public_key(
235 profile: &Profile,
236 subject_public_key: FieldValue,
237 ) -> Result<PublicKey> {
238 let subject_public_key = subject_public_key.into_bytes()?;
239 let subject_public_key = value_from_bytes(&subject_public_key).context("decode CBOR")?;
240 let subject_public_key = cose_key_from_cbor_value(subject_public_key, profile.key_ops_type)
241 .context("parsing subject public key")?;
242 PublicKey::from_cose_key(&subject_public_key)
243 .context("parsing subject public key from COSE_key")
244 }
245
validate_mode(profile: &Profile, mode: FieldValue) -> Result<Option<DiceMode>>246 fn validate_mode(profile: &Profile, mode: FieldValue) -> Result<Option<DiceMode>> {
247 Ok(if !mode.is_bytes() && profile.mode_type == ModeType::IntOrBytes {
248 mode.into_optional_i64()?
249 } else {
250 mode.into_optional_bytes()?
251 .map(|mode| {
252 if mode.len() != 1 {
253 bail!("Expected mode to be a single byte, actual byte count: {}", mode.len())
254 };
255 Ok(mode[0].into())
256 })
257 .transpose()?
258 }
259 .map(|mode| match mode {
260 1 => DiceMode::Normal,
261 2 => DiceMode::Debug,
262 3 => DiceMode::Recovery,
263 _ => DiceMode::NotConfigured,
264 }))
265 }
266
validate_config( profile: &Profile, config_desc: FieldValue, config_hash: FieldValue, config_format: ConfigFormat, ) -> Result<(Option<ConfigDesc>, Option<Vec<u8>>)>267 fn validate_config(
268 profile: &Profile,
269 config_desc: FieldValue,
270 config_hash: FieldValue,
271 config_format: ConfigFormat,
272 ) -> Result<(Option<ConfigDesc>, Option<Vec<u8>>)> {
273 let config_desc = config_desc.into_optional_bytes()?;
274 let config_hash = config_hash.into_optional_bytes()?;
275 if let Some(config_desc) = config_desc {
276 let config = config_desc_from_slice(profile, &config_desc).context("parsing descriptor");
277 if config.is_err() && config_format == ConfigFormat::AndroidOrIgnored {
278 return Ok((Some(ConfigDesc::default()), config_hash));
279 }
280 if !profile.config_hash_unverified {
281 let Some(ref hash) = config_hash else { bail!("hash required") };
282 match hash.len() {
283 32 => ensure!(hash == &sha256(&config_desc)),
284 48 => ensure!(hash == &sha384(&config_desc)),
285 64 => ensure!(hash == &sha512(&config_desc)),
286 _ => bail!("unsupported hash size"),
287 };
288 }
289 Ok((Some(config?), config_hash))
290 } else {
291 Ok((None, config_hash))
292 }
293 }
294
cbor_map_from_slice(bytes: &[u8]) -> Result<Vec<(Value, Value)>>295 fn cbor_map_from_slice(bytes: &[u8]) -> Result<Vec<(Value, Value)>> {
296 let value = value_from_bytes(bytes).context("Error parsing CBOR into a map")?;
297 let entries = match value {
298 Value::Map(entries) => entries,
299 _ => bail!("Not a map: {:?}", value),
300 };
301 Ok(entries)
302 }
303
config_desc_from_slice(profile: &Profile, bytes: &[u8]) -> Result<ConfigDesc>304 fn config_desc_from_slice(profile: &Profile, bytes: &[u8]) -> Result<ConfigDesc> {
305 let entries = cbor_map_from_slice(bytes)?;
306
307 let mut component_name = FieldValue::new("component name");
308 let mut component_version = FieldValue::new("component version");
309 let mut resettable = FieldValue::new("resettable");
310 let mut security_version = FieldValue::new("security version");
311 let mut rkp_vm_marker = FieldValue::new("rkp vm marker");
312 let mut extensions = HashMap::new();
313
314 for (key, value) in entries.into_iter() {
315 if let Some(Ok(key)) = key.as_integer().map(TryInto::try_into) {
316 let field = match key {
317 COMPONENT_NAME => &mut component_name,
318 COMPONENT_VERSION => &mut component_version,
319 RESETTABLE => &mut resettable,
320 SECURITY_VERSION => &mut security_version,
321 RKP_VM_MARKER => &mut rkp_vm_marker,
322 key if (CONFIG_DESC_RESERVED_MIN..=CONFIG_DESC_RESERVED_MAX).contains(&key) => {
323 bail!("Reserved key {}", key);
324 }
325 _ => match extensions.entry(key) {
326 Vacant(entry) => {
327 entry.insert(value);
328 continue;
329 }
330 Occupied(entry) => {
331 bail!("Duplicate values for {}: {:?} and {:?}", key, entry.get(), value)
332 }
333 },
334 };
335 field.set_once(value)?
336 } else {
337 bail!("Invalid key: {:?}", key);
338 }
339 }
340
341 let extensions =
342 extensions.into_iter().map(|(k, v)| (k.to_string(), format!("{v:?}"))).collect();
343
344 let security_version = if profile.security_version_optional {
345 security_version.into_optional_u64()
346 } else {
347 security_version.into_u64().map(Some)
348 }
349 .context("Security version")?;
350
351 Ok(ConfigDescBuilder::new()
352 .component_name(component_name.into_optional_string().context("Component name")?)
353 .component_version(
354 validate_version(profile, component_version).context("Component version")?,
355 )
356 .resettable(resettable.is_null().context("Resettable")?)
357 .security_version(security_version)
358 .rkp_vm_marker(rkp_vm_marker.is_null().context("RKP VM marker")?)
359 .extensions(extensions)
360 .build())
361 }
362
validate_version(profile: &Profile, field: FieldValue) -> Result<Option<ComponentVersion>>363 fn validate_version(profile: &Profile, field: FieldValue) -> Result<Option<ComponentVersion>> {
364 Ok(
365 if !field.is_integer()
366 && profile.component_version_type == ComponentVersionType::IntOrString
367 {
368 field.into_optional_string()?.map(ComponentVersion::String)
369 } else {
370 field.into_optional_i64()?.map(ComponentVersion::Integer)
371 },
372 )
373 }
374
375 #[cfg(test)]
376 mod tests {
377 use super::*;
378 use crate::cbor::dice::KeyOpsType;
379 use crate::cbor::serialize;
380 use crate::publickey::testkeys::{PrivateKey, ED25519_KEY_PEM};
381 use crate::session::{DiceProfileRange, Options};
382 use ciborium::cbor;
383 use coset::iana::{self, EnumI64};
384 use coset::CborSerializable;
385 use std::collections::HashMap;
386
387 impl Entry {
from_payload(payload: &Payload) -> Result<Self>388 pub(in super::super) fn from_payload(payload: &Payload) -> Result<Self> {
389 Ok(Self { payload: serialize(payload.to_cbor_value()?) })
390 }
391
sign(self, key: &PrivateKey) -> CoseSign1392 pub(in super::super) fn sign(self, key: &PrivateKey) -> CoseSign1 {
393 key.sign_cose_sign1(self.payload)
394 }
395 }
396
397 impl Payload {
to_cbor_value(&self) -> Result<Value>398 pub(in super::super) fn to_cbor_value(&self) -> Result<Value> {
399 let subject_public_key = self.subject_public_key().to_cose_key()?.to_vec()?;
400 let config_desc = serialize(self.config_desc().to_cbor_value());
401 let mut map = vec![
402 (Value::from(ISS), Value::from(self.issuer())),
403 (Value::from(SUB), Value::from(self.subject())),
404 (Value::from(SUBJECT_PUBLIC_KEY), Value::from(subject_public_key)),
405 (Value::from(MODE), encode_mode(self.mode())),
406 (Value::from(CODE_HASH), Value::from(self.code_hash())),
407 (Value::from(CONFIG_DESC), Value::from(config_desc)),
408 (Value::from(AUTHORITY_HASH), Value::from(self.authority_hash())),
409 (Value::from(KEY_USAGE), Value::from(vec![0x20])),
410 ];
411 if let Some(code_desc) = self.code_desc() {
412 map.push((Value::from(CODE_DESC), Value::from(code_desc)));
413 }
414 if let Some(config_hash) = self.config_hash() {
415 map.push((Value::from(CONFIG_HASH), Value::from(config_hash)));
416 }
417 if let Some(authority_desc) = self.authority_desc() {
418 map.push((Value::from(AUTHORITY_DESC), Value::from(authority_desc)));
419 }
420 Ok(Value::Map(map))
421 }
422 }
423
424 impl ConfigDesc {
to_cbor_value(&self) -> Value425 pub(in super::super) fn to_cbor_value(&self) -> Value {
426 let mut map = Vec::new();
427 if let Some(component_name) = self.component_name() {
428 map.push((Value::from(COMPONENT_NAME), Value::from(component_name)));
429 }
430 if let Some(component_version) = self.component_version() {
431 map.push((
432 Value::from(COMPONENT_VERSION),
433 match component_version {
434 ComponentVersion::Integer(n) => Value::from(*n),
435 ComponentVersion::String(s) => Value::from(s.as_str()),
436 },
437 ))
438 }
439 if self.resettable() {
440 map.push((Value::from(RESETTABLE), Value::Null));
441 }
442 if let Some(security_version) = self.security_version() {
443 map.push((Value::from(SECURITY_VERSION), Value::from(security_version)));
444 }
445 if self.rkp_vm_marker() {
446 map.push((Value::from(RKP_VM_MARKER), Value::Null));
447 }
448 Value::Map(map)
449 }
450 }
451
encode_mode(mode: DiceMode) -> Value452 fn encode_mode(mode: DiceMode) -> Value {
453 let mode = match mode {
454 DiceMode::NotConfigured => 0,
455 DiceMode::Normal => 1,
456 DiceMode::Debug => 2,
457 DiceMode::Recovery => 3,
458 };
459 Value::Bytes(vec![mode])
460 }
461
462 #[test]
valid_payload_sha256()463 fn valid_payload_sha256() {
464 let config_desc = serialize(cbor!({COMPONENT_NAME => "sha256 test"}).unwrap());
465 let config_hash = sha256(&config_desc).to_vec();
466 let mut fields = valid_payload_fields();
467 fields.insert(CODE_HASH, Value::Bytes(vec![1; 32]));
468 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
469 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
470 fields.insert(AUTHORITY_HASH, Value::Bytes(vec![2; 32]));
471 let session = Session { options: Options::default() };
472 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
473 }
474
475 #[test]
valid_payload_sha384()476 fn valid_payload_sha384() {
477 let config_desc = serialize(cbor!({COMPONENT_NAME => "sha384 test"}).unwrap());
478 let config_hash = sha384(&config_desc).to_vec();
479 let mut fields = valid_payload_fields();
480 fields.insert(CODE_HASH, Value::Bytes(vec![1; 48]));
481 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
482 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
483 fields.insert(AUTHORITY_HASH, Value::Bytes(vec![2; 48]));
484 let session = Session { options: Options::default() };
485 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
486 }
487
488 #[test]
valid_payload_sha512()489 fn valid_payload_sha512() {
490 let fields = valid_payload_fields();
491 let session = Session { options: Options::default() };
492 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
493 }
494
495 #[test]
key_usage_only_key_cert_sign()496 fn key_usage_only_key_cert_sign() {
497 let mut fields = valid_payload_fields();
498 fields.insert(KEY_USAGE, Value::Bytes(vec![0x20]));
499 let session = Session { options: Options::default() };
500 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
501 }
502
503 #[test]
key_usage_too_long()504 fn key_usage_too_long() {
505 let mut fields = valid_payload_fields();
506 fields.insert(KEY_USAGE, Value::Bytes(vec![0x20, 0x30, 0x40]));
507 let session = Session { options: Options::default() };
508 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
509 }
510
511 #[test]
key_usage_lacks_key_cert_sign()512 fn key_usage_lacks_key_cert_sign() {
513 let mut fields = valid_payload_fields();
514 fields.insert(KEY_USAGE, Value::Bytes(vec![0x10]));
515 let session = Session { options: Options::default() };
516 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
517 }
518
519 #[test]
key_usage_not_just_key_cert_sign()520 fn key_usage_not_just_key_cert_sign() {
521 let mut fields = valid_payload_fields();
522 fields.insert(KEY_USAGE, Value::Bytes(vec![0x21]));
523 let session = Session { options: Options::default() };
524 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
525 }
526
527 #[test]
mode_not_configured()528 fn mode_not_configured() {
529 let mut fields = valid_payload_fields();
530 fields.insert(MODE, Value::Bytes(vec![0]));
531 let session = Session { options: Options::default() };
532 let payload =
533 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
534 assert_eq!(payload.mode(), DiceMode::NotConfigured);
535 }
536
537 #[test]
mode_normal()538 fn mode_normal() {
539 let mut fields = valid_payload_fields();
540 fields.insert(MODE, Value::Bytes(vec![1]));
541 let session = Session { options: Options::default() };
542 let payload =
543 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
544 assert_eq!(payload.mode(), DiceMode::Normal);
545 }
546
547 #[test]
mode_debug()548 fn mode_debug() {
549 let mut fields = valid_payload_fields();
550 fields.insert(MODE, Value::Bytes(vec![2]));
551 let session = Session { options: Options::default() };
552 let payload =
553 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
554 assert_eq!(payload.mode(), DiceMode::Debug);
555 }
556
557 #[test]
mode_recovery()558 fn mode_recovery() {
559 let mut fields = valid_payload_fields();
560 fields.insert(MODE, Value::Bytes(vec![3]));
561 let session = Session { options: Options::default() };
562 let payload =
563 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
564 assert_eq!(payload.mode(), DiceMode::Recovery);
565 }
566
567 #[test]
mode_invalid_becomes_not_configured()568 fn mode_invalid_becomes_not_configured() {
569 let mut fields = valid_payload_fields();
570 fields.insert(MODE, Value::Bytes(vec![4]));
571 let session = Session { options: Options::default() };
572 let payload =
573 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
574 assert_eq!(payload.mode(), DiceMode::NotConfigured);
575 }
576
577 #[test]
mode_multiple_bytes()578 fn mode_multiple_bytes() {
579 let mut fields = valid_payload_fields();
580 fields.insert(MODE, Value::Bytes(vec![0, 1]));
581 let session = Session { options: Options::default() };
582 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
583 }
584
585 #[test]
mode_int_debug()586 fn mode_int_debug() {
587 let mut fields = valid_payload_fields();
588 fields.insert(MODE, Value::from(2));
589 let entries = encode_fields(fields);
590 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
591 .unwrap_err();
592 let profile = Profile { mode_type: ModeType::IntOrBytes, ..Profile::default() };
593 let payload = Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap();
594 assert_eq!(payload.mode(), DiceMode::Debug);
595 }
596
597 #[test]
subject_public_key_garbage()598 fn subject_public_key_garbage() {
599 let mut fields = valid_payload_fields();
600 fields.insert(SUBJECT_PUBLIC_KEY, Value::Bytes(vec![17; 64]));
601 let session = Session { options: Options::default() };
602 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
603 }
604
605 #[test]
key_usage_little_endian()606 fn key_usage_little_endian() {
607 let mut fields = valid_payload_fields();
608 fields.insert(KEY_USAGE, Value::Bytes(vec![0x20, 0x00, 0x00]));
609 let cbor = serialize_fields(fields);
610 let session = Session { options: Options::default() };
611 Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap();
612 }
613
614 #[test]
key_usage_little_endian_invalid()615 fn key_usage_little_endian_invalid() {
616 let mut fields = valid_payload_fields();
617 fields.insert(KEY_USAGE, Value::Bytes(vec![0x20, 0xbe, 0xef]));
618 let cbor = serialize_fields(fields);
619 let session = Session { options: Options::default() };
620 Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap_err();
621 }
622
623 #[test]
key_usage_big_endian()624 fn key_usage_big_endian() {
625 let mut fields = valid_payload_fields();
626 fields.insert(KEY_USAGE, Value::Bytes(vec![0x00, 0x20]));
627 let entries = encode_fields(fields);
628 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
629 .unwrap_err();
630 let profile = Profile { allow_big_endian_key_usage: true, ..Profile::default() };
631 Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap();
632 }
633
634 #[test]
key_usage_big_endian_invalid()635 fn key_usage_big_endian_invalid() {
636 let mut fields = valid_payload_fields();
637 fields.insert(KEY_USAGE, Value::Bytes(vec![0x00, 0xfe, 0x20]));
638 let entries = encode_fields(fields);
639 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
640 .unwrap_err();
641 let profile = Profile { allow_big_endian_key_usage: true, ..Profile::default() };
642 Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap_err();
643 }
644
645 #[test]
key_usage_invalid()646 fn key_usage_invalid() {
647 let mut fields = valid_payload_fields();
648 fields.insert(KEY_USAGE, Value::Bytes(vec![0x00, 0x10]));
649 let entries = encode_fields(fields);
650 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
651 .unwrap_err();
652 let profile = Profile { allow_big_endian_key_usage: true, ..Profile::default() };
653 Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap_err();
654 }
655
656 #[test]
key_usage_empty()657 fn key_usage_empty() {
658 let mut fields = valid_payload_fields();
659 fields.insert(KEY_USAGE, Value::Bytes(vec![]));
660 let entries = encode_fields(fields);
661 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
662 .unwrap_err();
663 let profile = Profile { allow_big_endian_key_usage: true, ..Profile::default() };
664 Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap_err();
665 }
666
667 #[test]
config_desc_custom_field_above()668 fn config_desc_custom_field_above() {
669 let mut fields = valid_payload_fields();
670 let config_desc = serialize(cbor!({-69999 => "custom"}).unwrap());
671 let config_hash = sha512(&config_desc).to_vec();
672 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
673 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
674 let session = Session { options: Options::default() };
675 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
676 }
677
678 #[test]
config_desc_reserved_field_max()679 fn config_desc_reserved_field_max() {
680 let mut fields = valid_payload_fields();
681 let config_desc = serialize(cbor!({-70000 => "reserved"}).unwrap());
682 let config_hash = sha512(&config_desc).to_vec();
683 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
684 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
685 let session = Session { options: Options::default() };
686 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
687 }
688
689 #[test]
config_desc_reserved_field_min()690 fn config_desc_reserved_field_min() {
691 let mut fields = valid_payload_fields();
692 let config_desc = serialize(cbor!({-70999 => "reserved"}).unwrap());
693 let config_hash = sha512(&config_desc).to_vec();
694 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
695 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
696 let session = Session { options: Options::default() };
697 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap_err();
698 }
699
700 #[test]
config_desc_custom_field_below()701 fn config_desc_custom_field_below() {
702 let mut fields = valid_payload_fields();
703 let config_desc = serialize(cbor!({-71000 => "custom"}).unwrap());
704 let config_hash = sha512(&config_desc).to_vec();
705 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
706 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
707 let session = Session { options: Options::default() };
708 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
709 }
710
711 #[test]
config_desc_custom_fields()712 fn config_desc_custom_fields() {
713 let mut fields = valid_payload_fields();
714 let config_desc = serialize(cbor!({-71000 => "custom hi", -69999 => "custom lo"}).unwrap());
715 let config_hash = sha512(&config_desc).to_vec();
716 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
717 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
718 let session = Session { options: Options::default() };
719 let payload =
720 Payload::from_cbor(&session, &serialize_fields(fields), ConfigFormat::Android).unwrap();
721 let extensions = payload.config_desc().extensions();
722 let extensions = HashMap::<_, _>::from_iter(extensions.to_owned());
723 assert_eq!(extensions.get("-71000").unwrap(), "Text(\"custom hi\")");
724 assert_eq!(extensions.get("-69999").unwrap(), "Text(\"custom lo\")");
725 assert_eq!(extensions.len(), 2);
726 }
727
728 #[test]
config_desc_not_android_spec()729 fn config_desc_not_android_spec() {
730 let mut fields = valid_payload_fields();
731 fields.insert(CONFIG_DESC, Value::Bytes(vec![0xcd; 64]));
732 let cbor = serialize_fields(fields);
733 let session = Session { options: Options::default() };
734 Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap_err();
735 let payload = Payload::from_cbor(&session, &cbor, ConfigFormat::AndroidOrIgnored).unwrap();
736 assert_eq!(payload.config_desc(), &ConfigDesc::default());
737 }
738
739 #[test]
config_desc_component_version_string()740 fn config_desc_component_version_string() {
741 let mut fields = valid_payload_fields();
742 let config_desc = serialize(
743 cbor!({COMPONENT_VERSION => "It's version 4", SECURITY_VERSION => 99999999}).unwrap(),
744 );
745 let config_hash = sha512(&config_desc).to_vec();
746 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
747 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
748 let entries = encode_fields(fields);
749 let profile =
750 Profile { component_version_type: ComponentVersionType::Int, ..Profile::default() };
751 Payload::from_entries(&profile, entries.clone(), ConfigFormat::Android).unwrap_err();
752 let payload =
753 Payload::from_entries(&Profile::default(), entries, ConfigFormat::Android).unwrap();
754 assert_eq!(
755 payload.config_desc().component_version(),
756 Some(&ComponentVersion::String("It's version 4".to_string()))
757 );
758 }
759
760 #[test]
config_desc_security_version()761 fn config_desc_security_version() {
762 let mut fields = valid_payload_fields();
763 let config_desc = serialize(cbor!({SECURITY_VERSION => 0x12345678}).unwrap());
764 let config_hash = sha512(&config_desc).to_vec();
765 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
766 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
767 let cbor = serialize_fields(fields);
768 let session = Session { options: Options::default() };
769 let payload = Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap();
770 assert_eq!(payload.config_desc().security_version(), Some(0x12345678));
771 }
772
773 #[test]
config_desc_security_version_omitted()774 fn config_desc_security_version_omitted() {
775 let mut fields = valid_payload_fields();
776 let config_desc = serialize(cbor!({}).unwrap());
777 let config_hash = sha512(&config_desc).to_vec();
778 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
779 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
780 let entries = encode_fields(fields);
781 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
782 .unwrap_err();
783 let profile = Profile { security_version_optional: true, ..Profile::default() };
784 let payload = Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap();
785 assert_eq!(payload.config_desc().security_version(), None);
786 }
787
788 #[test]
config_desc_security_version_fixed_size_encoding()789 fn config_desc_security_version_fixed_size_encoding() {
790 let mut fields = valid_payload_fields();
791 let config_desc = vec![
792 0xa1, // Map of one element.
793 0x3a, 0x00, 0x01, 0x11, 0x74, // SECURITY_VERSION.
794 0x1a, 0x00, 0x00, 0xca, 0xfe, // Non-deterministic encoding of 0xcafe.
795 ];
796 let config_hash = sha512(&config_desc).to_vec();
797 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
798 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
799 let cbor = serialize_fields(fields);
800 let session = Session { options: Options::default() };
801 let payload = Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap();
802 assert_eq!(payload.config_desc().security_version(), Some(0xcafe));
803 }
804
805 #[test]
config_desc_security_version_negative()806 fn config_desc_security_version_negative() {
807 let mut fields = valid_payload_fields();
808 let config_desc = serialize(cbor!({SECURITY_VERSION => Value::from(-12)}).unwrap());
809 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
810 let cbor = serialize_fields(fields);
811 let session = Session { options: Options::default() };
812 Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap_err();
813 }
814
815 #[test]
config_desc_resettable()816 fn config_desc_resettable() {
817 let mut fields = valid_payload_fields();
818 let config_desc = serialize(cbor!({RESETTABLE => null}).unwrap());
819 let config_hash = sha512(&config_desc).to_vec();
820 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
821 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
822 let cbor = serialize_fields(fields);
823 let session = Session { options: Options::default() };
824 let payload = Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap();
825 assert!(payload.config_desc().resettable());
826 }
827
828 #[test]
config_desc_rkp_vm_marker()829 fn config_desc_rkp_vm_marker() {
830 let mut fields = valid_payload_fields();
831 let config_desc = serialize(cbor!({RKP_VM_MARKER => null}).unwrap());
832 let config_hash = sha512(&config_desc).to_vec();
833 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
834 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
835 let cbor = serialize_fields(fields);
836 let session = Session { options: Options::default() };
837 let payload = Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap();
838 assert!(payload.config_desc().rkp_vm_marker());
839 }
840
841 #[test]
config_desc_nulls_omitted()842 fn config_desc_nulls_omitted() {
843 let mut fields = valid_payload_fields();
844 let config_desc = serialize(cbor!({}).unwrap());
845 let config_hash = sha512(&config_desc).to_vec();
846 fields.insert(CONFIG_DESC, Value::Bytes(config_desc));
847 fields.insert(CONFIG_HASH, Value::Bytes(config_hash));
848 let cbor = serialize_fields(fields);
849 let session = Session { options: Options::default() };
850 let payload = Payload::from_cbor(&session, &cbor, ConfigFormat::Android).unwrap();
851 assert!(!payload.config_desc().resettable());
852 assert!(!payload.config_desc().rkp_vm_marker());
853 }
854
855 #[test]
config_hash_missing()856 fn config_hash_missing() {
857 let mut fields = valid_payload_fields();
858 fields.remove(&CONFIG_HASH);
859 let entries = encode_fields(fields);
860 Payload::from_entries(&Profile::default(), entries, ConfigFormat::Android).unwrap_err();
861 }
862
863 #[test]
integer_key_ops()864 fn integer_key_ops() {
865 let mut fields = valid_payload_fields();
866 let subject_public_key = cbor!({
867 iana::KeyParameter::Kty.to_i64() => iana::KeyType::OKP.to_i64(),
868 iana::KeyParameter::Alg.to_i64() => iana::Algorithm::EdDSA.to_i64(),
869 iana::KeyParameter::KeyOps.to_i64() => iana::KeyOperation::Verify.to_i64(),
870 iana::OkpKeyParameter::Crv.to_i64() => iana::EllipticCurve::Ed25519.to_i64(),
871 iana::OkpKeyParameter::X.to_i64() => Value::Bytes(vec![0; 32]),
872 })
873 .unwrap();
874 fields.insert(SUBJECT_PUBLIC_KEY, Value::Bytes(serialize(subject_public_key)));
875 let entries = encode_fields(fields);
876 Payload::from_entries(&Profile::default(), entries.clone(), ConfigFormat::Android)
877 .unwrap_err();
878 let profile = Profile { key_ops_type: KeyOpsType::IntOrArray, ..Profile::default() };
879 Payload::from_entries(&profile, entries, ConfigFormat::Android).unwrap();
880 }
881
882 #[test]
extract_profile_version_named_profiles()883 fn extract_profile_version_named_profiles() {
884 let test_cases = [
885 ("android.14", ProfileVersion::Android14),
886 ("android.15", ProfileVersion::Android15),
887 ("android.16", ProfileVersion::Android16),
888 ];
889 for (profile_name, expected_version) in test_cases {
890 let mut fields = valid_payload_fields();
891 fields.insert(PROFILE_NAME, Value::from(profile_name));
892 let entries = encode_fields(fields);
893 let session = Session {
894 options: Options {
895 dice_profile_range: DiceProfileRange::new(expected_version, expected_version),
896 },
897 };
898 let profile_version =
899 PayloadFields::extract_profile_version(&session, &entries).unwrap();
900 assert_eq!(profile_version, expected_version);
901 }
902 }
903
904 #[test]
extract_profile_version_named_android_13_fails()905 fn extract_profile_version_named_android_13_fails() {
906 let session = Session {
907 options: Options {
908 dice_profile_range: DiceProfileRange::new(
909 ProfileVersion::Android13,
910 ProfileVersion::Android16,
911 ),
912 },
913 };
914 let mut fields = valid_payload_fields();
915 fields.insert(PROFILE_NAME, Value::from("android.13"));
916 let entries = encode_fields(fields);
917 PayloadFields::extract_profile_version(&session, &entries).unwrap_err();
918 }
919
920 #[test]
extract_profile_version_multiple_profile_name_entries_fails()921 fn extract_profile_version_multiple_profile_name_entries_fails() {
922 let session = Session {
923 options: Options {
924 dice_profile_range: DiceProfileRange::new(
925 ProfileVersion::Android13,
926 ProfileVersion::Android16,
927 ),
928 },
929 };
930 let mut fields = valid_payload_fields();
931 fields.insert(PROFILE_NAME, Value::from("android.15"));
932 let mut entries = encode_fields(fields);
933 entries.push((Value::from(PROFILE_NAME), Value::from("android.15")));
934 PayloadFields::extract_profile_version(&session, &entries).unwrap_err();
935 }
936
937 #[test]
extract_profile_version_out_of_range_fails()938 fn extract_profile_version_out_of_range_fails() {
939 let session = Session {
940 options: Options {
941 dice_profile_range: DiceProfileRange::new(
942 ProfileVersion::Android15,
943 ProfileVersion::Android15,
944 ),
945 },
946 };
947 let mut fields = valid_payload_fields();
948 fields.insert(PROFILE_NAME, Value::from("android.14"));
949 let entries = encode_fields(fields.clone());
950 PayloadFields::extract_profile_version(&session, &entries).unwrap_err();
951 fields.insert(PROFILE_NAME, Value::from("android.16"));
952 let entries = encode_fields(fields);
953 PayloadFields::extract_profile_version(&session, &entries).unwrap_err();
954 }
955
956 #[test]
extract_profile_version_default_when_not_named_up_to_android_14()957 fn extract_profile_version_default_when_not_named_up_to_android_14() {
958 let entries = encode_fields(valid_payload_fields());
959 for expected_version in [ProfileVersion::Android13, ProfileVersion::Android14] {
960 let session = Session {
961 options: Options {
962 dice_profile_range: DiceProfileRange::new(
963 expected_version,
964 ProfileVersion::Android16,
965 ),
966 },
967 };
968 let profile_version =
969 PayloadFields::extract_profile_version(&session, &entries).unwrap();
970 assert_eq!(profile_version, expected_version);
971 }
972 }
973
974 #[test]
extract_profile_version_named_profile_required_from_android_15()975 fn extract_profile_version_named_profile_required_from_android_15() {
976 let entries = encode_fields(valid_payload_fields());
977 for min_version in [ProfileVersion::Android15, ProfileVersion::Android16] {
978 let session = Session {
979 options: Options {
980 dice_profile_range: DiceProfileRange::new(
981 min_version,
982 ProfileVersion::Android16,
983 ),
984 },
985 };
986 PayloadFields::extract_profile_version(&session, &entries).unwrap_err();
987 }
988 }
989
valid_payload_fields() -> HashMap<i64, Value>990 fn valid_payload_fields() -> HashMap<i64, Value> {
991 let key = PrivateKey::from_pem(ED25519_KEY_PEM[0]).public_key();
992 let subject_public_key = key.to_cose_key().unwrap().to_vec().unwrap();
993 let config_desc = serialize(
994 cbor!({COMPONENT_NAME => "component name", SECURITY_VERSION => 1234}).unwrap(),
995 );
996 let config_hash = sha512(&config_desc).to_vec();
997 HashMap::from([
998 (ISS, Value::from("issuer")),
999 (SUB, Value::from("subject")),
1000 (SUBJECT_PUBLIC_KEY, Value::Bytes(subject_public_key)),
1001 (KEY_USAGE, Value::Bytes(vec![0x20])),
1002 (CODE_HASH, Value::Bytes(vec![1; 64])),
1003 (CONFIG_DESC, Value::Bytes(config_desc)),
1004 (CONFIG_HASH, Value::Bytes(config_hash)),
1005 (AUTHORITY_HASH, Value::Bytes(vec![2; 64])),
1006 (MODE, Value::Bytes(vec![0])),
1007 ])
1008 }
1009
encode_fields(mut fields: HashMap<i64, Value>) -> Vec<(Value, Value)>1010 fn encode_fields(mut fields: HashMap<i64, Value>) -> Vec<(Value, Value)> {
1011 fields.drain().map(|(k, v)| (Value::from(k), v)).collect()
1012 }
1013
serialize_fields(fields: HashMap<i64, Value>) -> Vec<u8>1014 fn serialize_fields(fields: HashMap<i64, Value>) -> Vec<u8> {
1015 serialize(Value::Map(encode_fields(fields)))
1016 }
1017 }
1018