diff --git a/src/error/mod.rs b/src/error/mod.rs index cf1b053..9dcd61f 100644 --- a/src/error/mod.rs +++ b/src/error/mod.rs @@ -17,7 +17,7 @@ use core::any::type_name; use core::fmt::{Display, Formatter}; use ciborium::value::Value; -use coset::{Algorithm, CoseError, CoseKey, KeyOperation, KeyType, Label}; +use coset::{Algorithm, CoseError, CoseKey, CoseRecipient, KeyOperation, KeyType, Label}; use strum_macros::IntoStaticStr; use {alloc::format, alloc::string::String, alloc::string::ToString}; @@ -264,7 +264,7 @@ where /// headers or the key itself). NoAlgorithmDeterminable, /// The provided key does not support the given operation. - KeyOperationNotPermitted(BTreeSet, KeyOperation), + KeyOperationNotPermitted(BTreeSet, BTreeSet), /// Key in given curve must be in different format. KeyTypeCurveMismatch(KeyType, EllipticCurve), /// Provided algorithm requires a different key type. @@ -296,6 +296,11 @@ where /// In the latter case, the error field will contain a list of all attempted keys and the /// corresponding error. NoMatchingKeyFound(Vec<(CoseKey, CoseCipherError)>), + /// TODO docs + NoDecryptableRecipientFound( + Vec<(CoseRecipient, Vec<(CoseKey, CoseCipherError)>)>, + Vec<(CoseKey, CoseCipherError)>, + ), /// A different error has occurred. Details are provided in the contained error. Other(T), } diff --git a/src/token/cose/encrypted/encrypt/mod.rs b/src/token/cose/encrypted/encrypt/mod.rs index a31e7f1..e8dc47f 100644 --- a/src/token/cose/encrypted/encrypt/mod.rs +++ b/src/token/cose/encrypted/encrypt/mod.rs @@ -57,28 +57,28 @@ pub trait CoseEncryptBuilderExt: Sized { /// # Examples /// /// TODO - fn try_encrypt( + fn try_encrypt( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + protected: Option
, unprotected: Option
, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; } impl CoseEncryptBuilderExt for CoseEncryptBuilder { - fn try_encrypt( + fn try_encrypt( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + protected: Option
, unprotected: Option
, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { let mut builder = self; if let Some(protected) = &protected { @@ -89,18 +89,19 @@ impl CoseEncryptBuilderExt for CoseEncryptBuilder { } builder.try_create_ciphertext( payload, - external_aad.lookup_aad( - Some(EncryptionContext::CoseEncrypt), - protected.as_ref(), - unprotected.as_ref(), - ), + external_aad + .lookup_aad( + Some(EncryptionContext::CoseEncrypt), + protected.as_ref(), + unprotected.as_ref(), + ) + .unwrap_or(&[] as &[u8]), |plaintext, aad| { encrypted::try_encrypt( backend, key_provider, protected.as_ref(), unprotected.as_ref(), - try_all_keys, plaintext, aad, ) @@ -151,12 +152,12 @@ pub trait CoseEncryptExt { /// # Examples /// /// TODO - fn try_decrypt( + fn try_decrypt( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result, CoseCipherError>; /// Attempts to decrypt the payload contained in this object using a cryptographic backend, @@ -199,39 +200,39 @@ pub trait CoseEncryptExt { fn try_decrypt_with_recipients< B: CoseKeyDistributionCipher + CoseEncryptCipher, CKP: CoseKeyProvider, - CAP: CoseAadProvider, + CAP: CoseAadProvider + ?Sized, >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result, CoseCipherError>; } impl CoseEncryptExt for CoseEncrypt { - fn try_decrypt( + fn try_decrypt( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result, CoseCipherError> { let backend = Rc::new(RefCell::new(backend)); - let key_provider = Rc::new(RefCell::new(key_provider)); self.decrypt( - external_aad.lookup_aad( - Some(EncryptionContext::CoseEncrypt), - Some(&self.protected.header), - Some(&self.unprotected), - ), + external_aad + .lookup_aad( + Some(EncryptionContext::CoseEncrypt), + Some(&self.protected.header), + Some(&self.unprotected), + ) + .unwrap_or(&[] as &[u8]), |ciphertext, aad| { try_decrypt( - &backend, - &key_provider, + backend, + key_provider, &self.protected.header, &self.unprotected, - try_all_keys, ciphertext, aad, ) @@ -242,36 +243,35 @@ impl CoseEncryptExt for CoseEncrypt { fn try_decrypt_with_recipients< B: CoseKeyDistributionCipher + CoseEncryptCipher, CKP: CoseKeyProvider, - CAP: CoseAadProvider, + CAP: CoseAadProvider + ?Sized, >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + external_aad: CAP, ) -> Result, CoseCipherError> { let backend = Rc::new(RefCell::new(backend)); - let key_provider = Rc::new(RefCell::new(key_provider)); - let mut nested_recipient_key_provider = CoseNestedRecipientSearchContext::new( + let nested_recipient_key_provider = CoseNestedRecipientSearchContext::new( &self.recipients, Rc::clone(&backend), - Rc::clone(&key_provider), - try_all_keys, + key_provider, + &external_aad, struct_to_recipient_context(EncryptionContext::CoseEncrypt), ); self.decrypt( - external_aad.lookup_aad( - Some(EncryptionContext::CoseEncrypt), - Some(&self.protected.header), - Some(&self.unprotected), - ), + external_aad + .lookup_aad( + Some(EncryptionContext::CoseEncrypt), + Some(&self.protected.header), + Some(&self.unprotected), + ) + .unwrap_or(&[] as &[u8]), |ciphertext, aad| { try_decrypt( - &backend, - &Rc::new(RefCell::new(&mut nested_recipient_key_provider)), + backend, + &nested_recipient_key_provider, &self.protected.header, &self.unprotected, - true, ciphertext, aad, ) diff --git a/src/token/cose/encrypted/encrypt/tests.rs b/src/token/cose/encrypted/encrypt/tests.rs index bf1e12f..8efa1cf 100644 --- a/src/token/cose/encrypted/encrypt/tests.rs +++ b/src/token/cose/encrypted/encrypt/tests.rs @@ -64,8 +64,8 @@ impl CoseStructTe if recipient.alg == Some(coset::Algorithm::Assigned(Algorithm::Direct)) || determine_algorithm::( None, - recipient.unprotected.as_ref(), recipient.protected.as_ref(), + recipient.unprotected.as_ref(), ) == Ok(coset::iana::Algorithm::Direct) { enc_key = recipient.key.clone(); @@ -83,13 +83,12 @@ impl CoseStructTe recipient_struct_builder = recipient_struct_builder .try_encrypt( backend, - &mut &recipient.key, - true, + &recipient.key, EncryptionContext::EncRecipient, recipient.protected.clone(), recipient.unprotected.clone(), parsed_key.k, - &mut (&[] as &[u8]), + &[] as &[u8], ) .expect("unable to create CoseRecipient structure"); } @@ -98,12 +97,11 @@ impl CoseStructTe .add_recipient(recipient_struct_builder.build()) .try_encrypt( backend, - &mut &enc_key, - false, + &enc_key, encrypt_cfg.protected.clone(), Some(unprotected), &case.input.plaintext.clone().into_bytes(), - &mut encrypt_cfg.external.as_slice(), + encrypt_cfg.external.as_slice(), ) .expect("unable to encrypt Encrypt object") .build() @@ -139,9 +137,9 @@ impl CoseStructTe key_with_alg }) .collect(); - let mut aad = test_case.external.as_slice(); + let aad = test_case.external.as_slice(); - let verify_result = self.try_decrypt_with_recipients(backend, &mut &keys, false, &mut aad); + let verify_result = self.try_decrypt_with_recipients(backend, &keys, aad); if case.fail { verify_result.expect_err("invalid token was successfully verified"); diff --git a/src/token/cose/encrypted/encrypt0/mod.rs b/src/token/cose/encrypted/encrypt0/mod.rs index 0cb4e06..daad962 100644 --- a/src/token/cose/encrypted/encrypt0/mod.rs +++ b/src/token/cose/encrypted/encrypt0/mod.rs @@ -48,38 +48,38 @@ pub trait CoseEncrypt0Ext { /// # Examples /// /// TODO - fn try_decrypt( + fn try_decrypt( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result, CoseCipherError>; } impl CoseEncrypt0Ext for CoseEncrypt0 { - fn try_decrypt( + fn try_decrypt( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result, CoseCipherError> { let backend = Rc::new(RefCell::new(backend)); - let key_provider = Rc::new(RefCell::new(key_provider)); self.decrypt( - external_aad.lookup_aad( - Some(EncryptionContext::CoseEncrypt0), - Some(&self.protected.header), - Some(&self.unprotected), - ), + external_aad + .lookup_aad( + Some(EncryptionContext::CoseEncrypt0), + Some(&self.protected.header), + Some(&self.unprotected), + ) + .unwrap_or(&[] as &[u8]), |ciphertext, aad| { encrypted::try_decrypt( - &backend, - &key_provider, + backend, + key_provider, &self.protected.header, &self.unprotected, - try_all_keys, ciphertext, aad, ) @@ -125,28 +125,28 @@ pub trait CoseEncrypt0BuilderExt: Sized { /// # Examples /// /// TODO - fn try_encrypt( + fn try_encrypt( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + protected: Option
, unprotected: Option
, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; } impl CoseEncrypt0BuilderExt for CoseEncrypt0Builder { - fn try_encrypt( + fn try_encrypt( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + protected: Option
, unprotected: Option
, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { let mut builder = self; if let Some(protected) = &protected { @@ -157,18 +157,19 @@ impl CoseEncrypt0BuilderExt for CoseEncrypt0Builder { } builder.try_create_ciphertext( payload, - external_aad.lookup_aad( - Some(EncryptionContext::CoseEncrypt0), - protected.as_ref(), - unprotected.as_ref(), - ), + external_aad + .lookup_aad( + Some(EncryptionContext::CoseEncrypt0), + protected.as_ref(), + unprotected.as_ref(), + ) + .unwrap_or(&[] as &[u8]), |plaintext, aad| { encrypted::try_encrypt( backend, key_provider, protected.as_ref(), unprotected.as_ref(), - try_all_keys, plaintext, aad, ) diff --git a/src/token/cose/encrypted/encrypt0/tests.rs b/src/token/cose/encrypted/encrypt0/tests.rs index f49d54d..fe0d391 100644 --- a/src/token/cose/encrypted/encrypt0/tests.rs +++ b/src/token/cose/encrypted/encrypt0/tests.rs @@ -53,12 +53,11 @@ impl CoseStructTestHelper for CoseEncrypt0 encrypt0 .try_encrypt( backend, - &mut &recipient.key, - false, + &recipient.key, encrypt0_cfg.protected.clone(), Some(unprotected), - &case.input.plaintext.clone().into_bytes(), - &mut encrypt0_cfg.external.as_slice(), + case.input.plaintext.clone().into_bytes().as_slice(), + encrypt0_cfg.external.as_slice(), ) .expect("unable to encrypt Encrypt0 object") .build() @@ -94,9 +93,9 @@ impl CoseStructTestHelper for CoseEncrypt0 key_with_alg }) .collect(); - let mut aad = test_case.external.as_slice(); + let aad = test_case.external.as_slice(); - let verify_result = self.try_decrypt(backend, &mut &keys, false, &mut aad); + let verify_result = self.try_decrypt(backend, &keys, aad); if case.fail { verify_result.expect_err("invalid token was successfully verified"); diff --git a/src/token/cose/encrypted/mod.rs b/src/token/cose/encrypted/mod.rs index a005ae6..df004ad 100644 --- a/src/token/cose/encrypted/mod.rs +++ b/src/token/cose/encrypted/mod.rs @@ -2,15 +2,12 @@ use alloc::collections::BTreeSet; use alloc::rc::Rc; use alloc::vec::Vec; use core::cell::RefCell; - use coset::{iana, Algorithm, Header, KeyOperation}; use crate::error::CoseCipherError; -use crate::token::cose::header_util::{ - check_for_duplicate_headers, determine_algorithm, determine_key_candidates, HeaderParam, -}; +use crate::token::cose::header_util::HeaderParam; use crate::token::cose::key::{CoseKeyProvider, CoseParsedKey, CoseSymmetricKey}; -use crate::token::cose::{key, CoseCipher}; +use crate::token::cose::{header_util, key, CoseCipher}; mod encrypt; mod encrypt0; @@ -58,128 +55,95 @@ pub trait CoseKeyDistributionCipher: CoseCipher { fn try_encrypt( backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option<&Header>, unprotected: Option<&Header>, - try_all_keys: bool, plaintext: &[u8], // NOTE: this should be treated as the AAD for the purposes of the cryptographic backend // (RFC 9052, Section 5.3). enc_structure: &[u8], ) -> Result, CoseCipherError> { - if let (Some(protected), Some(unprotected)) = (protected, unprotected) { - check_for_duplicate_headers(protected, unprotected)?; - } - let key = determine_key_candidates::( + header_util::try_cose_crypto_operation( key_provider, protected, unprotected, BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Encrypt)]), - try_all_keys, - ) - .next() - .ok_or(CoseCipherError::NoMatchingKeyFound(Vec::new()))?; - let parsed_key = CoseParsedKey::try_from(&key)?; - - match determine_algorithm(Some(&parsed_key), protected, unprotected)? { - alg @ (iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM) => { - // Check if this is a valid AES key. - let symm_key = key::ensure_valid_aes_key::(alg, parsed_key)?; - - let iv = protected - .into_iter() - .chain(unprotected.into_iter()) - .filter(|x| !x.iv.is_empty()) - .map(|x| x.iv.as_ref()) - .next() - .ok_or(CoseCipherError::MissingHeaderParam(HeaderParam::Generic( - iana::HeaderParameter::Iv, - )))?; - - backend.encrypt_aes_gcm(alg, symm_key, plaintext, enc_structure, iv) - } - alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))), - } -} - -fn try_decrypt_with_key( - backend: &mut B, - key: CoseParsedKey, - protected: &Header, - unprotected: &Header, - ciphertext: &[u8], - // NOTE: this should be treated as the AAD for the purposes of the cryptographic backend - // (RFC 9052, Section 5.3). - enc_structure: &[u8], -) -> Result, CoseCipherError> { - check_for_duplicate_headers(protected, unprotected)?; - - match determine_algorithm(Some(&key), Some(protected), Some(unprotected))? { - alg @ (iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM) => { - // Check if this is a valid AES key. - let symm_key = key::ensure_valid_aes_key::(alg, key)?; - - let iv = core::iter::once(protected) - .chain(core::iter::once(unprotected)) - .filter(|x| !x.iv.is_empty()) - .map(|x| x.iv.as_ref()) - .next() - .ok_or(CoseCipherError::MissingHeaderParam(HeaderParam::Generic( - iana::HeaderParameter::Iv, - )))?; - - // Authentication tag is 16 bytes long and should be included in the ciphertext. - if ciphertext.len() < 16 { - return Err(CoseCipherError::VerificationFailure); + |key, alg, protected, unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + match alg { + iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM => { + // Check if this is a valid AES key. + let symm_key = key::ensure_valid_aes_key::(alg, parsed_key)?; + + let iv = protected + .into_iter() + .chain(unprotected.into_iter()) + .filter(|x| !x.iv.is_empty()) + .map(|x| x.iv.as_ref()) + .next() + .ok_or(CoseCipherError::MissingHeaderParam(HeaderParam::Generic( + iana::HeaderParameter::Iv, + )))?; + + backend.encrypt_aes_gcm(alg, symm_key, plaintext, enc_structure, iv) + } + alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))), } - - backend.decrypt_aes_gcm(alg, symm_key, ciphertext, enc_structure, iv) - } - alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))), - } + }, + ) } pub(crate) fn try_decrypt( - backend: &Rc>, - key_provider: &Rc>, + backend: Rc>, + key_provider: &CKP, protected: &Header, unprotected: &Header, - try_all_keys: bool, ciphertext: &[u8], // NOTE: this should be treated as the AAD for the purposes of the cryptographic backend // (RFC 9052, Section 5.3). enc_structure: &[u8], ) -> Result, CoseCipherError> { - check_for_duplicate_headers(protected, unprotected)?; - let mut multi_verification_errors = Vec::new(); - for key in determine_key_candidates::( - *key_provider.borrow_mut(), + header_util::try_cose_crypto_operation( + key_provider, Some(protected), Some(unprotected), BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Decrypt)]), - try_all_keys, - ) { - match try_decrypt_with_key( - *backend.borrow_mut(), - CoseParsedKey::try_from(&key)?, - protected, - unprotected, - ciphertext, - enc_structure, - ) { - Ok(v) => return Ok(v), - Err(e) => { - multi_verification_errors.push((key.clone(), e)); - continue; + |key, alg, protected, unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + match alg { + iana::Algorithm::A128GCM | iana::Algorithm::A192GCM | iana::Algorithm::A256GCM => { + // Check if this is a valid AES key. + let symm_key = key::ensure_valid_aes_key::(alg, parsed_key)?; + + let iv = protected + .into_iter() + .chain(unprotected.into_iter()) + .filter(|x| !x.iv.is_empty()) + .map(|x| x.iv.as_ref()) + .next() + .ok_or(CoseCipherError::MissingHeaderParam(HeaderParam::Generic( + iana::HeaderParameter::Iv, + )))?; + + // Authentication tag is 16 bytes long and should be included in the ciphertext. + if ciphertext.len() < 16 { + return Err(CoseCipherError::VerificationFailure); + } + + (*backend.borrow_mut()).decrypt_aes_gcm( + alg, + symm_key, + ciphertext, + enc_structure, + iv, + ) + } + alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))), } - } - } - - Err(CoseCipherError::NoMatchingKeyFound( - multi_verification_errors, - )) + }, + ) } diff --git a/src/token/cose/header_util.rs b/src/token/cose/header_util.rs index 2b832d6..9910e33 100644 --- a/src/token/cose/header_util.rs +++ b/src/token/cose/header_util.rs @@ -1,13 +1,14 @@ -use alloc::boxed::Box; +use alloc::borrow::ToOwned; use alloc::collections::BTreeSet; use alloc::vec::Vec; use core::fmt::Display; +use alloc::borrow::Borrow; use coset::iana::EnumI64; use coset::{iana, Algorithm, CoseKey, Header, HeaderBuilder, KeyOperation, Label}; use crate::error::CoseCipherError; -use crate::token::cose::key::{CoseKeyProvider, CoseParsedKey}; +use crate::token::cose::key::CoseKeyProvider; use crate::token::cose::{CoseCipher, CoseEncryptCipher}; /// A header parameter that can be used in a COSE header. @@ -79,25 +80,29 @@ pub(crate) fn check_for_duplicate_headers( } } +/// Determines the value of a header param based on the provided `protected` and `unprotected` +/// header buckets and the `accessor` function that determines the header parameter from a header +/// reference. +pub(crate) fn determine_header_param Option, T>( + protected_header: Option<&Header>, + unprotected_header: Option<&Header>, + accessor: F, +) -> Option { + protected_header + .into_iter() + .chain(unprotected_header) + .find_map(accessor) +} + /// Determines the algorithm to use for the signing operation based on the supplied key and headers. pub(crate) fn determine_algorithm( - parsed_key: Option<&CoseParsedKey<'_, CE>>, - unprotected_header: Option<&Header>, + parsed_key: Option<&CoseKey>, protected_header: Option<&Header>, + unprotected_header: Option<&Header>, ) -> Result> { - // Check whether the algorithm has been explicitly set... - let alg = if let Some(Some(alg)) = protected_header.map(|v| v.alg.clone()) { - // ...in the protected header... - Ok(alg) - } else if let Some(Some(alg)) = unprotected_header.map(|v| v.alg.clone()) { - // ...in the unprotected header... - Ok(alg) - } else if let Some(alg) = parsed_key.and_then(|v| v.as_ref().alg.clone()) { - // ...or the key itself. - Ok(alg) - } else { - Err(CoseCipherError::NoAlgorithmDeterminable) - }?; + let alg = determine_header_param(protected_header, unprotected_header, |h| h.alg.clone()) + .or_else(|| parsed_key.and_then(|k| k.alg.clone())) + .ok_or(CoseCipherError::NoAlgorithmDeterminable)?; if let Algorithm::Assigned(alg) = alg { Ok(alg) @@ -106,26 +111,56 @@ pub(crate) fn determine_algorithm( } } -pub(crate) fn determine_key_candidates<'a, CKP: CoseKeyProvider>( - key_provider: &'a mut CKP, +/// Queries the key provider for keys and checks for each returned key whether it is a possible +/// candidate for the operation and algorithm. +/// +/// Returns an iterator that for each key returned by the key provider either returns the key plus +/// algorithm to use or the corresponding error that describes the reason why this key is not +/// suitable. +/// +/// This function performs the algorithm-independent checks for whether a key is a suitable +/// candidate, but not any algorithm-specific checks (e.g. required key parameters, key length, +/// etc.). Those will have to be checked by the caller. +pub(crate) fn determine_key_candidates<'a, CKP: CoseKeyProvider, CE: Display>( + key_provider: &'a CKP, protected: Option<&'a Header>, unprotected: Option<&'a Header>, operation: BTreeSet, - try_all_keys: bool, -) -> Box + 'a> { - let key_id = if try_all_keys { - None - } else { - protected - .map(|v| v.key_id.as_slice()) - .filter(|v| !v.is_empty()) - .or_else(|| unprotected.map(|v| v.key_id.as_slice())) - .filter(|v| !v.is_empty()) - }; - - Box::new(key_provider.lookup_key(key_id).filter(move |k| { - k.key_ops.is_empty() || k.key_ops.intersection(&operation).next().is_some() - })) +) -> impl Iterator)>> + 'a { + let key_id = protected + .map(|v| v.key_id.as_slice()) + .filter(|v| !v.is_empty()) + .or_else(|| unprotected.map(|v| v.key_id.as_slice())) + .filter(|v| !v.is_empty()); + + key_provider.lookup_key(key_id).map(move |k| { + let k_borrow: &CoseKey = k.borrow(); + if !k_borrow.key_ops.is_empty() + && k_borrow.key_ops.intersection(&operation).next().is_some() + { + return Err(( + k_borrow.clone(), + CoseCipherError::KeyOperationNotPermitted( + k_borrow.key_ops.clone(), + operation.clone(), + ), + )); + } + let chosen_alg = determine_algorithm(Some(k_borrow), protected, unprotected) + .map_err(|e| (k_borrow.clone(), e))?; + if let Some(key_alg) = k_borrow.alg.as_ref() { + if Algorithm::Assigned(chosen_alg) != *key_alg { + return Err(( + k_borrow.clone(), + CoseCipherError::KeyAlgorithmMismatch( + key_alg.clone(), + Algorithm::Assigned(chosen_alg), + ), + )); + } + } + Ok((k_borrow.to_owned(), chosen_alg)) + }) } pub trait HeaderBuilderExt: Sized { @@ -160,3 +195,36 @@ impl HeaderBuilderExt for HeaderBuilder { Ok(self.iv(iv)) } } + +pub(crate) fn try_cose_crypto_operation( + key_provider: &CKP, + protected: Option<&Header>, + unprotected: Option<&Header>, + key_ops: BTreeSet, + mut op: F, +) -> Result> +where + F: FnMut( + &CoseKey, + iana::Algorithm, + Option<&Header>, + Option<&Header>, + ) -> Result>, +{ + if let (Some(protected), Some(unprotected)) = (protected, unprotected) { + check_for_duplicate_headers(protected, unprotected)?; + } + let mut multi_verification_errors = Vec::new(); + for kc in determine_key_candidates::(key_provider, protected, unprotected, key_ops) { + multi_verification_errors.push(match kc { + Ok((key, alg)) => match op(&key, alg, protected, unprotected) { + Err(e) => (key, e), + v => return v, + }, + Err(e) => e, + }); + } + Err(CoseCipherError::NoMatchingKeyFound( + multi_verification_errors, + )) +} diff --git a/src/token/cose/key.rs b/src/token/cose/key.rs index f81a350..e1accde 100644 --- a/src/token/cose/key.rs +++ b/src/token/cose/key.rs @@ -1,10 +1,9 @@ -use alloc::boxed::Box; +use alloc::collections::BTreeMap; use alloc::vec::Vec; -use core::borrow::BorrowMut; +use ciborium::Value; +use core::borrow::Borrow; use core::fmt::Display; use core::marker::PhantomData; - -use ciborium::Value; use coset::iana::EnumI64; use coset::{ iana, Algorithm, AsCborValue, CoseKey, EncryptionContext, Header, KeyType, Label, @@ -12,7 +11,7 @@ use coset::{ }; use crate::error::CoseCipherError; -use crate::token::cose::CoseCipher; +use crate::token::cose::{determine_header_param, CoseCipher}; /// Finds a key parameter by its label. fn find_param_by_label<'a>(label: &Label, param_vec: &'a [(Label, Value)]) -> Option<&'a Value> { @@ -391,12 +390,18 @@ impl<'a, OE: Display> AsRef for CoseSymmetricKey<'a, OE> { } /// A trait for types that can provide [CoseKey]s for COSE structure operations. -pub trait CoseKeyProvider { +pub trait CoseKeyProvider: Sized { /// Look up a key for the signature based on the provided `key_id` hint. /// /// The iterator returned should contain all [CoseKey]s of the provider that have a key ID /// matching the one provided, or all [CoseKey]s available if key_id is None. - fn lookup_key(&mut self, key_id: Option<&[u8]>) -> impl Iterator; + fn lookup_key(&self, key_id: Option<&[u8]>) -> impl Iterator>; + + /// Create a [CoseKeyProvider] filtering this key providers output for keys with key IDs + /// matching the COSE structure's header. + fn match_key_ids(self) -> KeyProviderFilterMatchingKeyId { + KeyProviderFilterMatchingKeyId(self) + } } // Unfortunately, this implementation is exclusive with the implementation for &CoseKey, because at @@ -416,51 +421,58 @@ pub trait CoseKeyProvider { } }*/ -impl CoseKeyProvider for &Vec<&CoseKey> { - fn lookup_key(&mut self, key_id: Option<&[u8]>) -> impl Iterator { - let mut iter: Box> = Box::new(self.clone().into_iter()); - if let Some(kid) = key_id { - let test = Vec::from(kid); - iter = Box::new(iter.filter(move |k| k.key_id.as_slice() == test)); - } - iter.cloned() +impl CoseKeyProvider for Vec<&CoseKey> { + fn lookup_key(&self, _key_id: Option<&[u8]>) -> impl Iterator> { + self.clone().into_iter() } } -impl CoseKeyProvider for &Vec { - fn lookup_key(&mut self, key_id: Option<&[u8]>) -> impl Iterator { - let mut iter: Box> = Box::new(self.iter()); - - if let Some(kid) = key_id { - let kid = Vec::from(kid); - iter = Box::new(iter.filter(move |k| k.key_id.as_slice() == kid)); - } - iter.cloned() +impl CoseKeyProvider for Vec { + fn lookup_key(&self, _key_id: Option<&[u8]>) -> impl Iterator> { + self.iter() } } impl CoseKeyProvider for Option<&CoseKey> { - fn lookup_key(&mut self, key_id: Option<&[u8]>) -> impl Iterator { - let ret: Box> = match (self, &key_id) { - (Some(key), Some(key_id)) if key.key_id.as_slice() != *key_id => { - Box::new(core::iter::empty()) - } - (Some(key), Some(_key_id)) => Box::new(core::iter::once(*key)), - (v, _) => Box::new(v.iter().copied()), - }; - ret.cloned() + fn lookup_key(&self, _key_id: Option<&[u8]>) -> impl Iterator> { + self.iter().copied() + } +} + +impl CoseKeyProvider for Option { + fn lookup_key(&self, _key_id: Option<&[u8]>) -> impl Iterator> { + self.iter() + } +} + +impl CoseKeyProvider for CoseKey { + fn lookup_key(&self, _key_id: Option<&[u8]>) -> impl Iterator> { + core::iter::once(self) } } -impl CoseKeyProvider for &CoseKey { - fn lookup_key(&mut self, _key_id: Option<&[u8]>) -> impl Iterator { - core::iter::once(self.clone()) +/// [CoseKeyProvider] that filters another [CoseKeyProvider]s output to only output keys with +/// key IDs matching the ones provided in the COSE structure's headers. +pub struct KeyProviderFilterMatchingKeyId(T); + +impl CoseKeyProvider for KeyProviderFilterMatchingKeyId { + fn lookup_key(&self, key_id: Option<&[u8]>) -> impl Iterator> { + self.0.lookup_key(key_id).filter(move |k| { + let k: &CoseKey = k.borrow(); + key_id.map_or(true, |lookup_kid| k.key_id.as_slice().eq(lookup_kid)) + }) + } +} + +impl CoseKeyProvider for &T { + fn lookup_key(&self, key_id: Option<&[u8]>) -> impl Iterator> { + (*self).lookup_key(key_id) } } /// A trait for types that can determine the corresponding Additional Authenticated Data to be /// provided for a given COSE structure. -pub trait CoseAadProvider: BorrowMut { +pub trait CoseAadProvider: Sized { /// Look up the additional authenticated data for the given COSE structure. /// /// # Parameters @@ -472,101 +484,243 @@ pub trait CoseAadProvider: BorrowMut { /// - `unprotected` - Unprotected headers for the COSE structure for which AAD should be /// provided. fn lookup_aad( - &mut self, + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]>; + + /// Lookup up the additional authenticated data for nested COSE structures nested inside the + /// one whose decryption was actually requested. + /// + /// For the provided implementations, this will usually return an empty slice. + /// If you want to provide AAD for nested structures, either use a tuple + /// `(CoseAadProvider, CoseAadProvider)` or provide a tuple `(CoseAadProvider, bool)`. + /// + /// In the first case, the second arguments' [CoseAadProvider::lookup_aad] + /// will be used as [CoseAadProvider::lookup_nested_aad] if its + /// [CoseAadProvider::lookup_nested_aad] returns `None`. + /// + /// In the latter case, the boolean argument specifies whether [CoseAadProvider::lookup_aad] + /// should be used as a fallback if [CoseAadProvider::lookup_nested_aad] returns `None`. + /// + /// # Parameters + /// + /// - `context` - Type of object that should be encrypted with AAD. + /// If the AAD should be provided for a non-encrypted object, `context` is + /// `None`. + /// - `protected` - Protected headers for the COSE structure for which AAD should be provided. + /// - `unprotected` - Unprotected headers for the COSE structure for which AAD should be + /// provided. + #[allow(unused)] + fn lookup_nested_aad( + &self, context: Option, protected: Option<&Header>, unprotected: Option<&Header>, - ) -> &[u8]; + ) -> Option<&[u8]> { + None + } } -// See above, impossible due to missing specialization feature. -/*impl<'a, T: Iterator> CoseAadProvider for &mut T { - fn lookup_aad(&mut self, _signature: &CoseSignature) -> &'a [u8] { - self.next().map(|v| v.as_ref()).unwrap_or(&[] as &[u8]) +impl CoseAadProvider for Vec { + fn lookup_aad( + &self, + _context: Option, + _protected: Option<&Header>, + _unprotected: Option<&Header>, + ) -> Option<&[u8]> { + Some(self.as_ref()) } -}*/ +} impl CoseAadProvider for &[u8] { fn lookup_aad( - &mut self, - context: Option, + &self, + _context: Option, _protected: Option<&Header>, _unprotected: Option<&Header>, - ) -> &[u8] { - match context { - Some(EncryptionContext::CoseEncrypt | EncryptionContext::CoseEncrypt0) | None => self, - Some( - EncryptionContext::EncRecipient - | EncryptionContext::MacRecipient - | EncryptionContext::RecRecipient, - ) => &[] as &[u8], - } + ) -> Option<&[u8]> { + Some(self) } } impl CoseAadProvider for Option<&[u8]> { fn lookup_aad( - &mut self, - context: Option, + &self, + _context: Option, _protected: Option<&Header>, _unprotected: Option<&Header>, - ) -> &[u8] { - match context { - Some(EncryptionContext::CoseEncrypt | EncryptionContext::CoseEncrypt0) | None => { - self.unwrap_or(&[] as &[u8]) + ) -> Option<&[u8]> { + *self + } +} + +/// Look up additional authenticated data based on the key ID +#[cfg(feature = "std")] +impl, S: core::hash::BuildHasher> CoseAadProvider + for std::collections::HashMap<&[u8], AAD, S> +{ + fn lookup_aad( + &self, + _context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + determine_header_param(protected, unprotected, |v| { + if v.key_id.is_empty() { + None + } else { + Some(v.key_id.clone()) } - Some( - EncryptionContext::EncRecipient - | EncryptionContext::MacRecipient - | EncryptionContext::RecRecipient, - ) => &[] as &[u8], - } + }) + .and_then(|kid| self.get(kid.as_slice())) + .map(AsRef::as_ref) } } -impl<'a, 'b: 'a> CoseAadProvider for core::slice::Iter<'a, &'b [u8]> { +/// Look up additional authenticated data based on the key ID +impl> CoseAadProvider for BTreeMap<&[u8], AAD> { fn lookup_aad( - &mut self, - context: Option, - _protected: Option<&Header>, - _unprotected: Option<&Header>, - ) -> &[u8] { - match context { - Some(EncryptionContext::CoseEncrypt | EncryptionContext::CoseEncrypt0) | None => { - self.next().copied().unwrap_or(&[] as &[u8]) + &self, + _context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + determine_header_param(protected, unprotected, |v| { + if v.key_id.is_empty() { + None + } else { + Some(v.key_id.clone()) } - Some( - EncryptionContext::EncRecipient - | EncryptionContext::MacRecipient - | EncryptionContext::RecRecipient, - ) => &[] as &[u8], - } + }) + .and_then(|kid| self.get(kid.as_slice())) + .map(AsRef::as_ref) } } -impl<'a, 'b: 'a, I: Iterator, F> CoseAadProvider for &'a mut core::iter::Map -where - F: FnMut(I::Item) -> &'b [u8], -{ +/// Look up additional authenticated data based on the key ID +impl, AAD: AsRef<[u8]>> CoseAadProvider for alloc::vec::Vec<(KID, AAD)> { fn lookup_aad( - &mut self, - context: Option, - _protected: Option<&Header>, - _unprotected: Option<&Header>, - ) -> &[u8] { - match context { - Some(EncryptionContext::CoseEncrypt | EncryptionContext::CoseEncrypt0) | None => { - self.next().unwrap_or(&[] as &[u8]) + &self, + _context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + let kid = determine_header_param(protected, unprotected, |v| { + if v.key_id.is_empty() { + None + } else { + Some(v.key_id.clone()) } - Some( - EncryptionContext::EncRecipient - | EncryptionContext::MacRecipient - | EncryptionContext::RecRecipient, - ) => &[] as &[u8], + }); + if let Some(kid) = kid { + self.iter().find_map(|(key_kid, aad)| { + if key_kid.as_ref().eq(kid.as_slice()) { + Some(aad.as_ref()) + } else { + None + } + }) + } else { + None } } } +/// Use T's `lookup_aad` for the normal AAD lookup, use U's `lookup_nested_aad` and `lookup_aad` for +/// nested AAD lookups (`lookup_nested_aad` takes precedence, though). +impl CoseAadProvider for (T, U) { + fn lookup_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + self.0.lookup_aad(context, protected, unprotected) + } + + fn lookup_nested_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + self.1 + .lookup_nested_aad(context, protected, unprotected) + .or_else(|| self.1.lookup_aad(context, protected, unprotected)) + } +} + +/// Swap lookup_aad and lookup_nested_aad of an existing [CoseAadProvider]. +pub struct InvertedAadProvider(pub T); + +impl CoseAadProvider for InvertedAadProvider { + fn lookup_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + self.0.lookup_nested_aad(context, protected, unprotected) + } + + fn lookup_nested_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + self.0.lookup_aad(context, protected, unprotected) + } +} + +impl CoseAadProvider for &T { + fn lookup_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + (*self).lookup_aad(context, protected, unprotected) + } + fn lookup_nested_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + (*self).lookup_nested_aad(context, protected, unprotected) + } +} + +/// Use [CoseAadProvider::lookup_aad] as a fallback for [CoseAadProvider::lookup_nested_aad] if the +/// boolean is `true` and [CoseAadProvider::lookup_nested_aad] returns None. +impl CoseAadProvider for (T, bool) { + fn lookup_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + self.0.lookup_aad(context, protected, unprotected) + } + + fn lookup_nested_aad( + &self, + context: Option, + protected: Option<&Header>, + unprotected: Option<&Header>, + ) -> Option<&[u8]> { + self.0 + .lookup_nested_aad(context, protected, unprotected) + .or(self + .1 + .then(|| self.0.lookup_aad(context, protected, unprotected)) + .flatten()) + } +} + fn symmetric_key_size( algorithm: iana::Algorithm, ) -> Result> { diff --git a/src/token/cose/maced/mac/mod.rs b/src/token/cose/maced/mac/mod.rs index abe1c88..4cb0746 100644 --- a/src/token/cose/maced/mac/mod.rs +++ b/src/token/cose/maced/mac/mod.rs @@ -52,28 +52,28 @@ pub trait CoseMacBuilderExt: Sized { /// # Examples /// /// TODO - fn try_compute( + fn try_compute( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + protected: Option
, unprotected: Option
, payload: Vec, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; } impl CoseMacBuilderExt for CoseMacBuilder { - fn try_compute( + fn try_compute( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + protected: Option
, unprotected: Option
, payload: Vec, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { let mut builder = self; if let Some(protected) = &protected { @@ -84,7 +84,9 @@ impl CoseMacBuilderExt for CoseMacBuilder { } builder = builder.payload(payload); Ok(builder.create_tag( - external_aad.lookup_aad(None, protected.as_ref(), unprotected.as_ref()), + external_aad + .lookup_aad(None, protected.as_ref(), unprotected.as_ref()) + .unwrap_or(&[] as &[u8]), |input| { // TODO proper error handling here try_compute( @@ -92,7 +94,6 @@ impl CoseMacBuilderExt for CoseMacBuilder { key_provider, protected.as_ref(), unprotected.as_ref(), - try_all_keys, input, ) .expect("computing MAC failed") @@ -141,12 +142,12 @@ pub trait CoseMacExt { /// # Examples /// /// TODO - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result<(), CoseCipherError>; /// Attempts to verify the MAC using a cryptographic backend, performing a search through the @@ -185,35 +186,35 @@ pub trait CoseMacExt { fn try_verify_with_recipients< B: CoseKeyDistributionCipher + CoseMacCipher, CKP: CoseKeyProvider, - CAP: CoseAadProvider, + CAP: CoseAadProvider + ?Sized, >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result<(), CoseCipherError>; } impl CoseMacExt for CoseMac { - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result<(), CoseCipherError> { let backend = Rc::new(RefCell::new(backend)); - let key_provider = Rc::new(RefCell::new(key_provider)); self.verify_tag( - external_aad.lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)), + external_aad + .lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)) + .unwrap_or(&[] as &[u8]), |tag, input| { try_verify( &backend, - &key_provider, + key_provider, &self.protected.header, &self.unprotected, - try_all_keys, tag, input, ) @@ -224,33 +225,33 @@ impl CoseMacExt for CoseMac { fn try_verify_with_recipients< B: CoseKeyDistributionCipher + CoseMacCipher, CKP: CoseKeyProvider, - CAP: CoseAadProvider, + CAP: CoseAadProvider + ?Sized, >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result<(), CoseCipherError> { let backend = Rc::new(RefCell::new(backend)); - let key_provider = Rc::new(RefCell::new(key_provider)); // TODO handle iterator errors. - let mut nested_recipient_key_provider = CoseNestedRecipientSearchContext::new( + let nested_recipient_key_provider = CoseNestedRecipientSearchContext::new( &self.recipients, Rc::clone(&backend), - Rc::clone(&key_provider), - try_all_keys, + key_provider, + &external_aad, EncryptionContext::MacRecipient, ); self.verify_tag( - external_aad.lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)), + external_aad + .lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)) + .unwrap_or(&[] as &[u8]), |tag, input| { try_verify( &backend, - &Rc::new(RefCell::new(&mut nested_recipient_key_provider)), + &nested_recipient_key_provider, &self.protected.header, &self.unprotected, - true, tag, input, ) diff --git a/src/token/cose/maced/mac/tests.rs b/src/token/cose/maced/mac/tests.rs index a1a3701..f11e248 100644 --- a/src/token/cose/maced/mac/tests.rs +++ b/src/token/cose/maced/mac/tests.rs @@ -45,8 +45,8 @@ impl CoseStructTestHe if recipient.alg == Some(coset::Algorithm::Assigned(Algorithm::Direct)) || determine_algorithm::( None, - recipient.unprotected.as_ref(), recipient.protected.as_ref(), + recipient.unprotected.as_ref(), ) == Ok(Algorithm::Direct) { enc_key = recipient.key.clone(); @@ -64,13 +64,12 @@ impl CoseStructTestHe recipient_struct_builder = recipient_struct_builder .try_encrypt( backend, - &mut &recipient.key, - true, + &recipient.key, EncryptionContext::EncRecipient, recipient.protected.clone(), recipient.unprotected.clone(), parsed_key.k, - &mut (&[] as &[u8]), + &[] as &[u8], ) .expect("unable to create CoseRecipient structure"); } @@ -78,12 +77,11 @@ impl CoseStructTestHe mac.add_recipient(recipient_struct_builder.build()) .try_compute( backend, - &mut &enc_key, - false, + &enc_key, mac_cfg.protected.clone(), Some(unprotected), case.input.plaintext.clone().into_bytes(), - &mut mac_cfg.external.as_slice(), + mac_cfg.external.as_slice(), ) .expect("unable to encrypt Encrypt object") .build() @@ -115,9 +113,9 @@ impl CoseStructTestHe key_with_alg }) .collect(); - let mut aad = test_case.external.as_slice(); + let aad = test_case.external.as_slice(); - let verify_result = self.try_verify_with_recipients(backend, &mut &keys, false, &mut aad); + let verify_result = self.try_verify_with_recipients(backend, &keys, aad); if case.fail { verify_result.expect_err("invalid token was successfully verified"); diff --git a/src/token/cose/maced/mac0/mod.rs b/src/token/cose/maced/mac0/mod.rs index 158bc95..6a9fa33 100644 --- a/src/token/cose/maced/mac0/mod.rs +++ b/src/token/cose/maced/mac0/mod.rs @@ -44,29 +44,27 @@ pub trait CoseMac0BuilderExt: Sized { /// # Examples /// /// TODO - fn try_compute( + fn try_compute( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, protected: Option
, unprotected: Option
, // TODO remove payload (can be set individually) payload: Vec, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; } impl CoseMac0BuilderExt for CoseMac0Builder { - fn try_compute( + fn try_compute( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, protected: Option
, unprotected: Option
, payload: Vec, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { let mut builder = self; if let Some(protected) = &protected { @@ -77,14 +75,15 @@ impl CoseMac0BuilderExt for CoseMac0Builder { } builder = builder.payload(payload); builder.try_create_tag( - external_aad.lookup_aad(None, protected.as_ref(), unprotected.as_ref()), + external_aad + .lookup_aad(None, protected.as_ref(), unprotected.as_ref()) + .unwrap_or(&[] as &[u8]), |input| { try_compute( backend, key_provider, protected.as_ref(), unprotected.as_ref(), - try_all_keys, input, ) }, @@ -148,8 +147,7 @@ pub trait CoseMac0Ext { /// cose_object.try_verify( /// &mut OpensslContext::new(), /// &mut &key, - /// false, - /// &mut aad.as_slice() + /// &aad /// ).is_ok() /// ); /// ``` @@ -186,41 +184,38 @@ pub trait CoseMac0Ext { /// cose_object.try_verify( /// &mut OpensslContext::new(), /// &mut &key, - /// true, - /// &mut aad.as_slice() + /// &aad /// ), /// Err(CoseCipherError::NoMatchingKeyFound(_)) /// ) /// ); /// ``` - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + external_aad: CAP, ) -> Result<(), CoseCipherError>; } impl CoseMac0Ext for CoseMac0 { - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + external_aad: CAP, ) -> Result<(), CoseCipherError> { let backend = Rc::new(RefCell::new(backend)); - let key_provider = Rc::new(RefCell::new(key_provider)); self.verify_tag( - external_aad.lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)), + external_aad + .lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)) + .unwrap_or(&[] as &[u8]), |tag, input| { try_verify( &backend, - &key_provider, + key_provider, &self.protected.header, &self.unprotected, - try_all_keys, tag, input, ) diff --git a/src/token/cose/maced/mac0/tests.rs b/src/token/cose/maced/mac0/tests.rs index 6366ad4..bf54313 100644 --- a/src/token/cose/maced/mac0/tests.rs +++ b/src/token/cose/maced/mac0/tests.rs @@ -35,8 +35,8 @@ impl CoseStructTestHelper for CoseMac0 { let enc_key = if recipient.alg == Some(coset::Algorithm::Assigned(Algorithm::Direct)) || determine_algorithm::( None, - recipient.unprotected.as_ref(), recipient.protected.as_ref(), + recipient.unprotected.as_ref(), ) == Ok(Algorithm::Direct) { recipient.key.clone() @@ -54,12 +54,11 @@ impl CoseStructTestHelper for CoseMac0 { let mac0 = mac0 .try_compute( backend, - &mut &enc_key, - false, + &enc_key, mac0_cfg.protected.clone(), Some(unprotected), case.input.plaintext.clone().into_bytes(), - &mut mac0_cfg.external.as_slice(), + mac0_cfg.external.as_slice(), ) .expect("unable to encrypt Mac0 object"); @@ -101,9 +100,9 @@ impl CoseStructTestHelper for CoseMac0 { key_with_alg }) .collect(); - let mut aad = test_case.external.as_slice(); + let aad = test_case.external.as_slice(); - let verify_result = self.try_verify(backend, &mut &keys, false, &mut aad); + let verify_result = self.try_verify(backend, &keys, aad); if case.fail { verify_result.expect_err("invalid token was successfully verified"); diff --git a/src/token/cose/maced/mod.rs b/src/token/cose/maced/mod.rs index d1f68b9..8c024a8 100644 --- a/src/token/cose/maced/mod.rs +++ b/src/token/cose/maced/mod.rs @@ -11,11 +11,8 @@ pub use mac::{CoseMacBuilderExt, CoseMacExt}; pub use mac0::{CoseMac0BuilderExt, CoseMac0Ext}; use crate::error::CoseCipherError; -use crate::token::cose::header_util::{ - check_for_duplicate_headers, determine_algorithm, determine_key_candidates, -}; use crate::token::cose::key::{CoseKeyProvider, CoseParsedKey, CoseSymmetricKey, KeyParam}; -use crate::token::cose::CoseCipher; +use crate::token::cose::{header_util, CoseCipher}; pub trait CoseMacCipher: CoseCipher { fn compute_hmac( @@ -85,93 +82,57 @@ pub(crate) fn is_valid_hmac_key( fn try_compute( backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option<&Header>, unprotected: Option<&Header>, - try_all_keys: bool, input: &[u8], ) -> Result, CoseCipherError> { - if let (Some(protected), Some(unprotected)) = (protected, unprotected) { - check_for_duplicate_headers(protected, unprotected)?; - } - let key = determine_key_candidates::( + header_util::try_cose_crypto_operation( key_provider, protected, unprotected, BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::MacCreate)]), - try_all_keys, + |key, alg, _protected, _unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + + match alg { + iana::Algorithm::HMAC_256_256 => { + let symm_key = is_valid_hmac_key(alg, parsed_key)?; + backend.compute_hmac(alg, symm_key, input) + } + alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))), + } + }, ) - .next() - .ok_or(CoseCipherError::NoMatchingKeyFound(Vec::new()))?; - let parsed_key = CoseParsedKey::try_from(&key)?; - - match determine_algorithm(Some(&parsed_key), protected, unprotected)? { - alg @ iana::Algorithm::HMAC_256_256 => { - let symm_key = is_valid_hmac_key(alg, parsed_key)?; - backend.compute_hmac(alg, symm_key, input) - } - alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))), - } -} - -fn try_verify_with_key( - backend: &mut B, - key: CoseParsedKey, - protected: &Header, - unprotected: &Header, - tag: &[u8], - data: &[u8], -) -> Result<(), CoseCipherError> { - check_for_duplicate_headers(protected, unprotected)?; - - match determine_algorithm(Some(&key), Some(protected), Some(unprotected))? { - alg @ iana::Algorithm::HMAC_256_256 => { - let symm_key = is_valid_hmac_key(alg, key)?; - backend.verify_hmac(alg, symm_key, tag, data) - } - alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))), - } } pub(crate) fn try_verify( backend: &Rc>, - key_provider: &Rc>, + key_provider: &CKP, protected: &Header, unprotected: &Header, - try_all_keys: bool, tag: &[u8], data: &[u8], ) -> Result<(), CoseCipherError> { - check_for_duplicate_headers(protected, unprotected)?; - let mut multi_verification_errors = Vec::new(); - for key in determine_key_candidates::( - *key_provider.borrow_mut(), + header_util::try_cose_crypto_operation( + key_provider, Some(protected), Some(unprotected), - BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Decrypt)]), - try_all_keys, - ) { - match try_verify_with_key( - *backend.borrow_mut(), - CoseParsedKey::try_from(&key)?, - protected, - unprotected, - tag, - data, - ) { - Ok(v) => return Ok(v), - Err(e) => { - multi_verification_errors.push((key.clone(), e)); - continue; + BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::MacVerify)]), + |key, alg, _protected, _unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + + match alg { + iana::Algorithm::HMAC_256_256 => { + let symm_key = is_valid_hmac_key(alg, parsed_key)?; + (*backend.borrow_mut()).verify_hmac(alg, symm_key, tag, data) + } + alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))), } - } - } - - Err(CoseCipherError::NoMatchingKeyFound( - multi_verification_errors, - )) + }, + ) } diff --git a/src/token/cose/recipient/mod.rs b/src/token/cose/recipient/mod.rs index d9e1330..7ec5ff0 100644 --- a/src/token/cose/recipient/mod.rs +++ b/src/token/cose/recipient/mod.rs @@ -2,10 +2,9 @@ use alloc::boxed::Box; use alloc::collections::{BTreeSet, VecDeque}; use alloc::rc::Rc; use alloc::vec::Vec; +use core::borrow::Borrow; use core::cell::RefCell; use core::fmt::Display; -use core::marker::PhantomData; - use coset::{ iana, Algorithm, CoseKey, CoseKeyBuilder, CoseRecipient, CoseRecipientBuilder, EncryptionContext, Header, KeyOperation, @@ -16,51 +15,51 @@ use crate::token::cose::encrypted::CoseKeyDistributionCipher; use crate::token::cose::header_util::{determine_algorithm, determine_key_candidates}; use crate::token::cose::key::ensure_valid_aes_key; use crate::token::cose::key::{CoseAadProvider, CoseKeyProvider, CoseParsedKey}; +use crate::token::cose::{try_cose_crypto_operation, InvertedAadProvider}; pub(crate) struct CoseNestedRecipientSearchContext< 'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider, + AAD: CoseAadProvider, > { recipient_iter: &'a Vec, backend: Rc>, - key_provider: Rc>, - try_all_keys: bool, + key_provider: &'a CKP, + aad_provider: Rc>, context: EncryptionContext, - _key_lifetime_marker: PhantomData<&'a CKP>, } -impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> - CoseNestedRecipientSearchContext<'a, B, CKP> +impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider, AAD: CoseAadProvider> + CoseNestedRecipientSearchContext<'a, B, CKP, AAD> { pub(crate) fn new( recipient_iter: &'a Vec, backend: Rc>, - key_provider: Rc>, - try_all_keys: bool, + key_provider: &'a CKP, + aad_provider: AAD, context: EncryptionContext, - ) -> CoseNestedRecipientSearchContext<'a, B, CKP> { + ) -> CoseNestedRecipientSearchContext<'a, B, CKP, AAD> { CoseNestedRecipientSearchContext { recipient_iter, backend, key_provider, - try_all_keys, + aad_provider: Rc::new(InvertedAadProvider(aad_provider)), context, - _key_lifetime_marker: PhantomData, } } } -impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> CoseKeyProvider - for CoseNestedRecipientSearchContext<'a, B, CKP> +impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider, AAD: CoseAadProvider> CoseKeyProvider + for CoseNestedRecipientSearchContext<'a, B, CKP, AAD> { - fn lookup_key(&mut self, key_id: Option<&[u8]>) -> impl Iterator { + fn lookup_key(&self, key_id: Option<&[u8]>) -> impl Iterator> { let mut iter: Box> = Box::new(CoseNestedRecipientIterator { iteration_state: vec![self.recipient_iter.iter()], recipient_stack: vec![], backend: Rc::clone(&self.backend), - key_provider: Rc::clone(&self.key_provider), - try_all_keys: self.try_all_keys, + key_provider: self.key_provider, + aad_provider: Rc::clone(&self.aad_provider), current_key_candidates: VecDeque::default(), current_candidates_position: 0, last_error: None, @@ -74,20 +73,25 @@ impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> CoseKeyProvider } } -struct CoseNestedRecipientIterator<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> { +struct CoseNestedRecipientIterator< + 'a, + B: CoseKeyDistributionCipher, + CKP: CoseKeyProvider, + AAD: CoseAadProvider, +> { iteration_state: Vec>, recipient_stack: Vec<&'a CoseRecipient>, backend: Rc>, - key_provider: Rc>, - try_all_keys: bool, + key_provider: &'a CKP, + aad_provider: Rc>, current_key_candidates: VecDeque, current_candidates_position: usize, last_error: Option>, context: EncryptionContext, } -impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> Iterator - for CoseNestedRecipientIterator<'a, B, CKP> +impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider, AAD: CoseAadProvider> Iterator + for CoseNestedRecipientIterator<'a, B, CKP, AAD> { type Item = CoseKey; @@ -177,8 +181,8 @@ pub(crate) fn struct_to_recipient_context(ctx: EncryptionContext) -> EncryptionC } } -impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> - CoseNestedRecipientIterator<'a, B, CKP> +impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider, AAD: CoseAadProvider> + CoseNestedRecipientIterator<'a, B, CKP, AAD> { fn attempt_to_decrypt_nested( &mut self, @@ -190,13 +194,13 @@ impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> EncryptionContext::RecRecipient }; // Attempt to decrypt leaf node, return (non-search-terminating) error if that doesn't work. - let mut current_keys: Vec = leaf_recipient.try_decrypt( - *self.backend.borrow_mut(), - *self.key_provider.borrow_mut(), - self.try_all_keys, - ctx, - &mut (&[] as &[u8]), - )?; + let mut current_keys: Vec = leaf_recipient + .try_decrypt::<_, _, &InvertedAadProvider>( + *self.backend.borrow_mut(), + self.key_provider, + ctx, + self.aad_provider.borrow(), + )?; let iter = self.recipient_stack.iter().copied().rev(); @@ -207,13 +211,11 @@ impl<'a, B: CoseKeyDistributionCipher, CKP: CoseKeyProvider> struct_to_recipient_context(self.context) }; - match recipient.try_decrypt( + match recipient.try_decrypt::<_, _, &InvertedAadProvider>( *self.backend.borrow_mut(), - *self.key_provider.borrow_mut(), - // Use all keys in current_keys for intermediates. - true, + ¤t_keys, ctx, - &mut (&[] as &[u8]), + self.aad_provider.borrow(), ) { Ok(v) => current_keys = v, Err(e @ CoseCipherError::UnsupportedAlgorithm(_)) => return Err(e), @@ -279,8 +281,18 @@ fn determine_decrypt_key_ops_for_alg( ) -> Result, CoseCipherError> { Ok(BTreeSet::from_iter(match alg { iana::Algorithm::Direct => { - // TODO maybe needs to be all operations instead - vec![] + vec![ + KeyOperation::Assigned(iana::KeyOperation::WrapKey), + KeyOperation::Assigned(iana::KeyOperation::UnwrapKey), + KeyOperation::Assigned(iana::KeyOperation::MacCreate), + KeyOperation::Assigned(iana::KeyOperation::MacVerify), + KeyOperation::Assigned(iana::KeyOperation::Encrypt), + KeyOperation::Assigned(iana::KeyOperation::Decrypt), + KeyOperation::Assigned(iana::KeyOperation::DeriveBits), + KeyOperation::Assigned(iana::KeyOperation::DeriveKey), + KeyOperation::Assigned(iana::KeyOperation::Sign), + KeyOperation::Assigned(iana::KeyOperation::Verify), + ] } iana::Algorithm::Direct_HKDF_AES_128 | iana::Algorithm::Direct_HKDF_AES_256 @@ -358,35 +370,42 @@ pub trait CoseRecipientBuilderExt: Sized { /// # Examples /// /// TODO - fn try_encrypt( + fn try_encrypt< + B: CoseKeyDistributionCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, context: EncryptionContext, protected: Option
, unprotected: Option
, plaintext: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; } impl CoseRecipientBuilderExt for CoseRecipientBuilder { - fn try_encrypt( + fn try_encrypt< + B: CoseKeyDistributionCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + context: EncryptionContext, protected: Option
, unprotected: Option
, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { let mut builder = self; let alg = - match determine_algorithm::(None, unprotected.as_ref(), protected.as_ref()) { + match determine_algorithm::(None, protected.as_ref(), unprotected.as_ref()) { Ok(v) => v, Err(e) => { // A CoseRecipient MUST always have an algorithm set (see RFC 9052, @@ -395,20 +414,6 @@ impl CoseRecipientBuilderExt for CoseRecipientBuilder { } }; - // Determine key operations that fulfill the requirements of the algorithm. - let operation = determine_encrypt_key_ops_for_alg(alg)?; - - let key = determine_key_candidates::( - key_provider, - protected.as_ref(), - unprotected.as_ref(), - operation, - try_all_keys, - ) - .next() - .ok_or(CoseCipherError::NoMatchingKeyFound(Vec::new()))?; - let parsed_key = CoseParsedKey::try_from(&key)?; - // Direct => Key of will be used for lower layer directly, must not contain ciphertext. if iana::Algorithm::Direct == alg { return Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( @@ -416,6 +421,55 @@ impl CoseRecipientBuilderExt for CoseRecipientBuilder { ))); } + // TODO return an error if IV is set in headers? + + // Determine key operations that fulfill the requirements of the algorithm. + let operation = determine_encrypt_key_ops_for_alg(alg)?; + + builder = builder.try_create_ciphertext( + context, + payload, + external_aad + .lookup_aad(Some(context), protected.as_ref(), unprotected.as_ref()) + .unwrap_or(&[] as &[u8]), + |plaintext, _aad| { + try_cose_crypto_operation( + key_provider, + protected.as_ref(), + unprotected.as_ref(), + operation, + |key, alg, protected, _unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + match alg { + iana::Algorithm::A128KW + | iana::Algorithm::A192KW + | iana::Algorithm::A256KW => { + let symm_key = ensure_valid_aes_key(alg, parsed_key)?; + + if protected.is_some() && !protected.as_ref().unwrap().is_empty() { + return Err(CoseCipherError::AadUnsupported); + } + + backend.aes_key_wrap( + alg, + symm_key, + plaintext, + // Fixed IV, see RFC 9053, Section 6.2.1 + &[0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6], + ) + } + alg => { + // Unsupported algorithm - skip over this recipient. + Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))) + } + } + }, + ) + }, + )?; + if let Some(protected) = &protected { builder = builder.protected(protected.clone()); } @@ -423,41 +477,7 @@ impl CoseRecipientBuilderExt for CoseRecipientBuilder { builder = builder.unprotected(unprotected.clone()); } - match alg { - alg @ (iana::Algorithm::A128KW | iana::Algorithm::A192KW | iana::Algorithm::A256KW) => { - let symm_key = ensure_valid_aes_key(alg, parsed_key)?; - - if protected.is_some() && !protected.as_ref().unwrap().is_empty() { - return Err(CoseCipherError::AadUnsupported); - } - - builder.try_create_ciphertext( - context, - payload, - external_aad.lookup_aad( - Some(context), - protected.as_ref(), - unprotected.as_ref(), - ), - |plaintext, _aad| { - // Ignore AAD as this is not an AEAD algorithm, just an AE algorithm. - backend.aes_key_wrap( - alg, - symm_key, - plaintext, - // Fixed IV, see RFC 9053, Section 6.2.1 - &[0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6], - ) - }, - ) - } - alg => { - // Unsupported algorithm - skip over this recipient. - Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))) - } - } + Ok(builder) } } @@ -501,29 +521,36 @@ pub trait CoseRecipientExt { /// # Examples /// /// TODO - fn try_decrypt( + fn try_decrypt< + B: CoseKeyDistributionCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + context: EncryptionContext, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result, CoseCipherError>; } impl CoseRecipientExt for CoseRecipient { - fn try_decrypt( + fn try_decrypt< + B: CoseKeyDistributionCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, context: EncryptionContext, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result, CoseCipherError> { let alg = match determine_algorithm::( None, - Some(&self.unprotected), Some(&self.protected.header), + Some(&self.unprotected), ) { Ok(v) => v, Err(e) => { @@ -539,76 +566,79 @@ impl CoseRecipientExt for CoseRecipient { // Determine key operations that fulfill the requirements of the algorithm. let operation = determine_decrypt_key_ops_for_alg(alg)?; - let key_candidates: Vec = determine_key_candidates::( - key_provider, - Some(&self.protected.header), - Some(&self.unprotected), - operation, - try_all_keys, - ) - .collect(); - // Direct => Key of key provider will be used for lower layer directly. // TODO ensure that Direct is the only method used on the message (RFC 9052, Section 8.5.1) if iana::Algorithm::Direct == alg { - return Ok(key_candidates); - } - - let mut multi_verification_errors = Vec::new(); - - for key in key_candidates { - let parsed_key = CoseParsedKey::try_from(&key)?; - match alg { - alg @ (iana::Algorithm::A128KW - | iana::Algorithm::A192KW - | iana::Algorithm::A256KW) => { - let symm_key = match ensure_valid_aes_key(alg, parsed_key) { - Ok(v) => v, - Err(_e) => { - // Key is not an AES key, skip. - continue; - } - }; - if !self.protected.is_empty() { - return Err(CoseCipherError::AadUnsupported); - } - match self.decrypt( - context, - external_aad.lookup_aad( - Some(context), - Some(&self.protected.header), - Some(&self.unprotected), - ), - |ciphertext, _aad| { - // Ignore AAD as this is not an AEAD algorithm, just an AE algorithm. - backend.aes_key_unwrap( - alg, - symm_key, - ciphertext, - // Fixed IV, see RFC 9053, Section 6.2.1 - &[0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6], - ) - }, - ) { - Ok(v) => return Ok(vec![CoseKeyBuilder::new_symmetric_key(v).build()]), - Err(e) => { - multi_verification_errors.push((key.clone(), e)); - // Decryption using key failed, skip. - continue; - } - }; - } - alg => { - // Unsupported algorithm - skip over this recipient. - return Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))); + let mut successful_candidates = Vec::new(); + let mut multi_verification_errors = Vec::new(); + for kc in determine_key_candidates::( + key_provider, + Some(&self.protected.header), + Some(&self.unprotected), + operation, + ) + .map(|kc| kc.map(|(key, _alg)| key)) + { + match kc { + Ok(v) => successful_candidates.push(v), + Err(e) => multi_verification_errors.push(e), } } + if successful_candidates.is_empty() { + return Err(CoseCipherError::NoMatchingKeyFound( + multi_verification_errors, + )); + } + return Ok(successful_candidates); } - Err(CoseCipherError::NoMatchingKeyFound( - multi_verification_errors, - )) + match self.decrypt( + context, + external_aad + .lookup_aad( + Some(context), + Some(&self.protected.header), + Some(&self.unprotected), + ) + .unwrap_or(&[] as &[u8]), + |ciphertext, _aad| { + try_cose_crypto_operation( + key_provider, + Some(&self.protected.header), + Some(&self.unprotected), + operation, + |key, alg, _protected, _unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + match alg { + iana::Algorithm::A128KW + | iana::Algorithm::A192KW + | iana::Algorithm::A256KW => { + let symm_key = ensure_valid_aes_key(alg, parsed_key)?; + if !self.protected.is_empty() { + return Err(CoseCipherError::AadUnsupported); + } + // Ignore AAD as this is not an AEAD algorithm, just an AE algorithm. + backend.aes_key_unwrap( + alg, + symm_key, + ciphertext, + // Fixed IV, see RFC 9053, Section 6.2.1 + &[0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6], + ) + } + alg => { + // Unsupported algorithm + Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))) + } + } + }, + ) + }, + ) { + Ok(v) => Ok(vec![CoseKeyBuilder::new_symmetric_key(v).build()]), + Err(e) => Err(e), + } } } diff --git a/src/token/cose/signed/mod.rs b/src/token/cose/signed/mod.rs index 5596bdb..c397408 100644 --- a/src/token/cose/signed/mod.rs +++ b/src/token/cose/signed/mod.rs @@ -17,11 +17,8 @@ pub use sign::{CoseSignBuilderExt, CoseSignExt}; pub use sign1::{CoseSign1BuilderExt, CoseSign1Ext}; use crate::error::CoseCipherError; -use crate::token::cose::header_util::{ - check_for_duplicate_headers, determine_algorithm, determine_key_candidates, -}; use crate::token::cose::key::{CoseEc2Key, CoseKeyProvider, CoseParsedKey}; -use crate::token::cose::{key, CoseCipher}; +use crate::token::cose::{header_util, key, CoseCipher}; mod sign; mod sign1; @@ -169,108 +166,67 @@ pub trait CoseSignCipher: CoseCipher { fn try_sign( backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option<&Header>, unprotected: Option<&Header>, tosign: &[u8], ) -> Result, CoseCipherError> { - if let (Some(protected), Some(unprotected)) = (protected, unprotected) { - check_for_duplicate_headers(protected, unprotected)?; - } - let key = determine_key_candidates::( + header_util::try_cose_crypto_operation( key_provider, protected, unprotected, BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Sign)]), - false, - ) - .next() - .ok_or(CoseCipherError::NoMatchingKeyFound(Vec::new()))?; - let parsed_key = CoseParsedKey::try_from(&key)?; - - let mut sign_fn = match determine_algorithm(Some(&parsed_key), protected, unprotected)? { - alg @ (iana::Algorithm::ES256 - | iana::Algorithm::ES384 - | iana::Algorithm::ES512 - | iana::Algorithm::ES256K) => { - // Check if this is a valid ECDSA key. - let ec2_key = key::ensure_valid_ecdsa_key::(alg, parsed_key, true)?; - - // Perform signing operation using backend. - move |tosign| backend.sign_ecdsa(alg, &ec2_key, tosign) - } - alg => { - return Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))) - } - }; - - sign_fn(tosign) -} + |key, alg, _protected, _unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + match alg { + iana::Algorithm::ES256 + | iana::Algorithm::ES384 + | iana::Algorithm::ES512 + | iana::Algorithm::ES256K => { + // Check if this is a valid ECDSA key. + let ec2_key = key::ensure_valid_ecdsa_key::(alg, parsed_key, true)?; -fn try_verify_with_key( - backend: &mut B, - key: CoseParsedKey, - protected: &Header, - unprotected: &Header, - signature: &[u8], - toverify: &[u8], -) -> Result<(), CoseCipherError> { - check_for_duplicate_headers(protected, unprotected)?; - - match determine_algorithm(Some(&key), Some(protected), Some(unprotected))? { - alg @ (iana::Algorithm::ES256 - | iana::Algorithm::ES384 - | iana::Algorithm::ES512 - | iana::Algorithm::ES256K) => { - // Check if this is a valid ECDSA key. - let ec2_key = key::ensure_valid_ecdsa_key::(alg, key, false)?; - - backend.verify_ecdsa(alg, &ec2_key, signature, toverify) - } - alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( - alg, - ))), - } + // Perform signing operation using backend. + backend.sign_ecdsa(alg, &ec2_key, tosign) + } + alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))), + } + }, + ) } fn try_verify( backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: &Header, unprotected: &Header, - try_all_keys: bool, + signature: &[u8], toverify: &[u8], ) -> Result<(), CoseCipherError> { - check_for_duplicate_headers(protected, unprotected)?; - let mut multi_verification_errors = Vec::new(); - for key in determine_key_candidates::( + header_util::try_cose_crypto_operation( key_provider, Some(protected), Some(unprotected), BTreeSet::from_iter(vec![KeyOperation::Assigned(iana::KeyOperation::Verify)]), - try_all_keys, - ) { - let parsed_key = CoseParsedKey::try_from(&key)?; - match try_verify_with_key( - backend, - parsed_key, - protected, - unprotected, - signature, - toverify, - ) { - Ok(()) => return Ok(()), - Err(e) => { - multi_verification_errors.push((key.clone(), e)); - continue; - } - } - } + |key, alg, _protected, _unprotected| { + let parsed_key = CoseParsedKey::try_from(key)?; + match alg { + iana::Algorithm::ES256 + | iana::Algorithm::ES384 + | iana::Algorithm::ES512 + | iana::Algorithm::ES256K => { + // Check if this is a valid ECDSA key. + let ec2_key = key::ensure_valid_ecdsa_key::(alg, parsed_key, false)?; - Err(CoseCipherError::NoMatchingKeyFound( - multi_verification_errors, - )) + backend.verify_ecdsa(alg, &ec2_key, signature, toverify) + } + alg => Err(CoseCipherError::UnsupportedAlgorithm(Algorithm::Assigned( + alg, + ))), + } + }, + ) } diff --git a/src/token/cose/signed/sign/mod.rs b/src/token/cose/signed/sign/mod.rs index 2606141..02875a8 100644 --- a/src/token/cose/signed/sign/mod.rs +++ b/src/token/cose/signed/sign/mod.rs @@ -39,12 +39,12 @@ pub trait CoseSignBuilderExt: Sized { /// # Examples /// /// TODO - fn try_add_sign( + fn try_add_sign( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, sig: CoseSignature, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; /// Calculates and adds a signature for the CoseSign object using the given backend and @@ -77,27 +77,33 @@ pub trait CoseSignBuilderExt: Sized { /// # Examples /// /// TODO - fn try_add_sign_detached( + fn try_add_sign_detached< + B: CoseSignCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, sig: CoseSignature, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result>; } impl CoseSignBuilderExt for CoseSignBuilder { - fn try_add_sign( + fn try_add_sign( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, sig: CoseSignature, - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { self.try_add_created_signature( sig.clone(), - external_aad.lookup_aad(None, Some(&sig.protected.header), Some(&sig.unprotected)), + external_aad + .lookup_aad(None, Some(&sig.protected.header), Some(&sig.unprotected)) + .unwrap_or(&[] as &[u8]), |tosign| { signed::try_sign( backend, @@ -109,18 +115,24 @@ impl CoseSignBuilderExt for CoseSignBuilder { }, ) } - fn try_add_sign_detached( + fn try_add_sign_detached< + B: CoseSignCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, sig: CoseSignature, payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result> { self.try_add_detached_signature( sig.clone(), payload, - external_aad.lookup_aad(None, Some(&sig.protected.header), Some(&sig.unprotected)), + external_aad + .lookup_aad(None, Some(&sig.protected.header), Some(&sig.unprotected)) + .unwrap_or(&[] as &[u8]), |tosign| { signed::try_sign( backend, @@ -174,12 +186,12 @@ pub trait CoseSignExt { /// # Examples /// /// TODO - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - aad: &mut CAP, + key_provider: &CKP, + + aad: CAP, ) -> Result<(), CoseCipherError>; /// Attempts to verify the signature of this object and its detached payload using a @@ -220,39 +232,39 @@ pub trait CoseSignExt { /// # Examples /// /// TODO - fn try_verify_detached( + fn try_verify_detached( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + payload: &[u8], - aad: &mut CAP, + aad: CAP, ) -> Result<(), CoseCipherError>; } impl CoseSignExt for CoseSign { - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - aad: &mut CAP, + key_provider: &CKP, + + aad: CAP, ) -> Result<(), CoseCipherError> { for sigindex in 0..self.signatures.len() { match self.verify_signature( sigindex, - aad.borrow_mut().lookup_aad( + aad.lookup_aad( None, Some(&self.signatures[sigindex].protected.header), Some(&self.signatures[sigindex].unprotected), - ), + ) + .unwrap_or(&[] as &[u8]), |signature, toverify| { signed::try_verify( backend, key_provider, &self.signatures[sigindex].protected.header, &self.signatures[sigindex].unprotected, - try_all_keys, signature, toverify, ) @@ -267,15 +279,18 @@ impl CoseSignExt for CoseSign { Err(CoseCipherError::VerificationFailure) } - fn try_verify_detached( + fn try_verify_detached< + B: CoseSignCipher, + CKP: CoseKeyProvider, + CAP: CoseAadProvider + ?Sized, + >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + payload: &[u8], - aad: &mut CAP, + aad: CAP, ) -> Result<(), CoseCipherError> { - let aad = aad.borrow_mut(); for sigindex in 0..self.signatures.len() { match self.verify_detached_signature( sigindex, @@ -284,14 +299,14 @@ impl CoseSignExt for CoseSign { None, Some(&self.signatures[sigindex].protected.header), Some(&self.signatures[sigindex].unprotected), - ), + ) + .unwrap_or(&[] as &[u8]), |signature, toverify| { signed::try_verify( backend, key_provider, &self.signatures[sigindex].protected.header, &self.signatures[sigindex].unprotected, - try_all_keys, signature, toverify, ) diff --git a/src/token/cose/signed/sign/tests.rs b/src/token/cose/signed/sign/tests.rs index 722b954..57f0fed 100644 --- a/src/token/cose/signed/sign/tests.rs +++ b/src/token/cose/signed/sign/tests.rs @@ -43,11 +43,11 @@ impl CoseStructTestH signature = signature.protected(protected.clone()); } sign = sign - .try_add_sign::<_, &CoseKey, &[u8]>( + .try_add_sign( backend, - &mut &signer.key, + &signer.key, signature.build(), - &mut signer.external.as_slice(), + signer.external.as_slice(), ) .expect("unable to sign Sign object"); } @@ -98,9 +98,13 @@ impl CoseStructTestH key_with_alg }) .collect(); - let mut aads = test_case.signers.iter().map(|v| v.external.as_slice()); + let aads: Vec<(Vec, &[u8])> = test_case + .signers + .iter() + .map(|v| (v.key.key_id.clone(), v.external.as_slice())) + .collect(); - let verify_result = self.try_verify(backend, &mut &keys, false, &mut &mut aads); + let verify_result = self.try_verify(backend, &keys, &aads); if case.fail { verify_result.expect_err("invalid token was successfully verified"); diff --git a/src/token/cose/signed/sign1/mod.rs b/src/token/cose/signed/sign1/mod.rs index 625b3cb..1198b10 100644 --- a/src/token/cose/signed/sign1/mod.rs +++ b/src/token/cose/signed/sign1/mod.rs @@ -46,13 +46,13 @@ pub trait CoseSign1BuilderExt: Sized { /// the builder pattern, but it is necessary here, as we lack access to the `protected` /// and `unprotected` headers that were previously set (the field is private). /// This should be fixed when porting all of this to coset. - fn try_sign( + fn try_sign( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option
, unprotected: Option
, - aad: &mut CAP, + aad: CAP, ) -> Result>; /// Creates the signature for the CoseSign1 object using the given backend and detached payload. @@ -91,25 +91,25 @@ pub trait CoseSign1BuilderExt: Sized { /// the builder pattern, but it is necessary here, as we lack access to the `protected` /// and `unprotected` headers that were previously set (the field is private). /// This should be fixed when porting all of this to coset. - fn try_sign_detached( + fn try_sign_detached( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option
, unprotected: Option
, payload: &[u8], - aad: &mut CAP, + aad: CAP, ) -> Result>; } impl CoseSign1BuilderExt for CoseSign1Builder { - fn try_sign( + fn try_sign( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option
, unprotected: Option
, - aad: &mut CAP, + aad: CAP, ) -> Result> { let mut builder = self; if let Some(protected) = &protected { @@ -119,7 +119,8 @@ impl CoseSign1BuilderExt for CoseSign1Builder { builder = builder.unprotected(unprotected.clone()); } builder.try_create_signature( - aad.lookup_aad(None, protected.as_ref(), unprotected.as_ref()), + aad.lookup_aad(None, protected.as_ref(), unprotected.as_ref()) + .unwrap_or(&[] as &[u8]), |tosign| { signed::try_sign( backend, @@ -131,14 +132,14 @@ impl CoseSign1BuilderExt for CoseSign1Builder { }, ) } - fn try_sign_detached( + fn try_sign_detached( self, backend: &mut B, - key_provider: &mut CKP, + key_provider: &CKP, protected: Option
, unprotected: Option
, payload: &[u8], - aad: &mut CAP, + aad: CAP, ) -> Result> { let mut builder = self; if let Some(protected) = &protected { @@ -149,7 +150,8 @@ impl CoseSign1BuilderExt for CoseSign1Builder { } builder.try_create_detached_signature( payload, - aad.lookup_aad(None, protected.as_ref(), unprotected.as_ref()), + aad.lookup_aad(None, protected.as_ref(), unprotected.as_ref()) + .unwrap_or(&[] as &[u8]), |tosign| { signed::try_sign( backend, @@ -197,12 +199,12 @@ pub trait CoseSign1Ext { /// # Examples /// /// TODO - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result<(), CoseCipherError>; /// Attempts to verify the signature of this object and its detached payload using a @@ -240,33 +242,34 @@ pub trait CoseSign1Ext { /// # Examples /// /// TODO - fn try_verify_detached( + fn try_verify_detached( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result<(), CoseCipherError>; } impl CoseSign1Ext for CoseSign1 { - fn try_verify( + fn try_verify( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, - external_aad: &mut CAP, + key_provider: &CKP, + + external_aad: CAP, ) -> Result<(), CoseCipherError> { self.verify_signature( - external_aad.lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)), + external_aad + .lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)) + .unwrap_or(&[] as &[u8]), |signature, toverify| { signed::try_verify( backend, key_provider, &self.protected.header, &self.unprotected, - try_all_keys, signature, toverify, ) @@ -281,25 +284,26 @@ impl CoseSign1Ext for CoseSign1 { 'b, B: CoseSignCipher, CKP: CoseKeyProvider, - CAP: CoseAadProvider, + CAP: CoseAadProvider + ?Sized, >( &self, backend: &mut B, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + payload: &[u8], - external_aad: &mut CAP, + external_aad: CAP, ) -> Result<(), CoseCipherError> { self.verify_detached_signature( payload, - external_aad.lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)), + external_aad + .lookup_aad(None, Some(&self.protected.header), Some(&self.unprotected)) + .unwrap_or(&[] as &[u8]), |signature, toverify| { signed::try_verify( backend, key_provider, &self.protected.header, &self.unprotected, - try_all_keys, signature, toverify, ) diff --git a/src/token/cose/signed/sign1/tests.rs b/src/token/cose/signed/sign1/tests.rs index 65fd0e5..a69ce32 100644 --- a/src/token/cose/signed/sign1/tests.rs +++ b/src/token/cose/signed/sign1/tests.rs @@ -27,10 +27,10 @@ impl CoseStructTestHelper for CoseSign1 { .payload(case.input.plaintext.clone().into_bytes()) .try_sign( backend, - &mut &sign1_cfg.key, + &sign1_cfg.key, sign1_cfg.protected.clone(), sign1_cfg.unprotected.clone(), - &mut sign1_cfg.external.as_slice(), + sign1_cfg.external.as_slice(), ) .expect("unable to sign Sign1 object") .build() @@ -52,7 +52,7 @@ impl CoseStructTestHelper for CoseSign1 { let sign1_case = case.input.sign0.as_ref().expect("expected Sign1 test case"); let key: CoseKey = sign1_case.key.clone(); - let verify_result = self.try_verify(backend, &mut &key, false, &mut &*sign1_case.external); + let verify_result = self.try_verify(backend, &key, sign1_case.external.as_slice()); if case.fail { verify_result.expect_err("invalid token was successfully verified"); diff --git a/src/token/mod.rs b/src/token/mod.rs index 2a84353..00407d4 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -155,12 +155,12 @@ use crate::common::cbor_values::ByteString; use crate::error::AccessTokenError; -use crate::token::cose::CoseCipher; pub use crate::token::cose::CoseSignCipher; +use crate::token::cose::{CoseAadProvider, CoseCipher}; use ciborium::value::Value; use cose::determine_algorithm; use cose::CoseRecipientBuilderExt; -use cose::{generate_cek_for_alg, CoseKeyProvider, CoseParsedKey}; +use cose::{generate_cek_for_alg, CoseKeyProvider}; use cose::{ CoseEncrypt0BuilderExt, CoseEncrypt0Ext, CoseEncryptBuilderExt, CoseEncryptCipher, CoseEncryptExt, CoseKeyDistributionCipher, @@ -206,11 +206,11 @@ mod tests; /// let token: ByteString = encrypt_access_token::(&key, claims.clone(), None, None, None, rng)?; /// assert_eq!(decrypt_access_token::(&key, &token, None)?, claims); /// ``` -pub fn encrypt_access_token( +pub fn encrypt_access_token( backend: &mut T, key: &CoseKey, claims: ClaimsSet, - external_aad: Option<&[u8]>, + external_aad: &AAD, unprotected_header: Option
, protected_header: Option
, ) -> Result> @@ -220,12 +220,11 @@ where CoseEncrypt0Builder::new() .try_encrypt( backend, - &mut &*key, - true, + key, protected_header, unprotected_header, claims.to_vec()?.as_slice(), - &mut external_aad.unwrap_or(&[]), + external_aad, )? .build() .to_vec() @@ -257,11 +256,11 @@ where /// vec![&key1, &key2], claims.clone(), None, None, None rng /// )?; /// ``` -pub fn encrypt_access_token_multiple<'a, T, I>( +pub fn encrypt_access_token_multiple<'a, T, I, AAD: CoseAadProvider + ?Sized>( backend: &mut T, keys: I, claims: ClaimsSet, - external_aad: Option<&[u8]>, + external_aad: &AAD, unprotected_header: Option
, protected_header: Option
, ) -> Result> @@ -273,14 +272,14 @@ where let mut result = CoseEncryptBuilder::new(); let mut key_iter = keys.into_iter(); let preset_algorithm = - determine_algorithm(None, unprotected_header.as_ref(), protected_header.as_ref()); + determine_algorithm(None, protected_header.as_ref(), unprotected_header.as_ref()); let cek = if key_iter.len() == 1 && preset_algorithm.is_err() { let key = key_iter.next().unwrap(); let ce_alg = determine_algorithm( - Some(&CoseParsedKey::try_from(key)?), - unprotected_header.as_ref(), + Some(key), protected_header.as_ref(), + unprotected_header.as_ref(), )?; let recipient_header = HeaderBuilder::new() .algorithm(iana::Algorithm::Direct) @@ -301,17 +300,16 @@ where for key in key_iter { // TODO allow manually setting headers for each recipient. - let kek_alg = determine_algorithm(Some(&CoseParsedKey::try_from(key)?), None, None)?; + let kek_alg = determine_algorithm(Some(key), None, None)?; let recipient_header = HeaderBuilder::new().algorithm(kek_alg).build(); let recipient = CoseRecipientBuilder::new().try_encrypt( backend, - &mut &*key, - true, + key, EncryptionContext::EncRecipient, None, Some(recipient_header), cek_v.as_slice(), - &mut (&[] as &[u8]), + None, )?; result = result.add_recipient(recipient.build()); @@ -321,12 +319,11 @@ where result = result.try_encrypt( backend, - &mut &cek, - true, + &cek, protected_header, unprotected_header, claims.to_vec()?.as_slice(), - &mut external_aad.unwrap_or(&[]), + external_aad, )?; result.build().to_vec().map_err(AccessTokenError::from) } @@ -357,11 +354,11 @@ where /// let token: ByteString = sign_access_token::(&key, claims, None, None, None, rng)?; /// assert!(verify_access_token::(&key, &token, None).is_ok()); /// ``` -pub fn sign_access_token( +pub fn sign_access_token( backend: &mut T, key: &CoseKey, claims: ClaimsSet, - external_aad: Option<&[u8]>, + external_aad: &AAD, unprotected_header: Option
, protected_header: Option
, ) -> Result> @@ -372,10 +369,10 @@ where .payload(claims.to_vec()?) .try_sign( backend, - &mut &*key, + key, protected_header, unprotected_header, - &mut external_aad.unwrap_or(&[]), + external_aad, )? .build() .to_vec() @@ -407,11 +404,11 @@ where /// rng /// )?; /// ``` -pub fn sign_access_token_multiple<'a, T, I>( +pub fn sign_access_token_multiple<'a, T, I, AAD: CoseAadProvider + ?Sized>( backend: &mut T, keys: I, claims: ClaimsSet, - external_aad: Option<&[u8]>, + external_aad: &AAD, unprotected_header: Option
, protected_header: Option
, ) -> Result> @@ -427,12 +424,7 @@ where builder = builder.protected(protected); } for (key, signature) in keys { - builder = builder.try_add_sign::( - backend, - &mut &*key, - signature, - &mut external_aad.unwrap_or(&[]), - )?; + builder = builder.try_add_sign::(backend, &key, signature, external_aad)?; } builder.build().to_vec().map_err(AccessTokenError::from) @@ -507,19 +499,19 @@ pub fn get_token_headers(token: &ByteString) -> Option<(Header, ProtectedHeader) /// (e.g., if it's not in fact a [`CoseSign1`] structure but rather something else). /// - When there's a verification error coming from the cipher `T` /// (e.g., if the `token`'s data does not match its signature). -pub fn verify_access_token( +pub fn verify_access_token( backend: &mut T, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + token: &ByteString, - mut external_aad: Option<&[u8]>, + external_aad: &AAD, ) -> Result<(), AccessTokenError> where T: CoseSignCipher, CKP: CoseKeyProvider, { let sign = CoseSign1::from_slice(token.as_slice()).map_err(AccessTokenError::CoseError)?; - sign.try_verify(backend, key_provider, try_all_keys, &mut external_aad) + sign.try_verify(backend, key_provider, external_aad) .map_err(AccessTokenError::from) } @@ -538,19 +530,19 @@ where /// (e.g., if it's not in fact a [`CoseSign`] structure but rather something else). /// - When there's a verification error coming from the cipher `T` /// (e.g., if the `token`'s data does not match its signature). -pub fn verify_access_token_multiple( +pub fn verify_access_token_multiple( backend: &mut T, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + token: &ByteString, - mut external_aad: Option<&[u8]>, + external_aad: &AAD, ) -> Result<(), AccessTokenError> where T: CoseSignCipher, CKP: CoseKeyProvider, { let sign = CoseSign::from_slice(token.as_slice()).map_err(AccessTokenError::CoseError)?; - sign.try_verify(backend, key_provider, try_all_keys, &mut external_aad)?; + sign.try_verify(backend, key_provider, external_aad)?; Ok(()) } @@ -570,24 +562,19 @@ where /// - When there's a decryption error coming from the cipher given by `T`. /// - When the deserialized and decrypted [`CoseEncrypt0`] structure does not contain a valid /// [`ClaimsSet`]. -pub fn decrypt_access_token( +pub fn decrypt_access_token( backend: &mut T, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + token: &ByteString, - external_aad: Option<&[u8]>, + external_aad: &AAD, ) -> Result> where T: CoseEncryptCipher, CKP: CoseKeyProvider, { let encrypt = CoseEncrypt0::from_slice(token.as_slice()).map_err(AccessTokenError::from)?; - let result = encrypt.try_decrypt( - backend, - key_provider, - try_all_keys, - &mut external_aad.unwrap_or(&[] as &[u8]), - )?; + let result = encrypt.try_decrypt(backend, key_provider, external_aad)?; ClaimsSet::from_slice(result.as_slice()).map_err(AccessTokenError::from) } @@ -610,23 +597,18 @@ where /// [`ClaimsSet`]. /// - When the [`CoseEncrypt`] contains either multiple matching recipients or none at all for /// the given `kek`. -pub fn decrypt_access_token_multiple( +pub fn decrypt_access_token_multiple( backend: &mut T, - key_provider: &mut CKP, - try_all_keys: bool, + key_provider: &CKP, + token: &ByteString, - external_aad: Option<&[u8]>, + external_aad: &AAD, ) -> Result> where T: CoseEncryptCipher + CoseKeyDistributionCipher, CKP: CoseKeyProvider, { let encrypt = CoseEncrypt::from_slice(token.as_slice()).map_err(AccessTokenError::from)?; - let result = encrypt.try_decrypt_with_recipients( - backend, - key_provider, - try_all_keys, - &mut external_aad.unwrap_or(&[] as &[u8]), - )?; + let result = encrypt.try_decrypt_with_recipients(backend, key_provider, external_aad)?; ClaimsSet::from_slice(result.as_slice()).map_err(AccessTokenError::from) } diff --git a/src/token/tests.rs b/src/token/tests.rs index c029fab..58139f9 100644 --- a/src/token/tests.rs +++ b/src/token/tests.rs @@ -234,7 +234,7 @@ fn test_encrypt_decrypt( &mut backend, &key, claims.clone(), - Some(&aad), + &aad.as_slice(), Some(unprotected_header.clone()), Some(protected_header.clone()), )?; @@ -242,7 +242,7 @@ fn test_encrypt_decrypt( assert_header_is_part_of(&unprotected_header, &unprotected); assert_header_is_part_of(&protected_header, &protected.header); assert_eq!( - decrypt_access_token(&mut backend, &mut &key, false, &encrypted, Some(&aad))?, + decrypt_access_token(&mut backend, &key, &encrypted, &aad.as_slice())?, claims ); Ok(()) @@ -272,7 +272,7 @@ fn test_encrypt_decrypt_multiple( &mut backend, vec![&key1, &key2], claims.clone(), - Some(&aad), + &aad, Some(unprotected_header.clone()), Some(protected_header.clone()), )?; @@ -281,17 +281,11 @@ fn test_encrypt_decrypt_multiple( assert_header_is_part_of(&protected_header, &protected.header); for key in vec![key1, key2] { assert_eq!( - &decrypt_access_token_multiple(&mut backend, &mut &key, false, &encrypted, Some(&aad))?, + &decrypt_access_token_multiple(&mut backend, &key, &encrypted, &aad)?, &claims ); } - let failed = decrypt_access_token_multiple( - &mut backend, - &mut &invalid_key1, - false, - &encrypted, - Some(&aad), - ); + let failed = decrypt_access_token_multiple(&mut backend, &invalid_key1, &encrypted, &aad); assert!(failed .err() .filter(|x| matches!( @@ -299,13 +293,7 @@ fn test_encrypt_decrypt_multiple( AccessTokenError::CoseCipherError(CoseCipherError::NoMatchingKeyFound(_)) )) .is_some()); - let failed = decrypt_access_token_multiple( - &mut backend, - &mut &invalid_key2, - false, - &encrypted, - Some(&aad), - ); + let failed = decrypt_access_token_multiple(&mut backend, &invalid_key2, &encrypted, &aad); assert!(failed .err() .filter(|x| matches!( @@ -328,7 +316,7 @@ fn test_encrypt_decrypt_match_multiple( &mut backend, vec![&key1, &key1], claims, - Some(&aad), + &aad, Some(unprotected_header.clone()), Some(protected_header.clone()), )?; @@ -336,7 +324,7 @@ fn test_encrypt_decrypt_match_multiple( assert_header_is_part_of(&unprotected_header, &unprotected); assert_header_is_part_of(&protected_header, &protected.header); // In the future, this should only be an error in "strict mode". - decrypt_access_token_multiple(&mut backend, &mut &key1, false, &encrypted, Some(&aad)) + decrypt_access_token_multiple(&mut backend, &key1, &encrypted, &aad) .expect("error while decrypting"); Ok(()) } @@ -354,7 +342,7 @@ fn test_encrypt_decrypt_invalid_header( &mut backend, &key, claims.clone(), - Some(&aad), + &aad, Some(unprotected_invalid), Some(protected_header), ); @@ -374,7 +362,7 @@ fn test_encrypt_decrypt_invalid_header( &mut backend, &key, claims, - Some(&aad), + &aad, Some(unprotected_header), Some(protected_invalid), ); @@ -406,7 +394,7 @@ fn test_sign_verify() -> Result<(), AccessTokenError< as C &mut backend, &key, claims, - Some(&aad), + &aad, Some(unprotected_header.clone()), Some(protected_header.clone()), )?; @@ -417,7 +405,7 @@ fn test_sign_verify() -> Result<(), AccessTokenError< as C get_token_headers(&signed).ok_or(AccessTokenError::::UnknownCoseStructure)?; assert_header_is_part_of(&unprotected_header, &unprotected); assert_header_is_part_of(&protected_header, &protected.header); - verify_access_token(&mut backend, &mut &key, false, &signed, Some(&aad))?; + verify_access_token(&mut backend, &key, &signed, &aad)?; Ok(()) } @@ -446,7 +434,7 @@ fn test_sign_verify_multiple( (&key2, CoseSignature::default()), ], claims, - Some(&aad), + &aad, Some(unprotected_header.clone()), Some(protected_header.clone()), )?; @@ -455,33 +443,25 @@ fn test_sign_verify_multiple( assert_header_is_part_of(&unprotected_header, &unprotected); assert_header_is_part_of(&protected_header, &protected.header); for key in vec![key1, key2] { - verify_access_token_multiple(&mut backend, &mut &key, false, &signed, Some(&aad))?; + verify_access_token_multiple(&mut backend, &key, &signed, &aad)?; } - assert!(verify_access_token_multiple( - &mut backend, - &mut &invalid_key1, - false, - &signed, - Some(&aad) - ) - .err() - .filter(|x| matches!( - x, - AccessTokenError::CoseCipherError(CoseCipherError::VerificationFailure) - )) - .is_some()); - assert!(verify_access_token_multiple( - &mut backend, - &mut &invalid_key2, - false, - &signed, - Some(&aad) - ) - .err() - .filter(|x| matches!( - x, - AccessTokenError::CoseCipherError(CoseCipherError::VerificationFailure) - )) - .is_some()); + assert!( + verify_access_token_multiple(&mut backend, &invalid_key1, &signed, &aad) + .err() + .filter(|x| matches!( + x, + AccessTokenError::CoseCipherError(CoseCipherError::VerificationFailure) + )) + .is_some() + ); + assert!( + verify_access_token_multiple(&mut backend, &invalid_key2, &signed, &aad) + .err() + .filter(|x| matches!( + x, + AccessTokenError::CoseCipherError(CoseCipherError::VerificationFailure) + )) + .is_some() + ); Ok(()) } diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 9a9dd23..e592363 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -151,7 +151,7 @@ fn test_scenario( assert_eq!(request, result); let expires_in: u32 = 3600; - let token = sign_access_token::( + let token = sign_access_token( &mut backend, &key, ClaimsSetBuilder::new() @@ -164,7 +164,7 @@ fn test_scenario( ) .build(), // TODO: Proper headers - Some(aad.as_slice()), + &aad.as_slice(), Some(unprotected_headers), Some(protected_headers), ) @@ -180,12 +180,11 @@ fn test_scenario( let result = pseudo_send_receive(response.clone())?; assert_eq!(response, result); - verify_access_token::( + verify_access_token( &mut backend, &mut &key, - false, &response.access_token, - Some(aad.as_slice()), + &aad.as_slice(), ) .map_err(|x| x.to_string())?;