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