diff --git a/CHANGELOG.md b/CHANGELOG.md index 17ce0d7..a0f50fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 This release mainly adds support for multiple token recipients, deals with the newly released RFCs, and fixes `no_std` support. +Note that the cipher interfaces have been refactored in a major way. ### Added @@ -27,7 +28,7 @@ and fixes `no_std` support. - The ciphers' API has been majorly changed. As a result, the API for the token functions has changed as well. Users no longer need to pass in an instance of the cipher, they only need to specify the type parameter, as the cipher's methods no longer need `self` as a parameter. Additionally, users now need to pass in the `key` for the - corresponding operation, whereas the type of the key is defined in the cipher. For more information, read the + corresponding operation, specified as a `CoseKey`. For more information, read the documentation of `CoseEncryptCipher`, `CoseSignCipher`, or `CoseMacCipher`, as well as of the token functions. - The documentation has been updated to refer to the recently released RFCs instead of the now outdated internet drafts. diff --git a/src/common/test_helper.rs b/src/common/test_helper.rs index 969ba0c..d3541a9 100644 --- a/src/common/test_helper.rs +++ b/src/common/test_helper.rs @@ -16,8 +16,8 @@ use core::convert::identity; use core::fmt::{Debug, Display}; use ciborium::value::Value; -use coset::iana::Algorithm; -use coset::{CoseKey, CoseKeyBuilder, Header, Label, ProtectedHeader}; +use coset::iana::{Algorithm, SymmetricKeyParameter}; +use coset::{iana, CoseKey, CoseKeyBuilder, Header, Label, ProtectedHeader}; use rand::{CryptoRng, Error, RngCore}; #[cfg(not(feature = "std"))] @@ -29,9 +29,26 @@ use { use crate::common::cbor_map::ToCborMap; use crate::error::{AccessTokenError, CoseCipherError, MultipleCoseError}; -use crate::token::{MultipleEncryptCipher, MultipleSignCipher, ToCoseKey}; +use crate::token::{CoseCipher, MultipleEncryptCipher, MultipleSignCipher}; use crate::{CoseEncryptCipher, CoseMacCipher, CoseSignCipher}; +/// Returns the value of the given symmetric [`key`]. +/// +/// # Panics +/// If [`key`] is not a symmetric key or has no valid key value. +fn get_symmetric_key_value(key: &CoseKey) -> Vec { + let k_label = iana::SymmetricKeyParameter::K as i64; + key.params + .iter() + .find(|x| matches!(x.0, Label::Int(k_label))) + .and_then(|x| match x { + (_, Value::Bytes(x)) => Some(x), + _ => None, + }) + .expect("Key value must be present!") + .clone() +} + /// Helper function for tests which ensures that [`value`] serializes to the hexadecimal bytestring /// [expected_hex] and deserializes back to [`value`]. /// @@ -88,12 +105,15 @@ where #[derive(Copy, Clone)] pub(crate) struct FakeCrypto {} -impl FakeCrypto { - fn set_headers_common( - key: &FakeKey, +impl CoseCipher for FakeCrypto { + type Error = String; + + fn set_headers( + key: &CoseKey, unprotected_header: &mut Header, protected_header: &mut Header, - ) -> Result<(), CoseCipherError> { + rng: RNG, + ) -> Result<(), CoseCipherError> { // We have to later verify these headers really are used. if let Some(label) = unprotected_header .rest @@ -110,55 +130,17 @@ impl FakeCrypto { } unprotected_header.rest.push((Label::Int(47), Value::Null)); protected_header.alg = Some(coset::Algorithm::Assigned(Algorithm::Direct)); - protected_header.key_id = key.kid.to_vec(); + protected_header.key_id = key.key_id.clone(); Ok(()) } } -#[derive(Clone)] -pub(crate) struct FakeKey { - key: [u8; 5], - kid: [u8; 2], -} - -impl ToCoseKey for FakeKey { - fn to_cose_key(&self) -> CoseKey { - CoseKeyBuilder::new_symmetric_key(self.key.to_vec()) - .key_id(self.kid.to_vec()) - .build() - } -} - -impl TryFrom> for FakeKey { - type Error = String; - - // Should be 5 bytes of key + 2 bytes of kid - fn try_from(mut value: Vec) -> Result { - let kid_value = value.split_off(5); - let key: [u8; 5] = value.try_into().map_err(|_| "Invalid input size")?; - let kid: [u8; 2] = kid_value.try_into().map_err(|_| "Invalid input size")?; - Ok(FakeKey { key, kid }) - } -} - -impl From for Vec { - fn from(k: FakeKey) -> Self { - let mut key = k.key.to_vec(); - key.append(&mut k.kid.to_vec()); - key - } -} - /// Implements basic operations from the [`CoseEncryptCipher`] trait without actually using any /// "real" cryptography. /// This is purely to be used for testing and obviously offers no security at all. impl CoseEncryptCipher for FakeCrypto { - type EncryptKey = FakeKey; - type DecryptKey = Self::EncryptKey; - type Error = String; - fn encrypt( - key: &Self::EncryptKey, + key: &CoseKey, plaintext: &[u8], aad: &[u8], protected_header: &Header, @@ -166,14 +148,14 @@ impl CoseEncryptCipher for FakeCrypto { ) -> Vec { // We put the key and the AAD before the data. // Again, this obviously isn't secure in any sane definition of the word. - let mut result: Vec = key.key.to_vec(); + let mut result: Vec = get_symmetric_key_value(key); result.append(&mut aad.to_vec()); result.append(&mut plaintext.to_vec()); result } fn decrypt( - key: &Self::DecryptKey, + key: &CoseKey, ciphertext: &[u8], aad: &[u8], unprotected_header: &Header, @@ -181,57 +163,45 @@ impl CoseEncryptCipher for FakeCrypto { ) -> Result, CoseCipherError> { // Now we just split off the AAD and key we previously put at the end of the data. // We return an error if it does not match. - if key.kid.to_vec() != protected_header.header.key_id { + if key.key_id.clone() != protected_header.header.key_id { // Mismatching key return Err(CoseCipherError::DecryptionFailure); } - if ciphertext.len() < (aad.len() + key.key.len()) { + let key_value = get_symmetric_key_value(key); + if ciphertext.len() < (aad.len() + key_value.len()) { return Err(CoseCipherError::Other( "Encrypted data has invalid length!".to_string(), )); } let mut result: Vec = ciphertext.to_vec(); - let plaintext = result.split_off(aad.len() + key.key.len()); - let aad_result = result.split_off(key.key.len()); - if aad == aad_result && key.key == result.as_slice() { + let plaintext = result.split_off(aad.len() + key_value.len()); + let aad_result = result.split_off(key_value.len()); + if aad == aad_result && key_value == result.as_slice() { Ok(plaintext) } else { Err(CoseCipherError::DecryptionFailure) } } - - fn set_headers( - key: &Self::EncryptKey, - unprotected_header: &mut Header, - protected_header: &mut Header, - rng: RNG, - ) -> Result<(), CoseCipherError> { - Self::set_headers_common(key, unprotected_header, protected_header) - } } /// Implements basic operations from the [`CoseSign1Cipher`] trait without actually using any /// "real" cryptography. /// This is purely to be used for testing and obviously offers no security at all. impl CoseSignCipher for FakeCrypto { - type SignKey = FakeKey; - type VerifyKey = Self::SignKey; - type Error = String; - fn sign( - key: &Self::SignKey, + key: &CoseKey, target: &[u8], unprotected_header: &Header, protected_header: &Header, ) -> Vec { // We simply append the key behind the data. let mut signature = target.to_vec(); - signature.append(&mut key.key.to_vec()); + signature.append(&mut get_symmetric_key_value(key)); signature } fn verify( - key: &Self::VerifyKey, + key: &CoseKey, signature: &[u8], signed_data: &[u8], unprotected_header: &Header, @@ -240,9 +210,9 @@ impl CoseSignCipher for FakeCrypto { protected_signature_header: Option<&ProtectedHeader>, ) -> Result<(), CoseCipherError> { let matching_kid = if let Some(protected) = protected_signature_header { - protected.header.key_id == key.kid + protected.header.key_id == key.key_id } else { - protected_header.header.key_id == key.kid + protected_header.header.key_id == key.key_id }; let signed_again = Self::sign( key, @@ -256,45 +226,32 @@ impl CoseSignCipher for FakeCrypto { Err(CoseCipherError::VerificationFailure) } } - - fn set_headers( - key: &Self::SignKey, - unprotected_header: &mut Header, - protected_header: &mut Header, - rng: RNG, - ) -> Result<(), CoseCipherError> { - Self::set_headers_common(&key, unprotected_header, protected_header) - } } /// Implements basic operations from the [`CoseMac0Cipher`] trait without actually using any /// "real" cryptography. /// This is purely to be used for testing and obviously offers no security at all. impl CoseMacCipher for FakeCrypto { - type ComputeKey = FakeKey; - type VerifyKey = Self::ComputeKey; - type Error = String; - fn compute( - key: &Self::ComputeKey, + key: &CoseKey, target: &[u8], unprotected_header: &Header, protected_header: &Header, ) -> Vec { // We simply append the key behind the data. let mut tag = target.to_vec(); - tag.append(&mut key.key.to_vec()); + tag.append(&mut get_symmetric_key_value(key)); tag } fn verify( - key: &Self::VerifyKey, + key: &CoseKey, tag: &[u8], maced_data: &[u8], unprotected_header: &Header, protected_header: &ProtectedHeader, ) -> Result<(), CoseCipherError> { - if protected_header.header.key_id == key.kid + if protected_header.header.key_id == key.key_id && tag == Self::compute( key, @@ -308,24 +265,17 @@ impl CoseMacCipher for FakeCrypto { Err(CoseCipherError::VerificationFailure) } } - - fn set_headers( - key: &Self::ComputeKey, - unprotected_header: &mut Header, - protected_header: &mut Header, - rng: RNG, - ) -> Result<(), CoseCipherError> { - Self::set_headers_common(&key, unprotected_header, protected_header) - } } impl MultipleEncryptCipher for FakeCrypto { - fn generate_cek(rng: &mut RNG) -> Self::EncryptKey { + fn generate_cek(rng: &mut RNG) -> CoseKey { let mut key = [0; 5]; let mut kid = [0; 2]; rng.fill_bytes(&mut key); rng.fill_bytes(&mut kid); - FakeKey { key, kid } + CoseKeyBuilder::new_symmetric_key(key.to_vec()) + .key_id(kid.to_vec()) + .build() } } diff --git a/src/lib.rs b/src/lib.rs index 30ad8c6..576f740 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ //! of the token, using an existing cipher of type `FakeCrypto`[^cipher]: //! ``` //! # use ciborium::value::Value; -//! # use coset::{AsCborValue, CoseKey, CoseKeyBuilder, Header, Label, ProtectedHeader}; +//! # use coset::{AsCborValue, CoseKey, CoseKeyBuilder, Header, iana, Label, ProtectedHeader}; //! # use coset::cwt::{ClaimsSetBuilder, Timestamp}; //! # use coset::iana::{Algorithm, CwtClaimName}; //! # use rand::{CryptoRng, RngCore}; @@ -95,27 +95,25 @@ //! # use dcaf::common::cbor_values::{ByteString, ProofOfPossessionKey}; //! # use dcaf::common::cbor_values::ProofOfPossessionKey::PlainCoseKey; //! # use dcaf::error::{AccessTokenError, CoseCipherError}; -//! # use dcaf::token::ToCoseKey; +//! use dcaf::token::CoseCipher; //! -//! #[derive(Clone)] -//! # pub(crate) struct FakeKey { -//! # key: [u8; 5], -//! # kid: [u8; 2], -//! # } -//! # -//! # impl ToCoseKey for FakeKey { -//! # fn to_cose_key(&self) -> CoseKey { -//! # CoseKeyBuilder::new_symmetric_key(self.key.to_vec()) -//! # .key_id(self.kid.to_vec()) -//! # .build() -//! # } -//! # } -//! # //! # struct FakeCrypto {} //! # //! # #[derive(Clone, Copy)] //! # pub(crate) struct FakeRng; //! # +//! # fn get_k_from_key(key: &CoseKey) -> Option> { +//! # const K_PARAM: i64 = iana::SymmetricKeyParameter::K as i64; +//! # for (label, value) in key.params.iter() { +//! # if let Label::Int(K_PARAM) = label { +//! # if let Value::Bytes(k_val) = value { +//! # return Some(k_val.clone()); +//! # } +//! # } +//! # } +//! # None +//! # } +//! # //! # impl RngCore for FakeRng { //! # fn next_u32(&mut self) -> u32 { //! # 0 @@ -137,20 +135,11 @@ //! # //! # impl CryptoRng for FakeRng {} //! # -//! # /// Implements basic operations from the [`CoseSignCipher`](crate::token::CoseSignCipher) trait -//! # /// without actually using any "real" cryptography. -//! # /// This is purely to be used for testing and obviously offers no security at all. -//! # impl CoseSignCipher for FakeCrypto { +//! # impl CoseCipher for FakeCrypto { //! # type Error = String; -//! # type SignKey = FakeKey; -//! # type VerifyKey = Self::SignKey; //! # -//! # fn set_headers( -//! # key: &FakeKey, -//! # unprotected_header: &mut Header, -//! # protected_header: &mut Header, -//! # rng: RNG -//! # ) -> Result<(), CoseCipherError> { +//! # fn set_headers(key: &CoseKey, unprotected_header: &mut Header, protected_header: &mut Header, rng: RNG) -> Result<(), CoseCipherError> { +//! # // We have to later verify these headers really are used. //! # if let Some(label) = unprotected_header //! # .rest //! # .iter() @@ -161,28 +150,31 @@ //! # if protected_header.alg != None { //! # return Err(CoseCipherError::existing_header("alg")); //! # } -//! # if !protected_header.key_id.is_empty() { -//! # return Err(CoseCipherError::existing_header("key_id")); -//! # } //! # unprotected_header.rest.push((Label::Int(47), Value::Null)); //! # protected_header.alg = Some(coset::Algorithm::Assigned(Algorithm::Direct)); -//! # protected_header.key_id = key.kid.to_vec(); //! # Ok(()) //! # } +//! # } +//! # +//! # /// Implements basic operations from the [`CoseSignCipher`](crate::token::CoseSignCipher) trait +//! # /// without actually using any "real" cryptography. +//! # /// This is purely to be used for testing and obviously offers no security at all. +//! # impl CoseSignCipher for FakeCrypto { //! # fn sign( -//! # key: &Self::SignKey, +//! # key: &CoseKey, //! # target: &[u8], //! # unprotected_header: &Header, //! # protected_header: &Header, //! # ) -> Vec { //! # // We simply append the key behind the data. //! # let mut signature = target.to_vec(); -//! # signature.append(&mut key.key.to_vec()); +//! # let k = get_k_from_key(key); +//! # signature.append(&mut k.expect("k must be present in key!")); //! # signature //! # } //! # //! # fn verify( -//! # key: &Self::VerifyKey, +//! # key: &CoseKey, //! # signature: &[u8], //! # signed_data: &[u8], //! # unprotected_header: &Header, @@ -190,13 +182,13 @@ //! # unprotected_signature_header: Option<&Header>, //! # protected_signature_header: Option<&ProtectedHeader>, //! # ) -> Result<(), CoseCipherError> { -//! # let matching_kid = if let Some(protected) = protected_signature_header { -//! # protected.header.key_id == key.kid -//! # } else { -//! # protected_header.header.key_id == key.kid -//! # }; -//! # let signed_again = Self::sign(key, signed_data, unprotected_header, &protected_header.header); -//! # if matching_kid && signed_again == signature +//! # if signature +//! # == Self::sign( +//! # key, +//! # signed_data, +//! # unprotected_header, +//! # &protected_header.header, +//! # ) //! # { //! # Ok(()) //! # } else { @@ -206,12 +198,11 @@ //! # } //! //! let rng = FakeRng; -//! let key = FakeKey { key: [1,2,3,4,5], kid: [0xDC, 0xAF]}; -//! let cose_key: CoseKey = key.to_cose_key(); +//! let key = CoseKeyBuilder::new_symmetric_key(vec![1,2,3,4,5]).key_id(vec![0xDC, 0xAF]).build(); //! let claims = ClaimsSetBuilder::new() //! .audience(String::from("coaps://rs.example.com")) //! .issuer(String::from("coaps://as.example.com")) -//! .claim(CwtClaimName::Cnf, cose_key.to_cbor_value()?) +//! .claim(CwtClaimName::Cnf, key.clone().to_cbor_value()?) //! .build(); //! let token = sign_access_token::(&key, claims, None, None, None, rng)?; //! assert!(verify_access_token::(&key, &token, None).is_ok()); @@ -295,13 +286,13 @@ //! # COSE Cipher //! As mentioned before, cryptographic functions are outside the scope of this crate. //! For this reason, the various COSE cipher traits exist; namely, -//! [`CoseEncryptCipher`](core::token::CoseEncryptCipher), [`CoseSignCipher`](core::token::CoseSignCipher), -//! and [`CoseMacCipher`](core::token::CoseMacCipher), each implementing +//! [`CoseEncryptCipher`](token::CoseEncryptCipher), [`CoseSignCipher`](token::CoseSignCipher), +//! and [`CoseMacCipher`](token::CoseMacCipher), each implementing //! a corresponding COSE operation as specified in sections 4, 5, and 6 of //! [RFC 8152](https://www.rfc-editor.org/rfc/rfc8152). -//! There are also the traits [`MultipleEncryptCipher`](core::token::MultipleEncryptCipher), -//! [`MultipleSignCipher`](core::token::MultipleSignCipher), and -//! [`MultipleMacCipher`](core::token::MultipleMacCipher), +//! There are also the traits [`MultipleEncryptCipher`](token::MultipleEncryptCipher), +//! [`MultipleSignCipher`](token::MultipleSignCipher), and +//! [`MultipleMacCipher`](token::MultipleMacCipher), //! which are used for creating tokens intended for multiple recipients. //! //! Note that these ciphers *don't* need to wrap their results in, e.g., diff --git a/src/token/mod.rs b/src/token/mod.rs index ca97ad0..0bd1663 100644 --- a/src/token/mod.rs +++ b/src/token/mod.rs @@ -17,7 +17,7 @@ //! library, since much of this just builds on COSE functionality and isn't ACE-OAuth specific. //! //! In order to use any of these methods, you will need to provide a cipher which handles -//! the cryptographic operations by implementingeither [`CoseEncryptCipher`], +//! the cryptographic operations by implementing either [`CoseEncryptCipher`], //! [`CoseMacCipher`] or [`CoseSignCipher`], depending on the intended operation. //! If you plan to support `CoseEncrypt` or `CoseSign` rather than just `CoseEncrypt0` or //! `CoseSign1` (i.e., if you have multiple recipients with separate keys), you will also need to @@ -31,7 +31,7 @@ //! # // TODO: There's really too much hidden code here. Should be heavily refactored once we have //! # // crypto implementations available. Same goes for crate-level docs. //! # use ciborium::value::Value; -//! # use coset::{AsCborValue, CoseKey, CoseKeyBuilder, Header, Label, ProtectedHeader}; +//! # use coset::{AsCborValue, CoseKey, CoseKeyBuilder, Header, iana, Label, ProtectedHeader}; //! # use coset::cwt::{ClaimsSetBuilder, Timestamp}; //! # use coset::iana::{Algorithm, CwtClaimName}; //! # use rand::{CryptoRng, RngCore}; @@ -39,24 +39,22 @@ //! # use dcaf::common::cbor_values::{ByteString, ProofOfPossessionKey}; //! # use dcaf::common::cbor_values::ProofOfPossessionKey::PlainCoseKey; //! # use dcaf::error::{AccessTokenError, CoseCipherError}; -//! # use dcaf::token::ToCoseKey; +//! use dcaf::token::CoseCipher; //! -//! #[derive(Clone)] -//! # pub(crate) struct FakeKey { -//! # key: [u8; 5], -//! # kid: [u8; 2], -//! # } +//! # struct FakeCrypto {} //! # -//! # impl ToCoseKey for FakeKey { -//! # fn to_cose_key(&self) -> CoseKey { -//! # CoseKeyBuilder::new_symmetric_key(self.key.to_vec()) -//! # .key_id(self.kid.to_vec()) -//! # .build() +//! # fn get_k_from_key(key: &CoseKey) -> Option> { +//! # const K_PARAM: i64 = iana::SymmetricKeyParameter::K as i64; +//! # for (label, value) in key.params.iter() { +//! # if let Label::Int(K_PARAM) = label { +//! # if let Value::Bytes(k_val) = value { +//! # return Some(k_val.clone()); +//! # } +//! # } //! # } +//! # None //! # } //! # -//! # struct FakeCrypto {} -//! # //! # #[derive(Clone, Copy)] //! # pub(crate) struct FakeRng; //! # @@ -81,20 +79,11 @@ //! # //! # impl CryptoRng for FakeRng {} //! # -//! # /// Implements basic operations from the [`CoseSignCipher`] trait without actually using any -//! # /// "real" cryptography. -//! # /// This is purely to be used for testing and obviously offers no security at all. -//! # impl CoseSignCipher for FakeCrypto { +//! # impl CoseCipher for FakeCrypto { //! # type Error = String; -//! # type SignKey = FakeKey; -//! # type VerifyKey = Self::SignKey; //! # -//! # fn set_headers( -//! # key: &FakeKey, -//! # unprotected_header: &mut Header, -//! # protected_header: &mut Header, -//! # rng: RNG -//! # ) -> Result<(), CoseCipherError> { +//! # fn set_headers(key: &CoseKey, unprotected_header: &mut Header, protected_header: &mut Header, rng: RNG) -> Result<(), CoseCipherError> { +//! # // We have to later verify these headers really are used. //! # if let Some(label) = unprotected_header //! # .rest //! # .iter() @@ -105,28 +94,31 @@ //! # if protected_header.alg != None { //! # return Err(CoseCipherError::existing_header("alg")); //! # } -//! # if !protected_header.key_id.is_empty() { -//! # return Err(CoseCipherError::existing_header("key_id")); -//! # } //! # unprotected_header.rest.push((Label::Int(47), Value::Null)); //! # protected_header.alg = Some(coset::Algorithm::Assigned(Algorithm::Direct)); -//! # protected_header.key_id = key.kid.to_vec(); //! # Ok(()) //! # } +//! # } +//! # +//! # /// Implements basic operations from the [`CoseSignCipher`](crate::token::CoseSignCipher) trait +//! # /// without actually using any "real" cryptography. +//! # /// This is purely to be used for testing and obviously offers no security at all. +//! # impl CoseSignCipher for FakeCrypto { //! # fn sign( -//! # key: &Self::SignKey, +//! # key: &CoseKey, //! # target: &[u8], //! # unprotected_header: &Header, //! # protected_header: &Header, //! # ) -> Vec { //! # // We simply append the key behind the data. //! # let mut signature = target.to_vec(); -//! # signature.append(&mut key.key.to_vec()); +//! # let k = get_k_from_key(key); +//! # signature.append(&mut k.expect("k must be present in key!")); //! # signature //! # } //! # //! # fn verify( -//! # key: &Self::VerifyKey, +//! # key: &CoseKey, //! # signature: &[u8], //! # signed_data: &[u8], //! # unprotected_header: &Header, @@ -134,13 +126,13 @@ //! # unprotected_signature_header: Option<&Header>, //! # protected_signature_header: Option<&ProtectedHeader>, //! # ) -> Result<(), CoseCipherError> { -//! # let matching_kid = if let Some(protected) = protected_signature_header { -//! # protected.header.key_id == key.kid -//! # } else { -//! # protected_header.header.key_id == key.kid -//! # }; -//! # let signed_again = Self::sign(key, signed_data, unprotected_header, &protected_header.header); -//! # if matching_kid && signed_again == signature +//! # if signature +//! # == Self::sign( +//! # key, +//! # signed_data, +//! # unprotected_header, +//! # &protected_header.header, +//! # ) //! # { //! # Ok(()) //! # } else { @@ -150,12 +142,11 @@ //! # } //! //! let rng = FakeRng; -//! let key = FakeKey { key: [1,2,3,4,5], kid: [0xDC, 0xAF]}; -//! let cose_key: CoseKey = key.to_cose_key(); +//! let key = CoseKeyBuilder::new_symmetric_key(vec![1,2,3,4,5]).key_id(vec![0xDC, 0xAF]).build(); //! let claims = ClaimsSetBuilder::new() //! .audience(String::from("coaps://rs.example.com")) //! .issuer(String::from("coaps://as.example.com")) -//! .claim(CwtClaimName::Cnf, cose_key.to_cbor_value()?) +//! .claim(CwtClaimName::Cnf, key.clone().to_cbor_value()?) //! .build(); //! let token = sign_access_token::(&key, claims, None, None, None, rng)?; //! assert!(verify_access_token::(&key, &token, None).is_ok()); @@ -182,95 +173,76 @@ use crate::error::{AccessTokenError, CoseCipherError, MultipleCoseError}; #[cfg(test)] mod tests; -/// Trait for keys which can be converted to [CoseKey]s from a reference of the original type. -pub trait ToCoseKey { - /// Converts a reference of itself to a [CoseKey]. +/// Trait for common parts required for [`CoseSignCipher`], [`CoseEncryptCipher`] +/// and [`CoseMacCipher`]. +pub trait CoseCipher { + /// Error type that this cipher uses in [`Result`]s returned by cryptographic operations. + type Error: Display + Debug; + + /// Sets headers specific to this cipher by adding new header fields to the given + /// `unprotected_header` and `protected_header`. + /// + /// The given `key` may be used to extract information for the headers (e.g., the key ID) + /// and `rng` may be used to generate random values for the headers (e.g., an IV). + /// + /// Before actually changing the headers, it will be verified that none of the header fields + /// that are about to be set are already set, so as not to overwrite them. In such a + /// case, an error is returned. /// - /// Note that this may lead to fields of the key being copied, - /// as we merely pass a reference in, even though [CoseKey] is not associated with any lifetime. - fn to_cose_key(&self) -> CoseKey; + /// This will usually not be called by users of `dcaf-rs`, but instead by access methods + /// such as [`encrypt_access_token`], which will later pass it to [coset]'s methods. + /// + /// # Errors + /// - When the fields that this method would set on the given headers are already set. + /// + /// # Example + /// Let's say our cipher needs to set the content type to + /// [`Cbor`](coset::iana::CoapContentFormat::Cbor) (in the unprotected header) + /// and the key ID to the ID of the passed in `key` (in the protected header). + /// Our implementation would first need to verify that these + /// header fields haven't already been set, then actually set them, so an implementation + /// of this function might look like the following: + /// ```ignore + /// fn set_headers( + /// key: &FakeKey, + /// unprotected_header: &mut Header, + /// protected_header: &mut Header, + /// rng: RNG + /// ) -> Result<(), CoseCipherError> { + /// if unprotected_header.content_type.is_some() { + /// return Err(CoseCipherError::existing_header("content_type")); + /// } + /// if !protected_header.key_id.is_empty() { + /// return Err(CoseCipherError::existing_header("kid")); + /// } + /// unprotected_header.content_type = Some(ContentType::Assigned(coset::iana::CoapContentFormat::Cbor)); + /// protected_header.key_id = key.kid.to_vec(); + /// Ok(()) + /// } + /// ``` + fn set_headers( + key: &CoseKey, + unprotected_header: &mut Header, + protected_header: &mut Header, + rng: RNG, + ) -> Result<(), CoseCipherError>; } // TODO: Examples in here are currently either not run or do not exist because they require too much // setup (see crate-level docs). This should be fixed once we have cipher implementations. -macro_rules! add_common_cipher_functionality { - [$a:ty] => { - /// Error type that this cipher uses in [`Result`]s returned by cryptographic operations. - type Error: Display + Debug; - - /// Sets headers specific to this cipher by adding new header fields to the given - /// `unprotected_header` and `protected_header`. - /// - /// The given `key` may be used to extract information for the headers (e.g., the key ID) - /// and `rng` may be used to generate random values for the headers (e.g., an IV). - /// - /// Before actually changing the headers, it will be verified that none of the header fields - /// that are about to be set are already set, so as not to overwrite them. In such a - /// case, an error is returned. - /// - /// This will usually not be called by users of `dcaf-rs`, but instead by access methods - /// such as [`encrypt_access_token`], which will later pass it to [coset]'s methods. - /// - /// # Errors - /// - When the fields that this method would set on the given headers are already set. - /// - /// # Example - /// Let's say our cipher needs to set the content type to - /// [`Cbor`](coset::iana::CoapContentFormat::Cbor) (in the unprotected header) - /// and the key ID to the ID of the passed in `key` (in the protected header). - /// Our implementation would first need to verify that these - /// header fields haven't already been set, then actually set them, so an implementation - /// of this function might look like the following: - /// ```ignore - /// fn set_headers( - /// key: &FakeKey, - /// unprotected_header: &mut Header, - /// protected_header: &mut Header, - /// rng: RNG - /// ) -> Result<(), CoseCipherError> { - /// if unprotected_header.content_type.is_some() { - /// return Err(CoseCipherError::existing_header("content_type")); - /// } - /// if !protected_header.key_id.is_empty() { - /// return Err(CoseCipherError::existing_header("kid")); - /// } - /// unprotected_header.content_type = Some(ContentType::Assigned(coset::iana::CoapContentFormat::Cbor)); - /// protected_header.key_id = key.kid.to_vec(); - /// Ok(()) - /// } - /// ``` - fn set_headers( - key: &$a, - unprotected_header: &mut Header, - protected_header: &mut Header, - rng: RNG - ) -> Result<(), CoseCipherError>; - } -} - /// Provides basic operations for encrypting and decrypting COSE structures. /// /// This will be used by [`encrypt_access_token`] and [`decrypt_access_token`] (as well as the /// variants for multiple recipients: [`encrypt_access_token_multiple`] /// and [`decrypt_access_token_multiple`]) to apply the /// corresponding cryptographic operations to the constructed token bytestring. -/// The [`set_headers` method](CoseEncryptCipher::set_headers) can be used to set parameters this +/// The [`set_headers` method](CoseCipher::set_headers) can be used to set parameters this /// cipher requires to be set. -pub trait CoseEncryptCipher { - /// Type of the encryption key. Needs to be serializable to a vector of bytes in case - /// [`encrypt_access_token_multiple`] is used, in which we need to serialize the - /// Key Encryption Keys. - type EncryptKey: ToCoseKey + Into>; - - /// Type of the decryption key. Needs to be deserializable from a vector of bytes in case - /// [`decrypt_access_token_multiple`] is used, in which we need to deserialize the - /// Key Encryption Keys. - type DecryptKey: ToCoseKey + TryFrom>; - +pub trait CoseEncryptCipher: CoseCipher { /// Encrypts the `plaintext` and `aad` with the given `key`, returning the result. fn encrypt( - key: &Self::EncryptKey, + key: &CoseKey, plaintext: &[u8], aad: &[u8], protected_header: &Header, @@ -282,14 +254,12 @@ pub trait CoseEncryptCipher { /// # Errors /// If the `ciphertext` and `aad` are invalid, i.e., can't be decrypted. fn decrypt( - key: &Self::DecryptKey, + key: &CoseKey, ciphertext: &[u8], aad: &[u8], unprotected_header: &Header, protected_header: &ProtectedHeader, ) -> Result, CoseCipherError>; - - add_common_cipher_functionality![Self::EncryptKey]; } /// Intended for ciphers which can encrypt for multiple recipients. @@ -301,7 +271,7 @@ pub trait MultipleEncryptCipher: CoseEncryptCipher { /// The content of the `CoseEncrypt` will then be encrypted with the key, while each recipient /// will be encrypted with a corresponding Key Encryption Key (KEK) provided by the caller /// of [`encrypt_access_token_multiple`]. - fn generate_cek(rng: &mut RNG) -> Self::EncryptKey; + fn generate_cek(rng: &mut RNG) -> CoseKey; } /// Provides basic operations for signing and verifying COSE structures. @@ -310,18 +280,12 @@ pub trait MultipleEncryptCipher: CoseEncryptCipher { /// equivalents for multiple recipients: [`sign_access_token_multiple`] and /// [`verify_access_token_multiple`]) to apply the /// corresponding cryptographic operations to the constructed token bytestring. -/// The [`set_headers` method](CoseSignCipher::set_headers) can be used to set parameters +/// The [`set_headers` method](CoseCipher::set_headers) can be used to set parameters /// this cipher requires to be set. -pub trait CoseSignCipher { - /// Type of the key used to create signatures. - type SignKey: ToCoseKey; - - /// Type of the key used to verify signatures. - type VerifyKey: ToCoseKey; - +pub trait CoseSignCipher: CoseCipher { /// Cryptographically signs the `target` value with the `key` and returns the signature. fn sign( - key: &Self::SignKey, + key: &CoseKey, target: &[u8], unprotected_header: &Header, protected_header: &Header, @@ -339,7 +303,7 @@ pub trait CoseSignCipher { /// # Errors /// If the `signature` is invalid or does not belong to the `signed_data`. fn verify( - key: &Self::VerifyKey, + key: &CoseKey, signature: &[u8], signed_data: &[u8], unprotected_header: &Header, @@ -347,8 +311,6 @@ pub trait CoseSignCipher { unprotected_signature_header: Option<&Header>, protected_signature_header: Option<&ProtectedHeader>, ) -> Result<(), CoseCipherError>; - - add_common_cipher_functionality![Self::SignKey]; } /// Marker trait intended for ciphers which can create signatures for multiple recipients. @@ -359,16 +321,10 @@ pub trait MultipleSignCipher: CoseSignCipher {} /// Provides basic operations for generating and verifying MAC tags for COSE structures. /// /// This trait is currently not used by any access token function. -pub trait CoseMacCipher { - /// Type of the key used to compute MAC tags. - type ComputeKey: ToCoseKey; - - /// Type of the key used to verify MAC tags. - type VerifyKey: ToCoseKey; - +pub trait CoseMacCipher: CoseCipher { /// Generates a MAC tag for the given `target` with the given `key` and returns it. fn compute( - key: &Self::ComputeKey, + key: &CoseKey, target: &[u8], unprotected_header: &Header, protected_header: &Header, @@ -379,14 +335,12 @@ pub trait CoseMacCipher { /// # Errors /// If the `tag` is invalid or does not belong to the `maced_data`. fn verify( - key: &Self::VerifyKey, + key: &CoseKey, tag: &[u8], maced_data: &[u8], unprotected_header: &Header, protected_header: &ProtectedHeader, ) -> Result<(), CoseCipherError>; - - add_common_cipher_functionality![Self::ComputeKey]; } /// Marker trait intended for ciphers which can create MAC tags for multiple recipients. @@ -433,7 +387,7 @@ macro_rules! prepare_headers { /// assert_eq!(decrypt_access_token::(&key, &token, None)?, claims); /// ``` pub fn encrypt_access_token( - key: &T::EncryptKey, + key: &CoseKey, claims: ClaimsSet, external_aad: Option<&[u8]>, unprotected_header: Option
, @@ -485,7 +439,7 @@ where /// )?; /// ``` pub fn encrypt_access_token_multiple( - keys: Vec<&T::EncryptKey>, + keys: Vec<&CoseKey>, claims: ClaimsSet, external_aad: Option<&[u8]>, unprotected_header: Option
, @@ -507,7 +461,7 @@ where external_aad.unwrap_or(&[0; 0]), |payload, aad| T::encrypt(&key, payload, aad, &protected, &unprotected), ); - let serialized_key: Vec = key.into(); + let serialized_key: Vec = key.to_vec().map_err(AccessTokenError::CoseError)?; for rec_key in keys { let (rec_unprotected, rec_protected) = prepare_headers!(rec_key, None, None, &mut rng, T)?; builder = builder.add_recipient( @@ -556,7 +510,7 @@ where /// assert!(verify_access_token::(&key, &token, None).is_ok()); /// ``` pub fn sign_access_token( - key: &T::SignKey, + key: &CoseKey, claims: ClaimsSet, external_aad: Option<&[u8]>, unprotected_header: Option
, @@ -607,7 +561,7 @@ where /// )?; /// ``` pub fn sign_access_token_multiple( - keys: Vec<&T::SignKey>, + keys: Vec<&CoseKey>, claims: ClaimsSet, external_aad: Option<&[u8]>, unprotected_header: Option
, @@ -711,7 +665,7 @@ pub fn get_token_headers(token: &ByteString) -> Option<(Header, ProtectedHeader) /// - 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( - key: &T::VerifyKey, + key: &CoseKey, token: &ByteString, external_aad: Option<&[u8]>, ) -> Result<(), AccessTokenError> @@ -752,7 +706,7 @@ where /// - 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( - key: &T::VerifyKey, + key: &CoseKey, token: &ByteString, external_aad: Option<&[u8]>, ) -> Result<(), AccessTokenError> @@ -760,14 +714,14 @@ where T: CoseSignCipher, { let sign = CoseSign::from_slice(token.as_slice()).map_err(AccessTokenError::CoseError)?; - let kid = key.to_cose_key().key_id; + let kid = &key.key_id; let (unprotected, protected) = get_token_headers(token).ok_or(AccessTokenError::UnknownCoseStructure)?; let matching = sign .signatures .iter() .enumerate() - .filter(|(_, s)| s.unprotected.key_id == kid || s.protected.header.key_id == kid) + .filter(|(_, s)| &s.unprotected.key_id == kid || &s.protected.header.key_id == kid) .map(|(i, _)| i); let mut matching_kid = false; // We iterate over each signature whose kid matches until it completes successfully. @@ -817,7 +771,7 @@ where /// - When the deserialized and decrypted [`CoseEncrypt0`] structure does not contain a valid /// [`ClaimsSet`]. pub fn decrypt_access_token( - key: &T::DecryptKey, + key: &CoseKey, token: &ByteString, external_aad: Option<&[u8]>, ) -> Result> @@ -854,7 +808,7 @@ where /// - When the [`CoseEncrypt`] contains either multiple matching recipients or none at all for /// the given `kek`. pub fn decrypt_access_token_multiple( - kek: &K::DecryptKey, + kek: &CoseKey, token: &ByteString, external_aad: Option<&[u8]>, ) -> Result>> @@ -866,8 +820,7 @@ where let (unprotected, protected) = get_token_headers(token).ok_or(AccessTokenError::UnknownCoseStructure)?; let aad = external_aad.unwrap_or(&[0; 0]); - let cose_kek: CoseKey = kek.to_cose_key(); - let kek_id = cose_kek.key_id.as_slice(); + let kek_id = kek.key_id.as_slice(); // One of the recipient structures should contain CEK encrypted with our KEK. let recipients = encrypt .recipients @@ -884,7 +837,7 @@ where if let Some(content_key_result) = content_keys.next() { if content_keys.next().is_none() { let content_key = content_key_result.map_err(CoseCipherError::from_kek_error)?; - let target_key = C::DecryptKey::try_from(content_key) + let target_key = CoseKey::from_slice(&content_key) .map_err(|_| CoseCipherError::DecryptionFailure)?; // TODO: Verify protected header let result = encrypt diff --git a/src/token/tests.rs b/src/token/tests.rs index 5a9415a..c0e1c83 100644 --- a/src/token/tests.rs +++ b/src/token/tests.rs @@ -20,19 +20,23 @@ use coset::cwt::ClaimsSetBuilder; use coset::iana::{Algorithm, CoapContentFormat, CwtClaimName}; use coset::{AsCborValue, CoseKey, CoseKeyBuilder, CoseMac0Builder, HeaderBuilder}; -use crate::common::test_helper::{FakeCrypto, FakeKey, FakeRng}; +use crate::common::test_helper::{FakeCrypto, FakeRng}; use crate::error::CoseCipherError; use super::*; /// Generates a test key with content `[1,2,3,4,5]` and key id `[0xDC, 0xAF]`. -fn example_key_one() -> FakeKey { - FakeKey::try_from(vec![1, 2, 3, 4, 5, 0xDC, 0xAF]).expect("invalid test key") +fn example_key_one() -> CoseKey { + CoseKeyBuilder::new_symmetric_key(vec![1, 2, 3, 4, 5]) + .key_id(vec![0xDC, 0xAF]) + .build() } /// Generates a test key with content `[10, 9, 8, 7, 6]` and key id `[0xCA, 0xFE]`. -fn example_key_two() -> FakeKey { - FakeKey::try_from(vec![10, 9, 8, 7, 6, 0xCA, 0xFE]).expect("invalid test key") +fn example_key_two() -> CoseKey { + CoseKeyBuilder::new_symmetric_key(vec![10, 9, 8, 7, 6, 0xCA, 0xFE]) + .key_id(vec![0xCA, 0xFE]) + .build() } fn example_headers() -> (Header, Header) { @@ -60,10 +64,10 @@ fn example_aad() -> Vec { } fn example_claims( - key: CoseKey, -) -> Result::Error>> { + key: &CoseKey, +) -> Result::Error>> { Ok(ClaimsSetBuilder::new() - .claim(CwtClaimName::Cnf, key.to_cbor_value()?) + .claim(CwtClaimName::Cnf, key.clone().to_cbor_value()?) .build()) } @@ -104,8 +108,7 @@ fn assert_header_is_part_of(subset: &Header, superset: &Header) { } #[test] -fn test_get_headers_enc() -> Result<(), AccessTokenError<::Error>> -{ +fn test_get_headers_enc() -> Result<(), AccessTokenError<::Error>> { let (unprotected_header, protected_header) = example_headers(); let enc_test = CoseEncrypt0Builder::new() .unprotected(unprotected_header.clone()) @@ -122,7 +125,7 @@ fn test_get_headers_enc() -> Result<(), AccessTokenError< Result<(), AccessTokenError<::Error>> { +fn test_get_headers_sign() -> Result<(), AccessTokenError<::Error>> { let (unprotected_header, protected_header) = example_headers(); let sign_test = CoseSign1Builder::new() .unprotected(unprotected_header.clone()) @@ -139,7 +142,7 @@ fn test_get_headers_sign() -> Result<(), AccessTokenError< Result<(), AccessTokenError<::Error>> { +fn test_get_headers_mac() -> Result<(), AccessTokenError<::Error>> { let (unprotected_header, protected_header) = example_headers(); let mac_test = CoseMac0Builder::new() .unprotected(unprotected_header.clone()) @@ -170,11 +173,10 @@ fn test_get_headers_invalid() { } #[test] -fn test_encrypt_decrypt() -> Result<(), AccessTokenError<::Error>> -{ +fn test_encrypt_decrypt() -> Result<(), AccessTokenError<::Error>> { let key = example_key_one(); let (unprotected_header, protected_header) = example_headers(); - let claims = example_claims(key.to_cose_key())?; + let claims = example_claims(&key)?; let aad = example_aad(); let rng = FakeRng; let encrypted = encrypt_access_token::( @@ -196,15 +198,18 @@ fn test_encrypt_decrypt() -> Result<(), AccessTokenError< Result<(), AccessTokenError<::Error>> { +fn test_encrypt_decrypt_multiple() -> Result<(), AccessTokenError<::Error>> +{ const AUDIENCE: &str = "example_aud"; let (unprotected_header, protected_header) = example_headers(); let key1 = example_key_one(); let key2 = example_key_two(); - let invalid_key1 = FakeKey::try_from(vec![0, 0, 0, 0, 0, 0, 0]).expect("invalid test key"); - let invalid_key2 = - FakeKey::try_from(vec![0, 0, 0, 0, 0, 0xDC, 0xAF]).expect("invalid test key"); + let invalid_key1 = CoseKeyBuilder::new_symmetric_key(vec![0; 5]) + .key_id(vec![0, 0]) + .build(); + let invalid_key2 = CoseKeyBuilder::new_symmetric_key(vec![0; 5]) + .key_id(vec![0xDC, 0xAF]) + .build(); let rng = FakeRng; let aad = example_aad(); // Using example_claims doesn't make sense, since they contain a cnf for the key, @@ -255,7 +260,7 @@ fn test_encrypt_decrypt_multiple( #[test] fn test_encrypt_decrypt_match_multiple( -) -> Result<(), AccessTokenError<::Error>> { +) -> Result<(), AccessTokenError<::Error>> { let (unprotected_header, protected_header) = example_headers(); let key1 = example_key_one(); let rng = FakeRng; @@ -284,11 +289,11 @@ fn test_encrypt_decrypt_match_multiple( #[test] fn test_encrypt_decrypt_invalid_header( -) -> Result<(), AccessTokenError<::Error>> { +) -> Result<(), AccessTokenError<::Error>> { let key = example_key_one(); let (unprotected_header, protected_header) = example_headers(); let (unprotected_invalid, protected_invalid) = example_invalid_headers(); - let claims = example_claims(key.to_cose_key())?; + let claims = example_claims(&key)?; let aad = example_aad(); let rng = FakeRng; let encrypted = encrypt_access_token::( @@ -334,10 +339,10 @@ fn test_encrypt_decrypt_invalid_header( } #[test] -fn test_sign_verify() -> Result<(), AccessTokenError<::Error>> { +fn test_sign_verify() -> Result<(), AccessTokenError<::Error>> { let key = example_key_one(); let (unprotected_header, protected_header) = example_headers(); - let claims = example_claims(key.to_cose_key())?; + let claims = example_claims(&key)?; let aad = example_aad(); let rng = FakeRng; let signed = sign_access_token::( @@ -360,14 +365,16 @@ fn test_sign_verify() -> Result<(), AccessTokenError< Result<(), AccessTokenError<::Error>> -{ +fn test_sign_verify_multiple() -> Result<(), AccessTokenError<::Error>> { const AUDIENCE: &str = "example_aud"; let key1 = example_key_one(); let key2 = example_key_two(); - let invalid_key1 = FakeKey::try_from(vec![0, 0, 0, 0, 0, 0, 0]).expect("invalid test key"); - let invalid_key2 = - FakeKey::try_from(vec![0, 0, 0, 0, 0, 0xDC, 0xAF]).expect("invalid test key"); + let invalid_key1 = CoseKeyBuilder::new_symmetric_key(vec![0; 5]) + .key_id(vec![0, 0]) + .build(); + let invalid_key2 = CoseKeyBuilder::new_symmetric_key(vec![0; 5]) + .key_id(vec![0xDC, 0xAF]) + .build(); let (unprotected_header, protected_header) = example_headers(); let claims = ClaimsSetBuilder::new() .audience(AUDIENCE.to_string()) diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index e972bea..8819ebd 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -12,7 +12,7 @@ use ciborium::value::Value; use coset::cwt::{ClaimsSetBuilder, Timestamp}; use coset::iana::EllipticCurve::P_256; -use coset::iana::{Algorithm, CwtClaimName}; +use coset::iana::{Algorithm, CwtClaimName, EllipticCurve}; use coset::{ iana, CborSerializable, CoseKey, CoseKeyBuilder, Header, HeaderBuilder, KeyType, Label, ProtectedHeader, @@ -28,54 +28,31 @@ use dcaf::endpoints::token_req::{ TokenType, }; use dcaf::error::CoseCipherError; -use dcaf::token::{verify_access_token_multiple, ToCoseKey}; +use dcaf::token::CoseCipher; use dcaf::{sign_access_token, verify_access_token, CoseSignCipher}; -#[derive(Clone)] -pub(crate) struct EC2P256Key { - x: Vec, - y: Vec, +fn create_ec2p256_key(x: Vec, y: Vec) -> CoseKey { + CoseKeyBuilder::new_ec2_pub_key(P_256, x, y).build() } -impl ToCoseKey for EC2P256Key { - fn to_cose_key(&self) -> CoseKey { - CoseKeyBuilder::new_ec2_pub_key(P_256, self.x.to_vec(), self.y.to_vec()).build() - } -} - -impl TryFrom> for EC2P256Key { - type Error = String; - - fn try_from(value: Vec) -> Result { - let key = CoseKey::from_slice(value.as_slice()).map_err(|x| x.to_string())?; - assert_eq!(key.kty, KeyType::Assigned(iana::KeyType::EC2)); - assert_eq!( - get_param(Label::Int(iana::Ec2KeyParameter::Crv as i64), &key.params), - Some(Value::from(P_256 as u64)) - ); - - if let Some(Value::Bytes(x)) = - get_param(Label::Int(iana::Ec2KeyParameter::X as i64), &key.params) - { - if let Some(Value::Bytes(y)) = - get_param(Label::Int(iana::Ec2KeyParameter::Y as i64), &key.params) - { - return Ok(EC2P256Key { x, y }); +fn get_x_y_from_key(key: &CoseKey) -> (Vec, Vec) { + const X_PARAM: i64 = iana::Ec2KeyParameter::X as i64; + const Y_PARAM: i64 = iana::Ec2KeyParameter::Y as i64; + let mut x: Option> = None; + let mut y: Option> = None; + for (label, value) in key.params.iter() { + if let Label::Int(X_PARAM) = label { + if let Value::Bytes(x_val) = value { + x = Some(x_val.clone()); + } + } else if let Label::Int(Y_PARAM) = label { + if let Value::Bytes(y_val) = value { + y = Some(y_val.clone()); } } - return Err("x and y must be present in key as bytes".to_string()); - - fn get_param(label: Label, params: &Vec<(Label, Value)>) -> Option { - let mut iter = params.iter().filter(|x| x.0 == label); - iter.map(|x| x.1.clone()).next() - } - } -} - -impl From for Vec { - fn from(k: EC2P256Key) -> Self { - k.to_cose_key().to_vec().expect("couldn't serialize key") } + let test = x.and_then(|a| y.map(|b| (a, b))); + test.expect("X and Y value must be present in key!") } #[derive(Clone, Copy)] @@ -118,29 +95,52 @@ fn example_aad() -> Vec { #[derive(Copy, Clone)] pub(crate) struct FakeCrypto {} +impl CoseCipher for FakeCrypto { + type Error = String; + + fn set_headers( + key: &CoseKey, + unprotected_header: &mut Header, + protected_header: &mut Header, + rng: RNG, + ) -> Result<(), CoseCipherError> { + // We have to later verify these headers really are used. + if let Some(label) = unprotected_header + .rest + .iter() + .find(|x| x.0 == Label::Int(47)) + { + return Err(CoseCipherError::existing_header_label(&label.0)); + } + if protected_header.alg != None { + return Err(CoseCipherError::existing_header("alg")); + } + unprotected_header.rest.push((Label::Int(47), Value::Null)); + protected_header.alg = Some(coset::Algorithm::Assigned(Algorithm::Direct)); + Ok(()) + } +} + /// Implements basic operations from the [`CoseSign1Cipher`] trait without actually using any /// "real" cryptography. /// This is purely to be used for testing and obviously offers no security at all. impl CoseSignCipher for FakeCrypto { - type SignKey = EC2P256Key; - type VerifyKey = Self::SignKey; - type Error = String; - fn sign( - key: &Self::SignKey, + key: &CoseKey, target: &[u8], unprotected_header: &Header, protected_header: &Header, ) -> Vec { // We simply append the key behind the data. let mut signature = target.to_vec(); - signature.append(&mut key.x.to_vec()); - signature.append(&mut key.y.to_vec()); + let (x, y) = get_x_y_from_key(key); + signature.append(&mut x.clone()); + signature.append(&mut y.clone()); signature } fn verify( - key: &Self::VerifyKey, + key: &CoseKey, signature: &[u8], signed_data: &[u8], unprotected_header: &Header, @@ -161,28 +161,6 @@ impl CoseSignCipher for FakeCrypto { Err(CoseCipherError::VerificationFailure) } } - - fn set_headers( - key: &Self::SignKey, - unprotected_header: &mut Header, - protected_header: &mut Header, - rng: RNG, - ) -> Result<(), CoseCipherError> { - // We have to later verify these headers really are used. - if let Some(label) = unprotected_header - .rest - .iter() - .find(|x| x.0 == Label::Int(47)) - { - return Err(CoseCipherError::existing_header_label(&label.0)); - } - if protected_header.alg != None { - return Err(CoseCipherError::existing_header("alg")); - } - unprotected_header.rest.push((Label::Int(47), Value::Null)); - protected_header.alg = Some(coset::Algorithm::Assigned(Algorithm::Direct)); - Ok(()) - } } /// We assume the following scenario here: @@ -204,15 +182,16 @@ fn test_scenario() -> Result<(), String> { let scope = TextEncodedScope::try_from("first second").map_err(|x| x.to_string())?; assert!(scope.elements().eq(["first", "second"])); // Taken from RFC 8747, section 3.2. - let key = EC2P256Key { - x: hex::decode("d7cc072de2205bdc1537a543d53c60a6acb62eccd890c7fa27c9e354089bbe13") + let key = CoseKeyBuilder::new_ec2_pub_key( + P_256, + hex::decode("d7cc072de2205bdc1537a543d53c60a6acb62eccd890c7fa27c9e354089bbe13") .map_err(|x| x.to_string())?, - y: hex::decode("f95e1d4b851a2cc80fff87d8e23f22afb725d535e515d020731e79a3b4e47120") + hex::decode("f95e1d4b851a2cc80fff87d8e23f22afb725d535e515d020731e79a3b4e47120") .map_err(|x| x.to_string())?, - }; + ) + .build(); let (unprotected_headers, protected_headers) = example_headers(); - let mut crypto = FakeCrypto {}; let aad = example_aad(); let hint: AuthServerRequestCreationHint = AuthServerRequestCreationHint::builder() @@ -231,7 +210,7 @@ fn test_scenario() -> Result<(), String> { .client_nonce(nonce) .ace_profile() .client_id(client_id) - .req_cnf(PlainCoseKey(key.to_cose_key())) + .req_cnf(PlainCoseKey(key.clone())) .build() .map_err(|x| x.to_string())?; let result = pseudo_send_receive(request.clone())?; @@ -247,7 +226,7 @@ fn test_scenario() -> Result<(), String> { .issued_at(Timestamp::WholeSeconds(47)) .claim( CwtClaimName::Cnf, - PlainCoseKey(key.to_cose_key()).to_ciborium_value(), + PlainCoseKey(key.clone()).to_ciborium_value(), ) .build(), // TODO: Proper headers