1 // Copyright 2023 Google LLC
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 ////////////////////////////////////////////////////////////////////////////////
16
17 //! Definitions related to different types of keys and other cryptographic artifacts.
18 use crate::arc;
19 use crate::error::Error;
20 use crate::traits::{EcDsa, Rng};
21 use crate::FallibleAllocExt;
22 use crate::{ag_err, ag_verr};
23 use alloc::{
24 string::{String, ToString},
25 vec::Vec,
26 };
27 use authgraph_wire as wire;
28 use coset::{
29 cbor, cbor::value::Value, iana, AsCborValue, CborOrdering, CborSerializable, CoseError,
30 CoseKey, CoseSign1, Label,
31 };
32 use wire::ErrorCode;
33 use zeroize::ZeroizeOnDrop;
34
35 pub use wire::Key;
36
37 /// Length of an AES 256-bits key in bytes
38 pub const AES_256_KEY_LEN: usize = 32;
39
40 /// Size (in bytes) of a curve 25519 private key.
41 pub const CURVE25519_PRIV_KEY_LEN: usize = 32;
42
43 /// Version of the cert chain as per
44 /// hardware/interfaces/security/authgraph/aidl/android/hardware/security/authgraph/
45 /// ExplicitKeyDiceCertChain.cddl
46 pub const EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION: i32 = 1;
47
48 /// Version of the identity as per
49 /// hardware/interfaces/security/authgraph/aidl/android/hardware/security/authgraph/Identity.cddl
50 pub const IDENTITY_VERSION: i32 = 1;
51
52 /// Length of a SHA 256 digest in bytes
53 pub const SHA_256_LEN: usize = 32;
54
55 // Following constants represent the keys of the (key, value) pairs in a Dice certificate
56 const ISS: i64 = 1;
57 const SUB: i64 = 2;
58 const PROFILE_NAME: i64 = -4670554;
59 const SUBJECT_PUBLIC_KEY: i64 = -4670552;
60 const KEY_USAGE: i64 = -4670553;
61 const CODE_HASH: i64 = -4670545;
62 const CODE_DESC: i64 = -4670546;
63 const CONFIG_HASH: i64 = -4670547;
64 const CONFIG_DESC: i64 = -4670548;
65 const AUTHORITY_HASH: i64 = -4670549;
66 const AUTHORITY_DESC: i64 = -4670550;
67 const MODE: i64 = -4670551;
68
69 const COMPONENT_NAME: i64 = -70002;
70 const COMPONENT_VERSION: i64 = -70003;
71 const RESETTABLE: i64 = -70004;
72 const SECURITY_VERSION: i64 = -70005;
73 const RKP_VM_MARKER: i64 = -70006;
74
75 /// AES key of 256 bits
76 #[derive(Clone, ZeroizeOnDrop)]
77 pub struct AesKey(pub [u8; AES_256_KEY_LEN]);
78
79 impl TryFrom<arc::ArcPayload> for AesKey {
80 type Error = Error;
try_from(payload: arc::ArcPayload) -> Result<AesKey, Self::Error>81 fn try_from(payload: arc::ArcPayload) -> Result<AesKey, Self::Error> {
82 if payload.0.len() != AES_256_KEY_LEN {
83 return Err(ag_err!(
84 InvalidSharedKeyArcs,
85 "payload key has invalid length: {}",
86 payload.0.len()
87 ));
88 }
89 let mut key = AesKey([0; AES_256_KEY_LEN]);
90 key.0.copy_from_slice(&payload.0);
91 Ok(key)
92 }
93 }
94
95 /// EC key pair on P256 curve, created for ECDH.
96 pub struct EcExchangeKey {
97 /// Public key
98 pub pub_key: EcExchangeKeyPub,
99 /// Private key
100 pub priv_key: EcExchangeKeyPriv,
101 }
102
103 /// Public key of an EC key pair created for ECDH
104 #[derive(Clone)]
105 pub struct EcExchangeKeyPub(pub CoseKey);
106
107 /// Private key of an EC key pair created for ECDH.
108 /// It is up to the implementers of the AuthGraph traits to decide how to encode the private key.
109 #[derive(ZeroizeOnDrop)]
110 pub struct EcExchangeKeyPriv(pub Vec<u8>);
111
112 /// Shared secret agreed via ECDH
113 #[derive(ZeroizeOnDrop)]
114 pub struct EcdhSecret(pub Vec<u8>);
115
116 /// Pseudo random key of 256 bits that is output by extract/expand functions of key derivation
117 #[derive(ZeroizeOnDrop)]
118 pub struct PseudoRandKey(pub [u8; 32]);
119
120 /// A nonce of 16 bytes, used for key exchange
121 #[derive(Clone)]
122 pub struct Nonce16(pub [u8; 16]);
123
124 impl Nonce16 {
125 /// Create a random nonce of 16 bytes
new(rng: &dyn Rng) -> Self126 pub fn new(rng: &dyn Rng) -> Self {
127 let mut nonce = Nonce16([0u8; 16]);
128 rng.fill_bytes(&mut nonce.0);
129 nonce
130 }
131 }
132
133 /// A nonce of 12 bytes, used for AES-GCM encryption
134 pub struct Nonce12(pub [u8; 12]);
135
136 impl Nonce12 {
137 /// Create a random nonce of 12 bytes
new(rng: &dyn Rng) -> Self138 pub fn new(rng: &dyn Rng) -> Self {
139 let mut nonce = Nonce12([0u8; 12]);
140 rng.fill_bytes(&mut nonce.0);
141 nonce
142 }
143 }
144
145 impl TryFrom<&[u8]> for Nonce12 {
146 type Error = Error;
try_from(v: &[u8]) -> Result<Self, Self::Error>147 fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
148 if v.len() != 12 {
149 return Err(ag_err!(InvalidSharedKeyArcs, "nonce has invalid length: {}", v.len()));
150 }
151 let mut nonce = Nonce12([0; 12]);
152 nonce.0.copy_from_slice(v);
153 Ok(nonce)
154 }
155 }
156
157 /// Milliseconds since an epoch that is common between source and sink
158 pub struct MillisecondsSinceEpoch(pub i64);
159
160 /// Variants of EC private key used to create signature
161 #[derive(Clone, ZeroizeOnDrop)]
162 pub enum EcSignKey {
163 /// On curve Ed25519
164 Ed25519([u8; CURVE25519_PRIV_KEY_LEN]),
165 /// On NIST curve P-256
166 P256(Vec<u8>),
167 /// On NIST curve P-384
168 P384(Vec<u8>),
169 }
170
171 /// Variants of EC public key used to verify signature
172 #[derive(Clone, PartialEq)]
173 pub enum EcVerifyKey {
174 /// On curve Ed25519
175 Ed25519(CoseKey),
176 /// On NIST curve P-256
177 P256(CoseKey),
178 /// On NIST curve P-384
179 P384(CoseKey),
180 }
181
182 impl Default for EcVerifyKey {
default() -> Self183 fn default() -> Self {
184 EcVerifyKey::P256(CoseKey::default())
185 }
186 }
187
188 impl EcVerifyKey {
189 /// Return the `CoseKey` contained in any variant of this enum.
190 /// Assume that the `CoseKey` is checked for appropriate header parameters before it used for
191 /// signature verifictation.
get_key(self) -> CoseKey192 pub fn get_key(self) -> CoseKey {
193 match self {
194 EcVerifyKey::Ed25519(k) | EcVerifyKey::P256(k) | EcVerifyKey::P384(k) => k,
195 }
196 }
197
198 /// Similar to `get_key()`, return the `CoseKey` contained in any variant of this enum
199 /// (but by reference).
get_key_ref(&self) -> &CoseKey200 pub fn get_key_ref(&self) -> &CoseKey {
201 match self {
202 EcVerifyKey::Ed25519(k) | EcVerifyKey::P256(k) | EcVerifyKey::P384(k) => k,
203 }
204 }
205
206 /// Validate whether the CoseKey is in the expected canonical form as per the spec.
is_canonicalized(&self) -> bool207 pub fn is_canonicalized(&self) -> bool {
208 let mut expected = self.clone();
209 expected.canonicalize_cose_key();
210 *self == expected
211 }
212
213 /// Order the labels of the Cose Key, in order to ensure canonical encoding in accordance with
214 /// Core Deterministic Encoding Requirements [RFC 8949 s4.2.1].
canonicalize_cose_key(&mut self)215 pub fn canonicalize_cose_key(&mut self) {
216 match self {
217 EcVerifyKey::Ed25519(k) | EcVerifyKey::P256(k) | EcVerifyKey::P384(k) => {
218 k.canonicalize(CborOrdering::Lexicographic);
219 }
220 }
221 }
222
223 /// Return the Cose signing algorithm corresponds to the given public signing key.
224 /// Assume that the `CoseKey` is checked for appropriate header parameters before it is used for
225 /// signature verification.
get_cose_sign_algorithm(&self) -> iana::Algorithm226 pub fn get_cose_sign_algorithm(&self) -> iana::Algorithm {
227 match *self {
228 EcVerifyKey::Ed25519(_) => iana::Algorithm::EdDSA,
229 EcVerifyKey::P256(_) => iana::Algorithm::ES256,
230 EcVerifyKey::P384(_) => iana::Algorithm::ES384,
231 }
232 }
233
234 /// Construct `EcVerifyKey` from `CoseKey`.
from_cose_key(cose_key: CoseKey) -> Result<Self, CoseError>235 pub fn from_cose_key(cose_key: CoseKey) -> Result<Self, CoseError> {
236 // Only the algorithm is checked while decoding, other parameters are
237 // checked during validation of the `EcVerifykey`.
238 match cose_key.alg {
239 Some(coset::Algorithm::Assigned(iana::Algorithm::EdDSA)) => {
240 Ok(EcVerifyKey::Ed25519(cose_key))
241 }
242 Some(coset::Algorithm::Assigned(iana::Algorithm::ES256)) => {
243 Ok(EcVerifyKey::P256(cose_key))
244 }
245 Some(coset::Algorithm::Assigned(iana::Algorithm::ES384)) => {
246 Ok(EcVerifyKey::P384(cose_key))
247 }
248 Some(_) => {
249 Err(CoseError::UnexpectedItem("unsupported algorithm", "Ed25519 or P256 or P384"))
250 }
251 None => Err(CoseError::UnexpectedItem("algorithm is none", "Ed25519 or P256 or P384")),
252 }
253 }
254
255 /// Validate the key parameters
validate_cose_key_params(&self) -> Result<(), Error>256 pub fn validate_cose_key_params(&self) -> Result<(), Error> {
257 match self {
258 EcVerifyKey::Ed25519(cose_key) => check_cose_key_params(
259 cose_key,
260 iana::KeyType::OKP,
261 iana::Algorithm::EdDSA,
262 iana::EllipticCurve::Ed25519,
263 ErrorCode::InvalidCertChain,
264 ),
265 EcVerifyKey::P256(cose_key) => check_cose_key_params(
266 cose_key,
267 iana::KeyType::EC2,
268 iana::Algorithm::ES256,
269 iana::EllipticCurve::P_256,
270 ErrorCode::InvalidCertChain,
271 ),
272 EcVerifyKey::P384(cose_key) => check_cose_key_params(
273 cose_key,
274 iana::KeyType::EC2,
275 iana::Algorithm::ES384,
276 iana::EllipticCurve::P_384,
277 ErrorCode::InvalidCertChain,
278 ),
279 }
280 }
281 }
282
283 /// HMAC key of 256 bits
284 #[derive(ZeroizeOnDrop)]
285 pub struct HmacKey(pub [u8; 32]);
286
287 /// Identity of an AuthGraph participant. The CDDL is listed in hardware/interfaces/security/
288 /// authgraph/aidl/android/hardware/security/Identity.cddl
289 #[derive(Clone, PartialEq)]
290 pub struct Identity {
291 /// Version of the cddl
292 pub version: i32,
293 /// Certificate chain
294 pub cert_chain: CertChain,
295 /// Identity verification policy
296 pub policy: Option<Policy>,
297 }
298
299 /// Certificate chain containing the public signing key. The CDDL is listed in
300 /// hardware/interfaces/security/authgraph/aidl/android/hardware/security/
301 /// authgraph/ExplicitKeyDiceCertChain.cddl
302 #[derive(Clone, PartialEq)]
303 pub struct CertChain {
304 /// Version of the cddl
305 pub version: i32,
306 /// Root public key used to verify the signature in the first DiceChainEntry. If `cert_chain`
307 /// is none, this is the key used to verify the signature created by the AuthGraph participant.
308 pub root_key: EcVerifyKey,
309 /// Dice certificate chain.
310 pub dice_cert_chain: Option<Vec<DiceChainEntry>>,
311 }
312
313 /// An entry in the certificate chain (i.e. a certificate).
314 #[derive(Clone, PartialEq)]
315 pub struct DiceChainEntry {
316 /// A certificate is represented as CoseSign1. The `payload` field of CoseSign1 holds the CBOR
317 /// encoded payload that was signed.
318 pub signature: CoseSign1,
319 /// The payload signed in the certificate is partially decoded as
320 /// `DiceChainEntryPayloadPartiallyDecoded` for validation purposes.
321 pub payload: DiceChainEntryPayloadPartiallyDecoded,
322 }
323
324 /// Partially decoded payload for each entry in the DICE chain
325 #[derive(Default, Clone, PartialEq)]
326 pub struct DiceChainEntryPayloadPartiallyDecoded {
327 /// Issuer of the DiceChainEntry. Required as per the CDDL.
328 pub issuer: Option<String>,
329 /// The party whom the certificate is issued to. Required as per the CDDL.
330 pub subject: Option<String>,
331 /// Public signing key of the party whom the certificate is issued to. Required as per the CDDL.
332 pub subject_pub_key: Option<EcVerifyKey>,
333 /// The complete CBOR map containing all the fields (including the fields above) of the
334 /// DiceChainEntryPayload
335 pub full_map: Option<Value>,
336 }
337
338 /// Payload for each entry in the DICE chain
339 #[derive(Default, Clone, PartialEq)]
340 pub struct DiceChainEntryPayload {
341 /// Issuer of the DiceChainEntry. Required as per the CDDL.
342 pub issuer: Option<String>,
343 /// The party whom the certificate is issued to. Required as per the CDDL.
344 pub subject: Option<String>,
345 /// Profile name. Required as per the CDDL.
346 pub profile_name: Option<String>,
347 /// Public signing key of the party whom the certificate is issued to. Required as per the CDDL.
348 pub subject_pub_key: Option<EcVerifyKey>,
349 /// Usage of the key pair corresponding to `subject_public_key`. Required as per the CDDL.
350 pub key_usage: Option<Vec<u8>>,
351 /// Code hash. Required as per the CDDL.
352 pub code_hash: Option<Vec<u8>>,
353 /// Code descriptor. Optional as per the CDDL.
354 pub code_descriptor: Option<Vec<u8>>,
355 /// Configuration hash. Required as per the CDDL.
356 pub configuration_hash: Option<Vec<u8>>,
357 /// Configuration descriptor. Required as per the CDDL.
358 pub configuration_descriptor: Option<ConfigurationDescriptorOrLegacy>,
359 /// Authority hash. Required as per the CDDL.
360 pub authority_hash: Option<Vec<u8>>,
361 /// Authority descriptor. Optional as per the CDDL.
362 pub authority_descriptor: Option<Vec<u8>>,
363 /// Mode. Required as per the CDDL.
364 pub mode: Option<Vec<u8>>,
365 /// Any custom fields, if present
366 pub custom_fields: Vec<(i64, Value)>,
367 }
368
369 /// Configuration descriptor in `DiceChainEntryPayload`. All the fields are optional
370 #[derive(Default, Clone, PartialEq)]
371 pub struct ConfigurationDescriptor {
372 /// Component name
373 pub component_name: Option<String>,
374 /// Component version
375 pub component_version: Option<ComponentVersion>,
376 /// Resettable. If the field is present, the value is true, otherwise, it is false.
377 pub resettable: bool,
378 /// Security version
379 pub security_version: Option<u32>,
380 /// RKP VM Marker. If the field is present, the value is true, otherwise, it is false.
381 pub rkp_vm_marker: bool,
382 /// Any custom fields, if present
383 pub custom_fields: Vec<(i64, Value)>,
384 }
385
386 /// Configuration descriptor that allows for non-spec compliant legacy values.
387 #[derive(Clone, PartialEq)]
388 pub enum ConfigurationDescriptorOrLegacy {
389 /// Configuration descriptor complying with the CDDL schema.
390 Descriptor(ConfigurationDescriptor),
391 /// Raw legacy configuration descriptor (b/261647022).
392 Legacy(Vec<u8>),
393 }
394
395 /// Component version can be either an integer or a string, as per the CDDL.
396 #[derive(Clone, PartialEq)]
397 pub enum ComponentVersion {
398 /// Version represented as an integer
399 IntVersion(u32),
400 /// Version represented as a string
401 TextVersion(String),
402 }
403
404 /// Identity verification policy specifying how to validate the certificate chain. The CDDL is
405 /// listed in hardware/interfaces/security/authgraph/aidl/android/hardware/security/authgraph/
406 /// DicePolicy.cddl
407 #[derive(Clone, Eq, PartialEq)]
408 pub struct Policy(pub Vec<u8>);
409
410 /// The output of identity verification.
411 pub enum IdentityVerificationDecision {
412 /// The latest certificate chain is allowed by the identity verification policy, the identity
413 /// owner is not updated
414 Match,
415 /// The latest certificate chain is not allowed by the identity verification policy
416 Mismatch,
417 /// The latest certificate chain is allowed by the identity verification policy and the identity
418 /// owner is updated
419 Updated,
420 }
421
422 /// The structure containing the inputs for the `salt` used in extracting a pseudo random key
423 /// from the Diffie-Hellman secret.
424 /// salt = bstr .cbor [
425 /// source_version: int,
426 /// sink_ke_pub_key: bstr .cbor PlainPubKey,
427 /// source_ke_pub_key: bstr .cbor PlainPubKey,
428 /// sink_ke_nonce: bstr .size 16,
429 /// source_ke_nonce: bstr .size 16,
430 /// sink_cert_chain: bstr .cbor ExplicitKeyDiceCertChain,
431 /// source_cert_chain: bstr .cbor ExplicitKeyDiceCertChain,
432 /// ]
433 pub struct SaltInput {
434 /// Version advertised by the source (P1).
435 pub source_version: i32,
436 /// Public key from sink for key exchange
437 pub sink_ke_pub_key: EcExchangeKeyPub,
438 /// Public key from source for ke exchange
439 pub source_ke_pub_key: EcExchangeKeyPub,
440 /// Nonce from sink for key exchange
441 pub sink_ke_nonce: Nonce16,
442 /// Nonce from source for key exchange
443 pub source_ke_nonce: Nonce16,
444 /// ExplicitKeyDiceCertChain of sink
445 pub sink_cert_chain: CertChain,
446 /// ExplicitKeyDiceCertChain of source
447 pub source_cert_chain: CertChain,
448 }
449
450 /// The structure containing the inputs for the `session_id` computed during key agreement.
451 /// session_id = bstr .cbor [
452 /// sink_ke_nonce: bstr .size 16,
453 /// source_ke_nonce: bstr .size 16,
454 /// ]
455 pub struct SessionIdInput {
456 /// Nonce from sink for key exchange
457 pub sink_ke_nonce: Nonce16,
458 /// Nonce from source for key exchange
459 pub source_ke_nonce: Nonce16,
460 }
461
462 impl Identity {
463 /// A helper function to validate the peer's identity. The validation is mainly about the
464 /// Dice certificate chain (see `validate` method on `CertChain`), which is part of the
465 /// identity. Peer's identity is validated when the peer is authenticated (i.e. during
466 /// verification of the signature of the peer). Return the signature verification key upon
467 /// successful validation.
validate(&self, ecdsa: &dyn EcDsa) -> Result<EcVerifyKey, Error>468 pub fn validate(&self, ecdsa: &dyn EcDsa) -> Result<EcVerifyKey, Error> {
469 if self.version != IDENTITY_VERSION {
470 return Err(ag_err!(InvalidIdentity, "version mismatch"));
471 }
472 self.cert_chain.validate(ecdsa)
473 // TODO: Assume that the policy is None for now.
474 }
475 }
476
477 impl AsCborValue for Identity {
from_cbor_value(value: Value) -> Result<Self, CoseError>478 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
479 let mut array = match value {
480 Value::Array(a) if a.len() == 3 || a.len() == 2 => a,
481 _ => {
482 return Err(CoseError::UnexpectedItem("_", "array with two or three items"));
483 }
484 };
485 // TODO: Assume policy is none for now
486 let cert_chain = match array.remove(1) {
487 Value::Bytes(cert_chain_encoded) => CertChain::from_slice(&cert_chain_encoded)?,
488 _ => {
489 return Err(CoseError::UnexpectedItem("_", "encoded CertChain"));
490 }
491 };
492 let version: i32 = match array.remove(0) {
493 Value::Integer(i) => i.try_into()?,
494 _ => {
495 return Err(CoseError::UnexpectedItem("_", "Integer"));
496 }
497 };
498 Ok(Identity { version, cert_chain, policy: None })
499 }
500
to_cbor_value(self) -> Result<Value, CoseError>501 fn to_cbor_value(self) -> Result<Value, CoseError> {
502 let mut array = Vec::<Value>::new();
503 array.try_push(Value::Integer(self.version.into())).map_err(|_| CoseError::EncodeFailed)?;
504 array
505 .try_push(Value::Bytes(self.cert_chain.to_vec()?))
506 .map_err(|_| CoseError::EncodeFailed)?;
507 // TODO: encode policy if present
508 Ok(Value::Array(array))
509 }
510 }
511
512 impl CborSerializable for Identity {}
513
514 impl CertChain {
515 /// Perform the following validations on the decoded DICE cert chain:
516 /// 1. correctness of the `version`
517 /// 2. `root_key` is in accordance with Core Deterministic Encoding Requirements
518 /// [RFC 8949 s4.2.1]
519 /// 3. correctness of Cose key parameters of the `root_key`
520 /// 4. if dice_cert_chain is present, check for each DiceChainEntry,
521 /// i. Cose key parameters of `subject_pub_key`
522 /// ii. the signature is verified with the parent's `subject_pub_key` or with the `root_key`
523 /// for the first DiceChainEntry
524 /// iii.`subject` in the parent's DiceChainEntryPayload matches the `issuer` in the current
525 /// DiceChainEntryPayload (except for the first DiceChainEntry)
526 /// iv. no two identical `subject` or `subject_pub_key` in the DiceChainEntryPayloads.
validate(&self, ecdsa: &dyn EcDsa) -> Result<EcVerifyKey, Error>527 pub fn validate(&self, ecdsa: &dyn EcDsa) -> Result<EcVerifyKey, Error> {
528 if self.version != EXPLICIT_KEY_DICE_CERT_CHAIN_VERSION {
529 return Err(ag_err!(InvalidCertChain, "version mismatch"));
530 }
531 if !self.root_key.is_canonicalized() {
532 return Err(ag_err!(
533 InvalidCertChain,
534 "root key is not in the required canonical form"
535 ));
536 }
537 self.root_key.validate_cose_key_params()?;
538 match &self.dice_cert_chain {
539 None => Ok(self.root_key.clone()),
540 Some(dice_chain_entries) => {
541 let mut parent_pub_sign_key = &self.root_key;
542 let mut parent_subj: Option<&String> = None;
543 let mut subj_pub_key_list = Vec::<&EcVerifyKey>::new();
544 subj_pub_key_list.try_reserve(dice_chain_entries.len())?;
545 for (i, dice_chain_entry) in dice_chain_entries.iter().enumerate() {
546 let subject_pub_key =
547 &dice_chain_entry.payload.subject_pub_key.as_ref().ok_or_else(|| {
548 ag_err!(InternalError, "subject public key is missing")
549 })?;
550 subject_pub_key.validate_cose_key_params()?;
551
552 let subject = &dice_chain_entry
553 .payload
554 .subject
555 .as_ref()
556 .ok_or_else(|| ag_err!(InternalError, "subject is missing"))?;
557 dice_chain_entry.signature.verify_signature(&[], |sig, data| {
558 ecdsa.verify_signature(parent_pub_sign_key, data, sig)
559 })?;
560
561 if i != 0
562 && *parent_subj.ok_or_else(|| {
563 ag_err!(InvalidCertChain, "parent's subject field is not initialized")
564 })? != *dice_chain_entry
565 .payload
566 .issuer
567 .as_ref()
568 .ok_or_else(|| ag_err!(InvalidCertChain, "issuer is missing"))?
569 {
570 return Err(ag_err!(
571 InvalidCertChain,
572 "parent's subject does not match the current issuer"
573 ));
574 }
575
576 if subj_pub_key_list.contains(subject_pub_key) {
577 return Err(ag_err!(InvalidCertChain, "subject public key is repeated"));
578 }
579
580 parent_pub_sign_key = subject_pub_key;
581 subj_pub_key_list.push(subject_pub_key);
582 parent_subj = Some(subject);
583 }
584 Ok(parent_pub_sign_key.clone())
585 }
586 }
587 }
588 }
589
590 impl AsCborValue for CertChain {
from_cbor_value(value: Value) -> Result<Self, CoseError>591 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
592 let mut array = match value {
593 Value::Array(a) if a.len() >= 2 => a,
594 _ => {
595 return Err(CoseError::UnexpectedItem("_", "array with two or more items"));
596 }
597 };
598 let dice_chain_entries_optional = if array.len() > 2 {
599 let mut dice_chain_entries = Vec::<DiceChainEntry>::new();
600 // TODO: find the correct CoseError to return
601 dice_chain_entries.try_reserve(array.len() - 2).map_err(|_| CoseError::EncodeFailed)?;
602 for i in (2..array.len()).rev() {
603 let dice_chain_entry_encoded = array.remove(i);
604 let dice_chain_entry = DiceChainEntry::from_cbor_value(dice_chain_entry_encoded)?;
605 dice_chain_entries.push(dice_chain_entry);
606 }
607 dice_chain_entries.reverse();
608 Some(dice_chain_entries)
609 } else {
610 None
611 };
612 let root_cose_key = match array.remove(1) {
613 Value::Bytes(root_key_encoded) => {
614 let cose_key = CoseKey::from_slice(&root_key_encoded)?;
615 EcVerifyKey::from_cose_key(cose_key)?
616 }
617 _ => {
618 return Err(CoseError::UnexpectedItem("_", "encoded CoseKey"));
619 }
620 };
621 let version: i32 = match array.remove(0) {
622 Value::Integer(i) => i.try_into()?,
623 _ => {
624 return Err(CoseError::UnexpectedItem("_", "Integer"));
625 }
626 };
627 Ok(CertChain {
628 version,
629 root_key: root_cose_key,
630 dice_cert_chain: dice_chain_entries_optional,
631 })
632 }
633
to_cbor_value(mut self) -> Result<Value, CoseError>634 fn to_cbor_value(mut self) -> Result<Value, CoseError> {
635 let mut array = Vec::<Value>::new();
636 array.try_reserve(2).map_err(|_| CoseError::EncodeFailed)?;
637 array.push(Value::Integer(self.version.into()));
638 // Prepare the root key to be encoded in accordance with
639 // Core Deterministic Encoding Requirements [RFC 8949 s4.2.1], as specified in
640 // hardware/interfaces/security/authgraph/aidl/android/hardware/security/authgraph/
641 // ExplicitKeyDiceCertChain.cddl
642 self.root_key.canonicalize_cose_key();
643 array.push(Value::Bytes(self.root_key.get_key().to_vec()?));
644 if let Some(dice_chain_entries) = self.dice_cert_chain {
645 let len = dice_chain_entries.len();
646 array.try_reserve(len).map_err(|_| CoseError::EncodeFailed)?;
647 for dice_chain_entry in dice_chain_entries {
648 array.push(dice_chain_entry.to_cbor_value()?);
649 }
650 }
651 Ok(Value::Array(array))
652 }
653 }
654
655 impl CborSerializable for CertChain {}
656
657 impl AsCborValue for DiceChainEntry {
from_cbor_value(value: Value) -> Result<Self, CoseError>658 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
659 let signature = CoseSign1::from_cbor_value(value)?;
660 let payload = DiceChainEntryPayloadPartiallyDecoded::from_slice(
661 signature.payload.as_ref().ok_or(CoseError::EncodeFailed)?,
662 )?;
663 Ok(DiceChainEntry { signature, payload })
664 }
665
to_cbor_value(self) -> Result<Value, CoseError>666 fn to_cbor_value(self) -> Result<Value, CoseError> {
667 // We only need to encode the first field (i.e. `signature`) of `DiceChainEntry` because as
668 // per the CDDL, `DiceChainEntry` is just a CoseSign1. The corresponding Rust struct
669 // contains the additional `payload` field only for the purpose of validation, therefore, it
670 // does not need to be included in the CBOR encoding.
671 self.signature.to_cbor_value()
672 }
673 }
674
675 impl CborSerializable for DiceChainEntryPayloadPartiallyDecoded {}
676
677 impl AsCborValue for DiceChainEntryPayloadPartiallyDecoded {
from_cbor_value(value: Value) -> Result<Self, CoseError>678 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
679 let payload_map = match value {
680 Value::Map(ref map) => map,
681 _ => {
682 return Err(CoseError::UnexpectedItem("non-map", "map of entries"));
683 }
684 };
685 let mut dice_chain_entry_payload = DiceChainEntryPayloadPartiallyDecoded::default();
686 for (key, val) in payload_map {
687 let key_int: i64 = key
688 .as_integer()
689 .ok_or(CoseError::UnexpectedItem("None", "an Integer"))?
690 .try_into()
691 .map_err(|_| CoseError::UnexpectedItem("error", "an Integer convertible to i64"))?;
692 match (key_int, val) {
693 (ISS, Value::Text(issuer)) => match dice_chain_entry_payload.issuer {
694 None => dice_chain_entry_payload.issuer = Some(issuer.to_string()),
695 Some(_) => {
696 return Err(CoseError::UnexpectedItem(
697 "single entry for issuer",
698 "repeated entries for issuer",
699 ));
700 }
701 },
702 (SUB, Value::Text(subject)) => match dice_chain_entry_payload.subject {
703 None => dice_chain_entry_payload.subject = Some(subject.to_string()),
704 Some(_) => {
705 return Err(CoseError::UnexpectedItem(
706 "single entry for subject",
707 "repeated entries for subject",
708 ));
709 }
710 },
711 (SUBJECT_PUBLIC_KEY, Value::Bytes(sp_key_bytes)) => {
712 match dice_chain_entry_payload.subject_pub_key {
713 None => {
714 let cose_key = CoseKey::from_slice(sp_key_bytes)?;
715 let ec_verify_key = EcVerifyKey::from_cose_key(cose_key)?;
716 dice_chain_entry_payload.subject_pub_key = Some(ec_verify_key);
717 }
718 Some(_) => {
719 return Err(CoseError::UnexpectedItem(
720 "single entry for subject public key",
721 "repeated entries for subject public key",
722 ));
723 }
724 }
725 }
726 (_k, _v) => {}
727 }
728 }
729 dice_chain_entry_payload.full_map = Some(value);
730 Ok(dice_chain_entry_payload)
731 }
732
to_cbor_value(self) -> Result<Value, CoseError>733 fn to_cbor_value(self) -> Result<Value, CoseError> {
734 // This is not implemented because Authgraph protocol retrieves an already encoded DICE
735 // chain via `Device` trait and the first field of `DiceChainEntry` has the encoded payload
736 // that is signed.
737 unimplemented!()
738 }
739 }
740
741 impl CborSerializable for DiceChainEntry {}
742
743 impl AsCborValue for DiceChainEntryPayload {
from_cbor_value(value: Value) -> Result<Self, CoseError>744 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
745 let payload_map = match value {
746 Value::Map(map) => map,
747 _ => {
748 return Err(CoseError::UnexpectedItem("non-map", "map of entries"));
749 }
750 };
751 let mut dice_chain_entry_payload = DiceChainEntryPayload::default();
752 for (key, val) in payload_map {
753 let key_int: i64 = key
754 .as_integer()
755 .ok_or(CoseError::UnexpectedItem("None", "an Integer"))?
756 .try_into()
757 .map_err(|_| CoseError::UnexpectedItem("error", "an Integer convertible to i64"))?;
758 match (key_int, val) {
759 (ISS, Value::Text(issuer)) => match dice_chain_entry_payload.issuer {
760 None => dice_chain_entry_payload.issuer = Some(issuer),
761 Some(_) => {
762 return Err(CoseError::UnexpectedItem(
763 "single entry for issuer",
764 "repeated entries for issuer",
765 ));
766 }
767 },
768 (SUB, Value::Text(subject)) => match dice_chain_entry_payload.subject {
769 None => dice_chain_entry_payload.subject = Some(subject),
770 Some(_) => {
771 return Err(CoseError::UnexpectedItem(
772 "single entry for subject",
773 "repeated entries for subject",
774 ));
775 }
776 },
777 (PROFILE_NAME, Value::Text(profile_name)) => {
778 match dice_chain_entry_payload.profile_name {
779 None => dice_chain_entry_payload.profile_name = Some(profile_name),
780 Some(_) => {
781 return Err(CoseError::UnexpectedItem(
782 "single entry for profile name",
783 "repeated entries for profile name",
784 ));
785 }
786 }
787 }
788 (SUBJECT_PUBLIC_KEY, Value::Bytes(sp_key_bytes)) => {
789 match dice_chain_entry_payload.subject_pub_key {
790 None => {
791 let cose_key = CoseKey::from_slice(&sp_key_bytes)?;
792 let ec_verify_key = EcVerifyKey::from_cose_key(cose_key)?;
793 dice_chain_entry_payload.subject_pub_key = Some(ec_verify_key);
794 }
795 Some(_) => {
796 return Err(CoseError::UnexpectedItem(
797 "single entry for subject public key",
798 "repeated entries for subject public key",
799 ));
800 }
801 }
802 }
803 (KEY_USAGE, Value::Bytes(key_usage)) => match dice_chain_entry_payload.key_usage {
804 None => dice_chain_entry_payload.key_usage = Some(key_usage),
805 Some(_) => {
806 return Err(CoseError::UnexpectedItem(
807 "single entry for key usage",
808 "repeated entries for key usage",
809 ));
810 }
811 },
812 (CODE_HASH, Value::Bytes(code_hash)) => match dice_chain_entry_payload.code_hash {
813 None => dice_chain_entry_payload.code_hash = Some(code_hash),
814 Some(_) => {
815 return Err(CoseError::UnexpectedItem(
816 "single entry for code hash",
817 "repeated entries for code hash",
818 ));
819 }
820 },
821 (CODE_DESC, Value::Bytes(code_desc)) => {
822 match dice_chain_entry_payload.code_descriptor {
823 None => dice_chain_entry_payload.code_descriptor = Some(code_desc),
824 Some(_) => {
825 return Err(CoseError::UnexpectedItem(
826 "single or no entry for code descriptors",
827 "repeated entries for code descriptor",
828 ));
829 }
830 }
831 }
832 (CONFIG_HASH, Value::Bytes(config_hash)) => {
833 match dice_chain_entry_payload.configuration_hash {
834 None => dice_chain_entry_payload.configuration_hash = Some(config_hash),
835 Some(_) => {
836 return Err(CoseError::UnexpectedItem(
837 "single entry for configuration hash",
838 "repeated entries for configuration hash",
839 ));
840 }
841 }
842 }
843 (CONFIG_DESC, Value::Bytes(config_desc)) => {
844 match dice_chain_entry_payload.configuration_descriptor {
845 None => {
846 let desc = match ConfigurationDescriptor::from_slice(&config_desc) {
847 Ok(desc) => ConfigurationDescriptorOrLegacy::Descriptor(desc),
848 Err(_) => {
849 // Allow for legacy devices that use a different format
850 // (b/261647022).
851 ConfigurationDescriptorOrLegacy::Legacy(config_desc)
852 }
853 };
854 dice_chain_entry_payload.configuration_descriptor = Some(desc);
855 }
856 Some(_) => {
857 return Err(CoseError::UnexpectedItem(
858 "single entry for configuration descriptor",
859 "repeated entries for configuration descriptor",
860 ));
861 }
862 }
863 }
864 (AUTHORITY_HASH, Value::Bytes(authority_hash)) => {
865 match dice_chain_entry_payload.authority_hash {
866 None => dice_chain_entry_payload.authority_hash = Some(authority_hash),
867 Some(_) => {
868 return Err(CoseError::UnexpectedItem(
869 "single entry for authority hash",
870 "repeated entries for authority hash",
871 ));
872 }
873 }
874 }
875 (AUTHORITY_DESC, Value::Bytes(authority_desc)) => {
876 match dice_chain_entry_payload.authority_descriptor {
877 None => {
878 dice_chain_entry_payload.authority_descriptor = Some(authority_desc)
879 }
880 Some(_) => {
881 return Err(CoseError::UnexpectedItem(
882 "single or no entry for authority descriptors",
883 "repeated entries for authority descriptor",
884 ));
885 }
886 }
887 }
888 (MODE, Value::Bytes(mode)) => match dice_chain_entry_payload.mode {
889 None => dice_chain_entry_payload.mode = Some(mode),
890 Some(_) => {
891 return Err(CoseError::UnexpectedItem(
892 "single entry for mode",
893 "repeated entries for mode",
894 ));
895 }
896 },
897 (k, v) => {
898 dice_chain_entry_payload
899 .custom_fields
900 .try_push((k, v))
901 .map_err(|_| CoseError::EncodeFailed)?;
902 }
903 }
904 }
905 Ok(dice_chain_entry_payload)
906 }
907
to_cbor_value(self) -> Result<Value, CoseError>908 fn to_cbor_value(self) -> Result<Value, CoseError> {
909 // This is not implemented because Authgraph protocol retrieves an already encoded DICE
910 // chain via `Device` trait and the first field of `DiceChainEntry` has the encoded payload
911 // that is signed.
912 unimplemented!()
913 }
914 }
915
916 impl CborSerializable for DiceChainEntryPayload {}
917
918 impl AsCborValue for ConfigurationDescriptor {
from_cbor_value(value: Value) -> Result<Self, CoseError>919 fn from_cbor_value(value: Value) -> Result<Self, CoseError> {
920 let config_desc_map = match value {
921 Value::Map(map) => map,
922 _ => {
923 return Err(CoseError::UnexpectedItem("non-map", "map of entries"));
924 }
925 };
926 let mut config_descriptor = ConfigurationDescriptor::default();
927 for (key, val) in config_desc_map {
928 let key_int: i64 = key
929 .as_integer()
930 .ok_or(CoseError::UnexpectedItem("None", "an Integer"))?
931 .try_into()
932 .map_err(|_| CoseError::UnexpectedItem("error", "an Integer convertible to i64"))?;
933 match (key_int, val) {
934 (COMPONENT_NAME, Value::Text(comp_name)) => {
935 config_descriptor.component_name = Some(comp_name);
936 }
937 (COMPONENT_VERSION, Value::Text(comp_version)) => {
938 config_descriptor.component_version =
939 Some(ComponentVersion::TextVersion(comp_version));
940 }
941 (COMPONENT_VERSION, Value::Integer(comp_version)) => {
942 config_descriptor.component_version =
943 Some(ComponentVersion::IntVersion(comp_version.try_into().map_err(
944 |_| CoseError::UnexpectedItem("error", "an Integer convertible to u32"),
945 )?));
946 }
947 (RESETTABLE, Value::Null) => {
948 config_descriptor.resettable = true;
949 }
950 (SECURITY_VERSION, Value::Integer(security_version)) => {
951 config_descriptor.security_version =
952 Some(security_version.try_into().map_err(|_| {
953 CoseError::UnexpectedItem("error", "an Integer convertible to u32")
954 })?);
955 }
956 (RKP_VM_MARKER, Value::Null) => {
957 config_descriptor.rkp_vm_marker = true;
958 }
959 (k, v) => {
960 config_descriptor
961 .custom_fields
962 .try_push((k, v))
963 .map_err(|_| CoseError::EncodeFailed)?;
964 }
965 }
966 }
967 Ok(config_descriptor)
968 }
969
to_cbor_value(self) -> Result<Value, CoseError>970 fn to_cbor_value(self) -> Result<Value, CoseError> {
971 // This is not implemented because Authgraph protocol retrieves an already encoded DICE
972 // chain via `Device` trait and the first field of `DiceChainEntry` has the encoded payload
973 // that is signed.
974 unimplemented!()
975 }
976 }
977
978 impl CborSerializable for ConfigurationDescriptor {}
979
980 impl AsCborValue for SaltInput {
from_cbor_value(_value: Value) -> Result<Self, CoseError>981 fn from_cbor_value(_value: Value) -> Result<Self, CoseError> {
982 // This method will never be called, except (maybe) in case of unit testing
983 Err(CoseError::EncodeFailed)
984 }
985
to_cbor_value(self) -> Result<Value, CoseError>986 fn to_cbor_value(self) -> Result<Value, CoseError> {
987 let mut array = Vec::<Value>::new();
988 array.try_reserve(7).map_err(|_| CoseError::EncodeFailed)?;
989 array.push(Value::Integer(self.source_version.into()));
990 array.push(Value::Bytes(self.sink_ke_pub_key.0.to_vec()?));
991 array.push(Value::Bytes(self.source_ke_pub_key.0.to_vec()?));
992 array.push(Value::Bytes(self.sink_ke_nonce.0.to_vec()));
993 array.push(Value::Bytes(self.source_ke_nonce.0.to_vec()));
994 array.push(Value::Bytes(self.sink_cert_chain.to_vec()?));
995 array.push(Value::Bytes(self.source_cert_chain.to_vec()?));
996 Ok(Value::Array(array))
997 }
998 }
999
1000 impl CborSerializable for SaltInput {}
1001
1002 impl AsCborValue for SessionIdInput {
from_cbor_value(_value: Value) -> Result<Self, CoseError>1003 fn from_cbor_value(_value: Value) -> Result<Self, CoseError> {
1004 // This method will never be called, except (maybe) in case of unit testing
1005 Err(CoseError::EncodeFailed)
1006 }
1007
to_cbor_value(self) -> Result<Value, CoseError>1008 fn to_cbor_value(self) -> Result<Value, CoseError> {
1009 let mut array = Vec::<Value>::new();
1010 array.try_reserve(2).map_err(|_| CoseError::EncodeFailed)?;
1011 array.push(Value::Bytes(self.sink_ke_nonce.0.to_vec()));
1012 array.push(Value::Bytes(self.source_ke_nonce.0.to_vec()));
1013 Ok(Value::Array(array))
1014 }
1015 }
1016
1017 impl CborSerializable for SessionIdInput {}
1018
1019 /// Given a `CoseKey` and the set of expected parameters, check if the `CoseKey` contains them.
check_cose_key_params( cose_key: &coset::CoseKey, want_kty: iana::KeyType, want_alg: iana::Algorithm, want_curve: iana::EllipticCurve, err_code: ErrorCode, ) -> Result<(), Error>1020 pub fn check_cose_key_params(
1021 cose_key: &coset::CoseKey,
1022 want_kty: iana::KeyType,
1023 want_alg: iana::Algorithm,
1024 want_curve: iana::EllipticCurve,
1025 err_code: ErrorCode,
1026 ) -> Result<(), Error> {
1027 if cose_key.kty != coset::KeyType::Assigned(want_kty) {
1028 return Err(ag_verr!(err_code, "invalid kty {:?}, expect {want_kty:?}", cose_key.kty));
1029 }
1030 if cose_key.alg != Some(coset::Algorithm::Assigned(want_alg)) {
1031 return Err(ag_verr!(err_code, "invalid alg {:?}, expect {want_alg:?}", cose_key.alg));
1032 }
1033 let curve = cose_key
1034 .params
1035 .iter()
1036 .find_map(|(l, v)| match (l, v) {
1037 (Label::Int(l), Value::Integer(v)) if *l == iana::Ec2KeyParameter::Crv as i64 => {
1038 Some(*v)
1039 }
1040 _ => None,
1041 })
1042 .ok_or_else(|| ag_verr!(err_code, "no curve"))?;
1043 if curve != cbor::value::Integer::from(want_curve as u64) {
1044 return Err(ag_verr!(err_code, "invalid curve {curve:?}, expect {want_curve:?}"));
1045 }
1046 Ok(())
1047 }
1048
1049 #[cfg(test)]
1050 mod tests {
1051 use super::*;
1052
1053 #[test]
test_legacy_open_dice_payload()1054 fn test_legacy_open_dice_payload() {
1055 // Some legacy devices have an open-DICE format config descriptor (b/261647022) rather than
1056 // the format used in the Android RKP HAL. Ensure that they still parse.
1057 let data = hex::decode(concat!(
1058 "a8", // 8-map
1059 "01", // Issuer:
1060 "7828", // 40-tstr
1061 "32336462613837333030633932323934663836333566323738316464346633366362313934383835",
1062 "02", // Subject:
1063 "7828", // 40-tstr
1064 "33376165616366396230333465643064376166383665306634653431656163356335383134343966",
1065 "3a00474450", // Code Hash(-4670545):
1066 "5840", // 64-bstr
1067 "3c9aa93a6766f16f5fbd3dfc7e5059b39cdc8aa0cf546cc878d588a69cfcd654",
1068 "2fa509bd6cc14b7160a6bf34545ffdd840f0e91e35b274a7a952b5b0efcff1b0",
1069 "3a00474453", // Configuration Descriptor (-4670548):
1070 "5840", // 64-bstr
1071 // The RKP HAL expects the following data to match schema:
1072 //
1073 // { ? -70002 : tstr, ? -70003 : int / tstr, ? -70004 : null,
1074 // ? -70005 : uint, ? -70006 : null, }
1075 //
1076 // However, the open-DICE spec had:
1077 // If the configuration input is a hash this field contains the original
1078 // configuration data that was hashed. If it is not a hash, this field contains the
1079 // exact 64-byte configuration input value used to compute CDI values."
1080 "e2000000000001508609939b5a4f0f0800000000000000000101000000000000",
1081 "0000000000000000000000000000000000000000000000000000000000000000",
1082 "3a00474454", // Authority Hash (-4670549):
1083 "5840", // 64-bstr
1084 "4d00da66eabbb2b684641a57e96c8e64d76df1e31ea203bbbb9f439372c1a8ec",
1085 "aa550000aa550000aa550000aa550000aa550000aa550000aa550000aa550000",
1086 "3a00474456", // Mode (-4670551):
1087 "4101", // 1-bstr value 0x01
1088 "3a00474457", // Subject Public Key (-4670552):
1089 "5871", // 113-bstr
1090 "a601020338220481022002215830694a8fa269c3375b770ef61d06dec5a78595",
1091 "2ee96db3602b57c50d8fa67f97e874fbd3f5b42e66ac8ead3f3eb3b130f42258",
1092 "301b5574256be9f4770c3325422e53981b1a969387068a51aea68fe98f779be5",
1093 "75ecb077a60106852af654377e56d446a6",
1094 "3a00474458", // Key Usage (-4670553):
1095 "4120" // 1-bstr value 0x20
1096 ))
1097 .unwrap();
1098
1099 assert!(DiceChainEntryPayloadPartiallyDecoded::from_slice(&data).is_ok());
1100 assert!(DiceChainEntryPayload::from_slice(&data).is_ok());
1101 }
1102 }
1103