diff --git a/Cargo.toml b/Cargo.toml index b72fce6..07aad7e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,7 @@ openssl = "0.10" [dependencies.serde] version = "1.0" features = ["derive"] + +[features] +default = ["key_openssl_pkey"] +key_openssl_pkey = [] diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs new file mode 100644 index 0000000..5fb739b --- /dev/null +++ b/src/crypto/mod.rs @@ -0,0 +1,52 @@ +//! (Signing) cryptography abstraction + +use openssl::{hash::MessageDigest, nid::Nid}; + +use crate::{error::CoseError, sign::SignatureAlgorithm}; + +#[cfg(feature = "key_openssl_pkey")] +mod openssl_pkey; + +/// A public key that can verify an existing signature +pub trait SigningPublicKey { + /// This returns the signature algorithm and message digest to be used for this + /// public key. + fn get_parameters(&self) -> Result<(SignatureAlgorithm, MessageDigest), CoseError>; + + /// Given a vector of data and a signature, returns a boolean whether the signature + /// was valid. + fn verify(&self, data: &[u8], signature: &[u8]) -> Result; +} + +/// Follows the recommandations put in place by the RFC and doesn't deal with potential +/// mismatches: https://tools.ietf.org/html/rfc8152#section-8.1. +pub fn ec_curve_to_parameters( + curve_name: Nid, +) -> Result<(SignatureAlgorithm, MessageDigest, usize), CoseError> { + let sig_alg = match curve_name { + // Recommended to use with SHA256 + Nid::X9_62_PRIME256V1 => SignatureAlgorithm::ES256, + // Recommended to use with SHA384 + Nid::SECP384R1 => SignatureAlgorithm::ES384, + // Recommended to use with SHA512 + Nid::SECP521R1 => SignatureAlgorithm::ES512, + _ => { + return Err(CoseError::UnsupportedError(format!( + "Curve name {:?} is not supported", + curve_name + ))) + } + }; + + Ok(( + sig_alg, + sig_alg.suggested_message_digest(), + sig_alg.key_length(), + )) +} + +/// A private key that can produce new signatures +pub trait SigningPrivateKey: SigningPublicKey { + /// Given a slice of data, returns a signature + fn sign(&self, data: &[u8]) -> Result, CoseError>; +} diff --git a/src/crypto/openssl_pkey.rs b/src/crypto/openssl_pkey.rs new file mode 100644 index 0000000..2f4d805 --- /dev/null +++ b/src/crypto/openssl_pkey.rs @@ -0,0 +1,121 @@ +//! OpenSSL PKey(Ref) implementation for cryptography + +use openssl::{ + bn::BigNum, + ecdsa::EcdsaSig, + hash::MessageDigest, + pkey::{HasPrivate, HasPublic, PKey, PKeyRef}, +}; + +use crate::{ + crypto::{ec_curve_to_parameters, SigningPrivateKey, SigningPublicKey}, + error::CoseError, + sign::SignatureAlgorithm, +}; + +impl SigningPublicKey for PKey +where + T: HasPublic, +{ + fn get_parameters(&self) -> Result<(SignatureAlgorithm, MessageDigest), CoseError> { + self.as_ref().get_parameters() + } + + fn verify(&self, data: &[u8], signature: &[u8]) -> Result { + self.as_ref().verify(data, signature) + } +} + +impl SigningPublicKey for PKeyRef +where + T: HasPublic, +{ + fn get_parameters(&self) -> Result<(SignatureAlgorithm, MessageDigest), CoseError> { + let curve_name = self + .ec_key() + .map_err(|_| CoseError::UnsupportedError("Non-EC keys are not supported".to_string()))? + .group() + .curve_name() + .ok_or_else(|| { + CoseError::UnsupportedError("Anonymous EC keys are not supported".to_string()) + })?; + + let curve_parameters = ec_curve_to_parameters(curve_name)?; + + Ok((curve_parameters.0, curve_parameters.1)) + } + + fn verify(&self, data: &[u8], signature: &[u8]) -> Result { + let key = self.ec_key().map_err(|_| { + CoseError::UnsupportedError("Non-EC keys are not yet supported".to_string()) + })?; + + let curve_name = key.group().curve_name().ok_or_else(|| { + CoseError::UnsupportedError("Anonymous EC keys are not supported".to_string()) + })?; + + let (_, _, key_length) = ec_curve_to_parameters(curve_name)?; + + // Recover the R and S factors from the signature contained in the object + let (bytes_r, bytes_s) = signature.split_at(key_length); + + let r = BigNum::from_slice(&bytes_r).map_err(CoseError::SignatureError)?; + let s = BigNum::from_slice(&bytes_s).map_err(CoseError::SignatureError)?; + + let sig = EcdsaSig::from_private_components(r, s).map_err(CoseError::SignatureError)?; + sig.verify(data, &key).map_err(CoseError::SignatureError) + } +} + +impl SigningPrivateKey for PKey +where + T: HasPrivate, +{ + fn sign(&self, data: &[u8]) -> Result, CoseError> { + self.as_ref().sign(data) + } +} + +impl SigningPrivateKey for PKeyRef +where + T: HasPrivate, +{ + fn sign(&self, data: &[u8]) -> Result, CoseError> { + let key = self.ec_key().map_err(|_| { + CoseError::UnsupportedError("Non-EC keys are not yet supported".to_string()) + })?; + + let curve_name = key.group().curve_name().ok_or_else(|| { + CoseError::UnsupportedError("Anonymous EC keys are not supported".to_string()) + })?; + + let (_, _, key_length) = ec_curve_to_parameters(curve_name)?; + + // The spec defines the signature as: + // Signature = I2OSP(R, n) | I2OSP(S, n), where n = ceiling(key_length / 8) + // The Signer interface doesn't provide this, so this will use EcdsaSig interface instead + // and concatenate R and S. + // See https://tools.ietf.org/html/rfc8017#section-4.1 for details. + let signature = EcdsaSig::sign(data, &key).map_err(CoseError::SignatureError)?; + let bytes_r = signature.r().to_vec(); + let bytes_s = signature.s().to_vec(); + + // These should *never* exceed ceiling(key_length / 8) + assert!(bytes_r.len() <= key_length); + assert!(bytes_s.len() <= key_length); + + let mut signature_bytes = vec![0u8; key_length * 2]; + + // This is big-endian encoding so padding might be added at the start if the factor is + // too short. + let offset_copy = key_length - bytes_r.len(); + signature_bytes[offset_copy..offset_copy + bytes_r.len()].copy_from_slice(&bytes_r); + + // This is big-endian encoding so padding might be added at the start if the factor is + // too short. + let offset_copy = key_length - bytes_s.len() + key_length; + signature_bytes[offset_copy..offset_copy + bytes_s.len()].copy_from_slice(&bytes_s); + + Ok(signature_bytes) + } +} diff --git a/src/lib.rs b/src/lib.rs index 536e759..e0351b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ //! //! Currently only COSE Sign1 and COSE Encrypt0 are implemented. +pub mod crypto; pub mod encrypt; pub mod error; pub mod header_map; diff --git a/src/sign.rs b/src/sign.rs index af8fd87..28ccf39 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -1,17 +1,15 @@ //! COSE Signing -use openssl::bn::BigNum; -use openssl::ecdsa::EcdsaSig; +use std::str::FromStr; + use openssl::hash::{hash, MessageDigest}; -use openssl::nid::Nid; -use openssl::pkey::PKeyRef; -use openssl::pkey::{Private, Public}; use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; use serde_bytes::ByteBuf; use serde_cbor::Error as CborError; use serde_cbor::Value as CborValue; use serde_repr::{Deserialize_repr, Serialize_repr}; +use crate::crypto::{SigningPrivateKey, SigningPublicKey}; use crate::error::CoseError; use crate::header_map::{map_to_empty_or_serialized, HeaderMap}; @@ -27,6 +25,52 @@ pub enum SignatureAlgorithm { ES512 = -36, } +impl SignatureAlgorithm { + pub(crate) fn key_length(&self) -> usize { + match self { + SignatureAlgorithm::ES256 => 32, + SignatureAlgorithm::ES384 => 48, + // Not a typo + SignatureAlgorithm::ES512 => 66, + } + } + + pub(crate) fn suggested_message_digest(&self) -> MessageDigest { + match self { + SignatureAlgorithm::ES256 => MessageDigest::sha256(), + SignatureAlgorithm::ES384 => MessageDigest::sha384(), + SignatureAlgorithm::ES512 => MessageDigest::sha512(), + } + } +} + +impl FromStr for SignatureAlgorithm { + type Err = CoseError; + + fn from_str(s: &str) -> Result { + match s { + "ES256" => Ok(SignatureAlgorithm::ES256), + "ES384" => Ok(SignatureAlgorithm::ES384), + "ES512" => Ok(SignatureAlgorithm::ES512), + name => Err(CoseError::UnsupportedError(format!( + "Algorithm '{}' is not supported", + name + ))), + } + } +} + +impl ToString for SignatureAlgorithm { + fn to_string(&self) -> String { + match self { + SignatureAlgorithm::ES256 => "ES256", + SignatureAlgorithm::ES384 => "ES384", + SignatureAlgorithm::ES512 => "ES512", + } + .to_string() + } +} + impl From for HeaderMap { fn from(sig_alg: SignatureAlgorithm) -> Self { // Convenience method for creating the map that would go into the signature structures @@ -284,45 +328,15 @@ impl<'de> Deserialize<'de> for CoseSign1 { } impl CoseSign1 { - /// Follows the recommandations put in place by the RFC and doesn't deal with potential - /// mismatches: https://tools.ietf.org/html/rfc8152#section-8.1. - fn curve_to_parameters( - curve_name: Nid, - ) -> Result<(SignatureAlgorithm, MessageDigest, usize), CoseError> { - match curve_name { - // Recommended to use with SHA256 - Nid::X9_62_PRIME256V1 => Ok((SignatureAlgorithm::ES256, MessageDigest::sha256(), 32)), - // Recommended to use with SHA384 - Nid::SECP384R1 => Ok((SignatureAlgorithm::ES384, MessageDigest::sha384(), 48)), - // Recommended to use with SHA512 - Nid::SECP521R1 => Ok(( - SignatureAlgorithm::ES512, - MessageDigest::sha512(), - 66, /* Not a typo */ - )), - _ => Err(CoseError::UnsupportedError(format!( - "Curve name {:?} is not supported", - curve_name - ))), - } - } - /// Creates a CoseSign1 structure from the given payload and some unprotected data in the form /// of a HeaderMap. Signs the content with the given key using the recommedations from the spec /// and sets the protected part of the document to reflect the algorithm used. pub fn new( payload: &[u8], unprotected: &HeaderMap, - key: &PKeyRef, + key: &dyn SigningPrivateKey, ) -> Result { - let ec_key = key.ec_key().map_err(|_| CoseError::UnimplementedError)?; - - let curve_name = ec_key - .group() - .curve_name() - .ok_or(CoseError::UnimplementedError)?; - - let (sig_alg, _, _) = CoseSign1::curve_to_parameters(curve_name)?; + let (sig_alg, _) = key.get_parameters()?; let mut protected = HeaderMap::new(); protected.insert(1.into(), (sig_alg as i8).into()); @@ -337,16 +351,9 @@ impl CoseSign1 { payload: &[u8], protected: &HeaderMap, unprotected: &HeaderMap, - key: &PKeyRef, + key: &dyn SigningPrivateKey, ) -> Result { - let key = key.ec_key().map_err(|_| CoseError::UnimplementedError)?; - - let curve_name = key - .group() - .curve_name() - .ok_or(CoseError::UnimplementedError)?; - - let (_, digest, key_length) = CoseSign1::curve_to_parameters(curve_name)?; + let (_, digest) = key.get_parameters()?; // Create the SigStruct to sign let protected_bytes = @@ -363,37 +370,13 @@ impl CoseSign1 { ) .map_err(CoseError::SignatureError)?; - // The spec defines the signature as: - // Signature = I2OSP(R, n) | I2OSP(S, n), where n = ceiling(key_length / 8) - // The Signer interface doesn't provide this, so this will use EcdsaSig interface instead - // and concatenate R and S. - // See https://tools.ietf.org/html/rfc8017#section-4.1 for details. - let signature = - EcdsaSig::sign(struct_digest.as_ref(), &key).map_err(CoseError::SignatureError)?; - let bytes_r = signature.r().to_vec(); - let bytes_s = signature.s().to_vec(); - - // These should *never* exceed ceiling(key_length / 8) - assert!(bytes_r.len() <= key_length); - assert!(bytes_s.len() <= key_length); - - let mut signature_bytes = vec![0u8; key_length * 2]; - - // This is big-endian encoding so padding might be added at the start if the factor is - // too short. - let offset_copy = key_length - bytes_r.len(); - signature_bytes[offset_copy..offset_copy + bytes_r.len()].copy_from_slice(&bytes_r); - - // This is big-endian encoding so padding might be added at the start if the factor is - // too short. - let offset_copy = key_length - bytes_s.len() + key_length; - signature_bytes[offset_copy..offset_copy + bytes_s.len()].copy_from_slice(&bytes_s); + let signature = key.sign(struct_digest.as_ref())?; Ok(CoseSign1 { protected: ByteBuf::from(protected_bytes), unprotected: unprotected.clone(), payload: ByteBuf::from(payload.to_vec()), - signature: ByteBuf::from(signature_bytes), + signature: ByteBuf::from(signature), }) } @@ -443,17 +426,11 @@ impl CoseSign1 { /// This checks the signature included in the structure against the given public key and /// returns true if the signature matches the given key. - pub fn verify_signature(&self, key: &PKeyRef) -> Result { - let key = key.ec_key().map_err(|_| CoseError::UnimplementedError)?; - // Don't support anonymous curves - let curve_name = key.group().curve_name().ok_or_else(|| { - CoseError::UnsupportedError("Anonymous curves are not supported".to_string()) - })?; - + pub fn verify_signature(&self, key: &dyn SigningPublicKey) -> Result { // In theory, the digest itself does not have to match the curve, however, // this is the recommendation and the spec does not even provide a way to specify // another digest type, so, signatures will fail if this is done differently - let (signature_alg, digest, key_length) = CoseSign1::curve_to_parameters(curve_name)?; + let (signature_alg, digest) = key.get_parameters()?; // The spec reads as follows: // alg: This parameter is used to indicate the algorithm used for the @@ -503,15 +480,7 @@ impl CoseSign1 { ) .map_err(CoseError::SignatureError)?; - // Recover the R and S factors from the signature contained in the object - let (bytes_r, bytes_s) = self.signature.split_at(key_length); - - let r = BigNum::from_slice(&bytes_r).map_err(CoseError::SignatureError)?; - let s = BigNum::from_slice(&bytes_s).map_err(CoseError::SignatureError)?; - - let sig = EcdsaSig::from_private_components(r, s).map_err(CoseError::SignatureError)?; - sig.verify(&struct_digest, &key) - .map_err(CoseError::SignatureError) + key.verify(struct_digest.as_ref(), &self.signature) } /// This gets the `payload` and `protected` data of the document. @@ -519,7 +488,7 @@ impl CoseSign1 { /// otherwise returns `Err(CoseError::UnverifiedSignature)`. pub fn get_protected_and_payload( &self, - key: Option<&PKeyRef>, + key: Option<&dyn SigningPublicKey>, ) -> Result<(HeaderMap, Vec), CoseError> { if key.is_some() && !self.verify_signature(key.unwrap())? { return Err(CoseError::UnverifiedSignature); @@ -532,8 +501,8 @@ impl CoseSign1 { /// This gets the `payload` of the document. If `key` is provided, it only gets the payload /// if the signature is correctly verified, otherwise returns /// `Err(CoseError::UnverifiedSignature)`. - pub fn get_payload(&self, key: Option<&PKeyRef>) -> Result, CoseError> { - if key.is_some() && !self.verify_signature(&key.unwrap())? { + pub fn get_payload(&self, key: Option<&dyn SigningPublicKey>) -> Result, CoseError> { + if key.is_some() && !self.verify_signature(key.unwrap())? { return Err(CoseError::UnverifiedSignature); } Ok(self.payload.to_vec()) @@ -547,78 +516,255 @@ impl CoseSign1 { #[cfg(test)] mod tests { - use super::*; - use openssl::pkey::PKey; - - // Public domain work: Pride and Prejudice by Jane Austen, taken from https://www.gutenberg.org/files/1342/1342.txt - const TEXT: &[u8] = b"It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife."; - - #[test] - fn map_serialization() { - // Empty map - let map: HeaderMap = HeaderMap::new(); - assert_eq!(map_to_empty_or_serialized(&map).unwrap(), []); - - // Checks that the body_protected field will be serialized correctly - let map: HeaderMap = SignatureAlgorithm::ES256.into(); - assert_eq!( - map_to_empty_or_serialized(&map).unwrap(), - [0xa1, 0x01, 0x26] - ); - - let map: HeaderMap = SignatureAlgorithm::ES384.into(); - assert_eq!( - map_to_empty_or_serialized(&map).unwrap(), - [0xa1, 0x01, 0x38, 0x22] - ); - - let map: HeaderMap = SignatureAlgorithm::ES512.into(); - assert_eq!( - map_to_empty_or_serialized(&map).unwrap(), - [0xa1, 0x01, 0x38, 0x23] - ); - } + #[cfg(feature = "key_openssl_pkey")] + mod generic { + use crate::sign::*; + use openssl::pkey::{PKey, Private, Public}; + + // Public domain work: Pride and Prejudice by Jane Austen, taken from https://www.gutenberg.org/files/1342/1342.txt + const TEXT: &[u8] = b"It is a truth universally acknowledged, that a single man in possession of a good fortune, must be in want of a wife."; + + #[test] + fn map_serialization() { + // Empty map + let map: HeaderMap = HeaderMap::new(); + assert_eq!(map_to_empty_or_serialized(&map).unwrap(), []); + + // Checks that the body_protected field will be serialized correctly + let map: HeaderMap = SignatureAlgorithm::ES256.into(); + assert_eq!( + map_to_empty_or_serialized(&map).unwrap(), + [0xa1, 0x01, 0x26] + ); + + let map: HeaderMap = SignatureAlgorithm::ES384.into(); + assert_eq!( + map_to_empty_or_serialized(&map).unwrap(), + [0xa1, 0x01, 0x38, 0x22] + ); + + let map: HeaderMap = SignatureAlgorithm::ES512.into(); + assert_eq!( + map_to_empty_or_serialized(&map).unwrap(), + [0xa1, 0x01, 0x38, 0x23] + ); + } - #[test] - fn map_with_duplicates() { - // Check that HeaderMaps with duplicate entries emit error - // {1: 42, 2: 42} - let test = [0xa2, 0x01, 0x18, 0x2A, 0x02, 0x18, 0x2A]; - let map: HeaderMap = serde_cbor::from_slice(&test).unwrap(); - assert_eq!( - map.get(&CborValue::Integer(1)), - Some(&CborValue::Integer(42)) - ); - assert_eq!( - map.get(&CborValue::Integer(2)), - Some(&CborValue::Integer(42)) - ); - - // {1: 42, 2: 42, 1: 43} - let test = [0xa3, 0x01, 0x18, 0x2A, 0x02, 0x18, 0x2A, 0x01, 0x18, 0x2B]; - let map: Result = serde_cbor::from_slice(&test); - assert!(map.is_err()); - - // {1: 42, 2: 42, 2: 42} - let test = [0xa3, 0x01, 0x18, 0x2A, 0x02, 0x18, 0x2A, 0x02, 0x18, 0x2A]; - let map: Result = serde_cbor::from_slice(&test); - assert!(map.is_err()); - } + #[test] + fn map_with_duplicates() { + // Check that HeaderMaps with duplicate entries emit error + // {1: 42, 2: 42} + let test = [0xa2, 0x01, 0x18, 0x2A, 0x02, 0x18, 0x2A]; + let map: HeaderMap = serde_cbor::from_slice(&test).unwrap(); + assert_eq!( + map.get(&CborValue::Integer(1)), + Some(&CborValue::Integer(42)) + ); + assert_eq!( + map.get(&CborValue::Integer(2)), + Some(&CborValue::Integer(42)) + ); + + // {1: 42, 2: 42, 1: 43} + let test = [0xa3, 0x01, 0x18, 0x2A, 0x02, 0x18, 0x2A, 0x01, 0x18, 0x2B]; + let map: Result = serde_cbor::from_slice(&test); + assert!(map.is_err()); + + // {1: 42, 2: 42, 2: 42} + let test = [0xa3, 0x01, 0x18, 0x2A, 0x02, 0x18, 0x2A, 0x02, 0x18, 0x2A]; + let map: Result = serde_cbor::from_slice(&test); + assert!(map.is_err()); + } - #[test] - fn sig_structure_text() { - let map = HeaderMap::new(); + #[test] + fn sig_structure_text() { + let map = HeaderMap::new(); + + let map_serialized = map_to_empty_or_serialized(&map).unwrap(); + let sig_structure = SigStructure::new_sign1(&map_serialized, TEXT).unwrap(); + + assert_eq!( + vec![ + 0x84, /* "Signature1" */ + 0x6A, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31, + /* protected: */ + 0x40, /* unprotected: */ + 0x40, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, + 0x74, 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, + 0x79, 0x20, 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, + 0x64, 0x2C, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, + 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, + 0x73, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, + 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, + 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, + 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x77, 0x69, 0x66, + 0x65, 0x2E, + ], + sig_structure.as_bytes().unwrap() + ); + + let map: HeaderMap = SignatureAlgorithm::ES256.into(); + let map_serialized = map_to_empty_or_serialized(&map).unwrap(); + let sig_structure = SigStructure::new_sign1(&map_serialized, TEXT).unwrap(); + assert_eq!( + vec![ + 0x84, /* "Signature1" */ + 0x6A, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31, + /* protected: */ + 0x43, 0xA1, 0x01, 0x26, /* unprotected: */ + 0x40, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, + 0x74, 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, + 0x79, 0x20, 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, + 0x64, 0x2C, 0x20, 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, + 0x67, 0x6C, 0x65, 0x20, 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, + 0x73, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, + 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, + 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, + 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x77, 0x69, 0x66, + 0x65, 0x2E, + ], + sig_structure.as_bytes().unwrap() + ); + } - let map_serialized = map_to_empty_or_serialized(&map).unwrap(); - let sig_structure = SigStructure::new_sign1(&map_serialized, TEXT).unwrap(); + /// Static PRIME256V1/P-256 key to be used when cross-validating the implementation + fn get_ec256_test_key() -> (PKey, PKey) { + let alg = + openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1).unwrap(); + let x = openssl::bn::BigNum::from_hex_str( + "9ff7423a1aace5f3e33dfaeda2c7744e3d15c2a4f6382386c93fa60c1bdb260c", + ) + .unwrap(); + let y = openssl::bn::BigNum::from_hex_str( + "3489e6b132f36e5ece948e73bd44231a1c3d0dacf566712a44fe8a9835d5b6fe", + ) + .unwrap(); + let d = openssl::bn::BigNum::from_hex_str( + "8e21d79fb6955dbe7bb592d92de4690f8bf75dc1495b2433ba78d5828e1f933f", + ) + .unwrap(); - assert_eq!( - vec![ - 0x84, /* "Signature1" */ - 0x6A, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31, - /* protected: */ - 0x40, /* unprotected: */ - 0x40, /* payload: */ + let ec_public = + openssl::ec::EcKey::from_public_key_affine_coordinates(&alg, &x, &y).unwrap(); + let ec_private = + openssl::ec::EcKey::from_private_components(&alg, &d, &ec_public.public_key()) + .unwrap(); + ( + PKey::from_ec_key(ec_private).unwrap(), + PKey::from_ec_key(ec_public).unwrap(), + ) + } + + /// Static SECP384R1/P-384 key to be used when cross-validating the implementation + fn get_ec384_test_key() -> (PKey, PKey) { + let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP384R1).unwrap(); + let x = openssl::bn::BigNum::from_hex_str( + "5a829f62f2f4f095c0e922719285b4b981c677912870a413137a5d7319916fa8\ + 584a6036951d06ffeae99ca73ab1a2dc", + ) + .unwrap(); + let y = openssl::bn::BigNum::from_hex_str( + "e1b76e08cb20d6afcea7423f8b49ec841dde6f210a6174750bf8136a31549422\ + 4df153184557a6c29a1d7994804f604c", + ) + .unwrap(); + let d = openssl::bn::BigNum::from_hex_str( + "55c6aa815a31741bc37f0ffddea73af2397bad640816ef22bfb689efc1b6cc68\ + 2a73f7e5a657248e3abad500e46d5afc", + ) + .unwrap(); + let ec_public = + openssl::ec::EcKey::from_public_key_affine_coordinates(&alg, &x, &y).unwrap(); + let ec_private = + openssl::ec::EcKey::from_private_components(&alg, &d, &ec_public.public_key()) + .unwrap(); + ( + PKey::from_ec_key(ec_private).unwrap(), + PKey::from_ec_key(ec_public).unwrap(), + ) + } + + /// Static SECP521R1/P-512 key to be used when cross-validating the implementation + fn get_ec512_test_key() -> (PKey, PKey) { + let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP521R1).unwrap(); + let x = openssl::bn::BigNum::from_hex_str( + "004365ee31a93b6e69b2c895890aaae14194cd84601bbb59587ad08ab5960522\ + 7dc7b34288e6471b0f06050763b88b4fb017f279c86030b0069100401e4016a3\ + be8a", + ) + .unwrap(); + let y = openssl::bn::BigNum::from_hex_str( + "00792d772bf93cd965027df2df02d3f99ea1c4ecd18c20738ebae66854fd3afc\ + d2ea4e902bcd37a4d2a5c639caee71513acaf7d8f7ffa11042257c5d8c697409\ + 5713", + ) + .unwrap(); + let d = openssl::bn::BigNum::from_hex_str( + "007c6fd88271bcd6c5d6bada258691a27700abeff0ad86891a27f93a73f00947\ + 7c53b4e069db544429ad8220d18813f5f3ab90946ebdf4f41ca929999709f7c4\ + 89e8", + ) + .unwrap(); + let ec_public = + openssl::ec::EcKey::from_public_key_affine_coordinates(&alg, &x, &y).unwrap(); + let ec_private = + openssl::ec::EcKey::from_private_components(&alg, &d, &ec_public.public_key()) + .unwrap(); + ( + PKey::from_ec_key(ec_private).unwrap(), + PKey::from_ec_key(ec_public).unwrap(), + ) + } + + /// Randomly generate PRIME256V1/P-256 key to use for validating signining internally + fn generate_ec256_test_key() -> (PKey, PKey) { + let alg = + openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1).unwrap(); + let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); + let ec_public = + openssl::ec::EcKey::from_public_key(&alg, ec_private.public_key()).unwrap(); + ( + PKey::from_ec_key(ec_private).unwrap(), + PKey::from_ec_key(ec_public).unwrap(), + ) + } + + /// Randomly generate SECP384R1/P-384 key to use for validating signining internally + fn generate_ec384_test_key() -> (PKey, PKey) { + let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP384R1).unwrap(); + let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); + let ec_public = + openssl::ec::EcKey::from_public_key(&alg, ec_private.public_key()).unwrap(); + ( + PKey::from_ec_key(ec_private).unwrap(), + PKey::from_ec_key(ec_public).unwrap(), + ) + } + + /// Randomly generate SECP521R1/P-512 key to use for validating signing internally + fn generate_ec512_test_key() -> (PKey, PKey) { + let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP521R1).unwrap(); + let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); + let ec_public = + openssl::ec::EcKey::from_public_key(&alg, ec_private.public_key()).unwrap(); + ( + PKey::from_ec_key(ec_private).unwrap(), + PKey::from_ec_key(ec_public).unwrap(), + ) + } + + #[test] + fn cose_sign1_ec256_validate() { + let (_, ec_public) = get_ec256_test_key(); + + // This output was validated against COSE-C implementation + let cose_doc = CoseSign1::from_bytes(&[ + 0xd9, 0x00, 0x12, /* tag 18 */ + 0x84, /* Protected: {1: -7} */ + 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, @@ -627,21 +773,29 @@ mod tests { 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, - ], - sig_structure.as_bytes().unwrap() - ); - - let map: HeaderMap = SignatureAlgorithm::ES256.into(); - let map_serialized = map_to_empty_or_serialized(&map).unwrap(); - let sig_structure = SigStructure::new_sign1(&map_serialized, TEXT).unwrap(); - assert_eq!( - vec![ - 0x84, /* "Signature1" */ - 0x6A, 0x53, 0x69, 0x67, 0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x31, - /* protected: */ - 0x43, 0xA1, 0x01, 0x26, /* unprotected: */ - 0x40, /* payload: */ + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ + 0x58, 0x40, /* R: */ + 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, + 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, + 0xCE, 0x31, 0xE2, 0xD1, /* S: */ + 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, + 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, + 0x0F, 0xAB, 0x3C, 0xDD, + ]) + .unwrap(); + + assert_eq!(cose_doc.get_payload(Some(&ec_public)).unwrap(), TEXT); + } + + #[test] + fn cose_sign1_ec384_validate() { + let (_, ec_public) = get_ec384_test_key(); + + // This output was validated against COSE-C implementation + let cose_doc = CoseSign1::from_bytes(&[ + 0x84, /* Protected: {1: -35} */ + 0x44, 0xA1, 0x01, 0x38, 0x22, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, @@ -650,514 +804,318 @@ mod tests { 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, - ], - sig_structure.as_bytes().unwrap() - ); - } - - /// Static PRIME256V1/P-256 key to be used when cross-validating the implementation - fn get_ec256_test_key() -> (PKey, PKey) { - let alg = - openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1).unwrap(); - let x = openssl::bn::BigNum::from_hex_str( - "9ff7423a1aace5f3e33dfaeda2c7744e3d15c2a4f6382386c93fa60c1bdb260c", - ) - .unwrap(); - let y = openssl::bn::BigNum::from_hex_str( - "3489e6b132f36e5ece948e73bd44231a1c3d0dacf566712a44fe8a9835d5b6fe", - ) - .unwrap(); - let d = openssl::bn::BigNum::from_hex_str( - "8e21d79fb6955dbe7bb592d92de4690f8bf75dc1495b2433ba78d5828e1f933f", - ) - .unwrap(); - - let ec_public = - openssl::ec::EcKey::from_public_key_affine_coordinates(&alg, &x, &y).unwrap(); - let ec_private = - openssl::ec::EcKey::from_private_components(&alg, &d, &ec_public.public_key()).unwrap(); - ( - PKey::from_ec_key(ec_private).unwrap(), - PKey::from_ec_key(ec_public).unwrap(), - ) - } - - /// Static SECP384R1/P-384 key to be used when cross-validating the implementation - fn get_ec384_test_key() -> (PKey, PKey) { - let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP384R1).unwrap(); - let x = openssl::bn::BigNum::from_hex_str( - "5a829f62f2f4f095c0e922719285b4b981c677912870a413137a5d7319916fa8\ - 584a6036951d06ffeae99ca73ab1a2dc", - ) - .unwrap(); - let y = openssl::bn::BigNum::from_hex_str( - "e1b76e08cb20d6afcea7423f8b49ec841dde6f210a6174750bf8136a31549422\ - 4df153184557a6c29a1d7994804f604c", - ) - .unwrap(); - let d = openssl::bn::BigNum::from_hex_str( - "55c6aa815a31741bc37f0ffddea73af2397bad640816ef22bfb689efc1b6cc68\ - 2a73f7e5a657248e3abad500e46d5afc", - ) - .unwrap(); - let ec_public = - openssl::ec::EcKey::from_public_key_affine_coordinates(&alg, &x, &y).unwrap(); - let ec_private = - openssl::ec::EcKey::from_private_components(&alg, &d, &ec_public.public_key()).unwrap(); - ( - PKey::from_ec_key(ec_private).unwrap(), - PKey::from_ec_key(ec_public).unwrap(), - ) - } - - /// Static SECP521R1/P-512 key to be used when cross-validating the implementation - fn get_ec512_test_key() -> (PKey, PKey) { - let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP521R1).unwrap(); - let x = openssl::bn::BigNum::from_hex_str( - "004365ee31a93b6e69b2c895890aaae14194cd84601bbb59587ad08ab5960522\ - 7dc7b34288e6471b0f06050763b88b4fb017f279c86030b0069100401e4016a3\ - be8a", - ) - .unwrap(); - let y = openssl::bn::BigNum::from_hex_str( - "00792d772bf93cd965027df2df02d3f99ea1c4ecd18c20738ebae66854fd3afc\ - d2ea4e902bcd37a4d2a5c639caee71513acaf7d8f7ffa11042257c5d8c697409\ - 5713", - ) - .unwrap(); - let d = openssl::bn::BigNum::from_hex_str( - "007c6fd88271bcd6c5d6bada258691a27700abeff0ad86891a27f93a73f00947\ - 7c53b4e069db544429ad8220d18813f5f3ab90946ebdf4f41ca929999709f7c4\ - 89e8", - ) - .unwrap(); - let ec_public = - openssl::ec::EcKey::from_public_key_affine_coordinates(&alg, &x, &y).unwrap(); - let ec_private = - openssl::ec::EcKey::from_private_components(&alg, &d, &ec_public.public_key()).unwrap(); - ( - PKey::from_ec_key(ec_private).unwrap(), - PKey::from_ec_key(ec_public).unwrap(), - ) - } - - /// Randomly generate PRIME256V1/P-256 key to use for validating signining internally - fn generate_ec256_test_key() -> (PKey, PKey) { - let alg = - openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::X9_62_PRIME256V1).unwrap(); - let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); - let ec_public = openssl::ec::EcKey::from_public_key(&alg, ec_private.public_key()).unwrap(); - ( - PKey::from_ec_key(ec_private).unwrap(), - PKey::from_ec_key(ec_public).unwrap(), - ) - } - - /// Randomly generate SECP384R1/P-384 key to use for validating signining internally - fn generate_ec384_test_key() -> (PKey, PKey) { - let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP384R1).unwrap(); - let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); - let ec_public = openssl::ec::EcKey::from_public_key(&alg, ec_private.public_key()).unwrap(); - ( - PKey::from_ec_key(ec_private).unwrap(), - PKey::from_ec_key(ec_public).unwrap(), - ) - } + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* signature - length 48 x 2 */ + 0x58, 0x60, /* R: */ + 0xCD, 0x42, 0xD2, 0x76, 0x32, 0xD5, 0x41, 0x4E, 0x4B, 0x54, 0x5C, 0x95, 0xFD, 0xE6, + 0xE3, 0x50, 0x5B, 0x93, 0x58, 0x0F, 0x4B, 0x77, 0x31, 0xD1, 0x4A, 0x86, 0x52, 0x31, + 0x75, 0x26, 0x6C, 0xDE, 0xB2, 0x4A, 0xFF, 0x2D, 0xE3, 0x36, 0x4E, 0x9C, 0xEE, 0xE9, + 0xF9, 0xF7, 0x95, 0xA0, 0x15, 0x15, /* S: */ + 0x5B, 0xC7, 0x12, 0xAA, 0x28, 0x63, 0xE2, 0xAA, 0xF6, 0x07, 0x8A, 0x81, 0x90, 0x93, + 0xFD, 0xFC, 0x70, 0x59, 0xA3, 0xF1, 0x46, 0x7F, 0x64, 0xEC, 0x7E, 0x22, 0x1F, 0xD1, + 0x63, 0xD8, 0x0B, 0x3B, 0x55, 0x26, 0x25, 0xCF, 0x37, 0x9D, 0x1C, 0xBB, 0x9E, 0x51, + 0x38, 0xCC, 0xD0, 0x7A, 0x19, 0x31, + ]) + .unwrap(); - /// Randomly generate SECP521R1/P-512 key to use for validating signing internally - fn generate_ec512_test_key() -> (PKey, PKey) { - let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP521R1).unwrap(); - let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); - let ec_public = openssl::ec::EcKey::from_public_key(&alg, ec_private.public_key()).unwrap(); - ( - PKey::from_ec_key(ec_private).unwrap(), - PKey::from_ec_key(ec_public).unwrap(), - ) - } + assert_eq!(cose_doc.get_payload(Some(&ec_public)).unwrap(), TEXT); + } - #[test] - fn cose_sign1_ec256_validate() { - let (_, ec_public) = get_ec256_test_key(); - - // This output was validated against COSE-C implementation - let cose_doc = CoseSign1::from_bytes(&[ - 0xd9, 0x00, 0x12, /* tag 18 */ - 0x84, /* Protected: {1: -7} */ - 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ - 0x58, 0x40, /* R: */ - 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, - 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, - 0xCE, 0x31, 0xE2, 0xD1, /* S: */ - 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, - 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, - 0x0F, 0xAB, 0x3C, 0xDD, - ]) - .unwrap(); - - assert_eq!(cose_doc.get_payload(Some(&ec_public)).unwrap(), TEXT); - } + #[test] + fn cose_sign1_ec512_validate() { + let (_, ec_public) = get_ec512_test_key(); - #[test] - fn cose_sign1_ec384_validate() { - let (_, ec_public) = get_ec384_test_key(); - - // This output was validated against COSE-C implementation - let cose_doc = CoseSign1::from_bytes(&[ - 0x84, /* Protected: {1: -35} */ - 0x44, 0xA1, 0x01, 0x38, 0x22, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* signature - length 48 x 2 */ - 0x58, 0x60, /* R: */ - 0xCD, 0x42, 0xD2, 0x76, 0x32, 0xD5, 0x41, 0x4E, 0x4B, 0x54, 0x5C, 0x95, 0xFD, 0xE6, - 0xE3, 0x50, 0x5B, 0x93, 0x58, 0x0F, 0x4B, 0x77, 0x31, 0xD1, 0x4A, 0x86, 0x52, 0x31, - 0x75, 0x26, 0x6C, 0xDE, 0xB2, 0x4A, 0xFF, 0x2D, 0xE3, 0x36, 0x4E, 0x9C, 0xEE, 0xE9, - 0xF9, 0xF7, 0x95, 0xA0, 0x15, 0x15, /* S: */ - 0x5B, 0xC7, 0x12, 0xAA, 0x28, 0x63, 0xE2, 0xAA, 0xF6, 0x07, 0x8A, 0x81, 0x90, 0x93, - 0xFD, 0xFC, 0x70, 0x59, 0xA3, 0xF1, 0x46, 0x7F, 0x64, 0xEC, 0x7E, 0x22, 0x1F, 0xD1, - 0x63, 0xD8, 0x0B, 0x3B, 0x55, 0x26, 0x25, 0xCF, 0x37, 0x9D, 0x1C, 0xBB, 0x9E, 0x51, - 0x38, 0xCC, 0xD0, 0x7A, 0x19, 0x31, - ]) - .unwrap(); - - assert_eq!(cose_doc.get_payload(Some(&ec_public)).unwrap(), TEXT); - } + // This output was validated against COSE-C implementation + let cose_doc = CoseSign1::from_bytes(&[ + 0x84, /* Protected: {1: -36} */ + 0x44, 0xA1, 0x01, 0x38, 0x23, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, + 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, + 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, + 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* signature - length 66 x 2 */ + 0x58, 0x84, /* R: */ + 0x01, 0xE5, 0xAE, 0x6A, 0xE6, 0xE2, 0xE3, 0xC0, 0xB5, 0x1D, 0xD1, 0x62, 0x74, 0x1C, + 0xF9, 0x9D, 0xA6, 0x88, 0x19, 0x5C, 0xD9, 0x0E, 0x65, 0xFB, 0xBE, 0xE2, 0x38, 0x83, + 0x81, 0x32, 0x3C, 0xAE, 0xC9, 0x1B, 0x3D, 0x0E, 0x3A, 0xC1, 0x4D, 0x0B, 0x8B, 0x29, + 0xA8, 0x56, 0x2E, 0xB2, 0x17, 0x65, 0x9F, 0x27, 0xBE, 0xB4, 0x30, 0xA1, 0xD7, 0x4F, + 0x42, 0x35, 0x3A, 0x2C, 0x0A, 0xC5, 0x1F, 0xC2, 0x36, 0x48, /* S: */ + 0x00, 0x00, 0x89, 0xEA, 0xF7, 0x09, 0x50, 0xF8, 0x45, 0x83, 0xA7, 0xC4, 0x79, 0x2F, + 0xAD, 0xC6, 0x96, 0xC3, 0x03, 0x33, 0xF2, 0xDF, 0x19, 0x48, 0x83, 0x93, 0xAB, 0xAE, + 0x31, 0x6A, 0x2E, 0x17, 0x1D, 0x58, 0x87, 0x65, 0xC4, 0x36, 0xA2, 0xA2, 0x05, 0xAD, + 0x81, 0x51, 0xF3, 0x97, 0x3E, 0xC0, 0xB4, 0xA7, 0xB8, 0x97, 0xE4, 0x90, 0x8C, 0x79, + 0x6F, 0x85, 0x24, 0x84, 0xAE, 0x39, 0x26, 0xB3, 0xB8, 0x1B, + ]) + .unwrap(); - #[test] - fn cose_sign1_ec512_validate() { - let (_, ec_public) = get_ec512_test_key(); - - // This output was validated against COSE-C implementation - let cose_doc = CoseSign1::from_bytes(&[ - 0x84, /* Protected: {1: -36} */ - 0x44, 0xA1, 0x01, 0x38, 0x23, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* signature - length 66 x 2 */ - 0x58, 0x84, /* R: */ - 0x01, 0xE5, 0xAE, 0x6A, 0xE6, 0xE2, 0xE3, 0xC0, 0xB5, 0x1D, 0xD1, 0x62, 0x74, 0x1C, - 0xF9, 0x9D, 0xA6, 0x88, 0x19, 0x5C, 0xD9, 0x0E, 0x65, 0xFB, 0xBE, 0xE2, 0x38, 0x83, - 0x81, 0x32, 0x3C, 0xAE, 0xC9, 0x1B, 0x3D, 0x0E, 0x3A, 0xC1, 0x4D, 0x0B, 0x8B, 0x29, - 0xA8, 0x56, 0x2E, 0xB2, 0x17, 0x65, 0x9F, 0x27, 0xBE, 0xB4, 0x30, 0xA1, 0xD7, 0x4F, - 0x42, 0x35, 0x3A, 0x2C, 0x0A, 0xC5, 0x1F, 0xC2, 0x36, 0x48, /* S: */ - 0x00, 0x00, 0x89, 0xEA, 0xF7, 0x09, 0x50, 0xF8, 0x45, 0x83, 0xA7, 0xC4, 0x79, 0x2F, - 0xAD, 0xC6, 0x96, 0xC3, 0x03, 0x33, 0xF2, 0xDF, 0x19, 0x48, 0x83, 0x93, 0xAB, 0xAE, - 0x31, 0x6A, 0x2E, 0x17, 0x1D, 0x58, 0x87, 0x65, 0xC4, 0x36, 0xA2, 0xA2, 0x05, 0xAD, - 0x81, 0x51, 0xF3, 0x97, 0x3E, 0xC0, 0xB4, 0xA7, 0xB8, 0x97, 0xE4, 0x90, 0x8C, 0x79, - 0x6F, 0x85, 0x24, 0x84, 0xAE, 0x39, 0x26, 0xB3, 0xB8, 0x1B, - ]) - .unwrap(); - - assert_eq!(cose_doc.get_payload(Some(&ec_public)).unwrap(), TEXT); - } + assert_eq!(cose_doc.get_payload(Some(&ec_public)).unwrap(), TEXT); + } + #[test] + fn cose_sign1_ec256_text() { + let (ec_private, ec_public) = generate_ec256_test_key(); + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + + let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); + let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); + + assert_eq!( + cose_doc1.get_payload(None).unwrap(), + cose_doc2.get_payload(Some(&ec_public)).unwrap() + ); + assert!(!cose_doc2.get_unprotected().is_empty(),); + assert_eq!( + cose_doc2.get_unprotected().get(&CborValue::Integer(4)), + Some(&CborValue::Bytes(b"11".to_vec())), + ); + } - #[test] - fn cose_sign1_ec256_text() { - let (ec_private, ec_public) = generate_ec256_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); - - assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&ec_public)).unwrap() - ); - assert!(!cose_doc2.get_unprotected().is_empty(),); - assert_eq!( - cose_doc2.get_unprotected().get(&CborValue::Integer(4)), - Some(&CborValue::Bytes(b"11".to_vec())), - ); - } + #[test] + fn cose_sign1_ec256_text_tagged() { + let (ec_private, ec_public) = generate_ec256_test_key(); + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + + let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); + let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); + // Tag 6.18 should be present + assert_eq!(tagged_bytes[0], 6 << 5 | 18); + // The value should be a sequence + assert_eq!(tagged_bytes[1], 4 << 5 | 4); + let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); + + assert_eq!( + cose_doc1.get_payload(None).unwrap(), + cose_doc2.get_payload(Some(&ec_public)).unwrap() + ); + } - #[test] - fn cose_sign1_ec256_text_tagged() { - let (ec_private, ec_public) = generate_ec256_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); - // Tag 6.18 should be present - assert_eq!(tagged_bytes[0], 6 << 5 | 18); - // The value should be a sequence - assert_eq!(tagged_bytes[1], 4 << 5 | 4); - let cose_doc2 = CoseSign1::from_bytes(&tagged_bytes).unwrap(); - - assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&ec_public)).unwrap() - ); - } + #[test] + fn cose_sign1_ec256_text_with_extra_protected() { + let (ec_private, ec_public) = generate_ec256_test_key(); + + let mut protected = HeaderMap::new(); + protected.insert( + CborValue::Integer(1), + (SignatureAlgorithm::ES256 as i8).into(), + ); + protected.insert(CborValue::Integer(15), CborValue::Bytes(b"12".to_vec())); + + let mut unprotected = HeaderMap::new(); + unprotected.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + + let cose_doc1 = + CoseSign1::new_with_protected(TEXT, &protected, &unprotected, &ec_private).unwrap(); + let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); + + let (protected, payload) = cose_doc2 + .get_protected_and_payload(Some(&ec_public)) + .unwrap(); + + assert_eq!( + protected.get(&CborValue::Integer(1)), + Some(&CborValue::Integer(-7)), + ); + assert_eq!( + protected.get(&CborValue::Integer(15)), + Some(&CborValue::Bytes(b"12".to_vec())), + ); + assert_eq!(payload, TEXT,); + } - #[test] - fn cose_sign1_ec256_text_tagged_serde() { - let (ec_private, ec_public) = generate_ec256_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - let tagged_bytes = cose_doc1.as_bytes(true).unwrap(); - // Tag 6.18 should be present - assert_eq!(tagged_bytes[0], 6 << 5 | 18); - let cose_doc2: CoseSign1 = serde_cbor::from_slice(&tagged_bytes).unwrap(); - - assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&ec_public)).unwrap() - ); - } + #[test] + fn cose_sign1_ec384_text() { + let (ec_private, ec_public) = generate_ec384_test_key(); + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - #[test] - fn cose_sign1_ec256_text_with_extra_protected() { - let (ec_private, ec_public) = generate_ec256_test_key(); + let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); + let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); - let mut protected = HeaderMap::new(); - protected.insert( - CborValue::Integer(1), - (SignatureAlgorithm::ES256 as i8).into(), - ); - protected.insert(CborValue::Integer(15), CborValue::Bytes(b"12".to_vec())); + assert_eq!( + cose_doc1.get_payload(None).unwrap(), + cose_doc2.get_payload(Some(&ec_public)).unwrap() + ); + } - let mut unprotected = HeaderMap::new(); - unprotected.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + #[test] + fn cose_sign1_ec512_text() { + let (ec_private, ec_public) = generate_ec512_test_key(); + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + + let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); + let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); + + assert_eq!( + cose_doc1.get_payload(Some(&ec_public)).unwrap(), + TEXT.to_vec() + ); + assert_eq!( + cose_doc1.get_payload(None).unwrap(), + cose_doc2.get_payload(Some(&ec_public)).unwrap() + ); + } - let cose_doc1 = - CoseSign1::new_with_protected(TEXT, &protected, &unprotected, &ec_private).unwrap(); - let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); + #[test] + fn unknown_curve() { + let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP256K1).unwrap(); + let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); + let ec_private = PKey::from_ec_key(ec_private).unwrap(); + let map = HeaderMap::new(); + let result = CoseSign1::new(TEXT, &map, &ec_private); + assert!(result.is_err()); + } - let (protected, payload) = cose_doc2 - .get_protected_and_payload(Some(&ec_public)) - .unwrap(); + #[test] + fn validate_with_wrong_key() { + let (ec_private, ec_public) = generate_ec512_test_key(); + let (_, ec_public_other) = generate_ec512_test_key(); + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - assert_eq!( - protected.get(&CborValue::Integer(1)), - Some(&CborValue::Integer(-7)), - ); - assert_eq!( - protected.get(&CborValue::Integer(15)), - Some(&CborValue::Bytes(b"12".to_vec())), - ); - assert_eq!(payload, TEXT,); - } + let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - #[test] - fn cose_sign1_ec384_text() { - let (ec_private, ec_public) = generate_ec384_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - let cose_doc1_bytes = cose_doc1.as_bytes(false).unwrap(); - // The value should be a sequence - assert_eq!(cose_doc1_bytes[0], 4 << 5 | 4); - let cose_doc2 = CoseSign1::from_bytes(&cose_doc1_bytes).unwrap(); - - assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&ec_public)).unwrap() - ); - } + assert!(cose_doc1.verify_signature(&ec_public).unwrap()); + assert!(!cose_doc1.verify_signature(&ec_public_other).unwrap()); + } - #[test] - fn cose_sign1_ec512_text() { - let (ec_private, ec_public) = generate_ec512_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - let cose_doc2 = CoseSign1::from_bytes(&cose_doc1.as_bytes(false).unwrap()).unwrap(); - - assert_eq!( - cose_doc1.get_payload(Some(&ec_public)).unwrap(), - TEXT.to_vec() - ); - assert_eq!( - cose_doc1.get_payload(None).unwrap(), - cose_doc2.get_payload(Some(&ec_public)).unwrap() - ); - } + #[test] + fn validate_with_wrong_key_type() { + let (ec_private, ec_public) = generate_ec512_test_key(); + let (_, ec_public_other) = generate_ec384_test_key(); + let mut map = HeaderMap::new(); + map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); - #[test] - fn unknown_curve() { - let alg = openssl::ec::EcGroup::from_curve_name(openssl::nid::Nid::SECP256K1).unwrap(); - let ec_private = openssl::ec::EcKey::generate(&alg).unwrap(); - let ec_private = PKey::from_ec_key(ec_private).unwrap(); - let map = HeaderMap::new(); - let result = CoseSign1::new(TEXT, &map, &ec_private); - assert!(result.is_err()); - } + let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); - #[test] - fn validate_with_wrong_key() { - let (ec_private, ec_public) = generate_ec512_test_key(); - let (_, ec_public_other) = generate_ec512_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + assert!(cose_doc1.verify_signature(&ec_public).unwrap()); + assert!(!cose_doc1.verify_signature(&ec_public_other).unwrap()); + } - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); + #[test] + fn cose_sign1_ec256_tampered_content() { + let (_, ec_public) = get_ec256_test_key(); - assert!(cose_doc1.verify_signature(&ec_public).unwrap()); - assert!(!cose_doc1.verify_signature(&ec_public_other).unwrap()); - } - - #[test] - fn validate_with_wrong_key_type() { - let (ec_private, ec_public) = generate_ec512_test_key(); - let (_, ec_public_other) = generate_ec384_test_key(); - let mut map = HeaderMap::new(); - map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec())); + let cose_doc = CoseSign1::from_bytes(&[ + 0x84, /* Protected: {1: -7} */ + 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x75, 0x6F, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, + 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, + 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, + 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ + 0x58, 0x40, /* R: */ + 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, + 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, + 0xCE, 0x31, 0xE2, 0xD1, /* S: */ + 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, + 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, + 0x0F, 0xAB, 0x3C, 0xDD, + ]) + .unwrap(); - let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap(); + assert!(cose_doc.get_payload(Some(&ec_public)).is_err()); + } - assert!(cose_doc1.verify_signature(&ec_public).unwrap()); - assert!(!cose_doc1.verify_signature(&ec_public_other).unwrap()); - } + #[test] + fn cose_sign1_ec256_tampered_signature() { + let (_, ec_public) = get_ec256_test_key(); - #[test] - fn cose_sign1_ec256_tampered_content() { - let (_, ec_public) = get_ec256_test_key(); - - let cose_doc = CoseSign1::from_bytes(&[ - 0x84, /* Protected: {1: -7} */ - 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6F, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ - 0x58, 0x40, /* R: */ - 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, - 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, - 0xCE, 0x31, 0xE2, 0xD1, /* S: */ - 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, - 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, - 0x0F, 0xAB, 0x3C, 0xDD, - ]) - .unwrap(); - - assert!(cose_doc.get_payload(Some(&ec_public)).is_err()); - } + let cose_doc = CoseSign1::from_bytes(&[ + 0x84, /* Protected: {1: -7} */ + 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, + 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, + 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, + 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ + 0x58, 0x40, /* R: */ + 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, + 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5B, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, + 0xCE, 0x31, 0xE2, 0xD1, /* S: */ + 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, + 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, + 0x0F, 0xAB, 0x3C, 0xDD, + ]) + .unwrap(); - #[test] - fn cose_sign1_ec256_tampered_signature() { - let (_, ec_public) = get_ec256_test_key(); - - let cose_doc = CoseSign1::from_bytes(&[ - 0x84, /* Protected: {1: -7} */ - 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ - 0x58, 0x40, /* R: */ - 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, - 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5B, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, - 0xCE, 0x31, 0xE2, 0xD1, /* S: */ - 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, - 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, - 0x0F, 0xAB, 0x3C, 0xDD, - ]) - .unwrap(); - - assert!(cose_doc.get_payload(Some(&ec_public)).is_err()); - } + assert!(cose_doc.get_payload(Some(&ec_public)).is_err()); + } - #[test] - fn cose_sign1_ec256_invalid_tag() { - let cose_doc = CoseSign1::from_bytes(&[ - 0xd3, /* tag 19 */ - 0x84, /* Protected: {1: -7} */ - 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ - 0x58, 0x40, /* R: */ - 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, - 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, - 0xCE, 0x31, 0xE2, 0xD1, /* S: */ - 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, - 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, - 0x0F, 0xAB, 0x3C, 0xDD, - ]); - - match cose_doc.unwrap_err() { - CoseError::TagError(Some(19)) => (), - _ => panic!(), + #[test] + fn cose_sign1_ec256_invalid_tag() { + let cose_doc = CoseSign1::from_bytes(&[ + 0xd3, /* tag 19 */ + 0x84, /* Protected: {1: -7} */ + 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, + 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, + 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, + 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ + 0x58, 0x40, /* R: */ + 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, + 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, + 0xCE, 0x31, 0xE2, 0xD1, /* S: */ + 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, + 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, + 0x0F, 0xAB, 0x3C, 0xDD, + ]); + + match cose_doc.unwrap_err() { + CoseError::TagError(Some(19)) => (), + _ => panic!(), + } } - } - #[test] - fn cose_sign1_ec256_missing_tag() { - let cose_doc = CoseSign1::from_bytes_tagged(&[ - 0x84, /* Protected: {1: -7} */ - 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ - 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ - 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, - 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, - 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, - 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, - 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, - 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, - 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, - 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, - 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ - 0x58, 0x40, /* R: */ - 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, - 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, - 0xCE, 0x31, 0xE2, 0xD1, /* S: */ - 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, - 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, - 0x0F, 0xAB, 0x3C, 0xDD, - ]); - - match cose_doc.unwrap_err() { - CoseError::TagError(None) => (), - _ => panic!(), + #[test] + fn cose_sign1_ec256_missing_tag() { + let cose_doc = CoseSign1::from_bytes_tagged(&[ + 0x84, /* Protected: {1: -7} */ + 0x43, 0xA1, 0x01, 0x26, /* Unprotected: {4: '11'} */ + 0xA1, 0x04, 0x42, 0x31, 0x31, /* payload: */ + 0x58, 0x75, 0x49, 0x74, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0x72, 0x75, 0x74, + 0x68, 0x20, 0x75, 0x6E, 0x69, 0x76, 0x65, 0x72, 0x73, 0x61, 0x6C, 0x6C, 0x79, 0x20, + 0x61, 0x63, 0x6B, 0x6E, 0x6F, 0x77, 0x6C, 0x65, 0x64, 0x67, 0x65, 0x64, 0x2C, 0x20, + 0x74, 0x68, 0x61, 0x74, 0x20, 0x61, 0x20, 0x73, 0x69, 0x6E, 0x67, 0x6C, 0x65, 0x20, + 0x6D, 0x61, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x70, 0x6F, 0x73, 0x73, 0x65, 0x73, 0x73, + 0x69, 0x6F, 0x6E, 0x20, 0x6F, 0x66, 0x20, 0x61, 0x20, 0x67, 0x6F, 0x6F, 0x64, 0x20, + 0x66, 0x6F, 0x72, 0x74, 0x75, 0x6E, 0x65, 0x2C, 0x20, 0x6D, 0x75, 0x73, 0x74, 0x20, + 0x62, 0x65, 0x20, 0x69, 0x6E, 0x20, 0x77, 0x61, 0x6E, 0x74, 0x20, 0x6F, 0x66, 0x20, + 0x61, 0x20, 0x77, 0x69, 0x66, 0x65, 0x2E, /* Signature - length 32 x 2 */ + 0x58, 0x40, /* R: */ + 0x6E, 0x6D, 0xF6, 0x54, 0x89, 0xEA, 0x3B, 0x01, 0x88, 0x33, 0xF5, 0xFC, 0x4F, 0x84, + 0xF8, 0x1B, 0x4D, 0x5E, 0xFD, 0x5A, 0x09, 0xD5, 0xC6, 0x2F, 0x2E, 0x92, 0x38, 0x5D, + 0xCE, 0x31, 0xE2, 0xD1, /* S: */ + 0x5A, 0x53, 0xA9, 0xF0, 0x75, 0xE8, 0xFB, 0x39, 0x66, 0x9F, 0xCD, 0x4E, 0xB5, 0x22, + 0xC8, 0x5C, 0x92, 0x77, 0x45, 0x2F, 0xA8, 0x57, 0xF5, 0xFE, 0x37, 0x9E, 0xDD, 0xEF, + 0x0F, 0xAB, 0x3C, 0xDD, + ]); + + match cose_doc.unwrap_err() { + CoseError::TagError(None) => (), + _ => panic!(), + } } } }