From 7d9084747557c91c8255bf36e52bbe5b181a72ae Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 7 Sep 2024 04:14:48 +0300 Subject: [PATCH] Add MaybeTagged struct and wrap Cose objects in that --- src/cose.rs | 114 +++++++++++++++++++++++++++++- src/cose/mac0.rs | 71 +++---------------- src/cose/sign1.rs | 85 +++++----------------- src/definitions/device_request.rs | 5 +- src/definitions/device_signed.rs | 8 +-- src/definitions/issuer_signed.rs | 6 +- src/issuance/mdoc.rs | 8 +-- src/presentation/device.rs | 7 +- 8 files changed, 159 insertions(+), 145 deletions(-) diff --git a/src/cose.rs b/src/cose.rs index 3864ca7..9d84414 100644 --- a/src/cose.rs +++ b/src/cose.rs @@ -1,4 +1,10 @@ -use coset::iana; +use std::borrow::{Borrow, BorrowMut}; +use std::ops::{Deref, DerefMut}; + +use coset::{AsCborValue, iana, TaggedCborSerializable}; + +use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; + pub mod mac0; mod serialized_as_cbor_value; pub mod sign1; @@ -7,3 +13,109 @@ pub mod sign1; pub trait SignatureAlgorithm { fn algorithm(&self) -> iana::Algorithm; } + +#[derive(Debug, Clone)] +pub struct MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + pub tagged: bool, + pub inner: T, +} + +impl MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + pub fn new(tagged: bool, inner: T) -> Self { + Self { tagged, inner } + } +} + +impl Deref for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl Borrow for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + fn borrow(&self) -> &T { + &self.inner + } +} + +impl BorrowMut for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + fn borrow_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl AsRef for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + fn as_ref(&self) -> &T { + &self.inner + } +} + +/// Serialize manually using `ciborium::tag::Captured`, putting the tag if +/// necessary. +impl serde::Serialize for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let tag = if self.tagged { + Some(coset::CoseSign1::TAG) + } else { + None + }; + + ciborium::tag::Captured(tag, SerializedAsCborValue(&self.inner)).serialize(serializer) + } +} + +/// Deserialize manually using `ciborium::tag::Captured`, checking the tag. +impl<'de, T> serde::Deserialize<'de> for MaybeTagged +where + T: AsCborValue + TaggedCborSerializable + Clone, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) = + ciborium::tag::Captured::deserialize(deserializer)?; + let tagged = match tag { + Some(coset::CoseSign1::TAG) => true, + Some(_) => return Err(serde::de::Error::custom("unexpected tag")), + None => false, + }; + + Ok(Self { tagged, inner }) + } +} diff --git a/src/cose/mac0.rs b/src/cose/mac0.rs index 243b0a2..9e24adb 100644 --- a/src/cose/mac0.rs +++ b/src/cose/mac0.rs @@ -1,15 +1,11 @@ use ::hmac::Hmac; use coset::cwt::ClaimsSet; -use coset::{ - mac_structure_data, CborSerializable, CoseError, MacContext, RegisteredLabelWithPrivate, - TaggedCborSerializable, -}; +use coset::{mac_structure_data, CborSerializable, CoseError, MacContext, RegisteredLabelWithPrivate, CoseMac0}; use digest::{Mac, MacError}; use serde::{Deserialize, Serialize}; use sha2::Sha256; -use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; -use crate::cose::SignatureAlgorithm; +use crate::cose::{MaybeTagged, SignatureAlgorithm}; /// Prepared `COSE_Mac0` for remote signing. /// @@ -51,17 +47,10 @@ use crate::cose::SignatureAlgorithm; /// } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PreparedCoseMac0 { - tagged: bool, - cose_mac0: CoseMac0, + cose_mac0: MaybeTagged, tag_payload: Vec, } -#[derive(Clone, Debug)] -pub struct CoseMac0 { - pub tagged: bool, - pub inner: coset::CoseMac0, -} - /// Errors that can occur when building, signing or verifying a COSE_Mac0. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -145,11 +134,7 @@ impl PreparedCoseMac0 { ); Ok(Self { - tagged, - cose_mac0: CoseMac0 { - tagged, - inner: cose_mac0, - }, + cose_mac0: MaybeTagged::new(tagged, cose_mac0), tag_payload, }) } @@ -160,14 +145,14 @@ impl PreparedCoseMac0 { } /// Finalize the COSE_Mac0 by adding the tag. - pub fn finalize(self, tag: Vec) -> CoseMac0 { + pub fn finalize(self, tag: Vec) -> MaybeTagged { let mut cose_mac0 = self.cose_mac0; cose_mac0.inner.tag = tag; cose_mac0 } } -impl CoseMac0 { +impl MaybeTagged { /// Verify that the tag of a `COSE_Mac0` is authentic. pub fn verify( &self, @@ -233,41 +218,6 @@ impl CoseMac0 { } } -/// Serialize manually using `ciborium::tag::Captured`, putting the tag if -/// necessary. -impl Serialize for CoseMac0 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let tag = if self.tagged { - Some(coset::CoseMac0::TAG) - } else { - None - }; - - ciborium::tag::Captured(tag, SerializedAsCborValue(&self.inner)).serialize(serializer) - } -} - -/// Deserialize manually using `ciborium::tag::Captured`, checking the tag. -impl<'de> Deserialize<'de> for CoseMac0 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) = - ciborium::tag::Captured::deserialize(deserializer)?; - let tagged = match tag { - Some(coset::CoseMac0::TAG) => true, - Some(_) => return Err(serde::de::Error::custom("unexpected tag")), - None => false, - }; - - Ok(Self { tagged, inner }) - } -} - mod hmac { use coset::iana; use hmac::Hmac; @@ -286,8 +236,6 @@ mod hmac { #[cfg(test)] mod tests { - use std::io::Cursor; - use crate::cbor; use crate::cose::mac0::{CoseMac0, PreparedCoseMac0}; use coset::cwt::{ClaimsSet, Timestamp}; @@ -296,6 +244,7 @@ mod tests { use hex::FromHex; use hmac::Hmac; use sha2::Sha256; + use crate::cose::MaybeTagged; static COSE_MAC0: &str = include_str!("../../test/definitions/cose/mac0/serialized.cbor"); static KEY: &str = include_str!("../../test/definitions/cose/mac0/secret_key"); @@ -306,7 +255,7 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); - let mut parsed: CoseMac0 = ciborium::from_reader(Cursor::new(&bytes)) + let mut parsed: MaybeTagged = cbor::from_slice(&bytes) .expect("failed to parse COSE_MAC0 from bytes"); parsed.set_tagged(); let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_MAC0"); @@ -355,7 +304,7 @@ mod tests { Hmac::::new_from_slice(&key).expect("failed to create HMAC verifier"); let cose_mac0_bytes = Vec::::from_hex(COSE_MAC0).unwrap(); - let cose_mac0: CoseMac0 = ciborium::from_reader(Cursor::new(&cose_mac0_bytes)) + let cose_mac0: MaybeTagged = cbor::from_slice(&cose_mac0_bytes) .expect("failed to parse COSE_MAC0 from bytes"); cose_mac0 @@ -447,7 +396,7 @@ mod tests { #[test] fn deserializing_tdeserializing_signed_cwtagged_cwt() { let cose_mac0_bytes = hex::decode(RFC8392_MAC0).unwrap(); - let cose_mac0: CoseMac0 = ciborium::from_reader(Cursor::new(&cose_mac0_bytes)) + let cose_mac0: MaybeTagged = cbor::from_slice(&cose_mac0_bytes) .expect("failed to parse COSE_MAC0 from bytes"); let parsed_claims_set = cose_mac0 .claims_set() diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index 7a8a0f3..04800f2 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -1,13 +1,10 @@ -use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; -use crate::cose::SignatureAlgorithm; +use coset::{CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, sig_structure_data, SignatureContext}; use coset::cwt::ClaimsSet; -use coset::{ - sig_structure_data, CborSerializable, CoseError, RegisteredLabelWithPrivate, SignatureContext, - TaggedCborSerializable, -}; use serde::{Deserialize, Serialize}; use signature::Verifier; +use crate::cose::{MaybeTagged, SignatureAlgorithm}; + /// Prepared `COSE_Sign1` for remote signing. /// /// To produce a `COSE_Sign1,` do the following: @@ -54,19 +51,11 @@ use signature::Verifier; /// } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PreparedCoseSign1 { - tagged: bool, - cose_sign1: CoseSign1, + cose_sign1: MaybeTagged, #[serde(with = "serde_bytes")] // This optimizes (de)serialization of byte vectors signature_payload: Vec, } -/// COSE_Sign1 implementation. -#[derive(Clone, Debug)] -pub struct CoseSign1 { - pub tagged: bool, - pub inner: coset::CoseSign1, -} - /// Errors that can occur when building, signing or verifying a COSE_Sign1. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -151,11 +140,7 @@ impl PreparedCoseSign1 { ); Ok(Self { - tagged, - cose_sign1: CoseSign1 { - tagged, - inner: cose_sign1, - }, + cose_sign1: MaybeTagged::new(tagged, cose_sign1), signature_payload, }) } @@ -166,14 +151,14 @@ impl PreparedCoseSign1 { } /// Finalize the COSE_Sign1 by adding the signature. - pub fn finalize(self, signature: Vec) -> CoseSign1 { + pub fn finalize(self, signature: Vec) -> MaybeTagged { let mut cose_sign1 = self.cose_sign1; cose_sign1.inner.signature = signature; cose_sign1 } } -impl CoseSign1 { +impl MaybeTagged { /// Verify that the signature of a COSE_Sign1 is authentic. pub fn verify<'a, V, S>( &'a self, @@ -247,41 +232,6 @@ impl CoseSign1 { } } -/// Serialize manually using `ciborium::tag::Captured`, putting the tag if -/// necessary. -impl Serialize for CoseSign1 { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let tag = if self.tagged { - Some(coset::CoseSign1::TAG) - } else { - None - }; - - ciborium::tag::Captured(tag, SerializedAsCborValue(&self.inner)).serialize(serializer) - } -} - -/// Deserialize manually using `ciborium::tag::Captured`, checking the tag. -impl<'de> Deserialize<'de> for CoseSign1 { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) = - ciborium::tag::Captured::deserialize(deserializer)?; - let tagged = match tag { - Some(coset::CoseSign1::TAG) => true, - Some(_) => return Err(serde::de::Error::custom("unexpected tag")), - None => false, - }; - - Ok(Self { tagged, inner }) - } -} - mod p256 { use coset::iana; use p256::ecdsa::{SigningKey, VerifyingKey}; @@ -326,16 +276,16 @@ mod p384 { #[cfg(test)] mod tests { - use crate::cbor; - use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; - use crate::cose::SignatureAlgorithm; + use coset::{CborSerializable, Header, iana}; use coset::cwt::{ClaimsSet, Timestamp}; - use coset::{iana, CborSerializable, Header}; use hex::FromHex; use p256::ecdsa::{Signature, SigningKey, VerifyingKey}; use p256::SecretKey; use signature::{SignatureEncoding, Signer}; - use std::io::Cursor; + + use crate::cbor; + use crate::cose::{MaybeTagged, SignatureAlgorithm}; + use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); static COSE_KEY: &str = include_str!("../../test/definitions/cose/sign1/secret_key"); @@ -346,7 +296,7 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut parsed: CoseSign1 = ciborium::from_reader(Cursor::new(&bytes)) + let mut parsed: MaybeTagged = cbor::from_slice(&bytes) .expect("failed to parse COSE_Sign1 from bytes"); parsed.set_tagged(); let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_Sign1 to bytes"); @@ -361,9 +311,8 @@ mod tests { #[test] fn roundtrip_ciborium() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut parsed: CoseSign1 = + let parsed: MaybeTagged = cbor::from_slice(&bytes).expect("failed to parse COSE_MAC0 from bytes"); - parsed.set_tagged(); let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_Sign1 to bytes"); println!("bytes: {:?}", hex::encode(&bytes)); println!("roundtripped: {:?}", hex::encode(&roundtripped)); @@ -416,7 +365,7 @@ mod tests { let verifier: VerifyingKey = (&signer).into(); let cose_sign1_bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let cose_sign1: CoseSign1 = + let cose_sign1: MaybeTagged = cbor::from_slice(&cose_sign1_bytes).expect("failed to parse COSE_Sign1 from bytes"); cose_sign1 @@ -510,7 +459,7 @@ mod tests { #[test] fn deserializing_signed_cwt() { let cose_sign1_bytes = hex::decode(RFC8392_COSE_SIGN1).unwrap(); - let cose_sign1: CoseSign1 = + let cose_sign1: MaybeTagged = cbor::from_slice(&cose_sign1_bytes).expect("failed to parse COSE_Sign1 from bytes"); let parsed_claims_set = cose_sign1 .claims_set() @@ -526,7 +475,7 @@ mod tests { let bytes = hex::decode(COSE_SIGN1).unwrap(); // can parse tagged value - let parsed: CoseSign1 = cbor::from_slice(&bytes).unwrap(); + let parsed: MaybeTagged = cbor::from_slice(&bytes).unwrap(); assert!(parsed.is_tagged()); println!("successfully deserialized Value from tagged bytes"); diff --git a/src/definitions/device_request.rs b/src/definitions/device_request.rs index 3d922d2..1d4ef2d 100644 --- a/src/definitions/device_request.rs +++ b/src/definitions/device_request.rs @@ -1,8 +1,9 @@ //! This module contains the definitions for the device request functionality. -use crate::cose::sign1::CoseSign1; use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec, Tag24}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +use coset::CoseSign1; +use crate::cose::MaybeTagged; pub type ItemsRequestBytes = Tag24; pub type DocType = String; @@ -11,7 +12,7 @@ pub type IntentToRetain = bool; pub type DataElementIdentifier = String; pub type DataElements = NonEmptyMap; pub type Namespaces = NonEmptyMap; -pub type ReaderAuth = CoseSign1; +pub type ReaderAuth = MaybeTagged; /// Represents a device request. #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 423ff07..3712528 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -5,14 +5,14 @@ //! The [Error] enum represents the possible errors that can occur in this module. //! - [Error::UnableToEncode]: Indicates an error when encoding a value as CBOR. use crate::cbor::Value as CborValue; -use crate::cose::mac0::CoseMac0; -use crate::cose::sign1::CoseSign1; use crate::definitions::{ helpers::{NonEmptyMap, Tag24}, session::SessionTranscript, }; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +use coset::{CoseMac0, CoseSign1}; +use crate::cose::MaybeTagged; /// Represents a device-signed structure. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -38,9 +38,9 @@ pub type DeviceSignedItems = NonEmptyMap; #[serde(untagged)] pub enum DeviceAuth { #[serde(rename_all = "camelCase")] - Signature { device_signature: CoseSign1 }, + Signature { device_signature: MaybeTagged }, #[serde(rename_all = "camelCase")] - Mac { device_mac: CoseMac0 }, + Mac { device_mac: MaybeTagged }, } #[derive(Debug, Clone, Copy, Serialize, Deserialize)] diff --git a/src/definitions/issuer_signed.rs b/src/definitions/issuer_signed.rs index 638bbca..d2c1b1f 100644 --- a/src/definitions/issuer_signed.rs +++ b/src/definitions/issuer_signed.rs @@ -9,13 +9,15 @@ //! - [IssuerSignedItemBytes] type is an alias for [`Tag24`]. //! - [IssuerSignedItem] struct represents a signed item within the [IssuerSigned] object, including information such as digest ID, random bytes, element identifier, and element value. //! - [IssuerSigned] struct also includes a test module with a unit test for serialization and deserialization. + +use coset::CoseSign1; use crate::cbor::Value as CborValue; -use crate::cose::sign1::CoseSign1; use crate::definitions::{ helpers::{ByteStr, NonEmptyMap, NonEmptyVec, Tag24}, DigestId, }; use serde::{Deserialize, Serialize}; +use crate::cose::MaybeTagged; /// Represents an issuer-signed object. /// @@ -27,7 +29,7 @@ use serde::{Deserialize, Serialize}; pub struct IssuerSigned { #[serde(skip_serializing_if = "Option::is_none", rename = "nameSpaces")] pub namespaces: Option, - pub issuer_auth: CoseSign1, + pub issuer_auth: MaybeTagged, } pub type IssuerNamespaces = NonEmptyMap>; diff --git a/src/issuance/mdoc.rs b/src/issuance/mdoc.rs index 0ee12ae..2e12652 100644 --- a/src/issuance/mdoc.rs +++ b/src/issuance/mdoc.rs @@ -3,15 +3,15 @@ use std::collections::{BTreeMap, HashSet}; use anyhow::{anyhow, Result}; use async_signature::AsyncSigner; use coset::iana::Algorithm; -use coset::Label; +use coset::{CoseSign1, Label}; use rand::Rng; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256, Sha384, Sha512}; use signature::{SignatureEncoding, Signer}; use crate::cbor::Value as CborValue; -use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; -use crate::cose::SignatureAlgorithm; +use crate::cose::sign1::PreparedCoseSign1; +use crate::cose::{MaybeTagged, SignatureAlgorithm}; use crate::{ definitions::{ helpers::{NonEmptyMap, NonEmptyVec, Tag24}, @@ -30,7 +30,7 @@ pub struct Mdoc { pub doc_type: String, pub mso: Mso, pub namespaces: IssuerNamespaces, - pub issuer_auth: CoseSign1, + pub issuer_auth: MaybeTagged, } /// An incomplete mdoc, requiring a remotely signed signature to be completed. diff --git a/src/presentation/device.rs b/src/presentation/device.rs index 40b164b..f627c63 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -18,7 +18,7 @@ //! `simulated_device_and_reader_state.rs` which uses `State` pattern, `Arc` and `Mutex`. use crate::cbor::{CborError, Value as CborValue}; use crate::cose::mac0::PreparedCoseMac0; -use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; +use crate::cose::sign1::PreparedCoseSign1; use crate::definitions::device_signed::DeviceAuthType; use crate::definitions::IssuerSignedItem; use crate::{ @@ -40,13 +40,14 @@ use crate::{ }, issuance::Mdoc, }; -use coset::{CoseMac0Builder, CoseSign1Builder}; +use coset::{CoseMac0Builder, CoseSign1, CoseSign1Builder}; use p256::FieldBytes; use serde::{Deserialize, Serialize}; use session::SessionTranscript180135; use std::collections::BTreeMap; use std::num::ParseIntError; use uuid::Uuid; +use crate::cose::MaybeTagged; /// Initialisation state. /// @@ -151,7 +152,7 @@ type DocType = String; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Document { pub id: Uuid, - pub issuer_auth: CoseSign1, + pub issuer_auth: MaybeTagged, pub mso: Mso, pub namespaces: Namespaces, }