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