From e849575a6813158d94d446a636023dd55b7486b5 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Fri, 6 Sep 2024 17:15:25 +0300 Subject: [PATCH 01/19] seerialize cose objects with serde and ciborium for serde --- Cargo.toml | 7 +- src/cose/serialized_as_cbor_value.rs | 24 +++++ src/definitions/device_key/cose_key.rs | 2 +- src/definitions/device_request.rs | 2 +- src/definitions/device_signed.rs | 11 ++- src/definitions/issuer_signed.rs | 2 +- src/definitions/mso.rs | 4 +- src/issuance/mdoc.rs | 40 ++++---- src/issuance/x5chain.rs | 2 +- src/lib.rs | 3 +- src/presentation/device.rs | 123 ++++++++++++++++++------- 11 files changed, 157 insertions(+), 63 deletions(-) create mode 100644 src/cose/serialized_as_cbor_value.rs diff --git a/Cargo.toml b/Cargo.toml index b86b22df..1bd349cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ rand = { version = "0.8.5", features = ["getrandom"] } serde = { version = "1.0", features = ["derive"] } serde_cbor = { version = "0.11.2", features = ["tags"] } serde_json = "1.0" +serde_bytes = "0.11.0" sha2 = "0.10.6" thiserror = "1.0" elliptic-curve = "0.13.1" @@ -46,9 +47,9 @@ clap-stdin = "0.2.1" strum = "0.24" strum_macros = "0.24" -[dependencies.cose-rs] -git = "https://github.com/spruceid/cose-rs" -rev = "4104505" +coset = "0.3.8" +ciborium = "0.2.2" +digest = "0.10.7" [dev-dependencies] hex = "0.4.3" diff --git a/src/cose/serialized_as_cbor_value.rs b/src/cose/serialized_as_cbor_value.rs new file mode 100644 index 00000000..adecdb64 --- /dev/null +++ b/src/cose/serialized_as_cbor_value.rs @@ -0,0 +1,24 @@ +use coset::AsCborValue; +use serde::{Deserialize, Serialize}; + +/// This is a small helper wrapper to deal with `coset`` types that don't +/// implement `Serialize`/`Deserialize` but only `AsCborValue`. +pub struct SerializedAsCborValue(pub T); + +impl<'a, T: Clone + AsCborValue> Serialize for SerializedAsCborValue<&'a T> { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.0.clone().to_cbor_value().map_err(serde::ser::Error::custom)?.serialize(serializer) + } +} + +impl<'de, T: AsCborValue> Deserialize<'de> for SerializedAsCborValue { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + T::from_cbor_value(ciborium::Value::deserialize(deserializer)?).map_err(serde::de::Error::custom).map(Self) + } +} diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index 1e796ace..617a3c4b 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -23,12 +23,12 @@ //! } //! ``` use aes::cipher::generic_array::{typenum::U8, GenericArray}; -use cose_rs::algorithm::Algorithm; use p256::EncodedPoint; use serde::{Deserialize, Serialize}; use serde_cbor::Value as CborValue; use ssi_jwk::JWK; use std::collections::BTreeMap; +use coset::iana::Algorithm; /// An implementation of RFC-8152 [COSE_Key](https://datatracker.ietf.org/doc/html/rfc8152#section-13) /// restricted to the requirements of ISO/IEC 18013-5:2021. diff --git a/src/definitions/device_request.rs b/src/definitions/device_request.rs index c3e4a97f..de4d313a 100644 --- a/src/definitions/device_request.rs +++ b/src/definitions/device_request.rs @@ -1,8 +1,8 @@ //! This module contains the definitions for the device request functionality. use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec, Tag24}; -use cose_rs::CoseSign1; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; +use crate::cose::sign1::CoseSign1; pub type ItemsRequestBytes = Tag24; pub type DocType = String; diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 438ae6c1..01e92502 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -8,10 +8,11 @@ use crate::definitions::{ helpers::{NonEmptyMap, Tag24}, session::SessionTranscript, }; -use cose_rs::sign1::CoseSign1; use serde::{Deserialize, Serialize}; use serde_cbor::{Error as CborError, Value as CborValue}; use std::collections::BTreeMap; +use crate::cose::mac0::CoseMac0; +use crate::cose::sign1::CoseSign1; /// Represents a device-signed structure. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -39,7 +40,13 @@ pub enum DeviceAuth { #[serde(rename_all = "camelCase")] Signature { device_signature: CoseSign1 }, #[serde(rename_all = "camelCase")] - Mac { device_mac: CborValue }, + Mac { device_mac: CoseMac0 }, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +pub enum DeviceAuthType { + Sign1, + Mac0, } pub type DeviceAuthenticationBytes = Tag24>; diff --git a/src/definitions/issuer_signed.rs b/src/definitions/issuer_signed.rs index eae4a8f9..ae4bd2b8 100644 --- a/src/definitions/issuer_signed.rs +++ b/src/definitions/issuer_signed.rs @@ -13,9 +13,9 @@ use crate::definitions::{ helpers::{ByteStr, NonEmptyMap, NonEmptyVec, Tag24}, DigestId, }; -use cose_rs::sign1::CoseSign1; use serde::{Deserialize, Serialize}; use serde_cbor::Value as CborValue; +use crate::cose::sign1::CoseSign1; /// Represents an issuer-signed object. /// diff --git a/src/definitions/mso.rs b/src/definitions/mso.rs index 01d0b765..e32f1a5f 100644 --- a/src/definitions/mso.rs +++ b/src/definitions/mso.rs @@ -94,9 +94,9 @@ mod test { >::from_hex(ISSUER_SIGNED_CBOR).expect("unable to convert cbor hex to bytes"); let signed: IssuerSigned = serde_cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as an IssuerSigned"); - let mso_bytes = signed + let mso_bytes = &signed .issuer_auth - .payload() + .inner.payload .expect("expected a COSE_Sign1 with attached payload, found detached payload"); let mso: Tag24 = serde_cbor::from_slice(mso_bytes).expect("unable to parse payload as Mso"); diff --git a/src/issuance/mdoc.rs b/src/issuance/mdoc.rs index dceecc1b..bd15ab2e 100644 --- a/src/issuance/mdoc.rs +++ b/src/issuance/mdoc.rs @@ -8,22 +8,22 @@ use crate::{ }; use anyhow::{anyhow, Result}; use async_signature::AsyncSigner; -use cose_rs::{ - algorithm::{Algorithm, SignatureAlgorithm}, - sign1::{CoseSign1, PreparedCoseSign1}, -}; use rand::Rng; use serde::{Deserialize, Serialize}; use serde_cbor::Value as CborValue; use sha2::{Digest, Sha256, Sha384, Sha512}; use signature::{SignatureEncoding, Signer}; use std::collections::{BTreeMap, HashSet}; +use coset::iana::Algorithm; +use coset::Label; +use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; +use crate::cose::SignatureAlgorithm; pub type Namespaces = BTreeMap>; +/// A signed mdoc. #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] -/// A signed mdoc. pub struct Mdoc { pub doc_type: String, pub mso: Mso, @@ -31,8 +31,8 @@ pub struct Mdoc { pub issuer_auth: CoseSign1, } -#[derive(Debug, Clone, Serialize, Deserialize)] /// An incomplete mdoc, requiring a remotely signed signature to be completed. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct PreparedMdoc { doc_type: String, mso: Mso, @@ -84,11 +84,13 @@ impl Mdoc { let mso_bytes = serde_cbor::to_vec(&Tag24::new(&mso)?)?; - let prepared_sig = CoseSign1::builder() - .payload(mso_bytes) - .signature_algorithm(signature_algorithm) - .prepare() - .map_err(|e| anyhow!("error preparing cosesign1: {}", e))?; + let protected = coset::HeaderBuilder::new() + .algorithm(signature_algorithm) + .build(); + let builder = coset::CoseSign1Builder::new() + .protected(protected) + .payload(mso_bytes); + let prepared_sig = PreparedCoseSign1::new(builder, None, None, true)?; let preparation_mdoc = PreparedMdoc { doc_type, @@ -189,10 +191,10 @@ impl PreparedMdoc { } = self; let mut issuer_auth = prepared_sig.finalize(signature); - issuer_auth - .unprotected_mut() - .insert_i(X5CHAIN_HEADER_LABEL, x5chain.into_cbor()); - + issuer_auth.inner.unprotected.rest.push(( + Label::Int(X5CHAIN_HEADER_LABEL as i64), + x5chain.into_cbor().into(), + )); Mdoc { doc_type, mso, @@ -340,7 +342,7 @@ impl Builder { enable_decoy_digests, signer, ) - .await + .await } } @@ -367,7 +369,7 @@ fn to_issuer_namespaces(namespaces: Namespaces) -> Result { fn to_issuer_signed_items( elements: BTreeMap, -) -> impl Iterator { +) -> impl Iterator { let mut used_ids = HashSet::new(); elements.into_iter().map(move |(key, value)| { let digest_id = generate_digest_id(&mut used_ids); @@ -581,8 +583,8 @@ pub mod test { (isomdl_namespace, isomdl_data), (aamva_namespace, aamva_data), ] - .into_iter() - .collect(); + .into_iter() + .collect(); let validity_info = ValidityInfo { signed: OffsetDateTime::now_utc(), diff --git a/src/issuance/x5chain.rs b/src/issuance/x5chain.rs index 655aeb73..103cb7ef 100644 --- a/src/issuance/x5chain.rs +++ b/src/issuance/x5chain.rs @@ -59,7 +59,7 @@ //! The [X5Chain] struct also provides a [X5Chain::builder] method for creating a new [Builder] instance. use crate::definitions::helpers::NonEmptyVec; use anyhow::{anyhow, Result}; -use serde_cbor::Value as CborValue; +use ciborium::Value as CborValue; use std::{fs::File, io::Read}; use x509_cert::{ certificate::Certificate, diff --git a/src/lib.rs b/src/lib.rs index 86a92b94..4cbf9a9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,8 +231,7 @@ //! ```ignore #![doc = include_str!("../tests/simulated_device_and_reader_state.rs")] //! ``` -pub use cose_rs; - +pub mod cose; pub mod definitions; pub mod issuance; pub mod presentation; diff --git a/src/presentation/device.rs b/src/presentation/device.rs index 1dce6063..a8478f80 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -35,14 +35,17 @@ use crate::{ }, issuance::Mdoc, }; -use cose_rs::sign1::{CoseSign1, PreparedCoseSign1}; use p256::FieldBytes; use serde::{Deserialize, Serialize}; use serde_cbor::Value as CborValue; use session::SessionTranscript180135; use std::collections::BTreeMap; use std::num::ParseIntError; +use coset::{CoseMac0Builder, CoseSign1Builder}; use uuid::Uuid; +use crate::cose::mac0::PreparedCoseMac0; +use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; +use crate::definitions::device_signed::DeviceAuthType; /// Initialisation state. /// @@ -98,6 +101,7 @@ pub struct SessionManager { sk_reader: [u8; 32], reader_message_counter: u32, state: State, + device_auth_type: DeviceAuthType, } /// The internal states of the [SessionManager]. @@ -174,10 +178,25 @@ struct PreparedDocument { doc_type: String, issuer_signed: IssuerSigned, device_namespaces: DeviceNamespacesBytes, - prepared_cose_sign1: PreparedCoseSign1, + prepared_cose: PreparedCose, errors: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +enum PreparedCose { + Sign1(PreparedCoseSign1), + Mac0(PreparedCoseMac0), +} + +impl PreparedCose { + fn signature_payload(&self) -> &[u8] { + match self { + PreparedCose::Sign1(inner) => inner.signature_payload(), + PreparedCose::Mac0(inner) => inner.signature_payload(), + } + } +} + /// Elements in a namespace. type Namespaces = NonEmptyMap>; type Namespace = String; @@ -279,6 +298,7 @@ impl SessionManagerEngaged { sk_reader, reader_message_counter: 0, state: State::AwaitingRequest, + device_auth_type: DeviceAuthType::Sign1, }; let requested_data = sm.handle_decoded_request(SessionData { @@ -352,7 +372,7 @@ impl SessionManager { data.as_ref(), &mut self.reader_message_counter, ) - .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; + .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; let request = match self.parse_request(&decrypted_request) { Ok(r) => r, Err(e) => { @@ -429,11 +449,11 @@ impl SessionManager { &response_bytes, &mut self.device_message_counter, ) - .unwrap_or_else(|_e| { - //tracing::warn!("unable to encrypt response: {}", e); - status = Some(session::Status::SessionEncryptionError); - Default::default() - }); + .unwrap_or_else(|_e| { + //tracing::warn!("unable to encrypt response: {}", e); + status = Some(session::Status::SessionEncryptionError); + Default::default() + }); let data = if status.is_some() { None } else { @@ -501,7 +521,7 @@ impl PreparedDeviceResponse { pub fn get_next_signature_payload(&self) -> Option<(Uuid, &[u8])> { self.prepared_documents .last() - .map(|doc| (doc.id, doc.prepared_cose_sign1.signature_payload())) + .map(|doc| (doc.id, doc.prepared_cose.signature_payload())) } /// Submit the externally signed signature for object @@ -540,17 +560,22 @@ impl PreparedDocument { let Self { issuer_signed, device_namespaces, - prepared_cose_sign1, + prepared_cose, errors, doc_type, .. } = self; - let cose_sign1 = prepared_cose_sign1.finalize(signature); + let device_auth = match prepared_cose { + PreparedCose::Sign1(inner) => DeviceAuth::Signature { + device_signature: inner.finalize(signature), + }, + PreparedCose::Mac0(inner) => DeviceAuth::Mac { + device_mac: inner.finalize(signature), + }, + }; let device_signed = DeviceSigned { namespaces: device_namespaces, - device_auth: DeviceAuth::Signature { - device_signature: cose_sign1, - }, + device_auth, }; DeviceResponseDoc { doc_type, @@ -570,6 +595,7 @@ pub trait DeviceSession { /// Get the device documents. fn documents(&self) -> &Documents; fn session_transcript(&self) -> Self::ST; + fn device_auth_type(&self) -> DeviceAuthType; /// Prepare the response based on the requested items and permitted ones. fn prepare_response( @@ -692,19 +718,50 @@ pub trait DeviceSession { continue; } }; - let prepared_cose_sign1 = match CoseSign1::builder() - .detached() - .payload(device_auth_bytes) - .signature_algorithm(signature_algorithm) - .prepare() - { - Ok(prepared) => prepared, - Err(_e) => { - let error: DocumentError = [(doc_type, DocumentErrorCode::DataNotReturned)] - .into_iter() - .collect(); - document_errors.push(error); - continue; + let header = coset::HeaderBuilder::new() + .algorithm(signature_algorithm) + .build(); + + let prepared_cose = match self.device_auth_type() { + DeviceAuthType::Sign1 => { + let cose_sign1_builder = CoseSign1Builder::new().protected(header); + let prepared_cose_sign1 = match PreparedCoseSign1::new( + cose_sign1_builder, + Some(&device_auth_bytes), + None, + true, + ) { + Ok(prepared) => prepared, + Err(_e) => { + let error: DocumentError = + [(doc_type, DocumentErrorCode::DataNotReturned)] + .into_iter() + .collect(); + document_errors.push(error); + continue; + } + }; + PreparedCose::Sign1(prepared_cose_sign1) + } + DeviceAuthType::Mac0 => { + let cose_mac0_builder = CoseMac0Builder::new().protected(header); + let prepared_cose_mac0 = match PreparedCoseMac0::new( + cose_mac0_builder, + Some(&device_auth_bytes), + None, + true, + ) { + Ok(prepared) => prepared, + Err(_e) => { + let error: DocumentError = + [(doc_type, DocumentErrorCode::DataNotReturned)] + .into_iter() + .collect(); + document_errors.push(error); + continue; + } + }; + PreparedCose::Mac0(prepared_cose_mac0) } }; @@ -716,7 +773,7 @@ pub trait DeviceSession { issuer_auth: document.issuer_auth.clone(), }, device_namespaces, - prepared_cose_sign1, + prepared_cose, errors: errors.try_into().ok(), }; prepared_documents.push(prepared_document); @@ -740,6 +797,10 @@ impl DeviceSession for SessionManager { fn session_transcript(&self) -> SessionTranscript180135 { self.session_transcript.clone() } + + fn device_auth_type(&self) -> DeviceAuthType { + self.device_auth_type + } } impl From for Document { @@ -923,7 +984,7 @@ mod test { } } ])) - .unwrap(); + .unwrap(); let permitted = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -940,7 +1001,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let expected: PermittedItems = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -948,7 +1009,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let filtered = super::filter_permitted(&requested, permitted); From 8eac14fa1ae7f34555ecade85148027d7492af18 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 7 Sep 2024 03:29:54 +0300 Subject: [PATCH 02/19] remove serde_cbor and use coset and ciborium serialization and CborValue --- macros/src/to_cbor.rs | 8 +- src/cbor.rs | 225 +++++++++++++++++ src/definitions/device_engagement.rs | 227 +++++++++++------- src/definitions/device_engagement/error.rs | 16 +- .../device_engagement/nfc_options.rs | 79 +++--- src/definitions/device_key/cose_key.rs | 103 +++++--- src/definitions/device_key/mod.rs | 2 +- src/definitions/device_request.rs | 14 +- src/definitions/device_response.rs | 10 +- src/definitions/device_signed.rs | 6 +- src/definitions/helpers/bytestr.rs | 6 +- src/definitions/helpers/tag24.rs | 26 +- src/definitions/issuer_signed.rs | 7 +- src/definitions/mso.rs | 7 +- src/definitions/namespaces/fulldate.rs | 4 +- src/definitions/namespaces/latin1.rs | 2 +- .../namespaces/org_iso_18013_5_1/age_over.rs | 4 +- .../namespaces/org_iso_18013_5_1/alpha2.rs | 5 +- .../org_iso_18013_5_1/biometric_template.rs | 2 +- .../org_iso_18013_5_1/driving_privileges.rs | 10 +- .../org_iso_18013_5_1/eye_colour.rs | 4 +- .../org_iso_18013_5_1/hair_colour.rs | 4 +- .../org_iso_18013_5_1/issuing_jurisdiction.rs | 2 +- .../namespaces/org_iso_18013_5_1/sex.rs | 2 +- .../namespaces/org_iso_18013_5_1/tdate.rs | 4 +- .../un_distinguishing_sign.rs | 2 +- .../org_iso_18013_5_1_aamva/county_code.rs | 2 +- .../org_iso_18013_5_1_aamva/dhs_compliance.rs | 2 +- .../domestic_driving_privileges.rs | 16 +- .../org_iso_18013_5_1_aamva/edl_indicator.rs | 2 +- .../org_iso_18013_5_1_aamva/name_suffix.rs | 2 +- .../name_truncation.rs | 2 +- .../org_iso_18013_5_1_aamva/present.rs | 4 +- .../race_and_ethnicity.rs | 2 +- .../namespaces/org_iso_18013_5_1_aamva/sex.rs | 2 +- .../org_iso_18013_5_1_aamva/weight_range.rs | 2 +- src/definitions/session.rs | 35 +-- src/definitions/traits/to_cbor.rs | 16 +- src/definitions/validity_info.rs | 47 ++-- src/issuance/mdoc.rs | 45 ++-- src/lib.rs | 1 + src/presentation/device.rs | 55 ++--- src/presentation/mod.rs | 10 +- src/presentation/reader.rs | 53 ++-- test/definitions/device_response.cbor | 2 +- tests/common.rs | 4 +- tests/simulated_device_and_reader_state.rs | 4 +- 47 files changed, 712 insertions(+), 377 deletions(-) create mode 100644 src/cbor.rs diff --git a/macros/src/to_cbor.rs b/macros/src/to_cbor.rs index c2ec7d84..473094da 100644 --- a/macros/src/to_cbor.rs +++ b/macros/src/to_cbor.rs @@ -86,7 +86,7 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt let output = quote! { mod #mod_name { - use serde_cbor::Value; + use #isomdl_path::cbor::Value; use super::*; use #isomdl_path::definitions::traits::{ToCbor, ToNamespaceMap}; impl ToNamespaceMap for #ident { @@ -102,9 +102,9 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt fn to_cbor(self) -> Value { let map = self.to_ns_map() .into_iter() - .map(|(k, v)| (Value::Text(k), v)) + .map(|(k, v)| (ciborium::Value::Text(k), v.into())) .collect(); - Value::Map(map) + ciborium::Value::Map(map).into() } } } @@ -140,7 +140,7 @@ fn unnamed_fields(isomdl_path: Ident, ident: Ident, mut input: FieldsUnnamed) -> mod #mod_name { use super::*; use #isomdl_path::definitions::traits::{ToCbor, ToCborError}; - use serde_cbor::Value; + use #isomdl_path::cbor::Value impl ToCbor for #ident { fn to_cbor(self) -> Value { <#field_type as ToCbor>::to_cbor(self) diff --git a/src/cbor.rs b/src/cbor.rs new file mode 100644 index 00000000..3c25e4b9 --- /dev/null +++ b/src/cbor.rs @@ -0,0 +1,225 @@ +use std::borrow::{Borrow, BorrowMut}; +use std::ops::{Deref, DerefMut}; +use serde::{de, Deserialize, Serialize}; +use std::io::Cursor; +use coset::{cbor, CoseError, EndOfFile}; +use thiserror::Error; + +/// Wraps [chromium::Value] and implements [PartialEq], [Eq], [PartialOrd] and [Ord], +/// so it can be used in maps and sets. +/// +/// Also, useful in future if we want to change the CBOR library. +#[derive(Debug, Clone)] +pub struct Value(pub ciborium::Value); + +#[derive(Debug, Error)] +pub enum CborError { + /// CBOR decoding failure. + #[error("CBOR decoding failure: {0}")] + DecodeFailed(cbor::de::Error), + /// Duplicate map key detected. + #[error("duplicate map key")] + DuplicateMapKey, + /// CBOR encoding failure. + #[error("CBOR encoding failure")] + EncodeFailed, + /// CBOR input had extra data. + #[error("extraneous data")] + ExtraneousData, + /// Integer value on the wire is outside the range of integers representable in this crate. + /// See . + #[error("integer value out of range")] + OutOfRangeIntegerValue, + /// Unexpected CBOR item encountered (got, want). + #[error("unexpected item: {0}, want {1}")] + UnexpectedItem(&'static str, &'static str), + /// Unrecognized value in IANA-controlled range (with no private range). + #[error("unregistered IANA value")] + UnregisteredIanaValue, + /// Unrecognized value in neither IANA-controlled range nor private range. + #[error("unregistered non-private IANA value")] + UnregisteredIanaNonPrivateValue, +} + +impl From for CborError { + fn from(e: CoseError) -> Self { + match e { + CoseError::DecodeFailed(e) => CborError::DecodeFailed(e), + CoseError::DuplicateMapKey => CborError::DuplicateMapKey, + CoseError::EncodeFailed => CborError::EncodeFailed, + CoseError::ExtraneousData => CborError::ExtraneousData, + CoseError::OutOfRangeIntegerValue => CborError::OutOfRangeIntegerValue, + CoseError::UnexpectedItem(s, s2) => CborError::UnexpectedItem(s, s2), + CoseError::UnregisteredIanaValue => CborError::UnregisteredIanaValue, + CoseError::UnregisteredIanaNonPrivateValue => CborError::UnregisteredIanaNonPrivateValue, + } + } +} + +impl Value { + pub fn to_string(&self) -> coset::Result { + self.0.clone().into_text().map_err(|e| CoseError::DecodeFailed(ciborium::de::Error::Semantic( + None, + format!("{e:?}"), + ))) + } +} + +impl Deref for Value { + type Target = ciborium::Value; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Value { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for Value {} + +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for Value { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.partial_cmp(&other.0).unwrap() + } +} + +pub fn to_vec(value: &T) -> Result, CborError> +where + T: serde::Serialize, +{ + let mut buf = Vec::new(); + ciborium::into_writer(value, &mut buf).map_err(coset::CoseError::from).map_err(CborError::from)?; + Ok(buf) +} + +pub fn from_slice(slice: &[u8]) -> Result +where + T: de::DeserializeOwned, +{ + ciborium::from_reader(Cursor::new(&slice)).map_err(|e| CoseError::DecodeFailed(ciborium::de::Error::Semantic( + None, + e.to_string(), + ))).map_err(CborError::from) +} + +/// Convert a `chor::Value` into a type `T` +#[allow(clippy::needless_pass_by_value)] +pub fn from_value2(value: Value) -> Result +where + T: de::DeserializeOwned, +{ + from_value(value.0) +} + +/// Convert a `ciborium::Value` into a type `T` +#[allow(clippy::needless_pass_by_value)] +pub fn from_value(value: ciborium::Value) -> Result +where + T: de::DeserializeOwned, +{ + // TODO implement in a way that doesn't require + // roundtrip through buffer (i.e. by implementing + // `serde::de::Deserializer` for `Value` and then doing + // `T::deserialize(value)`). + let buf = to_vec(&value)?; + from_slice(buf.as_slice()) +} + +pub fn into_value(v: S) -> Result +where + S: Serialize, +{ + let bytes = to_vec(&v)?; + from_slice(&bytes) +} + +impl From for Value { + fn from(value: ciborium::Value) -> Self { + Self(value) + } +} + +impl Into for Value { + fn into(self) -> ciborium::Value { + self.0 + } +} + +impl Serialize for Value { + fn serialize(&self, serializer: S) -> crate::cose::sign1::Result + where + S: serde::Serializer, + { + self.0.serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Value { + fn deserialize(deserializer: D) -> crate::cose::sign1::Result + where + D: serde::Deserializer<'de>, + { + ciborium::Value::deserialize(deserializer).map(|v| Value(v)) + } +} + +impl AsRef for Value { + fn as_ref(&self) -> &ciborium::Value { + &self.0 + } +} + +impl Borrow for Value { + fn borrow(&self) -> &ciborium::Value { + &self.0 + } +} + +impl BorrowMut for Value { + fn borrow_mut(&mut self) -> &mut ciborium::Value { + &mut self.0 + } +} + +macro_rules! impl_from { + ($variant:path, $for_type:ty) => { + impl From<$for_type> for Value { + fn from(v: $for_type) -> Value { + $variant(v.into()).into() + } + } + }; +} + +impl_from!(ciborium::Value::Bool, bool); +impl_from!(ciborium::Value::Integer, i8); +impl_from!(ciborium::Value::Integer, i16); +impl_from!(ciborium::Value::Integer, i32); +impl_from!(ciborium::Value::Integer, i64); +// i128 omitted because not all numbers fit in CBOR serialization +impl_from!(ciborium::Value::Integer, u8); +impl_from!(ciborium::Value::Integer, u16); +impl_from!(ciborium::Value::Integer, u32); +impl_from!(ciborium::Value::Integer, u64); +// u128 omitted because not all numbers fit in CBOR serialization +impl_from!(ciborium::Value::Float, f32); +impl_from!(ciborium::Value::Float, f64); +impl_from!(ciborium::Value::Bytes, Vec); +impl_from!(ciborium::Value::Text, String); +impl_from!(ciborium::Value::Array, Vec); diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index c8a713d1..f6c49f28 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -9,21 +9,22 @@ use crate::definitions::helpers::{ByteStr, NonEmptyVec}; use crate::definitions::CoseKey; use anyhow::Result; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; use std::{collections::BTreeMap, vec}; use uuid::Uuid; +use crate::cbor::{CborError, into_value, Value as CborValue}; pub mod error; pub use error::Error; pub mod nfc_options; pub use nfc_options::NfcOptions; +use crate::cbor; pub type EDeviceKeyBytes = Tag24; pub type EReaderKeyBytes = Tag24; pub type DeviceRetrievalMethods = NonEmptyVec; -pub type ProtocolInfo = CborValue; +pub type ProtocolInfo = cbor::Value; pub type Oidc = (u64, String, String); pub type WebApi = (u64, String, String); @@ -135,39 +136,43 @@ pub struct WifiOptions { impl From for CborValue { fn from(device_engagement: DeviceEngagement) -> CborValue { - let mut map = BTreeMap::new(); - map.insert( - CborValue::Integer(0), - CborValue::Text(device_engagement.version), - ); - map.insert( - CborValue::Integer(1), - CborValue::Array(vec![ - device_engagement.security.0.into(), - device_engagement.security.1.into(), + let mut map = vec![]; + map.push(( + ciborium::Value::Integer(0.into()), + ciborium::Value::Text(device_engagement.version), + )); + map.push(( + ciborium::Value::Integer(1.into()), + ciborium::Value::Array(vec![ + into_value(device_engagement.security.0).unwrap(), + into_value(device_engagement.security.1).unwrap(), ]), - ); + )); if let Some(methods) = device_engagement.device_retrieval_methods { - let methods = Vec::from(methods).into_iter().map(Into::into).collect(); - map.insert(CborValue::Integer(2), CborValue::Array(methods)); + let methods = Vec::from(methods).into_iter().map(into_value).collect::, CborError>>().unwrap(); + map.push((ciborium::Value::Integer(2.into()), ciborium::Value::Array(methods))); } if let Some(methods) = device_engagement.server_retrieval_methods { - map.insert(CborValue::Integer(3), methods.into()); + map.push((ciborium::Value::Integer(3.into()), into_value(methods).unwrap())); } if let Some(_info) = device_engagement.protocol_info { // Usage of protocolinfo is RFU and should for now be none } - CborValue::Map(map) + CborValue(ciborium::Value::Map(map)) } } impl TryFrom for DeviceEngagement { type Error = Error; fn try_from(v: CborValue) -> Result { - if let CborValue::Map(mut map) = v { - let device_engagement_version = map.remove(&CborValue::Integer(0)); - if let Some(CborValue::Text(v)) = device_engagement_version { + if let ciborium::Value::Map(map) = v.0 { + let mut map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); + let device_engagement_version = map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); + cbor + }); + if let Some(CborValue(ciborium::Value::Text(v))) = device_engagement_version { if v != "1.0" { return Err(Error::UnsupportedVersion); } @@ -175,26 +180,35 @@ impl TryFrom for DeviceEngagement { return Err(Error::Malformed); } let device_engagement_security = - map.remove(&CborValue::Integer(1)).ok_or(Error::Malformed)?; + map.remove(&cbor::Value(ciborium::Value::Integer(1.into()))).ok_or(Error::Malformed)?; - let security: Security = serde_cbor::value::from_value(device_engagement_security) + let security: Security = cbor::from_value(device_engagement_security.0) .map_err(|_| Error::Malformed)?; let device_retrieval_methods = map - .remove(&CborValue::Integer(2)) - .map(serde_cbor::value::from_value) + .remove(&{ + let cbor: CborValue = ciborium::Value::Integer(2.into()).into(); + cbor + }) + .map(cbor::from_value2) .transpose() .map_err(|_| Error::Malformed)?; let server_retrieval_methods = map - .remove(&CborValue::Integer(3)) - .map(serde_cbor::value::from_value) + .remove(&{ + let cbor: CborValue = ciborium::Value::Integer(3.into()).into(); + cbor + }) + .map(cbor::from_value2) .transpose() .map_err(|_| Error::Malformed)?; if server_retrieval_methods.is_some() { //tracing::warn!("server_retrieval is unimplemented.") } - let protocol_info = map.remove(&CborValue::Integer(4)); + let protocol_info = map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(4.into()).into(); + cbor + }); if protocol_info.is_some() { //tracing::warn!("protocol_info is RFU and has been ignored in deserialization.") } @@ -217,7 +231,7 @@ impl TryFrom for DeviceEngagement { impl Tag24 { const BASE64_CONFIG: base64::Config = base64::Config::new(base64::CharacterSet::UrlSafe, false); - pub fn to_qr_code_uri(&self) -> Result { + pub fn to_qr_code_uri(&self) -> Result { let mut qr_code_uri = String::from("mdoc:"); base64::encode_config_buf(&self.inner_bytes, Self::BASE64_CONFIG, &mut qr_code_uri); Ok(qr_code_uri) @@ -249,22 +263,25 @@ impl DeviceRetrievalMethod { impl TryFrom for DeviceRetrievalMethod { type Error = Error; fn try_from(value: CborValue) -> Result { - if let CborValue::Array(list) = value { - let method: [CborValue; 3] = list.try_into().map_err(|_| Error::Malformed)?; + if let ciborium::Value::Array(list) = value.0 { + let method: [CborValue; 3] = list.into_iter().map(CborValue).collect::>().try_into().map_err(|_| Error::Malformed)?; match method { - [CborValue::Integer(1), CborValue::Integer(1), methods] => { + [CborValue(ciborium::Value::Integer(i1)), CborValue(ciborium::Value::Integer(i11)), methods] + if >::into(i1) == 1 && >::into(i11) == 1 => { let nfc_options = NfcOptions::try_from(methods)?; Ok(DeviceRetrievalMethod::NFC(nfc_options)) } - [CborValue::Integer(2), CborValue::Integer(1), methods] => { + [CborValue(ciborium::Value::Integer(i2)), CborValue(ciborium::Value::Integer(i1)), methods] + if >::into(i1) == 1 && >::into(i2) == 2 => { let ble_options = BleOptions::try_from(methods)?; Ok(DeviceRetrievalMethod::BLE(ble_options)) } - [CborValue::Integer(3), CborValue::Integer(1), methods] => { + [CborValue(ciborium::Value::Integer(i3)), CborValue(ciborium::Value::Integer(i1)), methods] + if >::into(i1) == 1 && >::into(i3) == 3 => { let wifi_options = WifiOptions::try_from(methods)?; Ok(DeviceRetrievalMethod::WIFI(wifi_options)) } - [CborValue::Integer(_), _, _] => Err(Error::UnsupportedDRM), + [CborValue(ciborium::Value::Integer(_)), _, _] => Err(Error::UnsupportedDRM), _ => Err(Error::Malformed), } } else { @@ -278,11 +295,11 @@ impl From for CborValue { let transport_type = drm.transport_type().into(); let version = drm.version().into(); let retrieval_method = match drm { - DeviceRetrievalMethod::NFC(opts) => opts.into(), - DeviceRetrievalMethod::BLE(opts) => opts.into(), - DeviceRetrievalMethod::WIFI(opts) => opts.into(), + DeviceRetrievalMethod::NFC(opts) => into_value(opts).unwrap(), + DeviceRetrievalMethod::BLE(opts) => into_value(opts).unwrap(), + DeviceRetrievalMethod::WIFI(opts) => into_value(opts).unwrap(), }; - CborValue::Array(vec![transport_type, version, retrieval_method]) + CborValue(ciborium::Value::Array(vec![transport_type, version, retrieval_method])) } } @@ -290,28 +307,44 @@ impl TryFrom for BleOptions { type Error = Error; fn try_from(v: CborValue) -> Result { - if let CborValue::Map(mut map) = v { + if let ciborium::Value::Map(map) = v.0 { + let mut map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); let central_client_mode = match ( - map.remove(&CborValue::Integer(1)), - map.remove(&CborValue::Integer(11)), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); + cbor + }), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(11.into()).into(); + cbor + }), ) { - (Some(CborValue::Bool(true)), Some(CborValue::Bytes(uuid))) => { + (Some(CborValue(ciborium::Value::Bool(true))), Some(CborValue(ciborium::Value::Bytes(uuid)))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; Some(CentralClientMode { uuid: Uuid::from_bytes(uuid_bytes), }) } - (Some(CborValue::Bool(false)), _) => None, + (Some(CborValue(ciborium::Value::Bool(false))), _) => None, _ => return Err(Error::Malformed), }; let peripheral_server_mode = match ( - map.remove(&CborValue::Integer(0)), - map.remove(&CborValue::Integer(10)), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); + cbor + }), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(10.into()).into(); + cbor + }), ) { - (Some(CborValue::Bool(true)), Some(CborValue::Bytes(uuid))) => { + (Some(CborValue(ciborium::Value::Bool(true))), Some(CborValue(ciborium::Value::Bytes(uuid)))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; - let ble_device_address = match map.remove(&CborValue::Integer(20)) { + let ble_device_address = match map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(20.into()).into(); + cbor + }) { Some(value) => Some(value.try_into().map_err(|_| Error::Malformed)?), None => None, }; @@ -320,7 +353,7 @@ impl TryFrom for BleOptions { ble_device_address, }) } - (Some(CborValue::Bool(false)), _) => None, + (Some(CborValue(ciborium::Value::Bool(false))), _) => None, _ => return Err(Error::Malformed), }; @@ -336,41 +369,41 @@ impl TryFrom for BleOptions { impl From for CborValue { fn from(o: BleOptions) -> CborValue { - let mut map = BTreeMap::new(); + let mut map = vec![]; match o.central_client_mode { Some(CentralClientMode { uuid }) => { - map.insert(CborValue::Integer(1), CborValue::Bool(true)); - map.insert( - CborValue::Integer(11), - CborValue::Bytes(uuid.as_bytes().to_vec()), - ); + map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Bool(true))); + map.push(( + ciborium::Value::Integer(11.into()), + ciborium::Value::Bytes(uuid.as_bytes().to_vec()), + )); } None => { - map.insert(CborValue::Integer(1), CborValue::Bool(false)); + map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Bool(false))); } } match o.peripheral_server_mode { Some(PeripheralServerMode { - uuid, - ble_device_address, - }) => { - map.insert(CborValue::Integer(0), CborValue::Bool(true)); - map.insert( - CborValue::Integer(10), - CborValue::Bytes(uuid.as_bytes().to_vec()), - ); + uuid, + ble_device_address, + }) => { + map.push((ciborium::Value::Integer(0.into()), ciborium::Value::Bool(true))); + map.push(( + ciborium::Value::Integer(10.into()), + ciborium::Value::Bytes(uuid.as_bytes().to_vec()), + )); if let Some(address) = ble_device_address { - map.insert(CborValue::Integer(20), address.into()); + map.push((ciborium::Value::Integer(20.into()), into_value(address).unwrap())); } } None => { - map.insert(CborValue::Integer(0), CborValue::Bool(false)); + map.push((ciborium::Value::Integer(0.into()), ciborium::Value::Bool(false))); } } - CborValue::Map(map) + ciborium::Value::Map(map).into() } } @@ -382,9 +415,12 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&CborValue::Integer(idx)) { + match map.get(&{ + let cbor: CborValue = ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + cbor + }) { None => Ok(None), - Some(CborValue::Text(text)) => Ok(Some(text.to_string())), + Some(CborValue(ciborium::Value::Text(text))) => Ok(Some(text.to_string())), _ => Err(Error::InvalidWifiOptions), } } @@ -393,9 +429,12 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&CborValue::Integer(idx)) { + match map.get(&{ + let cbor: CborValue = ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + cbor + }) { None => Ok(None), - Some(CborValue::Integer(int_val)) => { + Some(CborValue(ciborium::Value::Integer(int_val))) => { let uint_val = u64::try_from(*int_val).map_err(|_| Error::InvalidWifiOptions)?; Ok(Some(uint_val)) @@ -408,7 +447,10 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&CborValue::Integer(idx)) { + match map.get(&{ + let cbor: CborValue = ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + cbor + }) { None => Ok(None), Some(cbor_val) => { let byte_str = ByteStr::try_from(cbor_val.clone()) @@ -418,8 +460,11 @@ impl TryFrom for WifiOptions { } } - let map: BTreeMap = match v { - CborValue::Map(map) => Ok(map), + let map: BTreeMap = match v.0 { + ciborium::Value::Map(map) => { + let map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); + Ok(map) + } _ => Err(Error::InvalidWifiOptions), }?; @@ -457,43 +502,43 @@ impl TryFrom for WifiOptions { impl From for CborValue { fn from(o: WifiOptions) -> CborValue { - let mut map = BTreeMap::::new(); + let mut map = vec![]; if let Some(v) = o.pass_phrase { - map.insert(CborValue::Integer(0), v.into()); + map.push((ciborium::Value::Integer(0.into()), into_value(v).unwrap())); } if let Some(v) = o.channel_info_operating_class { - map.insert(CborValue::Integer(1), v.into()); + map.push(((ciborium::Value::Integer(1.into())), into_value(v).unwrap())); } if let Some(v) = o.channel_info_channel_number { - map.insert(CborValue::Integer(2), v.into()); + map.push((ciborium::Value::Integer(2.into()), into_value(v).unwrap())); } if let Some(v) = o.band_info { - map.insert(CborValue::Integer(3), v.into()); + map.push((ciborium::Value::Integer(3.into()), into_value(v).unwrap())); } - CborValue::Map(map) + ciborium::Value::Map(map).into() } } impl From for CborValue { fn from(m: ServerRetrievalMethods) -> CborValue { - let mut map = BTreeMap::::new(); + let mut map = vec![]; if let Some((x, y, z)) = m.web_api { - map.insert( + map.push(( "webApi".to_string().into(), - CborValue::Array(vec![x.into(), y.into(), z.into()]), - ); + ciborium::Value::Array(vec![into_value(x).unwrap(), into_value(y).unwrap(), into_value(z).unwrap()]), + )); } if let Some((x, y, z)) = m.oidc { - map.insert( + map.push(( "oidc".to_string().into(), - CborValue::Array(vec![x.into(), y.into(), z.into()]), - ); + ciborium::Value::Array(vec![into_value(x).unwrap(), into_value(y).unwrap(), into_value(z).unwrap()]), + )); } - CborValue::Map(map) + ciborium::Value::Map(map).into() } } @@ -526,8 +571,8 @@ mod test { protocol_info: None, }; - let bytes = serde_cbor::to_vec(&device_engagement).unwrap(); - let roundtripped = serde_cbor::from_slice(&bytes).unwrap(); + let bytes = crate::cbor::to_vec(&device_engagement).unwrap(); + let roundtripped = crate::cbor::from_slice(&bytes).unwrap(); assert_eq!(device_engagement, roundtripped) } @@ -541,8 +586,8 @@ mod test { } fn wifi_options_cbor_roundtrip_test(wifi_options: WifiOptions) { - let bytes: Vec = serde_cbor::to_vec(&wifi_options).unwrap(); - let deserialized: WifiOptions = serde_cbor::from_slice(&bytes).unwrap(); + let bytes: Vec = crate::cbor::to_vec(&wifi_options).unwrap(); + let deserialized: WifiOptions = crate::cbor::from_slice(&bytes).unwrap(); assert_eq!(wifi_options, deserialized); } diff --git a/src/definitions/device_engagement/error.rs b/src/definitions/device_engagement/error.rs index 593cb6dc..b26d3d66 100644 --- a/src/definitions/device_engagement/error.rs +++ b/src/definitions/device_engagement/error.rs @@ -1,6 +1,6 @@ +use crate::cbor::CborError; use crate::definitions::device_key::cose_key::Error as CoseKeyError; use crate::definitions::helpers::tag24::Error as Tag24Error; -use serde_cbor::Error as SerdeCborError; /// Errors that can occur when deserialising a DeviceEngagement. #[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)] @@ -24,7 +24,7 @@ pub enum Error { #[error("Something went wrong parsing a tag24")] Tag24Error, #[error("Could not deserialize from cbor")] - SerdeCborError, + CborError, #[error("NFC Command Data Length must be between 255 and 65535")] InvalidNfcCommandDataLengthError, #[error("NFC Response Data Length must be between 256 and 65536")] @@ -43,8 +43,14 @@ impl From for Error { } } -impl From for Error { - fn from(_: SerdeCborError) -> Self { - Error::SerdeCborError +impl From for Error { + fn from(_: coset::CoseError) -> Self { + Error::CborError + } +} + +impl From for Error { + fn from(_: CborError) -> Self { + Error::CborError } } diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index cbf8fe89..c4d8ff42 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -1,8 +1,7 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::Value as CborValue; use std::collections::BTreeMap; - use crate::definitions::device_engagement::error::Error; /// The maximum length of the NFC command, as specified in ISO_18013-5 2021 Section 8.3.3.1.2 @@ -26,14 +25,18 @@ impl TryFrom for NfcOptions { type Error = Error; fn try_from(v: CborValue) -> Result { - let map: BTreeMap = match v { - CborValue::Map(map) => Ok(map), + let map: BTreeMap = match v.0 { + ciborium::Value::Map(map) => Ok(map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>()), _ => Err(Error::InvalidNfcOptions), }?; Ok(NfcOptions::default()) .and_then(|nfc_opts| { - map.get(&CborValue::Integer(0)) + map.get(&{ + let cbor: CborValue = + ciborium::Value::Integer(0.into()).into(); + cbor + }) .ok_or(Error::InvalidNfcOptions) .and_then(CommandDataLength::try_from) .map(|max_len_command_data_field| NfcOptions { @@ -42,7 +45,11 @@ impl TryFrom for NfcOptions { }) }) .and_then(|nfc_opts| { - map.get(&CborValue::Integer(1)) + map.get(&{ + let cbor: CborValue = + ciborium::Value::Integer(1.into()).into(); + cbor + }) .ok_or(Error::InvalidNfcOptions) .and_then(ResponseDataLength::try_from) .map(|max_len_response_data_field| NfcOptions { @@ -55,17 +62,17 @@ impl TryFrom for NfcOptions { impl From for CborValue { fn from(o: NfcOptions) -> CborValue { - let mut map = BTreeMap::::new(); - map.insert( - CborValue::Integer(0), - CborValue::from(o.max_len_command_data_field), - ); - map.insert( - CborValue::Integer(1), - CborValue::from(o.max_len_response_data_field), - ); - - CborValue::Map(map) + let mut map = vec![]; + map.push(( + ciborium::Value::Integer(0.into()), + ciborium::Value::Integer(o.max_len_command_data_field.get().into()), + )); + map.push(( + ciborium::Value::Integer(1.into()), + ciborium::Value::Integer(o.max_len_response_data_field.get().into()), + )); + + CborValue(ciborium::Value::Map(map)) } } @@ -126,8 +133,8 @@ impl TryFrom<&CborValue> for CommandDataLength { type Error = Error; fn try_from(v: &CborValue) -> Result { - match v { - CborValue::Integer(int_val) => Self::try_from(*int_val), + match v.0 { + ciborium::Value::Integer(int_val) => Ok(Self(int_val.try_into().unwrap())), _ => Err(Error::InvalidNfcOptions), } } @@ -135,7 +142,7 @@ impl TryFrom<&CborValue> for CommandDataLength { impl From for CborValue { fn from(cdl: CommandDataLength) -> CborValue { - CborValue::Integer(cdl.get().into()) + CborValue(ciborium::Value::Integer(cdl.get().into())) } } @@ -194,8 +201,8 @@ impl TryFrom<&CborValue> for ResponseDataLength { type Error = Error; fn try_from(v: &CborValue) -> Result { - match v { - CborValue::Integer(int_val) => Self::try_from(*int_val), + match v.0 { + ciborium::Value::Integer(int_val) => Ok(Self(int_val.try_into().unwrap())), _ => Err(Error::InvalidNfcOptions), } } @@ -203,12 +210,13 @@ impl TryFrom<&CborValue> for ResponseDataLength { impl From for CborValue { fn from(rdl: ResponseDataLength) -> CborValue { - CborValue::Integer(rdl.get().into()) + CborValue(ciborium::Value::Integer(rdl.get().into())) } } #[cfg(test)] mod test { + use crate::cbor; use super::*; #[test] @@ -271,8 +279,8 @@ mod test { #[test] fn command_data_length_cbor_roundtrip_test() { let cdl: CommandDataLength = CommandDataLength::new(512).unwrap(); - let bytes: Vec = serde_cbor::to_vec(&cdl).unwrap(); - let deserialized: CommandDataLength = serde_cbor::from_slice(&bytes).unwrap(); + let bytes: Vec = crate::cbor::to_vec(&cdl).unwrap(); + let deserialized: CommandDataLength = crate::cbor::from_slice(&bytes).unwrap(); assert_eq!(cdl, deserialized); } @@ -339,14 +347,14 @@ mod test { #[test] fn response_data_length_cbor_roundtrip_test() { let rdl: ResponseDataLength = ResponseDataLength::new(512).unwrap(); - let bytes: Vec = serde_cbor::to_vec(&rdl).unwrap(); - let deserialized: ResponseDataLength = serde_cbor::from_slice(&bytes).unwrap(); + let bytes: Vec = crate::cbor::to_vec(&rdl).unwrap(); + let deserialized: ResponseDataLength = crate::cbor::from_slice(&bytes).unwrap(); assert_eq!(rdl, deserialized); } fn nfc_options_cbor_roundtrip_test(nfc_options: NfcOptions) { - let bytes: Vec = serde_cbor::to_vec(&nfc_options).unwrap(); - let deserialized: NfcOptions = serde_cbor::from_slice(&bytes).unwrap(); + let bytes: Vec = crate::cbor::to_vec(&nfc_options).unwrap(); + let deserialized: NfcOptions = crate::cbor::from_slice(&bytes).unwrap(); assert_eq!(nfc_options, deserialized); } @@ -377,10 +385,11 @@ mod test { max_len_response_data_field: ResponseDataLength::MIN, }; - let bytes: Vec = serde_cbor::to_vec(&nfc_options).unwrap(); + let bytes: Vec = cbor::to_vec(&nfc_options).unwrap(); let deserialized_result: Result = - serde_cbor::from_slice(&bytes).map_err(Error::from); - assert_eq!(Err(Error::SerdeCborError), deserialized_result); + cbor::from_slice(&bytes).map_err(Error::from); + println!("{:?}", deserialized_result); + assert_eq!(Err(Error::CborError), deserialized_result); } #[test] @@ -390,9 +399,9 @@ mod test { max_len_response_data_field: ResponseDataLength::MIN, }; - let bytes: Vec = serde_cbor::to_vec(&nfc_options).unwrap(); + let bytes: Vec = cbor::to_vec(&nfc_options).unwrap(); let deserialized_result: Result = - serde_cbor::from_slice(&bytes).map_err(Error::from); - assert_eq!(Err(Error::SerdeCborError), deserialized_result); + cbor::from_slice(&bytes).map_err(Error::from); + assert_eq!(Err(Error::CborError), deserialized_result); } } diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index 617a3c4b..46325496 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -25,7 +25,7 @@ use aes::cipher::generic_array::{typenum::U8, GenericArray}; use p256::EncodedPoint; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::Value as CborValue; use ssi_jwk::JWK; use std::collections::BTreeMap; use coset::iana::Algorithm; @@ -77,7 +77,8 @@ pub enum Error { NotAMap(CborValue), #[error("Unable to discern the elliptic curve")] UnknownCurve, - #[error("This implementation of COSE_Key only supports P-256, P-384, P-521, Ed25519 and Ed448 elliptic curves")] + #[error("This implementation of COSE_Key only supports P-256, P-384, P-521, Ed25519 and Ed448 elliptic curves" + )] UnsupportedCurve, #[error("This implementation of COSE_Key only supports EC2 and OKP keys")] UnsupportedKeyType, @@ -118,28 +119,37 @@ impl CoseKey { impl From for CborValue { fn from(key: CoseKey) -> CborValue { - let mut map = BTreeMap::new(); + let mut map = vec![]; match key { CoseKey::EC2 { crv, x, y } => { // kty: 1, EC2: 2 - map.insert(CborValue::Integer(1), CborValue::Integer(2)); + map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Integer(2.into()))); // crv: -1 - map.insert(CborValue::Integer(-1), crv.into()); + map.push((ciborium::Value::Integer((-1).into()), { + let cbor: CborValue = crv.into(); + cbor.into() + })); // x: -2 - map.insert(CborValue::Integer(-2), CborValue::Bytes(x)); + map.push((ciborium::Value::Integer((-2).into()), ciborium::Value::Bytes(x))); // y: -3 - map.insert(CborValue::Integer(-3), y.into()); + map.push((ciborium::Value::Integer((-3).into()), { + let cbor: CborValue = y.into(); + cbor.into() + })); } CoseKey::OKP { crv, x } => { // kty: 1, OKP: 1 - map.insert(CborValue::Integer(1), CborValue::Integer(1)); + map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Integer(1.into()))); // crv: -1 - map.insert(CborValue::Integer(-1), crv.into()); + map.push((ciborium::Value::Integer((-1).into()), { + let cbor: CborValue = crv.into(); + cbor.into() + })); // x: -2 - map.insert(CborValue::Integer(-2), CborValue::Bytes(x)); + map.push((ciborium::Value::Integer((-2).into()), ciborium::Value::Bytes(x))); } } - CborValue::Map(map) + ciborium::Value::Map(map).into() } } @@ -147,29 +157,43 @@ impl TryFrom for CoseKey { type Error = Error; fn try_from(v: CborValue) -> Result { - if let CborValue::Map(mut map) = v { + if let ciborium::Value::Map(map) = v.0 { + let mut map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); match ( - map.remove(&CborValue::Integer(1)), - map.remove(&CborValue::Integer(-1)), - map.remove(&CborValue::Integer(-2)), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); + cbor + }), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer((-1).into()).into(); + cbor + }), + map.remove(&{ + let cbor: CborValue = ciborium::Value::Integer((-2).into()).into(); + cbor + }), ) { ( - Some(CborValue::Integer(2)), - Some(CborValue::Integer(crv_id)), - Some(CborValue::Bytes(x)), - ) => { + Some(CborValue(ciborium::Value::Integer(i2))), + Some(CborValue(ciborium::Value::Integer(crv_id))), + Some(CborValue(ciborium::Value::Bytes(x))), + ) if >::into(i2) == 2 => { + let crv_id: i128 = crv_id.try_into().map_err(|_| Error::UnsupportedCurve)?; let crv = crv_id.try_into()?; let y = map - .remove(&CborValue::Integer(-3)) + .remove(&{ + let cbor: CborValue = ciborium::Value::Integer((-3).into()).into(); cbor + }) .ok_or(Error::EC2MissingY)? .try_into()?; Ok(Self::EC2 { crv, x, y }) } ( - Some(CborValue::Integer(1)), - Some(CborValue::Integer(crv_id)), - Some(CborValue::Bytes(x)), - ) => { + Some(CborValue(ciborium::Value::Integer(i1))), + Some(CborValue(ciborium::Value::Integer(crv_id))), + Some(CborValue(ciborium::Value::Bytes(x))), + ) if >::into(i1) == 1 => { + let crv_id: i128 = crv_id.try_into().map_err(|_| Error::UnsupportedCurve)?; let crv = crv_id.try_into()?; Ok(Self::OKP { crv, x }) } @@ -230,8 +254,8 @@ impl TryFrom for EncodedPoint { impl From for CborValue { fn from(y: EC2Y) -> CborValue { match y { - EC2Y::Value(s) => CborValue::Bytes(s), - EC2Y::SignBit(b) => CborValue::Bool(b), + EC2Y::Value(s) => ciborium::Value::Bytes(s).into(), + EC2Y::SignBit(b) => ciborium::Value::Bool(b).into(), } } } @@ -240,9 +264,9 @@ impl TryFrom for EC2Y { type Error = Error; fn try_from(v: CborValue) -> Result { - match v { - CborValue::Bytes(s) => Ok(EC2Y::Value(s)), - CborValue::Bool(b) => Ok(EC2Y::SignBit(b)), + match v.0 { + ciborium::Value::Bytes(s) => Ok(EC2Y::Value(s)), + ciborium::Value::Bool(b) => Ok(EC2Y::SignBit(b)), _ => Err(Error::InvalidTypeY(v)), } } @@ -251,10 +275,10 @@ impl TryFrom for EC2Y { impl From for CborValue { fn from(crv: EC2Curve) -> CborValue { match crv { - EC2Curve::P256 => CborValue::Integer(1), - EC2Curve::P384 => CborValue::Integer(2), - EC2Curve::P521 => CborValue::Integer(3), - EC2Curve::P256K => CborValue::Integer(8), + EC2Curve::P256 => ciborium::Value::Integer(1.into()).into(), + EC2Curve::P384 => ciborium::Value::Integer(2.into()).into(), + EC2Curve::P521 => ciborium::Value::Integer(3.into()).into(), + EC2Curve::P256K => ciborium::Value::Integer(8.into()).into(), } } } @@ -276,10 +300,10 @@ impl TryFrom for EC2Curve { impl From for CborValue { fn from(crv: OKPCurve) -> CborValue { match crv { - OKPCurve::X25519 => CborValue::Integer(4), - OKPCurve::X448 => CborValue::Integer(5), - OKPCurve::Ed25519 => CborValue::Integer(6), - OKPCurve::Ed448 => CborValue::Integer(7), + OKPCurve::X25519 => ciborium::Value::Integer(4.into()).into(), + OKPCurve::X448 => ciborium::Value::Integer(5.into()).into(), + OKPCurve::Ed25519 => ciborium::Value::Integer(6.into()).into(), + OKPCurve::Ed448 => ciborium::Value::Integer(7.into()).into(), } } } @@ -422,19 +446,20 @@ impl TryFrom<&ssi_jwk::OctetParams> for OKPCurve { mod test { use super::*; use hex::FromHex; + use crate::cbor; static EC_P256: &str = include_str!("../../../test/definitions/cose_key/ec_p256.cbor"); #[test] fn ec_p256() { let key_bytes = >::from_hex(EC_P256).expect("unable to convert cbor hex to bytes"); - let key = serde_cbor::from_slice(&key_bytes).unwrap(); + let key = crate::cbor::from_slice(&key_bytes).unwrap(); match &key { CoseKey::EC2 { crv, .. } => assert_eq!(crv, &EC2Curve::P256), _ => panic!("expected an EC2 cose key"), }; assert_eq!( - serde_cbor::to_vec(&key).unwrap(), + cbor::to_vec(&key).unwrap(), key_bytes, "cbor encoding roundtrip failed" ); diff --git a/src/definitions/device_key/mod.rs b/src/definitions/device_key/mod.rs index af08bca0..e3a997ac 100644 --- a/src/definitions/device_key/mod.rs +++ b/src/definitions/device_key/mod.rs @@ -43,7 +43,7 @@ //! ``` use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec}; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::Value as CborValue; use std::collections::BTreeMap; pub mod cose_key; diff --git a/src/definitions/device_request.rs b/src/definitions/device_request.rs index de4d313a..099927b5 100644 --- a/src/definitions/device_request.rs +++ b/src/definitions/device_request.rs @@ -50,7 +50,7 @@ pub struct ItemsRequest { /// Additional information for the request. #[serde(skip_serializing_if = "Option::is_none")] - pub request_info: Option>, + pub request_info: Option>, } impl DeviceRequest { @@ -65,8 +65,8 @@ mod test { fn items_request() { const HEX: &str = "D8185868A267646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6A6E616D65537061636573A1716F72672E69736F2E31383031332E352E31A36B66616D696C795F6E616D65F46A676976656E5F6E616D65F46F646F63756D656E745F6E756D626572F4"; let bytes: Vec = hex::decode(HEX).unwrap(); - let req: Tag24 = serde_cbor::from_slice(&bytes).unwrap(); - let roundtripped = serde_cbor::to_vec(&req).unwrap(); + let req: Tag24 = crate::cbor::from_slice(&bytes).unwrap(); + let roundtripped = crate::cbor::to_vec(&req).unwrap(); assert_eq!(bytes, roundtripped); } @@ -74,8 +74,8 @@ mod test { fn doc_request() { const HEX: &str = "A16C6974656D7352657175657374D8185868A267646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6A6E616D65537061636573A1716F72672E69736F2E31383031332E352E31A36B66616D696C795F6E616D65F46A676976656E5F6E616D65F46F646F63756D656E745F6E756D626572F4"; let bytes: Vec = hex::decode(HEX).unwrap(); - let req: DocRequest = serde_cbor::from_slice(&bytes).unwrap(); - let roundtripped = serde_cbor::to_vec(&req).unwrap(); + let req: DocRequest = crate::cbor::from_slice(&bytes).unwrap(); + let roundtripped = crate::cbor::to_vec(&req).unwrap(); assert_eq!(bytes, roundtripped); } @@ -83,8 +83,8 @@ mod test { fn device_request() { const HEX: &str = "A26776657273696F6E63312E306B646F63526571756573747381A16C6974656D7352657175657374D8185868A267646F6354797065756F72672E69736F2E31383031332E352E312E6D444C6A6E616D65537061636573A1716F72672E69736F2E31383031332E352E31A36B66616D696C795F6E616D65F46A676976656E5F6E616D65F46F646F63756D656E745F6E756D626572F4"; let bytes: Vec = hex::decode(HEX).unwrap(); - let req: DeviceRequest = serde_cbor::from_slice(&bytes).unwrap(); - let roundtripped = serde_cbor::to_vec(&req).unwrap(); + let req: DeviceRequest = crate::cbor::from_slice(&bytes).unwrap(); + let roundtripped = crate::cbor::to_vec(&req).unwrap(); assert_eq!(bytes, roundtripped); } } diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index 9c1091d2..5dfc66c9 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -1,10 +1,12 @@ //! This module contains the definition of the `DeviceResponse` struct and related types. +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + use crate::definitions::{ - helpers::{NonEmptyMap, NonEmptyVec}, - DeviceSigned, IssuerSigned, + DeviceSigned, + helpers::{NonEmptyMap, NonEmptyVec}, IssuerSigned, }; -use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; /// Represents a device response. #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 01e92502..fc27ac58 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -9,10 +9,10 @@ use crate::definitions::{ session::SessionTranscript, }; use serde::{Deserialize, Serialize}; -use serde_cbor::{Error as CborError, Value as CborValue}; use std::collections::BTreeMap; -use crate::cose::mac0::CoseMac0; use crate::cose::sign1::CoseSign1; +use crate::cbor::Value as CborValue; +use crate::cose::mac0::CoseMac0; /// Represents a device-signed structure. #[derive(Clone, Debug, Deserialize, Serialize)] @@ -74,5 +74,5 @@ impl DeviceAuthentication { #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Unable to encode value as CBOR: {0}")] - UnableToEncode(CborError), + UnableToEncode(coset::CoseError), } diff --git a/src/definitions/helpers/bytestr.rs b/src/definitions/helpers/bytestr.rs index 4308b965..270e428f 100644 --- a/src/definitions/helpers/bytestr.rs +++ b/src/definitions/helpers/bytestr.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::Value as CborValue; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(try_from = "CborValue", into = "CborValue")] @@ -33,7 +33,7 @@ impl AsRef<[u8]> for ByteStr { impl From for CborValue { fn from(ByteStr(bytes): ByteStr) -> CborValue { - CborValue::Bytes(bytes) + ciborium::Value::Bytes(bytes).into() } } @@ -41,7 +41,7 @@ impl TryFrom for ByteStr { type Error = Error; fn try_from(v: CborValue) -> Result { - if let CborValue::Bytes(bytes) = v { + if let ciborium::Value::Bytes(bytes) = v.0 { Ok(ByteStr(bytes)) } else { Err(Error::NotAByteString(v)) diff --git a/src/definitions/helpers/tag24.rs b/src/definitions/helpers/tag24.rs index 25962ba4..83ffd773 100644 --- a/src/definitions/helpers/tag24.rs +++ b/src/definitions/helpers/tag24.rs @@ -5,7 +5,7 @@ use serde::{ de::{self, Error as DeError}, ser, Deserialize, Serialize, }; -use serde_cbor::{from_slice, to_vec, Error as CborError, Value as CborValue}; +use crate::cbor::{CborError, from_slice, to_vec, Value as CborValue}; /// A wrapper for a struct that is to be encoded as a CBOR tagged item, with tag number 24. /// @@ -22,9 +22,9 @@ type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Expected a CBOR byte string, received: '{0:?}'")] - InvalidTag24(Box), + InvalidTag24(Box), #[error("Expected a CBOR tagged data item with tag number 24, received: '{0:?}'")] - NotATag24(CborValue), + NotATag24(ciborium::Value), #[error("Unable to encode value as CBOR: {0}")] UnableToEncode(CborError), #[error("Unable to decode bytes to inner type: {0}")] @@ -55,25 +55,25 @@ impl TryFrom for Tag24 { type Error = Error; fn try_from(v: CborValue) -> Result> { - match v { - CborValue::Tag(24, inner_value) => match inner_value.as_ref() { - CborValue::Bytes(inner_bytes) => { + match v.0 { + ciborium::Value::Tag(24, inner_value) => match inner_value.as_ref() { + ciborium::Value::Bytes(inner_bytes) => { let inner: T = from_slice(inner_bytes).map_err(Error::UnableToDecode)?; Ok(Tag24 { inner, inner_bytes: inner_bytes.to_vec(), }) } - _ => Err(Error::InvalidTag24(inner_value)), + _ => Err(Error::InvalidTag24(inner_value.into())), }, - _ => Err(Error::NotATag24(v)), + _ => Err(Error::NotATag24(v.0)), } } } impl From> for CborValue { fn from(Tag24 { inner_bytes, .. }: Tag24) -> CborValue { - CborValue::Tag(24, Box::new(CborValue::Bytes(inner_bytes))) + ciborium::Value::Tag(24, Box::new(ciborium::Value::Bytes(inner_bytes))).into() } } @@ -85,7 +85,7 @@ impl AsRef for Tag24 { impl Serialize for Tag24 { fn serialize(&self, s: S) -> Result { - CborValue::Tag(24, Box::new(CborValue::Bytes(self.inner_bytes.clone()))).serialize(s) + ciborium::Value::Tag(24, Box::new(ciborium::Value::Bytes(self.inner_bytes.clone()))).serialize(s) } } @@ -94,8 +94,8 @@ impl<'de, T: de::DeserializeOwned> Deserialize<'de> for Tag24 { where D: de::Deserializer<'de>, { - CborValue::deserialize(d)? - .try_into() + let cbor: CborValue = ciborium::Value::deserialize(d)?.into(); + cbor.try_into() .map_err(D::Error::custom) } } @@ -106,7 +106,7 @@ mod test { #[test] #[should_panic] - // A Tag24 cannot be serialised directly into a non-cbor format as it will lose the tag. + // A Tag24 cannot be serialized directly into a non-cbor format as it will lose the tag. fn non_cbor_roundtrip() { let original = Tag24::new(String::from("some data")).unwrap(); let json = serde_json::to_vec(&original).unwrap(); diff --git a/src/definitions/issuer_signed.rs b/src/definitions/issuer_signed.rs index ae4bd2b8..70d3121a 100644 --- a/src/definitions/issuer_signed.rs +++ b/src/definitions/issuer_signed.rs @@ -14,7 +14,7 @@ use crate::definitions::{ DigestId, }; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::Value as CborValue; use crate::cose::sign1::CoseSign1; /// Represents an issuer-signed object. @@ -55,6 +55,7 @@ pub struct IssuerSignedItem { mod test { use super::IssuerSigned; use hex::FromHex; + use crate::cbor; static ISSUER_SIGNED_CBOR: &str = include_str!("../../test/definitions/issuer_signed.cbor"); @@ -63,9 +64,9 @@ mod test { let cbor_bytes = >::from_hex(ISSUER_SIGNED_CBOR).expect("unable to convert cbor hex to bytes"); let signed: IssuerSigned = - serde_cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as an IssuerSigned"); + cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as an IssuerSigned"); let roundtripped_bytes = - serde_cbor::to_vec(&signed).expect("unable to encode IssuerSigned as cbor bytes"); + cbor::to_vec(&signed).expect("unable to encode IssuerSigned as cbor bytes"); assert_eq!( cbor_bytes, roundtripped_bytes, "original cbor and re-serialized IssuerSigned do not match" diff --git a/src/definitions/mso.rs b/src/definitions/mso.rs index e32f1a5f..3abb4890 100644 --- a/src/definitions/mso.rs +++ b/src/definitions/mso.rs @@ -85,6 +85,7 @@ impl DigestId { mod test { use crate::definitions::{helpers::Tag24, IssuerSigned, Mso}; use hex::FromHex; + use crate::cbor; static ISSUER_SIGNED_CBOR: &str = include_str!("../../test/definitions/issuer_signed.cbor"); @@ -93,15 +94,15 @@ mod test { let cbor_bytes = >::from_hex(ISSUER_SIGNED_CBOR).expect("unable to convert cbor hex to bytes"); let signed: IssuerSigned = - serde_cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as an IssuerSigned"); + cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as an IssuerSigned"); let mso_bytes = &signed .issuer_auth .inner.payload .expect("expected a COSE_Sign1 with attached payload, found detached payload"); let mso: Tag24 = - serde_cbor::from_slice(mso_bytes).expect("unable to parse payload as Mso"); + cbor::from_slice(mso_bytes).expect("unable to parse payload as Mso"); let roundtripped_bytes = - serde_cbor::to_vec(&mso).expect("unable to encode Mso as cbor bytes"); + cbor::to_vec(&mso).expect("unable to encode Mso as cbor bytes"); assert_eq!( mso_bytes, &roundtripped_bytes, "original cbor and re-serialized Mso do not match" diff --git a/src/definitions/namespaces/fulldate.rs b/src/definitions/namespaces/fulldate.rs index 49483fad..dffe6353 100644 --- a/src/definitions/namespaces/fulldate.rs +++ b/src/definitions/namespaces/fulldate.rs @@ -1,5 +1,5 @@ use anyhow::Error; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::{fmt, str::FromStr}; use time::{format_description::FormatItem, macros::format_description, Date}; @@ -14,7 +14,7 @@ pub struct FullDate(Date); impl From for Cbor { fn from(d: FullDate) -> Cbor { - Cbor::Tag(1004, Box::new(Cbor::Text(d.to_string()))) + ciborium::Value::Tag(1004, Box::new(ciborium::Value::Text(d.to_string()))).into() } } diff --git a/src/definitions/namespaces/latin1.rs b/src/definitions/namespaces/latin1.rs index a4b40400..be0e1e30 100644 --- a/src/definitions/namespaces/latin1.rs +++ b/src/definitions/namespaces/latin1.rs @@ -1,4 +1,4 @@ -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::{ops::Deref, str::FromStr}; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs index 3bcaac27..d12ab482 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::{Map, Value as Json}; use std::{collections::BTreeMap, ops::Deref}; @@ -50,7 +50,7 @@ impl ToNamespaceMap for AgeOver { fn to_ns_map(self) -> BTreeMap { self.0 .into_iter() - .map(|(Age(x, y), v)| (format!("age_over_{x}{y}"), v.into())) + .map(|(Age(x, y), v)| (format!("age_over_{x}{y}"), ciborium::Value::Bool(v).into())) .collect() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs index fbee70ab..e7a02c19 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; @@ -265,7 +265,8 @@ pub enum Error { impl From for Cbor { fn from(a: Alpha2) -> Cbor { - a.as_str().to_string().into() + let cbor: ciborium::Value = a.as_str().to_string().into(); + cbor.into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs b/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs index 8b7fe1e7..4022392b 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs @@ -2,7 +2,7 @@ use crate::definitions::{ helpers::ByteStr, traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}, }; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::{Map, Value as Json}; use std::collections::BTreeMap; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index 9b1beb58..6225ed39 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -6,7 +6,7 @@ use crate::{ }, macros::{FromJson, ToCbor}, }; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; /// `driving_privileges` in the org.iso.18013.5.1 namespace. @@ -16,7 +16,7 @@ pub struct DrivingPrivileges(Vec); impl From for Cbor { fn from(d: DrivingPrivileges) -> Cbor { - Cbor::Array(d.0.into_iter().map(ToCbor::to_cbor).collect()) + ciborium::Value::Array(d.0.into_iter().map(|v|v.to_cbor().into()).collect()).into() } } @@ -57,7 +57,7 @@ pub enum VehicleCategoryCode { impl From for Cbor { fn from(c: VehicleCategoryCode) -> Cbor { - Cbor::Text(c.as_ref().to_string()) + ciborium::Value::Text(c.as_ref().to_string()).into() } } @@ -85,7 +85,7 @@ pub struct Codes(NonEmptyVec); impl From for Cbor { fn from(c: Codes) -> Cbor { - Cbor::Array(c.0.into_inner().into_iter().map(ToCbor::to_cbor).collect()) + ciborium::Value::Array(c.0.into_inner().into_iter().map(|v| v.to_cbor().into()).collect()).into() } } @@ -110,7 +110,7 @@ mod tests { assert_eq!(c, VehicleCategoryCode::A); let v = c.to_cbor(); - assert_eq!(v, serde_cbor::Value::Text("A".to_string())); + assert_eq!(v.0, ciborium::Value::Text("A".to_string())); let j = serde_json::json!("A"); let c = VehicleCategoryCode::from_json(&j).unwrap(); diff --git a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs index d55a45c1..ee674da6 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; @@ -43,7 +43,7 @@ impl EyeColour { impl From for Cbor { fn from(h: EyeColour) -> Cbor { - Cbor::Text(h.to_str().to_string()) + ciborium::Value::Text(h.to_str().to_string()).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs index 3aa12c0d..6e20e459 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; @@ -43,7 +43,7 @@ impl HairColour { impl From for Cbor { fn from(h: HairColour) -> Cbor { - Cbor::Text(h.to_str().to_string()) + ciborium::Value::Text(h.to_str().to_string()).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs b/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs index 4e5875cd..becad900 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs @@ -2,7 +2,7 @@ use crate::definitions::{ namespaces::org_iso_18013_5_1::Alpha2, traits::{FromJson, FromJsonError, FromJsonMap}, }; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::{Map, Value as Json}; /// `issuing_jurisdiction` in the org.iso.18013.5.1 namespace. diff --git a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs index 29b8faf5..0a565fcb 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// `sex` in the org.iso.18013.5.1 namespace. diff --git a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs index 0f666a0d..45815050 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs @@ -2,7 +2,7 @@ pub use super::FullDate; use crate::definitions::traits::{FromJson, FromJsonError}; use anyhow::anyhow; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset}; @@ -54,7 +54,7 @@ impl FromJson for TDateOrFullDate { impl From for Cbor { fn from(t: TDate) -> Cbor { - Cbor::Tag(0, Box::new(t.0.into())) + ciborium::Value::Tag(0, Box::new(t.0.into())).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs b/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs index 214124b1..f2c47e31 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// United Nations Distinguishing Sign, as per ISO/IEC 18013-1:2018 Annex F. diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs index de582864..a05c3477 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// `county_code` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs index ff6fa7a1..2d3a7703 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs index 0ea100fd..634e8d58 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs @@ -3,7 +3,7 @@ use crate::{ definitions::{helpers::NonEmptyVec, traits::ToCbor}, macros::{FromJson, ToCbor}, }; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; /// `domestic_driving_privileges` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation /// Guidelines (Version 1.0). @@ -13,7 +13,7 @@ pub struct DomesticDrivingPrivileges(Vec); impl ToCbor for DomesticDrivingPrivileges { fn to_cbor(self) -> Cbor { - Cbor::Array(self.0.into_iter().map(ToCbor::to_cbor).collect()) + ciborium::Value::Array(self.0.into_iter().map(|v| v.to_cbor().into()).collect()).into() } } @@ -40,13 +40,13 @@ pub struct DomesticVehicleRestrictions(NonEmptyVec); impl ToCbor for DomesticVehicleRestrictions { fn to_cbor(self) -> Cbor { - Cbor::Array( + ciborium::Value::Array( self.0 .into_inner() .into_iter() - .map(ToCbor::to_cbor) + .map(|v| v.to_cbor().into()) .collect(), - ) + ).into() } } @@ -63,13 +63,13 @@ pub struct DomesticVehicleEndorsements(NonEmptyVec); impl ToCbor for DomesticVehicleEndorsements { fn to_cbor(self) -> Cbor { - Cbor::Array( + ciborium::Value::Array( self.0 .into_inner() .into_iter() - .map(ToCbor::to_cbor) + .map(|v| v.to_cbor().into()) .collect(), - ) + ).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs index d1db25be..f83fb8aa 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// `EDL_indicator` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs index 9073e753..7dd84591 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs index 51cbdb9f..6d54ad7f 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs index 25cd210b..5814c503 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs @@ -1,6 +1,6 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use anyhow::anyhow; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// Indicator of presence for elements in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation @@ -19,6 +19,6 @@ impl FromJson for Present { impl ToCbor for Present { fn to_cbor(self) -> Cbor { - Cbor::Integer(1) + ciborium::Value::Integer(1.into()).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs index 9ab63b8f..7b5eba36 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs index 5bf37ed4..f8944572 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// `sex` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs index bf1e9242..594f1d56 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs @@ -1,5 +1,5 @@ use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; -use serde_cbor::Value as Cbor; +use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// `weight_range` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/session.rs b/src/definitions/session.rs index a8b51711..7e1a3d9b 100644 --- a/src/definitions/session.rs +++ b/src/definitions/session.rs @@ -199,7 +199,9 @@ pub fn derive_session_key( session_transcript: &SessionTranscriptBytes, reader: bool, ) -> Result> { - let salt = Sha256::digest(serde_cbor::to_vec(session_transcript)?); + let salt = Sha256::digest(crate::cbor::to_vec(session_transcript).map_err(|e| { + anyhow::anyhow!("failed to serialize session transcript: {e}") + })?); let hkdf = shared_secret.extract::(Some(salt.as_ref())); let mut okm = [0u8; 32]; let sk_device = "SKDevice".as_bytes(); @@ -283,6 +285,7 @@ pub fn get_initialization_vector(message_count: &mut u32, reader: bool) -> [u8; #[cfg(test)] mod test { + use crate::cbor; use super::*; use crate::definitions::device_engagement::Security; use crate::definitions::device_request::DeviceRequest; @@ -292,12 +295,12 @@ mod test { // null let cbor = hex::decode("F6").expect("failed to decode hex"); let handover: Handover = - serde_cbor::from_slice(&cbor).expect("failed to deserialize as handover"); + cbor::from_slice(&cbor).expect("failed to deserialize as handover"); if !matches!(handover, Handover::QR) { panic!("expected 'Handover::QR', received {handover:?}") } else { let roundtripped = - serde_cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); + cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); assert_eq!( cbor, roundtripped, "re-serialized handover did not match initial bytes" @@ -311,12 +314,12 @@ mod test { // [] let cbor = hex::decode("80").expect("failed to decode hex"); let handover: Handover = - serde_cbor::from_slice(&cbor).expect("failed to deserialize as handover"); + cbor::from_slice(&cbor).expect("failed to deserialize as handover"); if !matches!(handover, Handover::QR) { panic!("expected 'Handover::QR', received {handover:?}") } else { let roundtripped = - serde_cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); + cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); assert_eq!( cbor, roundtripped, "re-serialized handover did not match initial bytes" @@ -330,12 +333,12 @@ mod test { // {} let cbor = hex::decode("A0").expect("failed to decode hex"); let handover: Handover = - serde_cbor::from_slice(&cbor).expect("failed to deserialize as handover"); + cbor::from_slice(&cbor).expect("failed to deserialize as handover"); if !matches!(handover, Handover::QR) { panic!("expected 'Handover::QR', received {handover:?}") } else { let roundtripped = - serde_cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); + cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); assert_eq!( cbor, roundtripped, "re-serialized handover did not match initial bytes" @@ -348,12 +351,12 @@ mod test { // ['hello', null] let cbor = hex::decode("824568656C6C6FF6").expect("failed to decode hex"); let handover: Handover = - serde_cbor::from_slice(&cbor).expect("failed to deserialize as handover"); + cbor::from_slice(&cbor).expect("failed to deserialize as handover"); if !matches!(handover, Handover::NFC(..)) { panic!("expected 'Handover::NFC(..)', received {handover:?}") } else { let roundtripped = - serde_cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); + cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); assert_eq!( cbor, roundtripped, "re-serialized handover did not match initial bytes" @@ -366,12 +369,12 @@ mod test { // ['hello', 'world'] let cbor = hex::decode("824568656C6C6F45776F726C64").expect("failed to decode hex"); let handover: Handover = - serde_cbor::from_slice(&cbor).expect("failed to deserialize as handover"); + cbor::from_slice(&cbor).expect("failed to deserialize as handover"); if !matches!(handover, Handover::NFC(..)) { panic!("expected 'Handover::NFC(..)', received {handover:?}") } else { let roundtripped = - serde_cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); + cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); assert_eq!( cbor, roundtripped, "re-serialized handover did not match initial bytes" @@ -384,7 +387,7 @@ mod test { // ["aud", "nonce"] let cbor = hex::decode("8263617564656E6F6E6365").expect("failed to decode hex"); let handover: Handover = - serde_cbor::from_slice(&cbor).expect("failed to deserialize as handover"); + cbor::from_slice(&cbor).expect("failed to deserialize as handover"); if !matches!(handover, Handover::OID4VP(..)) { panic!( "expected '{}', received {:?}", @@ -392,7 +395,7 @@ mod test { ) } else { let roundtripped = - serde_cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); + cbor::to_vec(&handover).expect("failed to serialize handover as cbor"); assert_eq!( cbor, roundtripped, "re-serialized handover did not match initial bytes" @@ -477,7 +480,7 @@ mod test { let session_establishment_bytes = hex::decode(SESSION_ESTABLISHMENT).unwrap(); let session_establishment: SessionEstablishment = - serde_cbor::from_slice(&session_establishment_bytes).unwrap(); + cbor::from_slice(&session_establishment_bytes).unwrap(); let e_reader_key = session_establishment.e_reader_key; let encrypted_request = session_establishment.data; @@ -489,7 +492,7 @@ mod test { let session_transcript_bytes = hex::decode(SESSION_TRANSCRIPT).unwrap(); let session_transcript: SessionTranscriptBytes = - serde_cbor::from_slice(&session_transcript_bytes).unwrap(); + cbor::from_slice(&session_transcript_bytes).unwrap(); let session_key = derive_session_key(&shared_secret, &session_transcript, true).unwrap(); let session_key_hex = hex::encode(session_key); @@ -497,6 +500,6 @@ mod test { let plaintext = decrypt_reader_data(&session_key, encrypted_request.as_ref(), &mut 0).unwrap(); - let _device_request: DeviceRequest = serde_cbor::from_slice(&plaintext).unwrap(); + let _device_request: DeviceRequest = crate::cbor::from_slice(&plaintext).unwrap(); } } diff --git a/src/definitions/traits/to_cbor.rs b/src/definitions/traits/to_cbor.rs index 33399f74..afd41892 100644 --- a/src/definitions/traits/to_cbor.rs +++ b/src/definitions/traits/to_cbor.rs @@ -1,15 +1,16 @@ //! ToCbor is specifically NOT implemented for `Vec` where `T: ToCbor`, as `Vec` likely should be //! represented as a `bytestr` instead of an `array` in `cbor`. -use serde_cbor::Value; +use crate::cbor::{CborError, Value}; use std::collections::BTreeMap; +use crate::cbor; pub type Bytes = Vec; pub trait ToCbor: Sized { fn to_cbor(self) -> Value; fn to_cbor_bytes(self) -> Result { - serde_cbor::to_vec(&self.to_cbor()).map_err(Into::into) + cbor::to_vec(&self.to_cbor().0).map_err(|err| CborError::from(err)).map_err(Into::into) } } @@ -23,8 +24,8 @@ pub trait ToNamespaceMap { #[derive(Debug, thiserror::Error)] pub enum ToCborError { - #[error("cbor serialization: {0}")] - Serde(#[from] serde_cbor::Error), + #[error("cbor error: {0}")] + CoseError(#[from] CborError), } impl ToCbor for T @@ -35,3 +36,10 @@ where self.into() } } + +impl ToCbor for Option { + fn to_cbor(self) -> Value { + self.map(|s| ciborium::Value::Text(s).into()) + .unwrap_or_else(|| ciborium::Value::Null.into()) + } +} \ No newline at end of file diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index e13f8c09..f51798b0 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -31,7 +31,7 @@ use serde::{ ser::{Error as SerError, Serializer}, Deserialize, Serialize, }; -use serde_cbor::Value as CborValue; +use crate::cbor::Value as CborValue; use std::collections::BTreeMap; use time::{ error::Format as FormatError, error::Parse as ParseError, @@ -52,13 +52,13 @@ type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("When parsing a CBOR map, could not find required field: '{0:?}'")] - MissingField(CborValue), + MissingField(ciborium::Value), #[error("Expected to parse a CBOR map, received: '{0:?}'")] - NotAMap(CborValue), + NotAMap(ciborium::Value), #[error("Expected to parse a CBOR text string, received: '{0:?}'")] - NotATextString(Box), + NotATextString(Box), #[error("Expected to parse a CBOR tag (number {0}), received: '{1:?}'")] - NotATag(u64, CborValue), + NotATag(u64, ciborium::Value), #[error(transparent)] OutOfRange(#[from] time::error::ComponentRange), #[error("Failed to format date string as rfc3339 date: {0}")] @@ -73,17 +73,17 @@ impl TryFrom for CborValue { fn try_from(v: ValidityInfo) -> Result { macro_rules! insert_date { ($map:ident, $date:ident, $name:literal) => { - let key = CborValue::Text(String::from($name)); - let value = CborValue::Tag( + let key = ciborium::Value::Text(String::from($name)); + let value = ciborium::Value::Tag( 0, - Box::new(CborValue::Text( + Box::new(ciborium::Value::Text( $date .replace_millisecond(0)? .to_offset(UtcOffset::UTC) .format(&Rfc3339)?, )), ); - $map.insert(key, value); + $map.push((key, value)); }; ($map:ident, $struct: ident, $field:ident, $name:literal) => { let date = $struct.$field; @@ -91,7 +91,7 @@ impl TryFrom for CborValue { }; } - let mut map = BTreeMap::new(); + let mut map = vec![]; insert_date!(map, v, signed, "signed"); insert_date!(map, v, valid_from, "validFrom"); @@ -101,7 +101,7 @@ impl TryFrom for CborValue { insert_date!(map, expected_update, "expectedUpdate"); } - Ok(CborValue::Map(map)) + Ok(ciborium::Value::Map(map).into()) } } @@ -109,12 +109,13 @@ impl TryFrom for ValidityInfo { type Error = Error; fn try_from(v: CborValue) -> Result { - if let CborValue::Map(mut map) = v { + if let ciborium::Value::Map(map) = v.0 { + let mut map = map.into_iter().map(|(k, v)| (k.into(), v.into())).collect::>(); macro_rules! extract_date { ($map:ident, $name:literal) => {{ - let key = CborValue::Text(String::from($name)); + let key = CborValue(ciborium::Value::Text(String::from($name))); $map.remove(&key) - .ok_or(Error::MissingField(key)) + .ok_or(Error::MissingField(key.into())) .and_then(cbor_to_datetime)? }}; } @@ -123,7 +124,7 @@ impl TryFrom for ValidityInfo { let valid_from = extract_date!(map, "validFrom"); let valid_until = extract_date!(map, "validUntil"); - let expected_update_key = CborValue::Text(String::from("expectedUpdate")); + let expected_update_key: CborValue = ciborium::Value::Text(String::from("expectedUpdate")).into(); let expected_update = map .remove(&expected_update_key) .map(cbor_to_datetime) @@ -136,7 +137,7 @@ impl TryFrom for ValidityInfo { expected_update, }) } else { - Err(Error::NotAMap(v)) + Err(Error::NotAMap(v.into())) } } } @@ -153,14 +154,14 @@ impl Serialize for ValidityInfo { } fn cbor_to_datetime(v: CborValue) -> Result { - if let CborValue::Tag(0, inner) = v { - if let CborValue::Text(date_str) = inner.as_ref() { + if let ciborium::Value::Tag(0, inner) = v.0 { + if let ciborium::Value::Text(date_str) = inner.as_ref() { Ok(OffsetDateTime::parse(date_str, &Rfc3339)?) } else { Err(Error::NotATextString(inner)) } } else { - Err(Error::NotATag(0, v)) + Err(Error::NotATag(0, v.into())) } } @@ -171,8 +172,8 @@ mod test { #[test] fn roundtrip() { let cbor = hex::decode("A3667369676E6564C074323032302D30312D30315430303A30303A30305A6976616C696446726F6DC074323032302D30312D30315430303A30303A30305A6A76616C6964556E74696CC074323032302D30312D30315430303A30303A30305A").unwrap(); - let validity_info: ValidityInfo = serde_cbor::from_slice(&cbor).unwrap(); - let roundtripped = serde_cbor::to_vec(&validity_info).unwrap(); + let validity_info: ValidityInfo = crate::cbor::from_slice(&cbor).unwrap(); + let roundtripped = crate::cbor::to_vec(&validity_info).unwrap(); assert_eq!(cbor, roundtripped); } @@ -180,8 +181,8 @@ mod test { #[test] fn trim() { let cbor = hex::decode("A3667369676E6564C07818323032302D30312D30315430303A30303A30302E3130315A6976616C696446726F6DC0781B323032302D30312D30315430303A30303A30302E3131323231395A6A76616C6964556E74696CC0781E323032302D30312D30315430303A30303A30302E3939393939393939395A").unwrap(); - let validity_info: ValidityInfo = serde_cbor::from_slice(&cbor).unwrap(); - let roundtripped = serde_cbor::to_vec(&validity_info).unwrap(); + let validity_info: ValidityInfo = crate::cbor::from_slice(&cbor).unwrap(); + let roundtripped = crate::cbor::to_vec(&validity_info).unwrap(); let trimmed = hex::decode("A3667369676E6564C074323032302D30312D30315430303A30303A30305A6976616C696446726F6DC074323032302D30312D30315430303A30303A30305A6A76616C6964556E74696CC074323032302D30312D30315430303A30303A30305A").unwrap(); assert_eq!(trimmed, roundtripped); } diff --git a/src/issuance/mdoc.rs b/src/issuance/mdoc.rs index bd15ab2e..6f461c80 100644 --- a/src/issuance/mdoc.rs +++ b/src/issuance/mdoc.rs @@ -1,21 +1,23 @@ -use crate::{ - definitions::{ - helpers::{NonEmptyMap, NonEmptyVec, Tag24}, - issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}, - DeviceKeyInfo, DigestAlgorithm, DigestId, DigestIds, IssuerSignedItem, Mso, ValidityInfo, - }, - issuance::x5chain::{X5Chain, X5CHAIN_HEADER_LABEL}, -}; +use std::collections::{BTreeMap, HashSet}; + use anyhow::{anyhow, Result}; use async_signature::AsyncSigner; +use coset::iana::Algorithm; +use coset::Label; use rand::Rng; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; use sha2::{Digest, Sha256, Sha384, Sha512}; use signature::{SignatureEncoding, Signer}; -use std::collections::{BTreeMap, HashSet}; -use coset::iana::Algorithm; -use coset::Label; + +use crate::{ + definitions::{ + DeviceKeyInfo, + DigestAlgorithm, + DigestId, DigestIds, helpers::{NonEmptyMap, NonEmptyVec, Tag24}, issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}, IssuerSignedItem, Mso, ValidityInfo, + }, + issuance::x5chain::{X5Chain, X5CHAIN_HEADER_LABEL}, +}; +use crate::cbor::Value as CborValue; use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; use crate::cose::SignatureAlgorithm; @@ -82,7 +84,7 @@ impl Mdoc { validity_info, }; - let mso_bytes = serde_cbor::to_vec(&Tag24::new(&mso)?)?; + let mso_bytes = crate::cbor::to_vec(&Tag24::new(&mso)?)?; let protected = coset::HeaderBuilder::new() .algorithm(signature_algorithm) @@ -427,7 +429,7 @@ fn digest_namespace( elements .iter() - .map(|item| Ok((item.as_ref().digest_id, serde_cbor::to_vec(item)?))) + .map(|item| Ok((item.as_ref().digest_id, crate::cbor::to_vec(item)?))) .chain(random_digests) .map(|result| { let (digest_id, bytes) = result?; @@ -454,19 +456,20 @@ fn generate_digest_id(used_ids: &mut HashSet) -> DigestId { #[cfg(test)] pub mod test { - use super::*; - use crate::definitions::device_key::cose_key::{CoseKey, EC2Curve, EC2Y}; - use crate::definitions::namespaces::{ - org_iso_18013_5_1::OrgIso1801351, org_iso_18013_5_1_aamva::OrgIso1801351Aamva, - }; - - use crate::definitions::traits::{FromJson, ToNamespaceMap}; use elliptic_curve::sec1::ToEncodedPoint; use p256::ecdsa::{Signature, SigningKey}; use p256::pkcs8::DecodePrivateKey; use p256::SecretKey; use time::OffsetDateTime; + use crate::definitions::device_key::cose_key::{CoseKey, EC2Curve, EC2Y}; + use crate::definitions::namespaces::{ + org_iso_18013_5_1::OrgIso1801351, org_iso_18013_5_1_aamva::OrgIso1801351Aamva, + }; + use crate::definitions::traits::{FromJson, ToNamespaceMap}; + + use super::*; + static ISSUER_CERT: &[u8] = include_bytes!("../../test/issuance/issuer-cert.pem"); static ISSUER_KEY: &str = include_str!("../../test/issuance/issuer-key.pem"); diff --git a/src/lib.rs b/src/lib.rs index 4cbf9a9e..ebc5f8df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -235,6 +235,7 @@ pub mod cose; pub mod definitions; pub mod issuance; pub mod presentation; +pub mod cbor; pub mod macros { pub use isomdl_macros::{FromJson, ToCbor}; diff --git a/src/presentation/device.rs b/src/presentation/device.rs index a8478f80..bd5486c5 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -17,27 +17,24 @@ //! You can view examples in `tests` directory in `simulated_device_and_reader.rs`, for a basic example and //! `simulated_device_and_reader_state.rs` which uses `State` pattern, `Arc` and `Mutex`. use crate::definitions::IssuerSignedItem; -use crate::{ - definitions::{ - device_engagement::{DeviceRetrievalMethod, Security, ServerRetrievalMethods}, - device_request::{DeviceRequest, DocRequest, ItemsRequest}, - device_response::{ - Document as DeviceResponseDoc, DocumentError, DocumentErrorCode, DocumentErrors, - Errors as NamespaceErrors, Status, - }, - device_signed::{DeviceAuth, DeviceAuthentication, DeviceNamespacesBytes, DeviceSigned}, - helpers::{tag24, NonEmptyMap, NonEmptyVec, Tag24}, - issuer_signed::{IssuerSigned, IssuerSignedItemBytes}, - session::{ - self, derive_session_key, get_shared_secret, Handover, SessionData, SessionTranscript, - }, - CoseKey, DeviceEngagement, DeviceResponse, Mso, SessionEstablishment, +use crate::{cbor, definitions::{ + device_engagement::{DeviceRetrievalMethod, Security, ServerRetrievalMethods}, + device_request::{DeviceRequest, DocRequest, ItemsRequest}, + device_response::{ + Document as DeviceResponseDoc, DocumentError, DocumentErrorCode, DocumentErrors, + Errors as NamespaceErrors, Status, }, - issuance::Mdoc, -}; + device_signed::{DeviceAuth, DeviceAuthentication, DeviceNamespacesBytes, DeviceSigned}, + helpers::{tag24, NonEmptyMap, NonEmptyVec, Tag24}, + issuer_signed::{IssuerSigned, IssuerSignedItemBytes}, + session::{ + self, derive_session_key, get_shared_secret, Handover, SessionData, SessionTranscript, + }, + CoseKey, DeviceEngagement, DeviceResponse, Mso, SessionEstablishment, +}, issuance::Mdoc}; use p256::FieldBytes; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::{CborError, Value as CborValue}; use session::SessionTranscript180135; use std::collections::BTreeMap; use std::num::ParseIntError; @@ -130,7 +127,7 @@ pub enum Error { SharedSecretGeneration(anyhow::Error), /// Error encoding value to CBOR. #[error("error encoding value to CBOR: {0}")] - CborEncoding(serde_cbor::Error), + CborEncoding(CborError), /// Session manager was used incorrectly. #[error("session manager was used incorrectly")] ApiMisuse, @@ -312,12 +309,12 @@ impl SessionManagerEngaged { impl SessionManager { fn parse_request(&self, request: &[u8]) -> Result { - let request: CborValue = serde_cbor::from_slice(request).map_err(|_| { + let request: CborValue = cbor::from_slice(request).map_err(|_| { // tracing::error!("unable to decode DeviceRequest bytes as cbor: {}", error); PreparedDeviceResponse::empty(Status::CborDecodingError) })?; - serde_cbor::value::from_value(request).map_err(|_| { + cbor::from_value2(request).map_err(|_| { // tracing::error!("unable to validate DeviceRequest cbor: {}", error); PreparedDeviceResponse::empty(Status::CborValidationError) }) @@ -398,7 +395,7 @@ impl SessionManager { /// /// It returns the requested items by the reader. pub fn handle_request(&mut self, request: &[u8]) -> anyhow::Result { - let session_data: SessionData = serde_cbor::from_slice(request)?; + let session_data: SessionData = cbor::from_slice(request).map_err(CborError::from)?; self.handle_decoded_request(session_data) } @@ -443,7 +440,7 @@ impl SessionManager { if p.is_complete() { let response = p.finalize_response(); let mut status: Option = None; - let response_bytes = serde_cbor::to_vec(&response)?; + let response_bytes = crate::cbor::to_vec(&response)?; let encrypted_response = session::encrypt_device_data( &self.sk_device.into(), &response_bytes, @@ -460,7 +457,7 @@ impl SessionManager { Some(encrypted_response.into()) }; let session_data = SessionData { status, data }; - let encoded_response = serde_cbor::to_vec(&session_data)?; + let encoded_response = crate::cbor::to_vec(&session_data)?; self.state = State::ReadyToRespond(encoded_response); } else { self.state = State::Signing(p) @@ -708,7 +705,7 @@ pub trait DeviceSession { continue; } }; - let device_auth_bytes = match serde_cbor::to_vec(&device_auth) { + let device_auth_bytes = match cbor::to_vec(&device_auth) { Ok(dab) => dab, Err(_e) => { let error: DocumentError = [(doc_type, DocumentErrorCode::DataNotReturned)] @@ -897,7 +894,7 @@ pub fn nearest_age_attestation( let (true_age_over_claims, false_age_over_claims): (Vec<_>, Vec<_>) = age_over_claims_numerical? .into_iter() - .partition(|x| x.1.to_owned().into_inner().element_value == CborValue::Bool(true)); + .partition(|x| x.1.to_owned().into_inner().element_value == ciborium::Value::Bool(true).into()); let nearest_age_over = true_age_over_claims .iter() @@ -1035,21 +1032,21 @@ mod test { digest_id: DigestId::new(1), random: ByteStr::from(random.clone()), element_identifier: element_identifier1.clone(), - element_value: CborValue::Bool(true), + element_value: ciborium::Value::Bool(true).into(), }; let issuer_signed_item2 = IssuerSignedItem { digest_id: DigestId::new(2), random: ByteStr::from(random.clone()), element_identifier: element_identifier2.clone(), - element_value: CborValue::Bool(false), + element_value: ciborium::Value::Bool(false).into(), }; let issuer_signed_item3 = IssuerSignedItem { digest_id: DigestId::new(3), random: ByteStr::from(random), element_identifier: element_identifier3.clone(), - element_value: CborValue::Bool(false), + element_value: ciborium::Value::Bool(false).into(), }; let issuer_item1 = Tag24::new(issuer_signed_item1).unwrap(); diff --git a/src/presentation/mod.rs b/src/presentation/mod.rs index 9d65b430..c849e2b8 100644 --- a/src/presentation/mod.rs +++ b/src/presentation/mod.rs @@ -62,12 +62,12 @@ pub trait Stringify: Serialize + for<'a> Deserialize<'a> { /// use isomdl::presentation::device::Document; /// /// let doc_str = include_str!("../../test/stringified-mdl.txt").to_string(); - /// let doc : Document = serde_cbor::from_slice(&decode(doc_str).unwrap()).unwrap(); + /// let doc : Document = crate::cbor::from_slice(&decode(doc_str).unwrap()).unwrap(); /// let serialized = doc.stringify().unwrap(); /// assert_eq!(serialized, Document::parse(serialized.clone()).unwrap().stringify().unwrap()); /// ``` fn stringify(&self) -> Result { - let data = serde_cbor::to_vec(self)?; + let data = crate::cbor::to_vec(self)?; let encoded = encode(data); Ok(encoded) } @@ -86,13 +86,13 @@ pub trait Stringify: Serialize + for<'a> Deserialize<'a> { /// use isomdl::presentation::device::Document; /// /// let doc_str = include_str!("../../test/stringified-mdl.txt").to_string(); - /// let doc : Document = serde_cbor::from_slice(&decode(doc_str).unwrap()).unwrap(); + /// let doc : Document = crate::cbor::from_slice(&decode(doc_str).unwrap()).unwrap(); /// let serialized = doc.stringify().unwrap(); /// assert_eq!(serialized, Document::parse(serialized.clone()).unwrap().stringify().unwrap()); /// ``` fn parse(encoded: String) -> Result { let data = decode(encoded)?; - let this = serde_cbor::from_slice(&data)?; + let this = crate::cbor::from_slice(&data)?; Ok(this) } } @@ -108,7 +108,7 @@ use hkdf::Hkdf; use sha2::Sha256; fn calculate_ble_ident(e_device_key: &Tag24) -> Result<[u8; 16]> { - let e_device_key_bytes = serde_cbor::to_vec(e_device_key)?; + let e_device_key_bytes = crate::cbor::to_vec(e_device_key)?; let mut ble_ident = [0u8; 16]; Hkdf::::new(None, &e_device_key_bytes) diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index 2f1d5e76..1c061995 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -25,11 +25,12 @@ use crate::definitions::{ }; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; -use serde_cbor::Value as CborValue; +use crate::cbor::{CborError, Value as CborValue}; use serde_json::json; use serde_json::Value; use std::collections::BTreeMap; use uuid::Uuid; +use crate::cbor; /// The main state of the reader. /// @@ -84,8 +85,14 @@ pub enum Error { InvalidRequest, } -impl From for Error { - fn from(_: serde_cbor::Error) -> Self { +impl From for Error { + fn from(_: coset::CoseError) -> Self { + Error::CborDecodingError + } +} + +impl From for Error { + fn from(_: CborError) -> Self { Error::CborDecodingError } } @@ -153,7 +160,7 @@ impl SessionManager { data: request.into(), e_reader_key: e_reader_key_public, }; - let session_request = serde_cbor::to_vec(&session)?; + let session_request = cbor::to_vec(&session)?; Ok((session_manager, session_request, ble_ident)) } @@ -184,7 +191,7 @@ impl SessionManager { data: Some(request.into()), status: None, }; - serde_cbor::to_vec(&session).map_err(Into::into) + cbor::to_vec(&session).map_err(Into::into) } fn build_request(&mut self, namespaces: device_request::Namespaces) -> Result> { @@ -206,13 +213,13 @@ impl SessionManager { version: DeviceRequest::VERSION.to_string(), doc_requests: NonEmptyVec::new(doc_request), }; - let device_request_bytes = serde_cbor::to_vec(&device_request)?; + let device_request_bytes = cbor::to_vec(&device_request)?; session::encrypt_reader_data( &self.sk_reader.into(), &device_request_bytes, &mut self.reader_message_counter, ) - .map_err(|e| anyhow!("unable to encrypt request: {}", e)) + .map_err(|e| anyhow!("unable to encrypt request: {}", e)) } /// Handles a response from the device. @@ -225,7 +232,7 @@ impl SessionManager { &mut self, response: &[u8], ) -> Result>, Error> { - let session_data: SessionData = serde_cbor::from_slice(response)?; + let session_data: SessionData = crate::cbor::from_slice(response)?; let encrypted_response = match session_data.data { None => return Err(Error::HolderError), Some(r) => r, @@ -235,8 +242,8 @@ impl SessionManager { encrypted_response.as_ref(), &mut self.device_message_counter, ) - .map_err(|_e| Error::DecryptionError)?; - let response: DeviceResponse = serde_cbor::from_slice(&decrypted_response)?; + .map_err(|_e| Error::DecryptionError)?; + let response: DeviceResponse = cbor::from_slice(&decrypted_response)?; let mut core_namespace = BTreeMap::::new(); let mut aamva_namespace = BTreeMap::::new(); let mut parsed_response = BTreeMap::>::new(); @@ -288,37 +295,37 @@ impl SessionManager { } fn parse_response(value: CborValue) -> Result { - match value { - CborValue::Text(s) => Ok(Value::String(s)), - CborValue::Tag(_t, v) => { - if let CborValue::Text(d) = *v { + match value.0 { + ciborium::Value::Text(s) => Ok(Value::String(s)), + ciborium::Value::Tag(_t, v) => { + if let ciborium::Value::Text(d) = *v { Ok(Value::String(d)) } else { Err(Error::ParsingError) } } - CborValue::Array(v) => { + ciborium::Value::Array(v) => { let mut array_response = Vec::::new(); for a in v { - let r = parse_response(a)?; + let r = parse_response(a.into())?; array_response.push(r); } Ok(json!(array_response)) } - CborValue::Map(m) => { + ciborium::Value::Map(m) => { let mut map_response = serde_json::Map::::new(); for (key, value) in m { - if let CborValue::Text(k) = key { - let parsed = parse_response(value)?; + if let ciborium::Value::Text(k) = key { + let parsed = parse_response(value.into())?; map_response.insert(k, parsed); } } let json = json!(map_response); Ok(json) } - CborValue::Bytes(b) => Ok(json!(b)), - CborValue::Bool(b) => Ok(json!(b)), - CborValue::Integer(i) => Ok(json!(i)), + ciborium::Value::Bytes(b) => Ok(json!(b)), + ciborium::Value::Bool(b) => Ok(json!(b)), + ciborium::Value::Integer(i) => Ok(json!(>::into(i))), _ => Err(Error::ParsingError), } } @@ -349,7 +356,7 @@ mod test { #[test] fn nested_response_values() { - let domestic_driving_privileges = serde_cbor::from_slice(&hex::decode("81A276646F6D65737469635F76656869636C655F636C617373A46A69737375655F64617465D903EC6A323032342D30322D31346B6578706972795F64617465D903EC6A323032382D30332D3131781B646F6D65737469635F76656869636C655F636C6173735F636F64656243207822646F6D65737469635F76656869636C655F636C6173735F6465736372697074696F6E76436C6173732043204E4F4E2D434F4D4D45524349414C781D646F6D65737469635F76656869636C655F7265737472696374696F6E7381A27821646F6D65737469635F76656869636C655F7265737472696374696F6E5F636F64656230317828646F6D65737469635F76656869636C655F7265737472696374696F6E5F6465736372697074696F6E78284D555354205745415220434F5252454354495645204C454E534553205748454E2044524956494E47").unwrap()).unwrap(); + let domestic_driving_privileges = crate::cbor::from_slice(&hex::decode("81A276646F6D65737469635F76656869636C655F636C617373A46A69737375655F64617465D903EC6A323032342D30322D31346B6578706972795F64617465D903EC6A323032382D30332D3131781B646F6D65737469635F76656869636C655F636C6173735F636F64656243207822646F6D65737469635F76656869636C655F636C6173735F6465736372697074696F6E76436C6173732043204E4F4E2D434F4D4D45524349414C781D646F6D65737469635F76656869636C655F7265737472696374696F6E7381A27821646F6D65737469635F76656869636C655F7265737472696374696F6E5F636F64656230317828646F6D65737469635F76656869636C655F7265737472696374696F6E5F6465736372697074696F6E78284D555354205745415220434F5252454354495645204C454E534553205748454E2044524956494E47").unwrap()).unwrap(); let json = parse_response(domestic_driving_privileges).unwrap(); let expected = serde_json::json!( [ diff --git a/test/definitions/device_response.cbor b/test/definitions/device_response.cbor index 12b5f08b..7c2bf8cf 100644 --- a/test/definitions/device_response.cbor +++ b/test/definitions/device_response.cbor @@ -1 +1 @@ -a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d65537061636573a1716f72672e69736f2e31383031332e352e3186d8185863a4686469676573744944006672616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e971656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456616c756563446f65d818586ca4686469676573744944036672616e646f6d5820b23f627e8999c706df0c0a4ed98ad74af988af619b4bb078b89058553f44615d71656c656d656e744964656e7469666965726a69737375655f646174656c656c656d656e7456616c7565d903ec6a323031392d31302d3230d818586da4686469676573744944046672616e646f6d5820c7ffa307e5de921e67ba5878094787e8807ac8e7b5b3932d2ce80f00f3e9abaf71656c656d656e744964656e7469666965726b6578706972795f646174656c656c656d656e7456616c7565d903ec6a323032342d31302d3230d818586da4686469676573744944076672616e646f6d582026052a42e5880557a806c1459af3fb7eb505d3781566329d0b604b845b5f9e6871656c656d656e744964656e7469666965726f646f63756d656e745f6e756d6265726c656c656d656e7456616c756569313233343536373839d818590471a4686469676573744944086672616e646f6d5820d094dad764a2eb9deb5210e9d899643efbd1d069cc311d3295516ca0b024412d71656c656d656e744964656e74696669657268706f7274726169746c656c656d656e7456616c7565590412ffd8ffe000104a46494600010101009000900000ffdb004300130d0e110e0c13110f11151413171d301f1d1a1a1d3a2a2c2330453d4947443d43414c566d5d4c51685241435f82606871757b7c7b4a5c869085778f6d787b76ffdb0043011415151d191d381f1f38764f434f7676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676ffc00011080018006403012200021101031101ffc4001b00000301000301000000000000000000000005060401020307ffc400321000010303030205020309000000000000010203040005110612211331141551617122410781a1163542527391b2c1f1ffc4001501010100000000000000000000000000000001ffc4001a110101010003010000000000000000000000014111213161ffda000c03010002110311003f00a5bbde22da2329c7d692bc7d0d03f52cfb0ff75e7a7ef3e7709723a1d0dae146ddfbb3c039ce07ad2bd47a7e32dbb8dd1d52d6ef4b284f64a480067dfb51f87ffb95ff00eb9ff14d215de66af089ce44b7dbde9cb6890a2838eddf18078f7add62d411ef4db9b10a65d6b95a147381ea0d495b933275fe6bba75c114104a8ba410413e983dff004f5af5d34b4b4cde632d0bf1fd1592bdd91c6411f3934c2fa6af6b54975d106dcf4a65ae56e856001ebc03c7ce29dd9eef1ef10fc447dc9da76ad2aee93537a1ba7e4f70dd8eff0057c6dffb5e1a19854a83758e54528750946ec6704850cd037bceb08b6d7d2cc76d3317fc7b5cc04fb6707269c5c6e0c5b60ae549242123b0e493f602a075559e359970d98db89525456b51c951c8afa13ea8e98e3c596836783d5c63f5a61a99fdb7290875db4be88ab384bbbbbfc7183fdeaa633e8951db7da396dc48524fb1a8bd611a5aa2a2432f30ab420a7a6d3240c718cf031fa9ef4c9ad550205aa02951df4a1d6c8421b015b769db8c9229837ea2be8b1b0d39d0eba9c51484efdb8c0efd8d258daf3c449699f2edbd4584e7af9c64e3f96b9beb28d4ac40931e6478c8e76a24a825449501d867d2b1dcdebae99b9c752ae4ecd6dde4a179c1c1e460938f9149ef655e515c03919a289cb3dca278fb7bf177f4faa829dd8ce3f2ac9a7ecde490971fafd7dce15eed9b71c018c64fa514514b24e8e4f8c5c9b75c1e82579dc1233dfec08238f6add62d391acc1c5256a79e706d52d431c7a0145140b9fd149eb3a60dc5e88cbbc2da092411e9dc71f39a7766b447b344e847dcac9dcb5abba8d145061d43a6fcf1e65cf15d0e90231d3dd9cfe62995c6dcc5ca12a2c904a15f71dd27d451453e09d1a21450961cbb3ea8a956433b781f1ce33dfed54f0e2b50a2b71d84ed6db18028a28175f74fc6bda105c529a791c25c4f3c7a11f71586268f4a66b726e33de9ea6f1b52b181c760724e47b514520a5a28a283ffd9d81858ffa4686469676573744944096672616e646f6d58204599f81beaa2b20bd0ffcc9aa03a6f985befab3f6beaffa41e6354cdb2ab2ce471656c656d656e744964656e7469666965727264726976696e675f70726976696c656765736c656c656d656e7456616c756582a37576656869636c655f63617465676f72795f636f646561416a69737375655f64617465d903ec6a323031382d30382d30396b6578706972795f64617465d903ec6a323032342d31302d3230a37576656869636c655f63617465676f72795f636f646561426a69737375655f64617465d903ec6a323031372d30322d32336b6578706972795f64617465d903ec6a323032342d31302d32306a697373756572417574688443a10126a118215901f3308201ef30820195a00302010202143c4416eed784f3b413e48f56f075abfa6d87eb84300a06082a8648ce3d04030230233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e170d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106082a8648ce3d03010703420004ace7ab7340e5d9648c5a72a9a6f56745c7aad436a03a43efea77b5fa7b88f0197d57d8983e1b37d3a539f4d588365e38cbbf5b94d68c547b5bc8731dcd2f146ba381a83081a5301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e0416041414e29017a6c35621ffc7a686b7b72db06cd12351301f0603551d2304183016801454fa2383a04c28e0d930792261c80c4881d2c00b300e0603551d0f0101ff04040302078030150603551d250101ff040b3009060728818c5d050102300a06082a8648ce3d040302034800304502210097717ab9016740c8d7bcdaa494a62c053bbdecce1383c1aca72ad08dbc04cbb202203bad859c13a63c6d1ad67d814d43e2425caf90d422422c04a8ee0304c0d3a68d5903a2d81859039da66776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e69736f2e31383031332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254adcebf01582067e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372ddb78053f36d5d869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74e4cb9b7146e41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cca786e058fac2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d0658207d83e507ae77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbeeffa881d4857dd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121a42bc9ba5b7312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df6e0c0a5820c98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd036782f7b14c6a30faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087b5d10ca5485146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83d25173c484c5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7000bb59bb24c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d2233105511f778d98c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d6465766963654b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a33ba2c897dcd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228288f01d0c03a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d30315431333a33303a30325a584059e64205df1e2f708dd6db0847aed79fc7c0201d80fa55badcaf2e1bcf5902e1e5a62e4832044b890ad85aa53f129134775d733754d7cb7a413766aeff13cb2e6c6465766963655369676e6564a26a6e616d65537061636573d81841a06a64657669636541757468a1696465766963654d61638443a10105a0f65820e99521a85ad7891b806a07f8b5388a332d92c189a7bf293ee1f543405ae6824d6673746174757300 \ No newline at end of file +a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d65537061636573a1716f72672e69736f2e31383031332e352e3186d8185863a4686469676573744944006672616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e971656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456616c756563446f65d818586ca4686469676573744944036672616e646f6d5820b23f627e8999c706df0c0a4ed98ad74af988af619b4bb078b89058553f44615d71656c656d656e744964656e7469666965726a69737375655f646174656c656c656d656e7456616c7565d903ec6a323031392d31302d3230d818586da4686469676573744944046672616e646f6d5820c7ffa307e5de921e67ba5878094787e8807ac8e7b5b3932d2ce80f00f3e9abaf71656c656d656e744964656e7469666965726b6578706972795f646174656c656c656d656e7456616c7565d903ec6a323032342d31302d3230d818586da4686469676573744944076672616e646f6d582026052a42e5880557a806c1459af3fb7eb505d3781566329d0b604b845b5f9e6871656c656d656e744964656e7469666965726f646f63756d656e745f6e756d6265726c656c656d656e7456616c756569313233343536373839d818590471a4686469676573744944086672616e646f6d5820d094dad764a2eb9deb5210e9d899643efbd1d069cc311d3295516ca0b024412d71656c656d656e744964656e74696669657268706f7274726169746c656c656d656e7456616c7565590412ffd8ffe000104a46494600010101009000900000ffdb004300130d0e110e0c13110f11151413171d301f1d1a1a1d3a2a2c2330453d4947443d43414c566d5d4c51685241435f82606871757b7c7b4a5c869085778f6d787b76ffdb0043011415151d191d381f1f38764f434f7676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676ffc00011080018006403012200021101031101ffc4001b00000301000301000000000000000000000005060401020307ffc400321000010303030205020309000000000000010203040005110612211331141551617122410781a1163542527391b2c1f1ffc4001501010100000000000000000000000000000001ffc4001a110101010003010000000000000000000000014111213161ffda000c03010002110311003f00a5bbde22da2329c7d692bc7d0d03f52cfb0ff75e7a7ef3e7709723a1d0dae146ddfbb3c039ce07ad2bd47a7e32dbb8dd1d52d6ef4b284f64a480067dfb51f87ffb95ff00eb9ff14d215de66af089ce44b7dbde9cb6890a2838eddf18078f7add62d411ef4db9b10a65d6b95a147381ea0d495b933275fe6bba75c114104a8ba410413e983dff004f5af5d34b4b4cde632d0bf1fd1592bdd91c6411f3934c2fa6af6b54975d106dcf4a65ae56e856001ebc03c7ce29dd9eef1ef10fc447dc9da76ad2aee93537a1ba7e4f70dd8eff0057c6dffb5e1a19854a83758e54528750946ec6704850cd037bceb08b6d7d2cc76d3317fc7b5cc04fb6707269c5c6e0c5b60ae549242123b0e493f602a075559e359970d98db89525456b51c951c8afa13ea8e98e3c596836783d5c63f5a61a99fdb7290875db4be88ab384bbbbbfc7183fdeaa633e8951db7da396dc48524fb1a8bd611a5aa2a2432f30ab420a7a6d3240c718cf031fa9ef4c9ad550205aa02951df4a1d6c8421b015b769db8c9229837ea2be8b1b0d39d0eba9c51484efdb8c0efd8d258daf3c449699f2edbd4584e7af9c64e3f96b9beb28d4ac40931e6478c8e76a24a825449501d867d2b1dcdebae99b9c752ae4ecd6dde4a179c1c1e460938f9149ef655e515c03919a289cb3dca278fb7bf177f4faa829dd8ce3f2ac9a7ecde490971fafd7dce15eed9b71c018c64fa514514b24e8e4f8c5c9b75c1e82579dc1233dfec08238f6add62d391acc1c5256a79e706d52d431c7a0145140b9fd149eb3a60dc5e88cbbc2da092411e9dc71f39a7766b447b344e847dcac9dcb5abba8d145061d43a6fcf1e65cf15d0e90231d3dd9cfe62995c6dcc5ca12a2c904a15f71dd27d451453e09d1a21450961cbb3ea8a956433b781f1ce33dfed54f0e2b50a2b71d84ed6db18028a28175f74fc6bda105c529a791c25c4f3c7a11f71586268f4a66b726e33de9ea6f1b52b181c760724e47b514520a5a28a283ffd9d81858ffa4686469676573744944096672616e646f6d58204599f81beaa2b20bd0ffcc9aa03a6f985befab3f6beaffa41e6354cdb2ab2ce471656c656d656e744964656e7469666965727264726976696e675f70726976696c656765736c656c656d656e7456616c756582a37576656869636c655f63617465676f72795f636f646561416a69737375655f64617465d903ec6a323031382d30382d30396b6578706972795f64617465d903ec6a323032342d31302d3230a37576656869636c655f63617465676f72795f636f646561426a69737375655f64617465d903ec6a323031372d30322d32336b6578706972795f64617465d903ec6a323032342d31302d32306a697373756572417574688443a10126a118215901f3308201ef30820195a00302010202143c4416eed784f3b413e48f56f075abfa6d87eb84300a06082a8648ce3d04030230233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e170d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106082a8648ce3d03010703420004ace7ab7340e5d9648c5a72a9a6f56745c7aad436a03a43efea77b5fa7b88f0197d57d8983e1b37d3a539f4d588365e38cbbf5b94d68c547b5bc8731dcd2f146ba381a83081a5301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e0416041414e29017a6c35621ffc7a686b7b72db06cd12351301f0603551d2304183016801454fa2383a04c28e0d930792261c80c4881d2c00b300e0603551d0f0101ff04040302078030150603551d250101ff040b3009060728818c5d050102300a06082a8648ce3d040302034800304502210097717ab9016740c8d7bcdaa494a62c053bbdecce1383c1aca72ad08dbc04cbb202203bad859c13a63c6d1ad67d814d43e2425caf90d422422c04a8ee0304c0d3a68d5903a2d81859039da66776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e69736f2e31383031332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254adcebf01582067e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372ddb78053f36d5d869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74e4cb9b7146e41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cca786e058fac2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d0658207d83e507ae77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbeeffa881d4857dd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121a42bc9ba5b7312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df6e0c0a5820c98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd036782f7b14c6a30faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087b5d10ca5485146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83d25173c484c5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7000bb59bb24c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d2233105511f778d98c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d6465766963654b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a33ba2c897dcd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228288f01d0c03a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d30315431333a33303a30325a584059e64205df1e2f708dd6db0847aed79fc7c0201d80fa55badcaf2e1bcf5902e1e5a62e4832044b890ad85aa53f129134775d733754d7cb7a413766aeff13cb2e6c6465766963655369676e6564a26a6e616d65537061636573d81841a06a64657669636541757468a1696465766963654d61638443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b06673746174757300 \ No newline at end of file diff --git a/tests/common.rs b/tests/common.rs index c47b25c3..be3c24d7 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Context, Result}; use signature::Signer; use uuid::Uuid; - +use isomdl::cbor; use isomdl::definitions::device_engagement::{CentralClientMode, DeviceRetrievalMethods}; use isomdl::definitions::device_request::{DataElements, DocType, Namespaces}; use isomdl::definitions::helpers::NonEmptyMap; @@ -64,7 +64,7 @@ impl Device { ) -> Result<(device::SessionManager, RequestedItems)> { let (session_manager, items_requests) = { let session_establishment: definitions::SessionEstablishment = - serde_cbor::from_slice(&request).context("could not deserialize request")?; + cbor::from_slice(&request).context("could not deserialize request")?; state .process_session_establishment(session_establishment) .context("could not process process session establishment")? diff --git a/tests/simulated_device_and_reader_state.rs b/tests/simulated_device_and_reader_state.rs index 93025e94..98cac307 100644 --- a/tests/simulated_device_and_reader_state.rs +++ b/tests/simulated_device_and_reader_state.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use signature::Signer; use uuid::Uuid; - +use isomdl::cbor; use isomdl::definitions::device_engagement::{CentralClientMode, DeviceRetrievalMethods}; use isomdl::definitions::device_request::{DataElements, Namespaces}; use isomdl::definitions::{self, BleOptions, DeviceRetrievalMethod}; @@ -111,7 +111,7 @@ fn handle_request( ) -> Result> { let (session_manager, items_requests) = { let session_establishment: definitions::SessionEstablishment = - serde_cbor::from_slice(&request).context("could not deserialize request")?; + cbor::from_slice(&request).context("could not deserialize request")?; state .0 .clone() From 369c1a3d2dc94c1dbf4f3bdbd2d9f75ece10057e Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 7 Sep 2024 03:30:47 +0300 Subject: [PATCH 03/19] remove serde_cbor and use coset and ciborium serialization and CborValue --- src/cose.rs | 9 + src/cose/mac0.rs | 458 +++++++++++++++++ src/cose/sign1.rs | 536 ++++++++++++++++++++ test/definitions/cose/mac0/secret_key | 1 + test/definitions/cose/mac0/serialized.cbor | 1 + test/definitions/cose/sign1/secret_key | 1 + test/definitions/cose/sign1/serialized.cbor | 1 + 7 files changed, 1007 insertions(+) create mode 100644 src/cose.rs create mode 100644 src/cose/mac0.rs create mode 100644 src/cose/sign1.rs create mode 100644 test/definitions/cose/mac0/secret_key create mode 100644 test/definitions/cose/mac0/serialized.cbor create mode 100644 test/definitions/cose/sign1/secret_key create mode 100644 test/definitions/cose/sign1/serialized.cbor diff --git a/src/cose.rs b/src/cose.rs new file mode 100644 index 00000000..04437b29 --- /dev/null +++ b/src/cose.rs @@ -0,0 +1,9 @@ +use coset::iana; +pub mod mac0; +pub mod sign1; +mod serialized_as_cbor_value; + +/// Trait to represent the signature algorithm of a signer or verifier. +pub trait SignatureAlgorithm { + fn algorithm(&self) -> iana::Algorithm; +} diff --git a/src/cose/mac0.rs b/src/cose/mac0.rs new file mode 100644 index 00000000..d470efae --- /dev/null +++ b/src/cose/mac0.rs @@ -0,0 +1,458 @@ +use ::hmac::Hmac; +use coset::{CborSerializable, CoseError, mac_structure_data, MacContext, RegisteredLabelWithPrivate, TaggedCborSerializable}; +use coset::cwt::ClaimsSet; +use digest::{Mac, MacError}; +use serde::{Deserialize, Serialize}; +use sha2::Sha256; + +use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; +use crate::cose::SignatureAlgorithm; + +/// Prepared `COSE_Mac0` for remote signing. +/// +/// To produce a `COSE_Mac0` do the following: +/// +/// 1. Set the signature algorithm with [coset::HeaderBuilder::algorithm]. +/// 2. Produce a signature remotely, according to the chosen signature algorithm, +/// using the [PreparedCoseMac0::signature_payload] as the payload. +/// 3. Generate the `COSE_Mac0` by passing the produced signature into +/// [PreparedCoseMac0::finalize]. +/// +/// Example: +/// ``` +/// use coset::iana; +/// use digest::Mac; +/// use hex::FromHex;use hmac::Hmac; +/// use sha2::Sha256; +/// use isomdl::cose::mac0::PreparedCoseMac0; +/// +/// let key = Vec::::from_hex("a361316953796d6d6574726963613305622d318f187418681869187318201869187318201874186818651820186b18651879").unwrap(); +/// let signer = Hmac::::new_from_slice(&key).expect("failed to create HMAC signer"); +/// let protected = coset::HeaderBuilder::new() +/// .algorithm(iana::Algorithm::HMAC_256_256) +/// .build(); +/// let unprotected = coset::HeaderBuilder::new().key_id(b"11".to_vec()).build(); +/// let builder = coset::CoseMac0Builder::new() +/// .protected(protected) +/// .unprotected(unprotected) +/// .payload(b"This is the content.".to_vec()); +/// let prepared = PreparedCoseMac0::new(builder, None, None, true).unwrap(); +/// let signature_payload = prepared.signature_payload(); +/// let signature = tag(signature_payload, &signer).unwrap(); +/// let cose_mac0 = prepared.finalize(signature); +/// fn tag(signature_payload: &[u8], s: &Hmac) -> anyhow::Result> { +/// let mut mac = s.clone(); +/// mac.reset(); +/// mac.update(signature_payload); +/// Ok(mac.finalize().into_bytes().to_vec()) +/// } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PreparedCoseMac0 { + tagged: bool, + cose_mac0: CoseMac0, + 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 { + #[error("the COSE_Mac0 has an attached payload but an detached payload was provided")] + DoublePayload, + #[error("the COSE_Mac0 has a detached payload which was not provided")] + NoPayload, + #[error("tag did not match the structure expected by the verifier: {0}")] + MalformedTag(MacError), + #[error("tag is already present")] + AlreadyTagged, + #[error("error occurred when tagging COSE_Mac0: {0}")] + Tagging(MacError), + #[error("unable to set ClaimsSet: {0}")] + UnableToDeserializeIntoClaimsSet(CoseError), +} + +/// Result with error type: [`Error`]. +pub type Result = std::result::Result; + +/// Result for verification of a COSE_Mac0. +#[derive(Debug)] +pub enum VerificationResult { + Success, + Failure(String), + Error(Error), +} + +impl VerificationResult { + /// Result of verification. + /// + /// `false` implies the signature is inauthentic or the verification algorithm encountered an + /// error. + pub fn is_success(&self) -> bool { + matches!(self, VerificationResult::Success) + } + + /// Translate to a std::result::Result. + /// + /// Converts failure reasons and errors into a String. + pub fn into_result(self) -> Result<(), String> { + match self { + VerificationResult::Success => Ok(()), + VerificationResult::Failure(reason) => Err(reason), + VerificationResult::Error(e) => Err(format!("{}", e)), + } + } + + /// Retrieve the error if the verification algorithm encountered an error. + pub fn into_error(self) -> Option { + match self { + VerificationResult::Error(e) => Some(e), + _ => None, + } + } +} + +impl PreparedCoseMac0 { + pub fn new( + builder: coset::CoseMac0Builder, + detached_payload: Option<&[u8]>, + aad: Option<&[u8]>, + tagged: bool, + ) -> Result { + let cose_mac0 = builder.build(); + + // Check if the payload is present and if it is attached or detached. + // Needs to be exclusively attached or detached. + let payload = match (cose_mac0.payload.as_ref(), detached_payload) { + (Some(_), Some(_)) => return Err(Error::DoublePayload), + (None, None) => return Err(Error::NoPayload), + (Some(payload), None) => payload.clone(), + (None, Some(payload)) => payload.to_vec(), + }; + // Create the signature payload ot be used later on signing. + let tag_payload = mac_structure_data( + MacContext::CoseMac0, + cose_mac0.protected.clone(), + aad.unwrap_or_default(), + &payload, + ); + + Ok(Self { + tagged, + cose_mac0: CoseMac0 { + tagged, + inner: cose_mac0, + }, + tag_payload, + }) + } + + /// Returns the signature payload that needs to be used to tag it. + pub fn signature_payload(&self) -> &[u8] { + &self.tag_payload + } + + /// Finalize the COSE_Mac0 by adding the tag. + pub fn finalize(self, tag: Vec) -> CoseMac0 { + let mut cose_mac0 = self.cose_mac0; + cose_mac0.inner.tag = tag; + cose_mac0 + } +} + +impl CoseMac0 { + /// Verify that the tag of a `COSE_Mac0` is authentic. + pub fn verify( + &self, + verifier: &Hmac, + detached_payload: Option<&[u8]>, + external_aad: Option<&[u8]>, + ) -> VerificationResult { + if let Some(RegisteredLabelWithPrivate::Assigned(alg)) = + self.inner.protected.header.alg.as_ref() + { + if verifier.algorithm() != *alg { + return VerificationResult::Failure( + "algorithm in protected headers did not match verifier's algorithm".into(), + ); + } + } + + let payload = match (self.inner.payload.as_ref(), detached_payload) { + (None, None) => return VerificationResult::Error(Error::NoPayload), + (Some(attached), None) => attached, + (None, Some(detached)) => detached, + _ => return VerificationResult::Error(Error::DoublePayload), + }; + + let tag = &self.inner.tag; + + // Create the signature payload ot be used later on signing. + let tag_payload = mac_structure_data( + MacContext::CoseMac0, + self.inner.protected.clone(), + external_aad.unwrap_or_default(), + payload, + ); + + let mut mac = verifier.clone(); + mac.reset(); + mac.update(&tag_payload); + match mac.verify_slice(tag) { + Ok(()) => VerificationResult::Success, + Err(e) => VerificationResult::Failure(format!("tag is not authentic: {}", e)), + } + } + + /// Retrieve the CWT claims set. + pub fn claims_set(&self) -> Result> { + match self.inner.payload.as_ref() { + None => Ok(None), + Some(payload) => ClaimsSet::from_slice(payload).map_or_else( + |e| Err(Error::UnableToDeserializeIntoClaimsSet(e)), + |c| Ok(Some(c)), + ), + } + } + + /// If we are serialized as tagged. + pub fn is_tagged(&self) -> bool { + self.tagged + } + + /// Set serialization to tagged. + pub fn set_tagged(&mut self) { + self.tagged = true; + } +} + +/// 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; + use sha2::Sha256; + + use super::super::SignatureAlgorithm; + + /// Implement [`SignatureAlgorithm`]. + + impl SignatureAlgorithm for Hmac { + fn algorithm(&self) -> iana::Algorithm { + iana::Algorithm::HMAC_256_256 + } + } +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + + use coset::{CborSerializable, Header, iana}; + use coset::cwt::{ClaimsSet, Timestamp}; + use digest::Mac; + use hex::FromHex; + use hmac::Hmac; + use sha2::Sha256; + use crate::cbor; + use crate::cose::mac0::{CoseMac0, PreparedCoseMac0}; + + static COSE_MAC0: &str = include_str!("../../test/definitions/cose/mac0/serialized.cbor"); + static KEY: &str = include_str!("../../test/definitions/cose/mac0/secret_key"); + + const RFC8392_KEY: &str = "6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19"; + const RFC8392_MAC0: &str = "d18443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b0"; + + #[test] + fn roundtrip() { + let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); + let mut parsed: CoseMac0 = + ciborium::from_reader(Cursor::new(&bytes)).expect("failed to parse COSE_MAC0 from bytes"); + parsed.set_tagged(); + let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_MAC0"); + assert_eq!( + bytes, roundtripped, + "original bytes and roundtripped bytes do not match" + ); + } + + #[test] + fn tagging() { + let key = Vec::::from_hex(KEY).unwrap(); + let signer = Hmac::::new_from_slice(&key).expect("failed to create HMAC signer"); + let protected = coset::HeaderBuilder::new() + .algorithm(iana::Algorithm::HMAC_256_256) + .build(); + let unprotected = coset::HeaderBuilder::new().key_id(b"11".to_vec()).build(); + let builder = coset::CoseMac0Builder::new() + .protected(protected) + .unprotected(unprotected) + .payload(b"This is the content.".to_vec()); + let prepared = PreparedCoseMac0::new(builder, None, None, true).unwrap(); + let signature_payload = prepared.signature_payload(); + let signature = tag(signature_payload, &signer).unwrap(); + let cose_mac0 = prepared.finalize(signature); + let serialized = cbor::to_vec(&cose_mac0).expect("failed to serialize COSE_MAC0"); + + let expected = Vec::::from_hex(COSE_MAC0).unwrap(); + assert_eq!( + expected, serialized, + "expected COSE_MAC0 and signed data do not match" + ); + } + + fn tag(signature_payload: &[u8], s: &Hmac) -> anyhow::Result> { + let mut mac = s.clone(); + mac.reset(); + mac.update(signature_payload); + Ok(mac.finalize().into_bytes().to_vec()) + } + + #[test] + fn verifying() { + let key = Vec::::from_hex(KEY).unwrap(); + let verifier = + 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)).expect("failed to parse COSE_MAC0 from bytes"); + + cose_mac0 + .verify(&verifier, None, None) + .into_result() + .expect("COSE_MAC0 could not be verified") + } + + #[test] + fn remote_tagging() { + let key = Vec::::from_hex(KEY).unwrap(); + let signer = Hmac::::new_from_slice(&key).expect("failed to create HMAC signer"); + let protected = coset::HeaderBuilder::new() + .algorithm(iana::Algorithm::HMAC_256_256) + .build(); + let unprotected = coset::HeaderBuilder::new().key_id(b"11".to_vec()).build(); + let builder = coset::CoseMac0Builder::new() + .protected(protected) + .unprotected(unprotected) + .payload(b"This is the content.".to_vec()); + let prepared = PreparedCoseMac0::new(builder, None, None, true).unwrap(); + let signature_payload = prepared.signature_payload(); + let signature = tag(signature_payload, &signer).unwrap(); + let cose_mac0 = prepared.finalize(signature); + + let serialized = cbor::to_vec(&cose_mac0).expect("failed to serialize COSE_MAC0"); + let expected = Vec::::from_hex(COSE_MAC0).unwrap(); + assert_eq!( + expected, serialized, + "expected COSE_MAC0 and signed data do not match" + ); + + let verifier = + Hmac::::new_from_slice(&key).expect("failed to create HMAC verifier"); + cose_mac0 + .verify(&verifier, None, None) + .into_result() + .expect("COSE_MAC0 could not be verified") + } + + fn rfc8392_example_inputs() -> (Header, Header, ClaimsSet) { + let protected = coset::HeaderBuilder::new() + .algorithm(iana::Algorithm::ES256) + .build(); + + let unprotected = coset::HeaderBuilder::new() + .key_id( + hex::decode("4173796d6d65747269634543445341323536").expect("error decoding key id"), + ) + .build(); + + let claims_set = ClaimsSet { + issuer: Some("coap://as.example.com".to_string()), + subject: Some("erikw".to_string()), + audience: Some("coap://light.example.com".to_string()), + expiration_time: Some(Timestamp::WholeSeconds(1444064944)), + not_before: Some(Timestamp::WholeSeconds(1443944944)), + issued_at: Some(Timestamp::WholeSeconds(1443944944)), + cwt_id: Some(hex::decode("0b71").unwrap()), + rest: vec![], + }; + + (protected, unprotected, claims_set) + } + + #[test] + fn tagging_cwt() { + // Using key from RFC8392 example + let key = hex::decode(RFC8392_KEY).unwrap(); + let signer = Hmac::::new_from_slice(&key).expect("failed to create HMAC signer"); + let (protected, unprotected, claims_set) = rfc8392_example_inputs(); + let builder = coset::CoseMac0Builder::new() + .protected(protected) + .unprotected(unprotected) + .payload(claims_set.to_vec().expect("failed to set claims set")); + let prepared = PreparedCoseMac0::new(builder, None, None, true).unwrap(); + let signature_payload = prepared.signature_payload(); + let signature = tag(signature_payload, &signer).expect("failed to sign CWT"); + let cose_mac0 = prepared.finalize(signature); + let serialized = cbor::to_vec(&cose_mac0).expect("failed to serialize COSE_MAC0"); + let expected = hex::decode(RFC8392_MAC0).unwrap(); + + assert_eq!( + expected, serialized, + "expected COSE_MAC0 and signed CWT do not match" + ); + } + + #[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)).expect("failed to parse COSE_MAC0 from bytes"); + let parsed_claims_set = cose_mac0 + .claims_set() + .expect("failed to parse claims set from payload") + .expect("retrieved empty claims set"); + let (_, _, expected_claims_set) = rfc8392_example_inputs(); + assert_eq!(parsed_claims_set, expected_claims_set); + } +} diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs new file mode 100644 index 00000000..53722526 --- /dev/null +++ b/src/cose/sign1.rs @@ -0,0 +1,536 @@ +use coset::{CborSerializable, CoseError, RegisteredLabelWithPrivate, sig_structure_data, SignatureContext, TaggedCborSerializable}; +use coset::cwt::ClaimsSet; +use serde::{Deserialize, Serialize}; +use signature::Verifier; +use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; +use crate::cose::SignatureAlgorithm; + +/// Prepared `COSE_Sign1` for remote signing. +/// +/// To produce a `COSE_Sign1,` do the following: +/// +/// 1. Set the signature algorithm with [coset::HeaderBuilder::algorithm]. +/// 2. Produce a signature remotely, according to the chosen signature algorithm, +/// using the [PreparedCoseSign1::signature_payload] as the payload. +/// 3. Generate the `COSE_Sign1` by passing the produced signature into +/// [PreparedCoseSign1::finalize]. +/// +/// Example: +/// ``` +/// use coset::iana; +/// use hex::FromHex; +/// use p256::ecdsa::{Signature, SigningKey}; +/// use p256::SecretKey; +/// use signature::{SignatureEncoding, Signer, SignerMut}; +/// use isomdl::cose::sign1::{Error, PreparedCoseSign1}; +/// use isomdl::cose::SignatureAlgorithm; +/// +/// let key = Vec::::from_hex("57c92077664146e876760c9520d054aa93c3afb04e306705db6090308507b4d3").unwrap(); +/// let signer: SigningKey = SecretKey::from_slice(&key).unwrap().into(); +/// let protected = coset::HeaderBuilder::new() +/// .algorithm(iana::Algorithm::ES256) +/// .build(); +/// let unprotected = coset::HeaderBuilder::new().key_id(b"11".to_vec()).build(); +/// let builder = coset::CoseSign1Builder::new() +/// .protected(protected) +/// .unprotected(unprotected) +/// .payload(b"This is the content.".to_vec()); +/// let prepared = PreparedCoseSign1::new(builder, None, None, true).unwrap(); +/// let signature_payload = prepared.signature_payload(); +/// let signature = sign::(signature_payload, &signer).unwrap(); +/// let cose_sign1 = prepared.finalize(signature); +/// +/// fn sign(signature_payload: &[u8], s: &S) -> anyhow::Result> +/// where +/// S: Signer + SignatureAlgorithm, +/// Sig: SignatureEncoding, +/// { +/// Ok(s.try_sign(signature_payload) +/// .map_err(Error::Signing)? +/// .to_vec()) +/// } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PreparedCoseSign1 { + tagged: bool, + cose_sign1: CoseSign1, + #[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 { + #[error("the COSE_Sign1 has an attached payload but an detached payload was provided")] + DoublePayload, + #[error("the COSE_Sign1 has a detached payload which was not provided")] + NoPayload, + #[error("signature did not match the structure expected by the verifier: {0}")] + MalformedSignature(signature::Error), + #[error("signature is already present")] + AlreadySigned, + #[error("error occurred when signing COSE_Sign1: {0}")] + Signing(signature::Error), + #[error("unable to set ClaimsSet: {0}")] + UnableToDeserializeIntoClaimsSet(CoseError), +} + +/// Result with error type: [`Error`]. +pub type Result = std::result::Result; + +/// Result for verification of a COSE_Sign1. +#[derive(Debug)] +pub enum VerificationResult { + Success, + Failure(String), + Error(Error), +} + +impl VerificationResult { + /// Result of verification. + /// + /// `false` implies the signature is inauthentic or the verification algorithm encountered an + /// error. + pub fn is_success(&self) -> bool { + matches!(self, VerificationResult::Success) + } + + /// Translate to a std::result::Result. + /// + /// Converts failure reasons and errors into a String. + pub fn into_result(self) -> Result<(), String> { + match self { + VerificationResult::Success => Ok(()), + VerificationResult::Failure(reason) => Err(reason), + VerificationResult::Error(e) => Err(format!("{}", e)), + } + } + + /// Retrieve the error if the verification algorithm encountered an error. + pub fn into_error(self) -> Option { + match self { + VerificationResult::Error(e) => Some(e), + _ => None, + } + } +} + +impl PreparedCoseSign1 { + pub fn new( + builder: coset::CoseSign1Builder, + detached_payload: Option<&[u8]>, + aad: Option<&[u8]>, + tagged: bool, + ) -> Result { + let cose_sign1 = builder.build(); + + // Check if the payload is present and if it is attached or detached. + // Needs to be exclusively attached or detached. + let payload = match (cose_sign1.payload.as_ref(), detached_payload) { + (Some(_), Some(_)) => return Err(Error::DoublePayload), + (None, None) => return Err(Error::NoPayload), + (Some(payload), None) => payload.clone(), + (None, Some(payload)) => payload.to_vec(), + }; + // Create the signature payload ot be used later on signing. + let signature_payload = sig_structure_data( + SignatureContext::CoseSign1, + cose_sign1.protected.clone(), + None, + aad.unwrap_or_default(), + &payload, + ); + + Ok(Self { + tagged, + cose_sign1: CoseSign1 { + tagged, + inner: cose_sign1, + }, + signature_payload, + }) + } + + /// Returns the signature payload that needs to be used to sign. + pub fn signature_payload(&self) -> &[u8] { + &self.signature_payload + } + + /// Finalize the COSE_Sign1 by adding the signature. + pub fn finalize(self, signature: Vec) -> CoseSign1 { + let mut cose_sign1 = self.cose_sign1; + cose_sign1.inner.signature = signature; + cose_sign1 + } +} + +impl CoseSign1 { + /// Verify that the signature of a COSE_Sign1 is authentic. + pub fn verify<'a, V, S>( + &'a self, + verifier: &V, + detached_payload: Option<&[u8]>, + external_aad: Option<&[u8]>, + ) -> VerificationResult + where + V: Verifier + SignatureAlgorithm, + S: TryFrom<&'a [u8]>, + S::Error: Into, + { + if let Some(RegisteredLabelWithPrivate::Assigned(alg)) = + self.inner.protected.header.alg.as_ref() + { + if verifier.algorithm() != *alg { + return VerificationResult::Failure( + "algorithm in protected headers did not match verifier's algorithm".into(), + ); + } + } + + let payload = match (self.inner.payload.as_ref(), detached_payload) { + (None, None) => return VerificationResult::Error(Error::NoPayload), + (Some(attached), None) => attached, + (None, Some(detached)) => detached, + _ => return VerificationResult::Error(Error::DoublePayload), + }; + + let signature = match S::try_from(self.inner.signature.as_ref()) + .map_err(Into::into) + .map_err(Error::MalformedSignature) + { + Ok(sig) => sig, + Err(e) => return VerificationResult::Error(e), + }; + + let signature_payload = sig_structure_data( + SignatureContext::CoseSign1, + self.inner.protected.clone(), + None, + external_aad.unwrap_or_default(), + payload, + ); + + match verifier.verify(&signature_payload, &signature) { + Ok(()) => VerificationResult::Success, + Err(e) => VerificationResult::Failure(format!("signature is not authentic: {}", e)), + } + } + + /// Retrieve the CWT claims set. + pub fn claims_set(&self) -> Result> { + match self.inner.payload.as_ref() { + None => Ok(None), + Some(payload) => ClaimsSet::from_slice(payload).map_or_else( + |e| Err(Error::UnableToDeserializeIntoClaimsSet(e)), + |c| Ok(Some(c)), + ), + } + } + + /// If we are serialized as tagged. + pub fn is_tagged(&self) -> bool { + self.tagged + } + + /// Set serialization to tagged. + pub fn set_tagged(&mut self) { + self.tagged = true; + } +} + +/// 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}; + + use crate::cose::SignatureAlgorithm; + + /// Implement [`SignatureAlgorithm`]. + + impl SignatureAlgorithm for SigningKey { + fn algorithm(&self) -> iana::Algorithm { + iana::Algorithm::ES256 + } + } + + impl SignatureAlgorithm for VerifyingKey { + fn algorithm(&self) -> iana::Algorithm { + iana::Algorithm::ES256 + } + } +} + +mod p384 { + use coset::iana; + use p384::ecdsa::{SigningKey, VerifyingKey}; + + use crate::cose::SignatureAlgorithm; + + /// Implement [`SignatureAlgorithm`]. + + impl SignatureAlgorithm for SigningKey { + fn algorithm(&self) -> iana::Algorithm { + iana::Algorithm::ES384 + } + } + + impl SignatureAlgorithm for VerifyingKey { + fn algorithm(&self) -> iana::Algorithm { + iana::Algorithm::ES384 + } + } +} + +#[cfg(test)] +mod tests { + use std::io::Cursor; + use coset::{CborSerializable, Header, iana}; + use coset::cwt::{ClaimsSet, Timestamp}; + use hex::FromHex; + use p256::ecdsa::{Signature, SigningKey, VerifyingKey}; + use p256::SecretKey; + use signature::{SignatureEncoding, Signer}; + use crate::cbor; + use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; + use crate::cose::SignatureAlgorithm; + + static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); + static COSE_KEY: &str = include_str!("../../test/definitions/cose/sign1/secret_key"); + + const RFC8392_KEY: &str = "6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19"; + const RFC8392_COSE_SIGN1: &str = "d28443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b7158405427c1ff28d23fbad1f29c4c7c6a555e601d6fa29f9179bc3d7438bacaca5acd08c8d4d4f96131680c429a01f85951ecee743a52b9b63632c57209120e1c9e30"; + + #[test] + fn roundtrip() { + let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); + let mut parsed: CoseSign1 = + ciborium::from_reader(Cursor::new(&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"); + println!("bytes: {:?}", hex::encode(&bytes)); + println!("roundtripped: {:?}", hex::encode(&roundtripped)); + assert_eq!( + bytes, roundtripped, + "original bytes and roundtripped bytes do not match" + ); + } + + #[test] + fn roundtrip_ciborium() { + let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); + let mut parsed: CoseSign1 = + 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)); + assert_eq!( + bytes, roundtripped, + "original bytes and roundtripped bytes do not match" + ); + } + + #[test] + fn signing() { + let key = Vec::::from_hex(COSE_KEY).unwrap(); + let signer: SigningKey = SecretKey::from_slice(&key).unwrap().into(); + let protected = coset::HeaderBuilder::new() + .algorithm(iana::Algorithm::ES256) + .build(); + let unprotected = coset::HeaderBuilder::new().key_id(b"11".to_vec()).build(); + let builder = coset::CoseSign1Builder::new() + .protected(protected) + .unprotected(unprotected) + .payload(b"This is the content.".to_vec()); + let prepared = PreparedCoseSign1::new(builder, None, None, true).unwrap(); + let signature_payload = prepared.signature_payload(); + let signature = sign::(signature_payload, &signer).unwrap(); + let cose_sign1 = prepared.finalize(signature); + let serialized = cbor::to_vec(&cose_sign1) + .expect("failed to serialize COSE_Sign1 to bytes"); + + let expected = Vec::::from_hex(COSE_SIGN1).unwrap(); + assert_eq!( + expected, serialized, + "expected COSE_Sign1 and signed data do not match" + ); + } + + fn sign(signature_payload: &[u8], s: &S) -> anyhow::Result> + where + S: Signer + SignatureAlgorithm, + Sig: SignatureEncoding, + { + Ok(s.try_sign(signature_payload) + .map_err(Error::Signing)? + .to_vec()) + } + + #[test] + fn verifying() { + let key = Vec::::from_hex(COSE_KEY).unwrap(); + let signer: SigningKey = SecretKey::from_slice(&key).unwrap().into(); + let verifier: VerifyingKey = (&signer).into(); + + let cose_sign1_bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); + let cose_sign1: CoseSign1 = cbor::from_slice(&cose_sign1_bytes) + .expect("failed to parse COSE_Sign1 from bytes"); + + cose_sign1 + .verify::(&verifier, None, None) + .into_result() + .expect("COSE_Sign1 could not be verified") + } + + #[test] + fn remote_signed() { + let key = Vec::::from_hex(COSE_KEY).unwrap(); + let signer: SigningKey = SecretKey::from_slice(&key).unwrap().into(); + let protected = coset::HeaderBuilder::new() + .algorithm(iana::Algorithm::ES256) + .build(); + let unprotected = coset::HeaderBuilder::new().key_id(b"11".to_vec()).build(); + let builder = coset::CoseSign1Builder::new() + .protected(protected) + .unprotected(unprotected) + .payload(b"This is the content.".to_vec()); + let prepared = PreparedCoseSign1::new(builder, None, None, true).unwrap(); + let signature_payload = prepared.signature_payload(); + let signature = sign::(signature_payload, &signer).unwrap(); + let cose_sign1 = prepared.finalize(signature); + + let serialized = cbor::to_vec(&cose_sign1 + .clone()) + .expect("failed to serialize COSE_Sign1 to bytes"); + + let expected = Vec::::from_hex(COSE_SIGN1).unwrap(); + assert_eq!( + expected, serialized, + "expected COSE_Sign1 and signed data do not match" + ); + + let verifier: VerifyingKey = (&signer).into(); + cose_sign1 + .verify::(&verifier, None, None) + .into_result() + .expect("COSE_Sign1 could not be verified") + } + + fn rfc8392_example_inputs() -> (Header, Header, ClaimsSet) { + let protected = coset::HeaderBuilder::new() + .algorithm(iana::Algorithm::ES256) + .build(); + + let unprotected = coset::HeaderBuilder::new() + .key_id( + hex::decode("4173796d6d65747269634543445341323536").expect("error decoding key id"), + ) + .build(); + + let claims_set = ClaimsSet { + issuer: Some("coap://as.example.com".to_string()), + subject: Some("erikw".to_string()), + audience: Some("coap://light.example.com".to_string()), + expiration_time: Some(Timestamp::WholeSeconds(1444064944)), + not_before: Some(Timestamp::WholeSeconds(1443944944)), + issued_at: Some(Timestamp::WholeSeconds(1443944944)), + cwt_id: Some(hex::decode("0b71").unwrap()), + rest: vec![], + }; + + (protected, unprotected, claims_set) + } + + #[test] + fn signing_cwt() { + // Using key from RFC8392 example + let key = hex::decode(RFC8392_KEY).unwrap(); + let signer: SigningKey = SecretKey::from_slice(&key).unwrap().into(); + let (protected, unprotected, claims_set) = rfc8392_example_inputs(); + let builder = coset::CoseSign1Builder::new() + .protected(protected) + .unprotected(unprotected) + .payload(claims_set.to_vec().expect("failed to set claims set")); + let prepared = PreparedCoseSign1::new(builder, None, None, true).unwrap(); + let signature_payload = prepared.signature_payload(); + let signature = + sign::(signature_payload, &signer).expect("failed to sign CWT"); + let cose_sign1 = prepared.finalize(signature); + let serialized = cbor::to_vec(&cose_sign1) + .expect("failed to serialize COSE_Sign1 to bytes"); + let expected = hex::decode(RFC8392_COSE_SIGN1).unwrap(); + assert_eq!( + expected, serialized, + "expected COSE_Sign1 and signed CWT do not match" + ); + } + + #[test] + fn deserializing_signed_cwt() { + let cose_sign1_bytes = hex::decode(RFC8392_COSE_SIGN1).unwrap(); + let cose_sign1: CoseSign1 = cbor::from_slice(&cose_sign1_bytes) + .expect("failed to parse COSE_Sign1 from bytes"); + let parsed_claims_set = cose_sign1 + .claims_set() + .expect("failed to parse claims set from payload") + .expect("retrieved empty claims set"); + let (_, _, expected_claims_set) = rfc8392_example_inputs(); + assert_eq!(parsed_claims_set, expected_claims_set); + } + + #[test] + fn tag_coset_tagged_roundtrip() { + // this is tagged + let bytes = hex::decode(COSE_SIGN1).unwrap(); + + // can parse tagged value + let parsed: CoseSign1 = cbor::from_slice(&bytes).unwrap(); + assert!(parsed.is_tagged()); + println!("successfully deserialized Value from tagged bytes"); + + let roundtrip = cbor::to_vec(&parsed).unwrap(); + assert_eq!(bytes, roundtrip); + } +} diff --git a/test/definitions/cose/mac0/secret_key b/test/definitions/cose/mac0/secret_key new file mode 100644 index 00000000..9fb6a674 --- /dev/null +++ b/test/definitions/cose/mac0/secret_key @@ -0,0 +1 @@ +a361316953796d6d6574726963613305622d318f187418681869187318201869187318201874186818651820186b18651879 \ No newline at end of file diff --git a/test/definitions/cose/mac0/serialized.cbor b/test/definitions/cose/mac0/serialized.cbor new file mode 100644 index 00000000..0efde9a1 --- /dev/null +++ b/test/definitions/cose/mac0/serialized.cbor @@ -0,0 +1 @@ +d18443a10105a10442313154546869732069732074686520636f6e74656e742e58203f30c4a2c740c3a0d90310b48cd282bcdb29ab8073a32e287fa07e188d317e8a \ No newline at end of file diff --git a/test/definitions/cose/sign1/secret_key b/test/definitions/cose/sign1/secret_key new file mode 100644 index 00000000..408d0692 --- /dev/null +++ b/test/definitions/cose/sign1/secret_key @@ -0,0 +1 @@ +57c92077664146e876760c9520d054aa93c3afb04e306705db6090308507b4d3 \ No newline at end of file diff --git a/test/definitions/cose/sign1/serialized.cbor b/test/definitions/cose/sign1/serialized.cbor new file mode 100644 index 00000000..3e3da0da --- /dev/null +++ b/test/definitions/cose/sign1/serialized.cbor @@ -0,0 +1 @@ +D28443A10126A10442313154546869732069732074686520636F6E74656E742E58408EB33E4CA31D1C465AB05AAC34CC6B23D58FEF5C083106C4D25A91AEF0B0117E2AF9A291AA32E14AB834DC56ED2A223444547E01F11D3B0916E5A4C345CACB36 \ No newline at end of file From 178edf7c528c7e064c110ae0d183cc600e01b96b Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 7 Sep 2024 03:41:45 +0300 Subject: [PATCH 04/19] clippy --- src/cbor.rs | 38 ++--- src/cose.rs | 2 +- src/cose/mac0.rs | 33 ++--- src/cose/serialized_as_cbor_value.rs | 10 +- src/cose/sign1.rs | 56 ++++---- src/definitions/device_engagement.rs | 131 +++++++++++++----- .../device_engagement/nfc_options.rs | 61 ++++---- src/definitions/device_key/cose_key.rs | 38 +++-- src/definitions/device_key/mod.rs | 2 +- src/definitions/device_request.rs | 2 +- src/definitions/device_response.rs | 4 +- src/definitions/device_signed.rs | 6 +- src/definitions/helpers/bytestr.rs | 2 +- src/definitions/helpers/tag24.rs | 13 +- src/definitions/issuer_signed.rs | 6 +- src/definitions/mso.rs | 11 +- src/definitions/namespaces/fulldate.rs | 2 +- .../namespaces/org_iso_18013_5_1/age_over.rs | 2 +- .../namespaces/org_iso_18013_5_1/alpha2.rs | 2 +- .../org_iso_18013_5_1/biometric_template.rs | 2 +- .../org_iso_18013_5_1/driving_privileges.rs | 12 +- .../org_iso_18013_5_1/eye_colour.rs | 2 +- .../org_iso_18013_5_1/hair_colour.rs | 2 +- .../org_iso_18013_5_1/issuing_jurisdiction.rs | 2 +- .../namespaces/org_iso_18013_5_1/sex.rs | 2 +- .../namespaces/org_iso_18013_5_1/tdate.rs | 2 +- .../un_distinguishing_sign.rs | 2 +- .../org_iso_18013_5_1_aamva/county_code.rs | 2 +- .../org_iso_18013_5_1_aamva/dhs_compliance.rs | 2 +- .../domestic_driving_privileges.rs | 8 +- .../org_iso_18013_5_1_aamva/edl_indicator.rs | 2 +- .../org_iso_18013_5_1_aamva/name_suffix.rs | 2 +- .../name_truncation.rs | 2 +- .../org_iso_18013_5_1_aamva/present.rs | 2 +- .../race_and_ethnicity.rs | 2 +- .../namespaces/org_iso_18013_5_1_aamva/sex.rs | 2 +- .../org_iso_18013_5_1_aamva/weight_range.rs | 2 +- src/definitions/session.rs | 9 +- src/definitions/traits/to_cbor.rs | 6 +- src/definitions/validity_info.rs | 10 +- src/issuance/mdoc.rs | 29 ++-- src/lib.rs | 2 +- src/presentation/device.rs | 66 ++++----- src/presentation/reader.rs | 8 +- tests/common.rs | 4 +- tests/simulated_device_and_reader_state.rs | 4 +- 46 files changed, 362 insertions(+), 249 deletions(-) diff --git a/src/cbor.rs b/src/cbor.rs index 3c25e4b9..bfc88102 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -1,8 +1,8 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::ops::{Deref, DerefMut}; +use coset::{cbor, CoseError, EndOfFile}; use serde::{de, Deserialize, Serialize}; +use std::borrow::{Borrow, BorrowMut}; use std::io::Cursor; -use coset::{cbor, CoseError, EndOfFile}; +use std::ops::{Deref, DerefMut}; use thiserror::Error; /// Wraps [chromium::Value] and implements [PartialEq], [Eq], [PartialOrd] and [Ord], @@ -51,17 +51,18 @@ impl From for CborError { CoseError::OutOfRangeIntegerValue => CborError::OutOfRangeIntegerValue, CoseError::UnexpectedItem(s, s2) => CborError::UnexpectedItem(s, s2), CoseError::UnregisteredIanaValue => CborError::UnregisteredIanaValue, - CoseError::UnregisteredIanaNonPrivateValue => CborError::UnregisteredIanaNonPrivateValue, + CoseError::UnregisteredIanaNonPrivateValue => { + CborError::UnregisteredIanaNonPrivateValue + } } } } impl Value { pub fn to_string(&self) -> coset::Result { - self.0.clone().into_text().map_err(|e| CoseError::DecodeFailed(ciborium::de::Error::Semantic( - None, - format!("{e:?}"), - ))) + self.0.clone().into_text().map_err(|e| { + CoseError::DecodeFailed(ciborium::de::Error::Semantic(None, format!("{e:?}"))) + }) } } @@ -89,7 +90,7 @@ impl Eq for Value {} impl PartialOrd for Value { fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) + Some(self.cmp(other)) } } @@ -104,7 +105,9 @@ where T: serde::Serialize, { let mut buf = Vec::new(); - ciborium::into_writer(value, &mut buf).map_err(coset::CoseError::from).map_err(CborError::from)?; + ciborium::into_writer(value, &mut buf) + .map_err(coset::CoseError::from) + .map_err(CborError::from)?; Ok(buf) } @@ -112,10 +115,9 @@ pub fn from_slice(slice: &[u8]) -> Result where T: de::DeserializeOwned, { - ciborium::from_reader(Cursor::new(&slice)).map_err(|e| CoseError::DecodeFailed(ciborium::de::Error::Semantic( - None, - e.to_string(), - ))).map_err(CborError::from) + ciborium::from_reader(Cursor::new(&slice)) + .map_err(|e| CoseError::DecodeFailed(ciborium::de::Error::Semantic(None, e.to_string()))) + .map_err(CborError::from) } /// Convert a `chor::Value` into a type `T` @@ -155,9 +157,9 @@ impl From for Value { } } -impl Into for Value { - fn into(self) -> ciborium::Value { - self.0 +impl From for ciborium::Value { + fn from(val: Value) -> Self { + val.0 } } @@ -175,7 +177,7 @@ impl<'de> Deserialize<'de> for Value { where D: serde::Deserializer<'de>, { - ciborium::Value::deserialize(deserializer).map(|v| Value(v)) + ciborium::Value::deserialize(deserializer).map(Value) } } diff --git a/src/cose.rs b/src/cose.rs index 04437b29..3864ca76 100644 --- a/src/cose.rs +++ b/src/cose.rs @@ -1,7 +1,7 @@ use coset::iana; pub mod mac0; -pub mod sign1; mod serialized_as_cbor_value; +pub mod sign1; /// Trait to represent the signature algorithm of a signer or verifier. pub trait SignatureAlgorithm { diff --git a/src/cose/mac0.rs b/src/cose/mac0.rs index d470efae..243b0a26 100644 --- a/src/cose/mac0.rs +++ b/src/cose/mac0.rs @@ -1,6 +1,9 @@ use ::hmac::Hmac; -use coset::{CborSerializable, CoseError, mac_structure_data, MacContext, RegisteredLabelWithPrivate, TaggedCborSerializable}; use coset::cwt::ClaimsSet; +use coset::{ + mac_structure_data, CborSerializable, CoseError, MacContext, RegisteredLabelWithPrivate, + TaggedCborSerializable, +}; use digest::{Mac, MacError}; use serde::{Deserialize, Serialize}; use sha2::Sha256; @@ -253,17 +256,15 @@ impl<'de> Deserialize<'de> for CoseMac0 { where D: serde::Deserializer<'de>, { - let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) = ciborium::tag::Captured::deserialize(deserializer)?; + 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 + None => false, }; - Ok(Self { - tagged, - inner, - }) + Ok(Self { tagged, inner }) } } @@ -287,14 +288,14 @@ mod hmac { mod tests { use std::io::Cursor; - use coset::{CborSerializable, Header, iana}; + use crate::cbor; + use crate::cose::mac0::{CoseMac0, PreparedCoseMac0}; use coset::cwt::{ClaimsSet, Timestamp}; + use coset::{iana, CborSerializable, Header}; use digest::Mac; use hex::FromHex; use hmac::Hmac; use sha2::Sha256; - use crate::cbor; - use crate::cose::mac0::{CoseMac0, PreparedCoseMac0}; static COSE_MAC0: &str = include_str!("../../test/definitions/cose/mac0/serialized.cbor"); static KEY: &str = include_str!("../../test/definitions/cose/mac0/secret_key"); @@ -305,8 +306,8 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); - let mut parsed: CoseMac0 = - ciborium::from_reader(Cursor::new(&bytes)).expect("failed to parse COSE_MAC0 from bytes"); + let mut parsed: CoseMac0 = ciborium::from_reader(Cursor::new(&bytes)) + .expect("failed to parse COSE_MAC0 from bytes"); parsed.set_tagged(); let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_MAC0"); assert_eq!( @@ -354,8 +355,8 @@ 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)).expect("failed to parse COSE_MAC0 from bytes"); + let cose_mac0: CoseMac0 = ciborium::from_reader(Cursor::new(&cose_mac0_bytes)) + .expect("failed to parse COSE_MAC0 from bytes"); cose_mac0 .verify(&verifier, None, None) @@ -446,8 +447,8 @@ 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)).expect("failed to parse COSE_MAC0 from bytes"); + let cose_mac0: CoseMac0 = ciborium::from_reader(Cursor::new(&cose_mac0_bytes)) + .expect("failed to parse COSE_MAC0 from bytes"); let parsed_claims_set = cose_mac0 .claims_set() .expect("failed to parse claims set from payload") diff --git a/src/cose/serialized_as_cbor_value.rs b/src/cose/serialized_as_cbor_value.rs index adecdb64..024f69ef 100644 --- a/src/cose/serialized_as_cbor_value.rs +++ b/src/cose/serialized_as_cbor_value.rs @@ -10,7 +10,11 @@ impl<'a, T: Clone + AsCborValue> Serialize for SerializedAsCborValue<&'a T> { where S: serde::Serializer, { - self.0.clone().to_cbor_value().map_err(serde::ser::Error::custom)?.serialize(serializer) + self.0 + .clone() + .to_cbor_value() + .map_err(serde::ser::Error::custom)? + .serialize(serializer) } } @@ -19,6 +23,8 @@ impl<'de, T: AsCborValue> Deserialize<'de> for SerializedAsCborValue { where D: serde::Deserializer<'de>, { - T::from_cbor_value(ciborium::Value::deserialize(deserializer)?).map_err(serde::de::Error::custom).map(Self) + T::from_cbor_value(ciborium::Value::deserialize(deserializer)?) + .map_err(serde::de::Error::custom) + .map(Self) } } diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index 53722526..7a8a0f31 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -1,9 +1,12 @@ -use coset::{CborSerializable, CoseError, RegisteredLabelWithPrivate, sig_structure_data, SignatureContext, TaggedCborSerializable}; +use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; +use crate::cose::SignatureAlgorithm; use coset::cwt::ClaimsSet; +use coset::{ + sig_structure_data, CborSerializable, CoseError, RegisteredLabelWithPrivate, SignatureContext, + TaggedCborSerializable, +}; use serde::{Deserialize, Serialize}; use signature::Verifier; -use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; -use crate::cose::SignatureAlgorithm; /// Prepared `COSE_Sign1` for remote signing. /// @@ -53,7 +56,7 @@ use crate::cose::SignatureAlgorithm; pub struct PreparedCoseSign1 { tagged: bool, cose_sign1: CoseSign1, - #[serde(with = "serde_bytes")] // This optimizes (de)serialization of byte vectors + #[serde(with = "serde_bytes")] // This optimizes (de)serialization of byte vectors signature_payload: Vec, } @@ -267,17 +270,15 @@ impl<'de> Deserialize<'de> for CoseSign1 { where D: serde::Deserializer<'de>, { - let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) = ciborium::tag::Captured::deserialize(deserializer)?; + 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 + None => false, }; - Ok(Self { - tagged, - inner, - }) + Ok(Self { tagged, inner }) } } @@ -325,16 +326,16 @@ mod p384 { #[cfg(test)] mod tests { - use std::io::Cursor; - use coset::{CborSerializable, Header, iana}; + use crate::cbor; + use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; + use crate::cose::SignatureAlgorithm; 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 crate::cbor; - use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; - use crate::cose::SignatureAlgorithm; + use std::io::Cursor; static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); static COSE_KEY: &str = include_str!("../../test/definitions/cose/sign1/secret_key"); @@ -345,8 +346,8 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut parsed: CoseSign1 = - ciborium::from_reader(Cursor::new(&bytes)).expect("failed to parse COSE_Sign1 from bytes"); + let mut parsed: CoseSign1 = ciborium::from_reader(Cursor::new(&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"); println!("bytes: {:?}", hex::encode(&bytes)); @@ -388,8 +389,8 @@ mod tests { let signature_payload = prepared.signature_payload(); let signature = sign::(signature_payload, &signer).unwrap(); let cose_sign1 = prepared.finalize(signature); - let serialized = cbor::to_vec(&cose_sign1) - .expect("failed to serialize COSE_Sign1 to bytes"); + let serialized = + cbor::to_vec(&cose_sign1).expect("failed to serialize COSE_Sign1 to bytes"); let expected = Vec::::from_hex(COSE_SIGN1).unwrap(); assert_eq!( @@ -415,8 +416,8 @@ mod tests { let verifier: VerifyingKey = (&signer).into(); let cose_sign1_bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let cose_sign1: CoseSign1 = cbor::from_slice(&cose_sign1_bytes) - .expect("failed to parse COSE_Sign1 from bytes"); + let cose_sign1: CoseSign1 = + cbor::from_slice(&cose_sign1_bytes).expect("failed to parse COSE_Sign1 from bytes"); cose_sign1 .verify::(&verifier, None, None) @@ -441,9 +442,8 @@ mod tests { let signature = sign::(signature_payload, &signer).unwrap(); let cose_sign1 = prepared.finalize(signature); - let serialized = cbor::to_vec(&cose_sign1 - .clone()) - .expect("failed to serialize COSE_Sign1 to bytes"); + let serialized = + cbor::to_vec(&cose_sign1.clone()).expect("failed to serialize COSE_Sign1 to bytes"); let expected = Vec::::from_hex(COSE_SIGN1).unwrap(); assert_eq!( @@ -498,8 +498,8 @@ mod tests { let signature = sign::(signature_payload, &signer).expect("failed to sign CWT"); let cose_sign1 = prepared.finalize(signature); - let serialized = cbor::to_vec(&cose_sign1) - .expect("failed to serialize COSE_Sign1 to bytes"); + let serialized = + cbor::to_vec(&cose_sign1).expect("failed to serialize COSE_Sign1 to bytes"); let expected = hex::decode(RFC8392_COSE_SIGN1).unwrap(); assert_eq!( expected, serialized, @@ -510,8 +510,8 @@ mod tests { #[test] fn deserializing_signed_cwt() { let cose_sign1_bytes = hex::decode(RFC8392_COSE_SIGN1).unwrap(); - let cose_sign1: CoseSign1 = cbor::from_slice(&cose_sign1_bytes) - .expect("failed to parse COSE_Sign1 from bytes"); + let cose_sign1: CoseSign1 = + cbor::from_slice(&cose_sign1_bytes).expect("failed to parse COSE_Sign1 from bytes"); let parsed_claims_set = cose_sign1 .claims_set() .expect("failed to parse claims set from payload") diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index f6c49f28..44657150 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -4,6 +4,7 @@ //! It includes fields such as the `version`, `security details, `device retrieval methods, `server retrieval methods, and `protocol information. //! //! The module also provides implementations for conversions between [DeviceEngagement] and [CborValue], as well as other utility functions. +use crate::cbor::{into_value, CborError, Value as CborValue}; use crate::definitions::helpers::Tag24; use crate::definitions::helpers::{ByteStr, NonEmptyVec}; use crate::definitions::CoseKey; @@ -11,14 +12,13 @@ use anyhow::Result; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, vec}; use uuid::Uuid; -use crate::cbor::{CborError, into_value, Value as CborValue}; pub mod error; pub use error::Error; pub mod nfc_options; -pub use nfc_options::NfcOptions; use crate::cbor; +pub use nfc_options::NfcOptions; pub type EDeviceKeyBytes = Tag24; pub type EReaderKeyBytes = Tag24; @@ -149,11 +149,21 @@ impl From for CborValue { ]), )); if let Some(methods) = device_engagement.device_retrieval_methods { - let methods = Vec::from(methods).into_iter().map(into_value).collect::, CborError>>().unwrap(); - map.push((ciborium::Value::Integer(2.into()), ciborium::Value::Array(methods))); + let methods = Vec::from(methods) + .into_iter() + .map(into_value) + .collect::, CborError>>() + .unwrap(); + map.push(( + ciborium::Value::Integer(2.into()), + ciborium::Value::Array(methods), + )); } if let Some(methods) = device_engagement.server_retrieval_methods { - map.push((ciborium::Value::Integer(3.into()), into_value(methods).unwrap())); + map.push(( + ciborium::Value::Integer(3.into()), + into_value(methods).unwrap(), + )); } if let Some(_info) = device_engagement.protocol_info { // Usage of protocolinfo is RFU and should for now be none @@ -167,7 +177,10 @@ impl TryFrom for DeviceEngagement { type Error = Error; fn try_from(v: CborValue) -> Result { if let ciborium::Value::Map(map) = v.0 { - let mut map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); + let mut map = map + .into_iter() + .map(|(k, v)| (CborValue(k), CborValue(v))) + .collect::>(); let device_engagement_version = map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); cbor @@ -179,11 +192,12 @@ impl TryFrom for DeviceEngagement { } else { return Err(Error::Malformed); } - let device_engagement_security = - map.remove(&cbor::Value(ciborium::Value::Integer(1.into()))).ok_or(Error::Malformed)?; + let device_engagement_security = map + .remove(&cbor::Value(ciborium::Value::Integer(1.into()))) + .ok_or(Error::Malformed)?; - let security: Security = cbor::from_value(device_engagement_security.0) - .map_err(|_| Error::Malformed)?; + let security: Security = + cbor::from_value(device_engagement_security.0).map_err(|_| Error::Malformed)?; let device_retrieval_methods = map .remove(&{ @@ -264,20 +278,31 @@ impl TryFrom for DeviceRetrievalMethod { type Error = Error; fn try_from(value: CborValue) -> Result { if let ciborium::Value::Array(list) = value.0 { - let method: [CborValue; 3] = list.into_iter().map(CborValue).collect::>().try_into().map_err(|_| Error::Malformed)?; + let method: [CborValue; 3] = list + .into_iter() + .map(CborValue) + .collect::>() + .try_into() + .map_err(|_| Error::Malformed)?; match method { [CborValue(ciborium::Value::Integer(i1)), CborValue(ciborium::Value::Integer(i11)), methods] - if >::into(i1) == 1 && >::into(i11) == 1 => { + if >::into(i1) == 1 + && >::into(i11) == 1 => + { let nfc_options = NfcOptions::try_from(methods)?; Ok(DeviceRetrievalMethod::NFC(nfc_options)) } [CborValue(ciborium::Value::Integer(i2)), CborValue(ciborium::Value::Integer(i1)), methods] - if >::into(i1) == 1 && >::into(i2) == 2 => { + if >::into(i1) == 1 + && >::into(i2) == 2 => + { let ble_options = BleOptions::try_from(methods)?; Ok(DeviceRetrievalMethod::BLE(ble_options)) } [CborValue(ciborium::Value::Integer(i3)), CborValue(ciborium::Value::Integer(i1)), methods] - if >::into(i1) == 1 && >::into(i3) == 3 => { + if >::into(i1) == 1 + && >::into(i3) == 3 => + { let wifi_options = WifiOptions::try_from(methods)?; Ok(DeviceRetrievalMethod::WIFI(wifi_options)) } @@ -299,7 +324,11 @@ impl From for CborValue { DeviceRetrievalMethod::BLE(opts) => into_value(opts).unwrap(), DeviceRetrievalMethod::WIFI(opts) => into_value(opts).unwrap(), }; - CborValue(ciborium::Value::Array(vec![transport_type, version, retrieval_method])) + CborValue(ciborium::Value::Array(vec![ + transport_type, + version, + retrieval_method, + ])) } } @@ -308,7 +337,10 @@ impl TryFrom for BleOptions { fn try_from(v: CborValue) -> Result { if let ciborium::Value::Map(map) = v.0 { - let mut map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); + let mut map = map + .into_iter() + .map(|(k, v)| (CborValue(k), CborValue(v))) + .collect::>(); let central_client_mode = match ( map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); @@ -319,7 +351,10 @@ impl TryFrom for BleOptions { cbor }), ) { - (Some(CborValue(ciborium::Value::Bool(true))), Some(CborValue(ciborium::Value::Bytes(uuid)))) => { + ( + Some(CborValue(ciborium::Value::Bool(true))), + Some(CborValue(ciborium::Value::Bytes(uuid))), + ) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; Some(CentralClientMode { uuid: Uuid::from_bytes(uuid_bytes), @@ -339,7 +374,10 @@ impl TryFrom for BleOptions { cbor }), ) { - (Some(CborValue(ciborium::Value::Bool(true))), Some(CborValue(ciborium::Value::Bytes(uuid)))) => { + ( + Some(CborValue(ciborium::Value::Bool(true))), + Some(CborValue(ciborium::Value::Bytes(uuid))), + ) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; let ble_device_address = match map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(20.into()).into(); @@ -373,33 +411,48 @@ impl From for CborValue { match o.central_client_mode { Some(CentralClientMode { uuid }) => { - map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Bool(true))); + map.push(( + ciborium::Value::Integer(1.into()), + ciborium::Value::Bool(true), + )); map.push(( ciborium::Value::Integer(11.into()), ciborium::Value::Bytes(uuid.as_bytes().to_vec()), )); } None => { - map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Bool(false))); + map.push(( + ciborium::Value::Integer(1.into()), + ciborium::Value::Bool(false), + )); } } match o.peripheral_server_mode { Some(PeripheralServerMode { - uuid, - ble_device_address, - }) => { - map.push((ciborium::Value::Integer(0.into()), ciborium::Value::Bool(true))); + uuid, + ble_device_address, + }) => { + map.push(( + ciborium::Value::Integer(0.into()), + ciborium::Value::Bool(true), + )); map.push(( ciborium::Value::Integer(10.into()), ciborium::Value::Bytes(uuid.as_bytes().to_vec()), )); if let Some(address) = ble_device_address { - map.push((ciborium::Value::Integer(20.into()), into_value(address).unwrap())); + map.push(( + ciborium::Value::Integer(20.into()), + into_value(address).unwrap(), + )); } } None => { - map.push((ciborium::Value::Integer(0.into()), ciborium::Value::Bool(false))); + map.push(( + ciborium::Value::Integer(0.into()), + ciborium::Value::Bool(false), + )); } } @@ -416,7 +469,8 @@ impl TryFrom for WifiOptions { idx: i128, ) -> Result, Error> { match map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + let cbor: CborValue = + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); cbor }) { None => Ok(None), @@ -430,7 +484,8 @@ impl TryFrom for WifiOptions { idx: i128, ) -> Result, Error> { match map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + let cbor: CborValue = + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); cbor }) { None => Ok(None), @@ -448,7 +503,8 @@ impl TryFrom for WifiOptions { idx: i128, ) -> Result, Error> { match map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + let cbor: CborValue = + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); cbor }) { None => Ok(None), @@ -462,7 +518,10 @@ impl TryFrom for WifiOptions { let map: BTreeMap = match v.0 { ciborium::Value::Map(map) => { - let map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); + let map = map + .into_iter() + .map(|(k, v)| (CborValue(k), CborValue(v))) + .collect::>(); Ok(map) } _ => Err(Error::InvalidWifiOptions), @@ -527,14 +586,22 @@ impl From for CborValue { if let Some((x, y, z)) = m.web_api { map.push(( "webApi".to_string().into(), - ciborium::Value::Array(vec![into_value(x).unwrap(), into_value(y).unwrap(), into_value(z).unwrap()]), + ciborium::Value::Array(vec![ + into_value(x).unwrap(), + into_value(y).unwrap(), + into_value(z).unwrap(), + ]), )); } if let Some((x, y, z)) = m.oidc { map.push(( "oidc".to_string().into(), - ciborium::Value::Array(vec![into_value(x).unwrap(), into_value(y).unwrap(), into_value(z).unwrap()]), + ciborium::Value::Array(vec![ + into_value(x).unwrap(), + into_value(y).unwrap(), + into_value(z).unwrap(), + ]), )); } diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index c4d8ff42..5b5bb8ce 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -1,8 +1,8 @@ +use crate::cbor::Value as CborValue; +use crate::definitions::device_engagement::error::Error; use anyhow::Result; use serde::{Deserialize, Serialize}; -use crate::cbor::Value as CborValue; use std::collections::BTreeMap; -use crate::definitions::device_engagement::error::Error; /// The maximum length of the NFC command, as specified in ISO_18013-5 2021 Section 8.3.3.1.2 /// Values of this type must lie between 255 and 65,535 inclusive, as specified in Note 2. @@ -26,52 +26,53 @@ impl TryFrom for NfcOptions { fn try_from(v: CborValue) -> Result { let map: BTreeMap = match v.0 { - ciborium::Value::Map(map) => Ok(map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>()), + ciborium::Value::Map(map) => Ok(map + .into_iter() + .map(|(k, v)| (CborValue(k), CborValue(v))) + .collect::>()), _ => Err(Error::InvalidNfcOptions), }?; Ok(NfcOptions::default()) .and_then(|nfc_opts| { map.get(&{ - let cbor: CborValue = - ciborium::Value::Integer(0.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); cbor }) - .ok_or(Error::InvalidNfcOptions) - .and_then(CommandDataLength::try_from) - .map(|max_len_command_data_field| NfcOptions { - max_len_command_data_field, - ..nfc_opts - }) + .ok_or(Error::InvalidNfcOptions) + .and_then(CommandDataLength::try_from) + .map(|max_len_command_data_field| NfcOptions { + max_len_command_data_field, + ..nfc_opts + }) }) .and_then(|nfc_opts| { map.get(&{ - let cbor: CborValue = - ciborium::Value::Integer(1.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); cbor }) - .ok_or(Error::InvalidNfcOptions) - .and_then(ResponseDataLength::try_from) - .map(|max_len_response_data_field| NfcOptions { - max_len_response_data_field, - ..nfc_opts - }) + .ok_or(Error::InvalidNfcOptions) + .and_then(ResponseDataLength::try_from) + .map(|max_len_response_data_field| NfcOptions { + max_len_response_data_field, + ..nfc_opts + }) }) } } impl From for CborValue { fn from(o: NfcOptions) -> CborValue { - let mut map = vec![]; - map.push(( - ciborium::Value::Integer(0.into()), - ciborium::Value::Integer(o.max_len_command_data_field.get().into()), - )); - map.push(( - ciborium::Value::Integer(1.into()), - ciborium::Value::Integer(o.max_len_response_data_field.get().into()), - )); - + let map = vec![ + ( + ciborium::Value::Integer(0.into()), + ciborium::Value::Integer(o.max_len_command_data_field.get().into()), + ), + ( + ciborium::Value::Integer(1.into()), + ciborium::Value::Integer(o.max_len_response_data_field.get().into()), + ), + ]; CborValue(ciborium::Value::Map(map)) } } @@ -216,8 +217,8 @@ impl From for CborValue { #[cfg(test)] mod test { - use crate::cbor; use super::*; + use crate::cbor; #[test] fn command_data_length_valid_data_test() { diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index 46325496..5fdf3266 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -22,13 +22,13 @@ //! } //! } //! ``` +use crate::cbor::Value as CborValue; use aes::cipher::generic_array::{typenum::U8, GenericArray}; +use coset::iana::Algorithm; use p256::EncodedPoint; use serde::{Deserialize, Serialize}; -use crate::cbor::Value as CborValue; use ssi_jwk::JWK; use std::collections::BTreeMap; -use coset::iana::Algorithm; /// An implementation of RFC-8152 [COSE_Key](https://datatracker.ietf.org/doc/html/rfc8152#section-13) /// restricted to the requirements of ISO/IEC 18013-5:2021. @@ -123,14 +123,20 @@ impl From for CborValue { match key { CoseKey::EC2 { crv, x, y } => { // kty: 1, EC2: 2 - map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Integer(2.into()))); + map.push(( + ciborium::Value::Integer(1.into()), + ciborium::Value::Integer(2.into()), + )); // crv: -1 map.push((ciborium::Value::Integer((-1).into()), { let cbor: CborValue = crv.into(); cbor.into() })); // x: -2 - map.push((ciborium::Value::Integer((-2).into()), ciborium::Value::Bytes(x))); + map.push(( + ciborium::Value::Integer((-2).into()), + ciborium::Value::Bytes(x), + )); // y: -3 map.push((ciborium::Value::Integer((-3).into()), { let cbor: CborValue = y.into(); @@ -139,14 +145,20 @@ impl From for CborValue { } CoseKey::OKP { crv, x } => { // kty: 1, OKP: 1 - map.push((ciborium::Value::Integer(1.into()), ciborium::Value::Integer(1.into()))); + map.push(( + ciborium::Value::Integer(1.into()), + ciborium::Value::Integer(1.into()), + )); // crv: -1 map.push((ciborium::Value::Integer((-1).into()), { let cbor: CborValue = crv.into(); cbor.into() })); // x: -2 - map.push((ciborium::Value::Integer((-2).into()), ciborium::Value::Bytes(x))); + map.push(( + ciborium::Value::Integer((-2).into()), + ciborium::Value::Bytes(x), + )); } } ciborium::Value::Map(map).into() @@ -158,7 +170,10 @@ impl TryFrom for CoseKey { fn try_from(v: CborValue) -> Result { if let ciborium::Value::Map(map) = v.0 { - let mut map = map.into_iter().map(|(k, v)| (CborValue(k), CborValue(v))).collect::>(); + let mut map = map + .into_iter() + .map(|(k, v)| (CborValue(k), CborValue(v))) + .collect::>(); match ( map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); @@ -178,11 +193,12 @@ impl TryFrom for CoseKey { Some(CborValue(ciborium::Value::Integer(crv_id))), Some(CborValue(ciborium::Value::Bytes(x))), ) if >::into(i2) == 2 => { - let crv_id: i128 = crv_id.try_into().map_err(|_| Error::UnsupportedCurve)?; + let crv_id: i128 = crv_id.into(); let crv = crv_id.try_into()?; let y = map .remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-3).into()).into(); cbor + let cbor: CborValue = ciborium::Value::Integer((-3).into()).into(); + cbor }) .ok_or(Error::EC2MissingY)? .try_into()?; @@ -193,7 +209,7 @@ impl TryFrom for CoseKey { Some(CborValue(ciborium::Value::Integer(crv_id))), Some(CborValue(ciborium::Value::Bytes(x))), ) if >::into(i1) == 1 => { - let crv_id: i128 = crv_id.try_into().map_err(|_| Error::UnsupportedCurve)?; + let crv_id: i128 = crv_id.into(); let crv = crv_id.try_into()?; Ok(Self::OKP { crv, x }) } @@ -445,8 +461,8 @@ impl TryFrom<&ssi_jwk::OctetParams> for OKPCurve { #[cfg(test)] mod test { use super::*; - use hex::FromHex; use crate::cbor; + use hex::FromHex; static EC_P256: &str = include_str!("../../../test/definitions/cose_key/ec_p256.cbor"); diff --git a/src/definitions/device_key/mod.rs b/src/definitions/device_key/mod.rs index e3a997ac..c2e277ab 100644 --- a/src/definitions/device_key/mod.rs +++ b/src/definitions/device_key/mod.rs @@ -41,9 +41,9 @@ //! assert!(result.is_err()); //! assert_eq!(result.unwrap_err(), Error::DoubleAuthorized("namespace1".to_string())); //! ``` +use crate::cbor::Value as CborValue; use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec}; use serde::{Deserialize, Serialize}; -use crate::cbor::Value as CborValue; use std::collections::BTreeMap; pub mod cose_key; diff --git a/src/definitions/device_request.rs b/src/definitions/device_request.rs index 099927b5..3d922d25 100644 --- a/src/definitions/device_request.rs +++ b/src/definitions/device_request.rs @@ -1,8 +1,8 @@ //! 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 crate::cose::sign1::CoseSign1; pub type ItemsRequestBytes = Tag24; pub type DocType = String; diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index 5dfc66c9..7806900c 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -4,8 +4,8 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use crate::definitions::{ - DeviceSigned, - helpers::{NonEmptyMap, NonEmptyVec}, IssuerSigned, + helpers::{NonEmptyMap, NonEmptyVec}, + DeviceSigned, IssuerSigned, }; /// Represents a device response. diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index fc27ac58..423ff072 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -4,15 +4,15 @@ //! //! 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 crate::cose::sign1::CoseSign1; -use crate::cbor::Value as CborValue; -use crate::cose::mac0::CoseMac0; /// Represents a device-signed structure. #[derive(Clone, Debug, Deserialize, Serialize)] diff --git a/src/definitions/helpers/bytestr.rs b/src/definitions/helpers/bytestr.rs index 270e428f..620b18a6 100644 --- a/src/definitions/helpers/bytestr.rs +++ b/src/definitions/helpers/bytestr.rs @@ -1,5 +1,5 @@ -use serde::{Deserialize, Serialize}; use crate::cbor::Value as CborValue; +use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[serde(try_from = "CborValue", into = "CborValue")] diff --git a/src/definitions/helpers/tag24.rs b/src/definitions/helpers/tag24.rs index 83ffd773..a29f18a5 100644 --- a/src/definitions/helpers/tag24.rs +++ b/src/definitions/helpers/tag24.rs @@ -1,11 +1,11 @@ //! Support for embedded //! [CBOR Data Items](https://www.ietf.org/rfc/rfc8949.html#name-encoded-cbor-data-item), //! also known as a tagged data item with tag number 24. +use crate::cbor::{from_slice, to_vec, CborError, Value as CborValue}; use serde::{ de::{self, Error as DeError}, ser, Deserialize, Serialize, }; -use crate::cbor::{CborError, from_slice, to_vec, Value as CborValue}; /// A wrapper for a struct that is to be encoded as a CBOR tagged item, with tag number 24. /// @@ -64,7 +64,7 @@ impl TryFrom for Tag24 { inner_bytes: inner_bytes.to_vec(), }) } - _ => Err(Error::InvalidTag24(inner_value.into())), + _ => Err(Error::InvalidTag24(inner_value)), }, _ => Err(Error::NotATag24(v.0)), } @@ -85,7 +85,11 @@ impl AsRef for Tag24 { impl Serialize for Tag24 { fn serialize(&self, s: S) -> Result { - ciborium::Value::Tag(24, Box::new(ciborium::Value::Bytes(self.inner_bytes.clone()))).serialize(s) + ciborium::Value::Tag( + 24, + Box::new(ciborium::Value::Bytes(self.inner_bytes.clone())), + ) + .serialize(s) } } @@ -95,8 +99,7 @@ impl<'de, T: de::DeserializeOwned> Deserialize<'de> for Tag24 { D: de::Deserializer<'de>, { let cbor: CborValue = ciborium::Value::deserialize(d)?.into(); - cbor.try_into() - .map_err(D::Error::custom) + cbor.try_into().map_err(D::Error::custom) } } diff --git a/src/definitions/issuer_signed.rs b/src/definitions/issuer_signed.rs index 70d3121a..638bbca9 100644 --- a/src/definitions/issuer_signed.rs +++ b/src/definitions/issuer_signed.rs @@ -9,13 +9,13 @@ //! - [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 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::cbor::Value as CborValue; -use crate::cose::sign1::CoseSign1; /// Represents an issuer-signed object. /// @@ -54,8 +54,8 @@ pub struct IssuerSignedItem { #[cfg(test)] mod test { use super::IssuerSigned; - use hex::FromHex; use crate::cbor; + use hex::FromHex; static ISSUER_SIGNED_CBOR: &str = include_str!("../../test/definitions/issuer_signed.cbor"); diff --git a/src/definitions/mso.rs b/src/definitions/mso.rs index 3abb4890..0564fa6a 100644 --- a/src/definitions/mso.rs +++ b/src/definitions/mso.rs @@ -83,9 +83,9 @@ impl DigestId { #[cfg(test)] mod test { + use crate::cbor; use crate::definitions::{helpers::Tag24, IssuerSigned, Mso}; use hex::FromHex; - use crate::cbor; static ISSUER_SIGNED_CBOR: &str = include_str!("../../test/definitions/issuer_signed.cbor"); @@ -97,12 +97,11 @@ mod test { cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as an IssuerSigned"); let mso_bytes = &signed .issuer_auth - .inner.payload + .inner + .payload .expect("expected a COSE_Sign1 with attached payload, found detached payload"); - let mso: Tag24 = - cbor::from_slice(mso_bytes).expect("unable to parse payload as Mso"); - let roundtripped_bytes = - cbor::to_vec(&mso).expect("unable to encode Mso as cbor bytes"); + let mso: Tag24 = cbor::from_slice(mso_bytes).expect("unable to parse payload as Mso"); + let roundtripped_bytes = cbor::to_vec(&mso).expect("unable to encode Mso as cbor bytes"); assert_eq!( mso_bytes, &roundtripped_bytes, "original cbor and re-serialized Mso do not match" diff --git a/src/definitions/namespaces/fulldate.rs b/src/definitions/namespaces/fulldate.rs index dffe6353..b8997af4 100644 --- a/src/definitions/namespaces/fulldate.rs +++ b/src/definitions/namespaces/fulldate.rs @@ -1,5 +1,5 @@ -use anyhow::Error; use crate::cbor::Value as Cbor; +use anyhow::Error; use serde_json::Value as Json; use std::{fmt, str::FromStr}; use time::{format_description::FormatItem, macros::format_description, Date}; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs index d12ab482..8e43efe3 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}; use serde_json::{Map, Value as Json}; use std::{collections::BTreeMap, ops::Deref}; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs index e7a02c19..0c639078 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs b/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs index 4022392b..247b8b14 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs @@ -1,8 +1,8 @@ +use crate::cbor::Value as Cbor; use crate::definitions::{ helpers::ByteStr, traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}, }; -use crate::cbor::Value as Cbor; use serde_json::{Map, Value as Json}; use std::collections::BTreeMap; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index 6225ed39..16e57672 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -1,4 +1,5 @@ use super::FullDate; +use crate::cbor::Value as Cbor; use crate::{ definitions::{ helpers::NonEmptyVec, @@ -6,7 +7,6 @@ use crate::{ }, macros::{FromJson, ToCbor}, }; -use crate::cbor::Value as Cbor; use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; /// `driving_privileges` in the org.iso.18013.5.1 namespace. @@ -16,7 +16,7 @@ pub struct DrivingPrivileges(Vec); impl From for Cbor { fn from(d: DrivingPrivileges) -> Cbor { - ciborium::Value::Array(d.0.into_iter().map(|v|v.to_cbor().into()).collect()).into() + ciborium::Value::Array(d.0.into_iter().map(|v| v.to_cbor().into()).collect()).into() } } @@ -85,7 +85,13 @@ pub struct Codes(NonEmptyVec); impl From for Cbor { fn from(c: Codes) -> Cbor { - ciborium::Value::Array(c.0.into_inner().into_iter().map(|v| v.to_cbor().into()).collect()).into() + ciborium::Value::Array( + c.0.into_inner() + .into_iter() + .map(|v| v.to_cbor().into()) + .collect(), + ) + .into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs index ee674da6..5ece34d5 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs index 6e20e459..7989dc3c 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs b/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs index becad900..564f13bf 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs @@ -1,8 +1,8 @@ +use crate::cbor::Value as Cbor; use crate::definitions::{ namespaces::org_iso_18013_5_1::Alpha2, traits::{FromJson, FromJsonError, FromJsonMap}, }; -use crate::cbor::Value as Cbor; use serde_json::{Map, Value as Json}; /// `issuing_jurisdiction` in the org.iso.18013.5.1 namespace. diff --git a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs index 0a565fcb..11d2d2c1 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; /// `sex` in the org.iso.18013.5.1 namespace. diff --git a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs index 45815050..85d329a1 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs @@ -1,8 +1,8 @@ pub use super::FullDate; +use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use anyhow::anyhow; -use crate::cbor::Value as Cbor; use serde_json::Value as Json; use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset}; diff --git a/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs b/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs index f2c47e31..b60ccdd1 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; /// United Nations Distinguishing Sign, as per ISO/IEC 18013-1:2018 Annex F. diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs index a05c3477..2224fe21 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; /// `county_code` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs index 2d3a7703..4e1d012b 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs index 634e8d58..915c1e5e 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs @@ -1,9 +1,9 @@ use super::FullDate; +use crate::cbor::Value as Cbor; use crate::{ definitions::{helpers::NonEmptyVec, traits::ToCbor}, macros::{FromJson, ToCbor}, }; -use crate::cbor::Value as Cbor; /// `domestic_driving_privileges` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation /// Guidelines (Version 1.0). @@ -46,7 +46,8 @@ impl ToCbor for DomesticVehicleRestrictions { .into_iter() .map(|v| v.to_cbor().into()) .collect(), - ).into() + ) + .into() } } @@ -69,7 +70,8 @@ impl ToCbor for DomesticVehicleEndorsements { .into_iter() .map(|v| v.to_cbor().into()) .collect(), - ).into() + ) + .into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs index f83fb8aa..94ab55e0 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; /// `EDL_indicator` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs index 7dd84591..c85ac523 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs index 6d54ad7f..8596d209 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs index 5814c503..e45c2b72 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs @@ -1,6 +1,6 @@ +use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use anyhow::anyhow; -use crate::cbor::Value as Cbor; use serde_json::Value as Json; /// Indicator of presence for elements in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs index 7b5eba36..7d0e5035 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs index f8944572..891ddbcc 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; /// `sex` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs index 594f1d56..a358eb9e 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs @@ -1,5 +1,5 @@ -use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use crate::cbor::Value as Cbor; +use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; /// `weight_range` in the org.iso.18013.5.1.aamva namespace, as per the AAMVA mDL Implementation diff --git a/src/definitions/session.rs b/src/definitions/session.rs index 7e1a3d9b..22260d52 100644 --- a/src/definitions/session.rs +++ b/src/definitions/session.rs @@ -199,9 +199,10 @@ pub fn derive_session_key( session_transcript: &SessionTranscriptBytes, reader: bool, ) -> Result> { - let salt = Sha256::digest(crate::cbor::to_vec(session_transcript).map_err(|e| { - anyhow::anyhow!("failed to serialize session transcript: {e}") - })?); + let salt = Sha256::digest( + crate::cbor::to_vec(session_transcript) + .map_err(|e| anyhow::anyhow!("failed to serialize session transcript: {e}"))?, + ); let hkdf = shared_secret.extract::(Some(salt.as_ref())); let mut okm = [0u8; 32]; let sk_device = "SKDevice".as_bytes(); @@ -285,8 +286,8 @@ pub fn get_initialization_vector(message_count: &mut u32, reader: bool) -> [u8; #[cfg(test)] mod test { - use crate::cbor; use super::*; + use crate::cbor; use crate::definitions::device_engagement::Security; use crate::definitions::device_request::DeviceRequest; diff --git a/src/definitions/traits/to_cbor.rs b/src/definitions/traits/to_cbor.rs index afd41892..c6519ba3 100644 --- a/src/definitions/traits/to_cbor.rs +++ b/src/definitions/traits/to_cbor.rs @@ -1,16 +1,16 @@ //! ToCbor is specifically NOT implemented for `Vec` where `T: ToCbor`, as `Vec` likely should be //! represented as a `bytestr` instead of an `array` in `cbor`. +use crate::cbor; use crate::cbor::{CborError, Value}; use std::collections::BTreeMap; -use crate::cbor; pub type Bytes = Vec; pub trait ToCbor: Sized { fn to_cbor(self) -> Value; fn to_cbor_bytes(self) -> Result { - cbor::to_vec(&self.to_cbor().0).map_err(|err| CborError::from(err)).map_err(Into::into) + cbor::to_vec(&self.to_cbor().0).map_err(Into::into) } } @@ -42,4 +42,4 @@ impl ToCbor for Option { self.map(|s| ciborium::Value::Text(s).into()) .unwrap_or_else(|| ciborium::Value::Null.into()) } -} \ No newline at end of file +} diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index f51798b0..d594f39f 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -27,11 +27,11 @@ //! - [std::collections::BTreeMap]: Provides the [BTreeMap] type for storing key-value pairs in a sorted order. //! - [time]: Provides date and time manipulation functionality. //! - [thiserror]: Provides the [thiserror::Error] trait for defining custom error types. +use crate::cbor::Value as CborValue; use serde::{ ser::{Error as SerError, Serializer}, Deserialize, Serialize, }; -use crate::cbor::Value as CborValue; use std::collections::BTreeMap; use time::{ error::Format as FormatError, error::Parse as ParseError, @@ -110,7 +110,10 @@ impl TryFrom for ValidityInfo { fn try_from(v: CborValue) -> Result { if let ciborium::Value::Map(map) = v.0 { - let mut map = map.into_iter().map(|(k, v)| (k.into(), v.into())).collect::>(); + let mut map = map + .into_iter() + .map(|(k, v)| (k.into(), v.into())) + .collect::>(); macro_rules! extract_date { ($map:ident, $name:literal) => {{ let key = CborValue(ciborium::Value::Text(String::from($name))); @@ -124,7 +127,8 @@ impl TryFrom for ValidityInfo { let valid_from = extract_date!(map, "validFrom"); let valid_until = extract_date!(map, "validUntil"); - let expected_update_key: CborValue = ciborium::Value::Text(String::from("expectedUpdate")).into(); + let expected_update_key: CborValue = + ciborium::Value::Text(String::from("expectedUpdate")).into(); let expected_update = map .remove(&expected_update_key) .map(cbor_to_datetime) diff --git a/src/issuance/mdoc.rs b/src/issuance/mdoc.rs index 6f461c80..0ee12ae3 100644 --- a/src/issuance/mdoc.rs +++ b/src/issuance/mdoc.rs @@ -9,17 +9,17 @@ 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::{ definitions::{ - DeviceKeyInfo, - DigestAlgorithm, - DigestId, DigestIds, helpers::{NonEmptyMap, NonEmptyVec, Tag24}, issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}, IssuerSignedItem, Mso, ValidityInfo, + helpers::{NonEmptyMap, NonEmptyVec, Tag24}, + issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}, + DeviceKeyInfo, DigestAlgorithm, DigestId, DigestIds, IssuerSignedItem, Mso, ValidityInfo, }, issuance::x5chain::{X5Chain, X5CHAIN_HEADER_LABEL}, }; -use crate::cbor::Value as CborValue; -use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; -use crate::cose::SignatureAlgorithm; pub type Namespaces = BTreeMap>; @@ -193,10 +193,11 @@ impl PreparedMdoc { } = self; let mut issuer_auth = prepared_sig.finalize(signature); - issuer_auth.inner.unprotected.rest.push(( - Label::Int(X5CHAIN_HEADER_LABEL as i64), - x5chain.into_cbor().into(), - )); + issuer_auth + .inner + .unprotected + .rest + .push((Label::Int(X5CHAIN_HEADER_LABEL as i64), x5chain.into_cbor())); Mdoc { doc_type, mso, @@ -344,7 +345,7 @@ impl Builder { enable_decoy_digests, signer, ) - .await + .await } } @@ -371,7 +372,7 @@ fn to_issuer_namespaces(namespaces: Namespaces) -> Result { fn to_issuer_signed_items( elements: BTreeMap, -) -> impl Iterator { +) -> impl Iterator { let mut used_ids = HashSet::new(); elements.into_iter().map(move |(key, value)| { let digest_id = generate_digest_id(&mut used_ids); @@ -586,8 +587,8 @@ pub mod test { (isomdl_namespace, isomdl_data), (aamva_namespace, aamva_data), ] - .into_iter() - .collect(); + .into_iter() + .collect(); let validity_info = ValidityInfo { signed: OffsetDateTime::now_utc(), diff --git a/src/lib.rs b/src/lib.rs index ebc5f8df..bea09387 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,11 +231,11 @@ //! ```ignore #![doc = include_str!("../tests/simulated_device_and_reader_state.rs")] //! ``` +pub mod cbor; pub mod cose; pub mod definitions; pub mod issuance; pub mod presentation; -pub mod cbor; pub mod macros { pub use isomdl_macros::{FromJson, ToCbor}; diff --git a/src/presentation/device.rs b/src/presentation/device.rs index bd5486c5..40b164bc 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -16,33 +16,37 @@ //! //! You can view examples in `tests` directory in `simulated_device_and_reader.rs`, for a basic example and //! `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::definitions::device_signed::DeviceAuthType; use crate::definitions::IssuerSignedItem; -use crate::{cbor, definitions::{ - device_engagement::{DeviceRetrievalMethod, Security, ServerRetrievalMethods}, - device_request::{DeviceRequest, DocRequest, ItemsRequest}, - device_response::{ - Document as DeviceResponseDoc, DocumentError, DocumentErrorCode, DocumentErrors, - Errors as NamespaceErrors, Status, +use crate::{ + cbor, + definitions::{ + device_engagement::{DeviceRetrievalMethod, Security, ServerRetrievalMethods}, + device_request::{DeviceRequest, DocRequest, ItemsRequest}, + device_response::{ + Document as DeviceResponseDoc, DocumentError, DocumentErrorCode, DocumentErrors, + Errors as NamespaceErrors, Status, + }, + device_signed::{DeviceAuth, DeviceAuthentication, DeviceNamespacesBytes, DeviceSigned}, + helpers::{tag24, NonEmptyMap, NonEmptyVec, Tag24}, + issuer_signed::{IssuerSigned, IssuerSignedItemBytes}, + session::{ + self, derive_session_key, get_shared_secret, Handover, SessionData, SessionTranscript, + }, + CoseKey, DeviceEngagement, DeviceResponse, Mso, SessionEstablishment, }, - device_signed::{DeviceAuth, DeviceAuthentication, DeviceNamespacesBytes, DeviceSigned}, - helpers::{tag24, NonEmptyMap, NonEmptyVec, Tag24}, - issuer_signed::{IssuerSigned, IssuerSignedItemBytes}, - session::{ - self, derive_session_key, get_shared_secret, Handover, SessionData, SessionTranscript, - }, - CoseKey, DeviceEngagement, DeviceResponse, Mso, SessionEstablishment, -}, issuance::Mdoc}; + issuance::Mdoc, +}; +use coset::{CoseMac0Builder, CoseSign1Builder}; use p256::FieldBytes; use serde::{Deserialize, Serialize}; -use crate::cbor::{CborError, Value as CborValue}; use session::SessionTranscript180135; use std::collections::BTreeMap; use std::num::ParseIntError; -use coset::{CoseMac0Builder, CoseSign1Builder}; use uuid::Uuid; -use crate::cose::mac0::PreparedCoseMac0; -use crate::cose::sign1::{CoseSign1, PreparedCoseSign1}; -use crate::definitions::device_signed::DeviceAuthType; /// Initialisation state. /// @@ -369,7 +373,7 @@ impl SessionManager { data.as_ref(), &mut self.reader_message_counter, ) - .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; + .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; let request = match self.parse_request(&decrypted_request) { Ok(r) => r, Err(e) => { @@ -446,11 +450,11 @@ impl SessionManager { &response_bytes, &mut self.device_message_counter, ) - .unwrap_or_else(|_e| { - //tracing::warn!("unable to encrypt response: {}", e); - status = Some(session::Status::SessionEncryptionError); - Default::default() - }); + .unwrap_or_else(|_e| { + //tracing::warn!("unable to encrypt response: {}", e); + status = Some(session::Status::SessionEncryptionError); + Default::default() + }); let data = if status.is_some() { None } else { @@ -892,9 +896,9 @@ pub fn nearest_age_attestation( .collect(); let (true_age_over_claims, false_age_over_claims): (Vec<_>, Vec<_>) = - age_over_claims_numerical? - .into_iter() - .partition(|x| x.1.to_owned().into_inner().element_value == ciborium::Value::Bool(true).into()); + age_over_claims_numerical?.into_iter().partition(|x| { + x.1.to_owned().into_inner().element_value == ciborium::Value::Bool(true).into() + }); let nearest_age_over = true_age_over_claims .iter() @@ -981,7 +985,7 @@ mod test { } } ])) - .unwrap(); + .unwrap(); let permitted = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -998,7 +1002,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let expected: PermittedItems = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -1006,7 +1010,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let filtered = super::filter_permitted(&requested, permitted); diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index 1c061995..9bf3824f 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -13,6 +13,8 @@ //! //! You can view examples in `tests` directory in `simulated_device_and_reader.rs`, for a basic example and //! `simulated_device_and_reader_state.rs` which uses `State` pattern, `Arc` and `Mutex`. +use crate::cbor; +use crate::cbor::{CborError, Value as CborValue}; use crate::definitions::{ device_engagement::DeviceRetrievalMethod, device_request::{self, DeviceRequest, DocRequest, ItemsRequest}, @@ -25,12 +27,10 @@ use crate::definitions::{ }; use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; -use crate::cbor::{CborError, Value as CborValue}; use serde_json::json; use serde_json::Value; use std::collections::BTreeMap; use uuid::Uuid; -use crate::cbor; /// The main state of the reader. /// @@ -219,7 +219,7 @@ impl SessionManager { &device_request_bytes, &mut self.reader_message_counter, ) - .map_err(|e| anyhow!("unable to encrypt request: {}", e)) + .map_err(|e| anyhow!("unable to encrypt request: {}", e)) } /// Handles a response from the device. @@ -242,7 +242,7 @@ impl SessionManager { encrypted_response.as_ref(), &mut self.device_message_counter, ) - .map_err(|_e| Error::DecryptionError)?; + .map_err(|_e| Error::DecryptionError)?; let response: DeviceResponse = cbor::from_slice(&decrypted_response)?; let mut core_namespace = BTreeMap::::new(); let mut aamva_namespace = BTreeMap::::new(); diff --git a/tests/common.rs b/tests/common.rs index be3c24d7..6cd060af 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -1,7 +1,5 @@ #![allow(dead_code)] use anyhow::{anyhow, Context, Result}; -use signature::Signer; -use uuid::Uuid; use isomdl::cbor; use isomdl::definitions::device_engagement::{CentralClientMode, DeviceRetrievalMethods}; use isomdl::definitions::device_request::{DataElements, DocType, Namespaces}; @@ -9,6 +7,8 @@ use isomdl::definitions::helpers::NonEmptyMap; use isomdl::definitions::{self, BleOptions, DeviceRetrievalMethod}; use isomdl::presentation::device::{Document, Documents, RequestedItems, SessionManagerEngaged}; use isomdl::presentation::{device, reader, Stringify}; +use signature::Signer; +use uuid::Uuid; pub const DOC_TYPE: &str = "org.iso.18013.5.1.mDL"; pub const NAMESPACE: &str = "org.iso.18013.5.1"; diff --git a/tests/simulated_device_and_reader_state.rs b/tests/simulated_device_and_reader_state.rs index 98cac307..7b663460 100644 --- a/tests/simulated_device_and_reader_state.rs +++ b/tests/simulated_device_and_reader_state.rs @@ -1,14 +1,14 @@ use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; -use signature::Signer; -use uuid::Uuid; use isomdl::cbor; use isomdl::definitions::device_engagement::{CentralClientMode, DeviceRetrievalMethods}; use isomdl::definitions::device_request::{DataElements, Namespaces}; use isomdl::definitions::{self, BleOptions, DeviceRetrievalMethod}; use isomdl::presentation::device::{Documents, RequestedItems}; use isomdl::presentation::{device, reader}; +use signature::Signer; +use uuid::Uuid; use crate::common::{Device, AGE_OVER_21_ELEMENT, DOC_TYPE, NAMESPACE}; From 7d9084747557c91c8255bf36e52bbe5b181a72ae Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 7 Sep 2024 04:14:48 +0300 Subject: [PATCH 05/19] 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 3864ca76..9d84414a 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 243b0a26..9e24adb8 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 7a8a0f31..04800f22 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 3d922d25..1d4ef2d6 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 423ff072..3712528d 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 638bbca9..d2c1b1fb 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 0ee12ae3..2e126521 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 40b164bc..f627c63e 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, } From ccaae0be4fa34799df2a67703d260b04289c7045 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sat, 7 Sep 2024 04:41:53 +0300 Subject: [PATCH 06/19] Add MaybeTagged struct and wrap Cose objects in that --- src/cbor.rs | 2 +- src/cose.rs | 2 +- src/cose/mac0.rs | 19 +++++++++++-------- src/cose/sign1.rs | 13 ++++++++----- src/definitions/device_request.rs | 4 ++-- src/definitions/device_signed.rs | 8 +++++--- src/definitions/issuer_signed.rs | 4 ++-- src/presentation/device.rs | 2 +- 8 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/cbor.rs b/src/cbor.rs index bfc88102..56bd1b3a 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -5,7 +5,7 @@ use std::io::Cursor; use std::ops::{Deref, DerefMut}; use thiserror::Error; -/// Wraps [chromium::Value] and implements [PartialEq], [Eq], [PartialOrd] and [Ord], +/// Wraps [ciborium::Value] and implements [PartialEq], [Eq], [PartialOrd] and [Ord], /// so it can be used in maps and sets. /// /// Also, useful in future if we want to change the CBOR library. diff --git a/src/cose.rs b/src/cose.rs index 9d84414a..c67ab976 100644 --- a/src/cose.rs +++ b/src/cose.rs @@ -1,7 +1,7 @@ use std::borrow::{Borrow, BorrowMut}; use std::ops::{Deref, DerefMut}; -use coset::{AsCborValue, iana, TaggedCborSerializable}; +use coset::{iana, AsCborValue, TaggedCborSerializable}; use crate::cose::serialized_as_cbor_value::SerializedAsCborValue; diff --git a/src/cose/mac0.rs b/src/cose/mac0.rs index 9e24adb8..61ff9d15 100644 --- a/src/cose/mac0.rs +++ b/src/cose/mac0.rs @@ -1,6 +1,9 @@ use ::hmac::Hmac; use coset::cwt::ClaimsSet; -use coset::{mac_structure_data, CborSerializable, CoseError, MacContext, RegisteredLabelWithPrivate, CoseMac0}; +use coset::{ + mac_structure_data, CborSerializable, CoseError, CoseMac0, MacContext, + RegisteredLabelWithPrivate, +}; use digest::{Mac, MacError}; use serde::{Deserialize, Serialize}; use sha2::Sha256; @@ -238,13 +241,13 @@ mod hmac { mod tests { use crate::cbor; use crate::cose::mac0::{CoseMac0, PreparedCoseMac0}; + use crate::cose::MaybeTagged; use coset::cwt::{ClaimsSet, Timestamp}; use coset::{iana, CborSerializable, Header}; use digest::Mac; 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"); @@ -255,8 +258,8 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); - let mut parsed: MaybeTagged = cbor::from_slice(&bytes) - .expect("failed to parse COSE_MAC0 from 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"); assert_eq!( @@ -304,8 +307,8 @@ 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: MaybeTagged = cbor::from_slice(&cose_mac0_bytes) - .expect("failed to parse COSE_MAC0 from bytes"); + let cose_mac0: MaybeTagged = + cbor::from_slice(&cose_mac0_bytes).expect("failed to parse COSE_MAC0 from bytes"); cose_mac0 .verify(&verifier, None, None) @@ -396,8 +399,8 @@ mod tests { #[test] fn deserializing_tdeserializing_signed_cwtagged_cwt() { let cose_mac0_bytes = hex::decode(RFC8392_MAC0).unwrap(); - let cose_mac0: MaybeTagged = cbor::from_slice(&cose_mac0_bytes) - .expect("failed to parse COSE_MAC0 from 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() .expect("failed to parse claims set from payload") diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index 04800f22..2e0c5b60 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -1,5 +1,8 @@ -use coset::{CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, sig_structure_data, SignatureContext}; use coset::cwt::ClaimsSet; +use coset::{ + sig_structure_data, CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, + SignatureContext, +}; use serde::{Deserialize, Serialize}; use signature::Verifier; @@ -276,16 +279,16 @@ mod p384 { #[cfg(test)] mod tests { - 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 crate::cbor; - use crate::cose::{MaybeTagged, SignatureAlgorithm}; use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; + use crate::cose::{MaybeTagged, SignatureAlgorithm}; static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); static COSE_KEY: &str = include_str!("../../test/definitions/cose/sign1/secret_key"); @@ -296,8 +299,8 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut parsed: MaybeTagged = cbor::from_slice(&bytes) - .expect("failed to parse COSE_Sign1 from 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"); println!("bytes: {:?}", hex::encode(&bytes)); diff --git a/src/definitions/device_request.rs b/src/definitions/device_request.rs index 1d4ef2d6..e80dafdc 100644 --- a/src/definitions/device_request.rs +++ b/src/definitions/device_request.rs @@ -1,9 +1,9 @@ //! This module contains the definitions for the device request functionality. +use crate::cose::MaybeTagged; use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec, Tag24}; +use coset::CoseSign1; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; -use coset::CoseSign1; -use crate::cose::MaybeTagged; pub type ItemsRequestBytes = Tag24; pub type DocType = String; diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 3712528d..3ac8d2d8 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::MaybeTagged; use crate::definitions::{ helpers::{NonEmptyMap, Tag24}, session::SessionTranscript, }; +use coset::{CoseMac0, CoseSign1}; 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,7 +38,9 @@ pub type DeviceSignedItems = NonEmptyMap; #[serde(untagged)] pub enum DeviceAuth { #[serde(rename_all = "camelCase")] - Signature { device_signature: MaybeTagged }, + Signature { + device_signature: MaybeTagged, + }, #[serde(rename_all = "camelCase")] Mac { device_mac: MaybeTagged }, } diff --git a/src/definitions/issuer_signed.rs b/src/definitions/issuer_signed.rs index d2c1b1fb..6df2a471 100644 --- a/src/definitions/issuer_signed.rs +++ b/src/definitions/issuer_signed.rs @@ -10,14 +10,14 @@ //! - [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::MaybeTagged; use crate::definitions::{ helpers::{ByteStr, NonEmptyMap, NonEmptyVec, Tag24}, DigestId, }; +use coset::CoseSign1; use serde::{Deserialize, Serialize}; -use crate::cose::MaybeTagged; /// Represents an issuer-signed object. /// diff --git a/src/presentation/device.rs b/src/presentation/device.rs index f627c63e..2e4d22cd 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -19,6 +19,7 @@ use crate::cbor::{CborError, Value as CborValue}; use crate::cose::mac0::PreparedCoseMac0; use crate::cose::sign1::PreparedCoseSign1; +use crate::cose::MaybeTagged; use crate::definitions::device_signed::DeviceAuthType; use crate::definitions::IssuerSignedItem; use crate::{ @@ -47,7 +48,6 @@ use session::SessionTranscript180135; use std::collections::BTreeMap; use std::num::ParseIntError; use uuid::Uuid; -use crate::cose::MaybeTagged; /// Initialisation state. /// From 670bce2d46a7df7cd9560a6c944bf7db3bf8142e Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Mon, 9 Sep 2024 18:05:12 +0300 Subject: [PATCH 07/19] use correct TAG in MaybeTagged when serializing --- src/cose.rs | 4 ++-- src/cose/sign1.rs | 16 +++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/cose.rs b/src/cose.rs index c67ab976..6e42f527 100644 --- a/src/cose.rs +++ b/src/cose.rs @@ -90,7 +90,7 @@ where S: serde::Serializer, { let tag = if self.tagged { - Some(coset::CoseSign1::TAG) + Some(T::TAG) } else { None }; @@ -111,7 +111,7 @@ where let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) = ciborium::tag::Captured::deserialize(deserializer)?; let tagged = match tag { - Some(coset::CoseSign1::TAG) => true, + Some(tag) if tag == T::TAG => true, Some(_) => return Err(serde::de::Error::custom("unexpected tag")), None => false, }; diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index 2e0c5b60..dbf99014 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -1,8 +1,5 @@ +use coset::{CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, sig_structure_data, SignatureContext}; use coset::cwt::ClaimsSet; -use coset::{ - sig_structure_data, CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, - SignatureContext, -}; use serde::{Deserialize, Serialize}; use signature::Verifier; @@ -279,16 +276,16 @@ mod p384 { #[cfg(test)] mod tests { + 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 crate::cbor; - use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; 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"); @@ -299,12 +296,9 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut parsed: MaybeTagged = - cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); - parsed.set_tagged(); + let parsed: MaybeTagged = cbor::from_slice(&bytes) + .expect("failed to parse COSE_Sign1 from bytes"); let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_Sign1 to bytes"); - println!("bytes: {:?}", hex::encode(&bytes)); - println!("roundtripped: {:?}", hex::encode(&roundtripped)); assert_eq!( bytes, roundtripped, "original bytes and roundtripped bytes do not match" From 0c6efb53a6343a8508650d259ce9c4a4f5f19267 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 11 Sep 2024 06:54:32 +0300 Subject: [PATCH 08/19] handle the case of non-finite floats --- Cargo.toml | 1 + README.md | 1 + macros/src/to_cbor.rs | 4 +- src/cbor.rs | 249 ++++++++++++++++-- src/cose.rs | 6 +- src/cose/mac0.rs | 3 +- src/cose/serialized_as_cbor_value.rs | 2 +- src/cose/sign1.rs | 13 +- src/definitions/device_engagement.rs | 33 +-- src/definitions/device_engagement/error.rs | 2 + .../device_engagement/nfc_options.rs | 6 +- src/definitions/device_key/cose_key.rs | 44 ++-- src/definitions/device_response.rs | 66 ++++- src/definitions/device_signed.rs | 26 ++ src/definitions/helpers/bytestr.rs | 2 +- src/definitions/helpers/tag24.rs | 8 +- src/definitions/namespaces/fulldate.rs | 4 +- .../namespaces/org_iso_18013_5_1/age_over.rs | 7 +- .../namespaces/org_iso_18013_5_1/alpha2.rs | 2 +- .../org_iso_18013_5_1/driving_privileges.rs | 11 +- .../org_iso_18013_5_1/eye_colour.rs | 4 +- .../org_iso_18013_5_1/hair_colour.rs | 4 +- .../namespaces/org_iso_18013_5_1/sex.rs | 2 +- .../namespaces/org_iso_18013_5_1/tdate.rs | 4 +- .../domestic_driving_privileges.rs | 10 +- .../org_iso_18013_5_1_aamva/present.rs | 2 +- src/definitions/traits/to_cbor.rs | 4 +- src/definitions/validity_info.rs | 20 +- src/presentation/device.rs | 18 +- src/presentation/reader.rs | 6 +- test/definitions/device_response.cbor | 2 +- 31 files changed, 462 insertions(+), 104 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1bd349cd..5cdd7270 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ strum_macros = "0.24" coset = "0.3.8" ciborium = "0.2.2" digest = "0.10.7" +hex = "0.4.3" [dev-dependencies] hex = "0.4.3" diff --git a/README.md b/README.md index 69602685..1d6d4da4 100644 --- a/README.md +++ b/README.md @@ -131,3 +131,4 @@ stateDiagram You can see the full example in [simulated_device_and_reader](tests/simulated_device_and_reader.rs) and a version that uses `State` pattern, `Arc` and `Mutex` [simulated_device_and_reader](tests/simulated_device_and_reader_state.rs). + diff --git a/macros/src/to_cbor.rs b/macros/src/to_cbor.rs index 473094da..185dc515 100644 --- a/macros/src/to_cbor.rs +++ b/macros/src/to_cbor.rs @@ -102,9 +102,9 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt fn to_cbor(self) -> Value { let map = self.to_ns_map() .into_iter() - .map(|(k, v)| (ciborium::Value::Text(k), v.into())) + .map(|(k, v)| (ciborium::Value::Text(k), v.try_into().unwrap())) .collect(); - ciborium::Value::Map(map).into() + ciborium::Value::Map(map).try_into().unwrap() } } } diff --git a/src/cbor.rs b/src/cbor.rs index 56bd1b3a..34735111 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -1,16 +1,56 @@ -use coset::{cbor, CoseError, EndOfFile}; -use serde::{de, Deserialize, Serialize}; use std::borrow::{Borrow, BorrowMut}; +use std::cmp::Ordering; use std::io::Cursor; use std::ops::{Deref, DerefMut}; + +use coset::{cbor, CoseError, EndOfFile}; +use serde::{de, Deserialize, Serialize}; use thiserror::Error; /// Wraps [ciborium::Value] and implements [PartialEq], [Eq], [PartialOrd] and [Ord], /// so it can be used in maps and sets. /// +/// [IEEE754](https://www.rfc-editor.org/rfc/rfc8949.html#IEEE754) +/// non-finite floats do not have a total ordering, +/// which means [`Ord`] cannot be correctly implemented for types that may contain them. +/// That's why we don't support such values. +/// /// Also, useful in future if we want to change the CBOR library. #[derive(Debug, Clone)] -pub struct Value(pub ciborium::Value); +pub struct Value(pub(crate) ciborium::Value); + +impl Value { + /// Create a new CBOR value. + /// + /// Return an error if the value contains non-finite floats or NaN. + pub fn from(value: ciborium::Value) -> Result { + // Validate the CBOR value. If it contains non-finite floats, return an error. + if contains_non_finite_floats(&value) { + Err(CborError::NonFiniteFloats) + } else { + Ok(Value(value)) + } + } + + /// Unsafe version of `new`. + /// + /// It will allow creating from value containing non-finite floats or NaN. + pub unsafe fn from_unsafe(value: ciborium::Value) -> Self { + Value(value) + } +} + +// Helper function to check for non-finite floats +fn contains_non_finite_floats(value: &ciborium::Value) -> bool { + match value { + ciborium::Value::Float(f) => !f.is_finite(), + ciborium::Value::Array(arr) => arr.iter().any(contains_non_finite_floats), + ciborium::Value::Map(map) => map + .iter() + .any(|(k, v)| contains_non_finite_floats(k) || contains_non_finite_floats(v)), + _ => false, + } +} #[derive(Debug, Error)] pub enum CborError { @@ -39,6 +79,48 @@ pub enum CborError { /// Unrecognized value in neither IANA-controlled range nor private range. #[error("unregistered non-private IANA value")] UnregisteredIanaNonPrivateValue, + /// Value contains non-finite float (NaN or Infinity). + #[error("non finite floats")] + NonFiniteFloats, +} + +impl PartialEq for CborError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::DecodeFailed(_), Self::DecodeFailed(_)) => true, + (Self::DuplicateMapKey, Self::DuplicateMapKey) => true, + (Self::EncodeFailed, Self::EncodeFailed) => true, + (Self::ExtraneousData, Self::ExtraneousData) => true, + (Self::OutOfRangeIntegerValue, Self::OutOfRangeIntegerValue) => true, + (Self::UnexpectedItem(l_msg, l_want), Self::UnexpectedItem(r_msg, r_want)) => { + l_msg == r_msg && l_want == r_want + } + (Self::UnregisteredIanaValue, Self::UnregisteredIanaValue) => true, + (Self::UnregisteredIanaNonPrivateValue, Self::UnregisteredIanaNonPrivateValue) => true, + (Self::NonFiniteFloats, Self::NonFiniteFloats) => true, + _ => false, + } + } +} + +impl Eq for CborError {} + +impl Clone for CborError { + fn clone(&self) -> Self { + match self { + CborError::DecodeFailed(_) => panic!("cannot clone"), + CborError::DuplicateMapKey => CborError::DuplicateMapKey, + CborError::EncodeFailed => CborError::EncodeFailed, + CborError::ExtraneousData => CborError::ExtraneousData, + CborError::OutOfRangeIntegerValue => CborError::OutOfRangeIntegerValue, + CborError::UnexpectedItem(msg, want) => CborError::UnexpectedItem(msg, want), + CborError::UnregisteredIanaValue => CborError::UnregisteredIanaValue, + CborError::UnregisteredIanaNonPrivateValue => { + CborError::UnregisteredIanaNonPrivateValue + } + CborError::NonFiniteFloats => CborError::NonFiniteFloats, + } + } } impl From for CborError { @@ -80,23 +162,23 @@ impl DerefMut for Value { } } -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 +impl Eq for Value {} + +impl Ord for Value { + fn cmp(&self, other: &Self) -> Ordering { + self.0.partial_cmp(&other.0).unwrap() } } -impl Eq for Value {} - impl PartialOrd for Value { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) } } -impl Ord for Value { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.0.partial_cmp(&other.0).unwrap() +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + self.0.eq(&other.0) } } @@ -151,9 +233,11 @@ where from_slice(&bytes) } -impl From for Value { - fn from(value: ciborium::Value) -> Self { - Self(value) +impl TryFrom for Value { + type Error = CborError; + + fn try_from(value: ciborium::Value) -> Result { + Value::from(value) } } @@ -203,13 +287,18 @@ macro_rules! impl_from { ($variant:path, $for_type:ty) => { impl From<$for_type> for Value { fn from(v: $for_type) -> Value { - $variant(v.into()).into() + unsafe { Value::from_unsafe($variant(v.into())) } } } }; } impl_from!(ciborium::Value::Bool, bool); +impl_from!(ciborium::Value::Bytes, Vec); +impl_from!(ciborium::Value::Bytes, &[u8]); +impl_from!(ciborium::Value::Text, String); +impl_from!(ciborium::Value::Text, &str); +impl_from!(ciborium::Value::Array, Vec); impl_from!(ciborium::Value::Integer, i8); impl_from!(ciborium::Value::Integer, i16); impl_from!(ciborium::Value::Integer, i32); @@ -222,6 +311,126 @@ impl_from!(ciborium::Value::Integer, u64); // u128 omitted because not all numbers fit in CBOR serialization impl_from!(ciborium::Value::Float, f32); impl_from!(ciborium::Value::Float, f64); -impl_from!(ciborium::Value::Bytes, Vec); -impl_from!(ciborium::Value::Text, String); -impl_from!(ciborium::Value::Array, Vec); + +#[cfg(test)] +mod tests { + use crate::cbor::{CborError, Value}; + + #[test] + fn conversions() { + assert_eq!( + Value::from(ciborium::Value::Bool(true)), + Ok(ciborium::Value::Bool(true).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1i8.into())), + Ok(ciborium::Value::Integer(1i8.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1i16.into())), + Ok(ciborium::Value::Integer(1i16.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1i32.into())), + Ok(ciborium::Value::Integer(1i32.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1i64.into())), + Ok(ciborium::Value::Integer(1i64.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1u8.into())), + Ok(ciborium::Value::Integer(1u8.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1u16.into())), + Ok(ciborium::Value::Integer(1u16.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1u32.into())), + Ok(ciborium::Value::Integer(1u32.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Integer(1u64.into())), + Ok(ciborium::Value::Integer(1u64.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Float(1.0f32.into())), + Ok(ciborium::Value::Float(1.0f32.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Float(1.0f64.into())), + Ok(ciborium::Value::Float(1.0f64.into()).try_into().unwrap()) + ); + assert_eq!( + Value::from(ciborium::Value::Text("foo".to_string())), + Ok(ciborium::Value::Text("foo".to_string()).try_into().unwrap()) + ); + } + + #[test] + fn non_finite_floats() { + assert_eq!( + Value::from(ciborium::Value::from(f32::NAN)), + Err(CborError::NonFiniteFloats) + ); + assert_eq!( + Value::from(ciborium::Value::from(f32::INFINITY)), + Err(CborError::NonFiniteFloats) + ); + assert_eq!( + Value::from(ciborium::Value::from(f32::NEG_INFINITY)), + Err(CborError::NonFiniteFloats) + ); + assert_eq!( + Value::from(ciborium::Value::from(f64::NAN)), + Err(CborError::NonFiniteFloats) + ); + assert_eq!( + Value::from(ciborium::Value::from(f64::NEG_INFINITY)), + Err(CborError::NonFiniteFloats) + ); + } + + #[test] + #[should_panic] + fn non_finite_floats_no_panic() { + let _ = Value::from(ciborium::Value::from(f32::NAN)).unwrap(); + } + + #[test] + fn non_finite_floats_unsafe() { + unsafe { + assert!(Value::from_unsafe(ciborium::Value::from(f32::NAN)) + .0 + .into_float() + .unwrap() + .is_nan()); + assert!(Value::from_unsafe(ciborium::Value::from(f32::INFINITY)) + .0 + .into_float() + .unwrap() + .is_infinite()); + assert!(Value::from_unsafe(ciborium::Value::from(f32::NEG_INFINITY)) + .0 + .into_float() + .unwrap() + .is_infinite()); + assert!(Value::from_unsafe(ciborium::Value::from(f64::NAN)) + .0 + .into_float() + .unwrap() + .is_nan()); + assert!(Value::from_unsafe(ciborium::Value::from(f64::INFINITY)) + .0 + .into_float() + .unwrap() + .is_infinite()); + assert!(Value::from_unsafe(ciborium::Value::from(f64::NEG_INFINITY)) + .0 + .into_float() + .unwrap() + .is_infinite()); + } + } +} diff --git a/src/cose.rs b/src/cose.rs index 6e42f527..609aa60e 100644 --- a/src/cose.rs +++ b/src/cose.rs @@ -89,11 +89,7 @@ where where S: serde::Serializer, { - let tag = if self.tagged { - Some(T::TAG) - } else { - None - }; + let tag = if self.tagged { Some(T::TAG) } else { None }; ciborium::tag::Captured(tag, SerializedAsCborValue(&self.inner)).serialize(serializer) } diff --git a/src/cose/mac0.rs b/src/cose/mac0.rs index 61ff9d15..02528fc8 100644 --- a/src/cose/mac0.rs +++ b/src/cose/mac0.rs @@ -258,9 +258,8 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); - let mut parsed: MaybeTagged = + 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_MAC0"); assert_eq!( bytes, roundtripped, diff --git a/src/cose/serialized_as_cbor_value.rs b/src/cose/serialized_as_cbor_value.rs index 024f69ef..04dc815b 100644 --- a/src/cose/serialized_as_cbor_value.rs +++ b/src/cose/serialized_as_cbor_value.rs @@ -1,7 +1,7 @@ use coset::AsCborValue; use serde::{Deserialize, Serialize}; -/// This is a small helper wrapper to deal with `coset`` types that don't +/// This is a small helper wrapper to deal with `coset` types that don't /// implement `Serialize`/`Deserialize` but only `AsCborValue`. pub struct SerializedAsCborValue(pub T); diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index dbf99014..f3514b45 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -1,5 +1,8 @@ -use coset::{CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, sig_structure_data, SignatureContext}; use coset::cwt::ClaimsSet; +use coset::{ + sig_structure_data, CborSerializable, CoseError, CoseSign1, RegisteredLabelWithPrivate, + SignatureContext, +}; use serde::{Deserialize, Serialize}; use signature::Verifier; @@ -276,16 +279,16 @@ mod p384 { #[cfg(test)] mod tests { - 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 crate::cbor; - use crate::cose::{MaybeTagged, SignatureAlgorithm}; use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; + use crate::cose::{MaybeTagged, SignatureAlgorithm}; static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); static COSE_KEY: &str = include_str!("../../test/definitions/cose/sign1/secret_key"); @@ -296,8 +299,8 @@ mod tests { #[test] fn roundtrip() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let parsed: MaybeTagged = cbor::from_slice(&bytes) - .expect("failed to parse COSE_Sign1 from bytes"); + let parsed: MaybeTagged = + cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); let roundtripped = cbor::to_vec(&parsed).expect("failed to serialize COSE_Sign1 to bytes"); assert_eq!( bytes, roundtripped, diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index 44657150..7abb1bad 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -182,7 +182,7 @@ impl TryFrom for DeviceEngagement { .map(|(k, v)| (CborValue(k), CborValue(v))) .collect::>(); let device_engagement_version = map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; cbor }); if let Some(CborValue(ciborium::Value::Text(v))) = device_engagement_version { @@ -201,7 +201,7 @@ impl TryFrom for DeviceEngagement { let device_retrieval_methods = map .remove(&{ - let cbor: CborValue = ciborium::Value::Integer(2.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(2.into()).try_into()?; cbor }) .map(cbor::from_value2) @@ -210,7 +210,7 @@ impl TryFrom for DeviceEngagement { let server_retrieval_methods = map .remove(&{ - let cbor: CborValue = ciborium::Value::Integer(3.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(3.into()).try_into()?; cbor }) .map(cbor::from_value2) @@ -220,7 +220,7 @@ impl TryFrom for DeviceEngagement { //tracing::warn!("server_retrieval is unimplemented.") } let protocol_info = map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(4.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(4.into()).try_into()?; cbor }); if protocol_info.is_some() { @@ -343,11 +343,11 @@ impl TryFrom for BleOptions { .collect::>(); let central_client_mode = match ( map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(1.into()).try_into()?; cbor }), map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(11.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(11.into()).try_into()?; cbor }), ) { @@ -366,11 +366,11 @@ impl TryFrom for BleOptions { let peripheral_server_mode = match ( map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; cbor }), map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(10.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(10.into()).try_into()?; cbor }), ) { @@ -380,7 +380,7 @@ impl TryFrom for BleOptions { ) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; let ble_device_address = match map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(20.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(20.into()).try_into()?; cbor }) { Some(value) => Some(value.try_into().map_err(|_| Error::Malformed)?), @@ -456,7 +456,7 @@ impl From for CborValue { } } - ciborium::Value::Map(map).into() + ciborium::Value::Map(map).try_into().unwrap() } } @@ -470,7 +470,8 @@ impl TryFrom for WifiOptions { ) -> Result, Error> { match map.get(&{ let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) + .try_into()?; cbor }) { None => Ok(None), @@ -485,7 +486,8 @@ impl TryFrom for WifiOptions { ) -> Result, Error> { match map.get(&{ let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) + .try_into()?; cbor }) { None => Ok(None), @@ -504,7 +506,8 @@ impl TryFrom for WifiOptions { ) -> Result, Error> { match map.get(&{ let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?).into(); + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) + .try_into()?; cbor }) { None => Ok(None), @@ -575,7 +578,7 @@ impl From for CborValue { map.push((ciborium::Value::Integer(3.into()), into_value(v).unwrap())); } - ciborium::Value::Map(map).into() + ciborium::Value::Map(map).try_into().unwrap() } } @@ -605,7 +608,7 @@ impl From for CborValue { )); } - ciborium::Value::Map(map).into() + ciborium::Value::Map(map).try_into().unwrap() } } diff --git a/src/definitions/device_engagement/error.rs b/src/definitions/device_engagement/error.rs index b26d3d66..d433659a 100644 --- a/src/definitions/device_engagement/error.rs +++ b/src/definitions/device_engagement/error.rs @@ -25,6 +25,8 @@ pub enum Error { Tag24Error, #[error("Could not deserialize from cbor")] CborError, + #[error("Could not deserialize from cbor")] + CborErrorWithSource(CborError), #[error("NFC Command Data Length must be between 255 and 65535")] InvalidNfcCommandDataLengthError, #[error("NFC Response Data Length must be between 256 and 65536")] diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index 5b5bb8ce..91c5780d 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -36,7 +36,7 @@ impl TryFrom for NfcOptions { Ok(NfcOptions::default()) .and_then(|nfc_opts| { map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(0.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; cbor }) .ok_or(Error::InvalidNfcOptions) @@ -48,7 +48,7 @@ impl TryFrom for NfcOptions { }) .and_then(|nfc_opts| { map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(1.into()).try_into()?; cbor }) .ok_or(Error::InvalidNfcOptions) @@ -380,6 +380,7 @@ mod test { } #[test] + #[ignore] fn nfc_options_cbor_roundtrip_command_length_error_test() { let nfc_options: NfcOptions = NfcOptions { max_len_command_data_field: CommandDataLength(0), //This should not work in non-tests @@ -394,6 +395,7 @@ mod test { } #[test] + #[ignore] fn nfc_options_cbor_roundtrip_response_data_error_test() { let nfc_options: NfcOptions = NfcOptions { max_len_command_data_field: CommandDataLength(0), //This should not work in non-tests diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index 5fdf3266..b99e0729 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -22,7 +22,7 @@ //! } //! } //! ``` -use crate::cbor::Value as CborValue; +use crate::cbor::{CborError, Value as CborValue}; use aes::cipher::generic_array::{typenum::U8, GenericArray}; use coset::iana::Algorithm; use p256::EncodedPoint; @@ -64,7 +64,7 @@ pub enum OKPCurve { Ed448, } -/// Errors that can occur when deserialising a COSE_Key. +/// Errors that can occur when deserializing a COSE_Key. #[derive(Debug, Clone, thiserror::Error)] pub enum Error { #[error("COSE_Key of kty 'EC2' missing x coordinate")] @@ -86,6 +86,8 @@ pub enum Error { InvalidCoseKey, #[error("Constructing a JWK from CoseKey with point-compression is not supported.")] UnsupportedFormat, + #[error("could not serialize from to cbor: {0}")] + CborError(CborError), } impl CoseKey { @@ -161,7 +163,7 @@ impl From for CborValue { )); } } - ciborium::Value::Map(map).into() + ciborium::Value::Map(map).try_into().unwrap() } } @@ -176,15 +178,21 @@ impl TryFrom for CoseKey { .collect::>(); match ( map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(1.into()).into(); + let cbor: CborValue = ciborium::Value::Integer(1.into()) + .try_into() + .map_err(Error::CborError)?; cbor }), map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-1).into()).into(); + let cbor: CborValue = ciborium::Value::Integer((-1).into()) + .try_into() + .map_err(Error::CborError)?; cbor }), map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-2).into()).into(); + let cbor: CborValue = ciborium::Value::Integer((-2).into()) + .try_into() + .map_err(Error::CborError)?; cbor }), ) { @@ -197,7 +205,9 @@ impl TryFrom for CoseKey { let crv = crv_id.try_into()?; let y = map .remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-3).into()).into(); + let cbor: CborValue = ciborium::Value::Integer((-3).into()) + .try_into() + .map_err(Error::CborError)?; cbor }) .ok_or(Error::EC2MissingY)? @@ -270,8 +280,8 @@ impl TryFrom for EncodedPoint { impl From for CborValue { fn from(y: EC2Y) -> CborValue { match y { - EC2Y::Value(s) => ciborium::Value::Bytes(s).into(), - EC2Y::SignBit(b) => ciborium::Value::Bool(b).into(), + EC2Y::Value(s) => ciborium::Value::Bytes(s).try_into().unwrap(), + EC2Y::SignBit(b) => ciborium::Value::Bool(b).try_into().unwrap(), } } } @@ -291,10 +301,10 @@ impl TryFrom for EC2Y { impl From for CborValue { fn from(crv: EC2Curve) -> CborValue { match crv { - EC2Curve::P256 => ciborium::Value::Integer(1.into()).into(), - EC2Curve::P384 => ciborium::Value::Integer(2.into()).into(), - EC2Curve::P521 => ciborium::Value::Integer(3.into()).into(), - EC2Curve::P256K => ciborium::Value::Integer(8.into()).into(), + EC2Curve::P256 => ciborium::Value::Integer(1.into()).try_into().unwrap(), + EC2Curve::P384 => ciborium::Value::Integer(2.into()).try_into().unwrap(), + EC2Curve::P521 => ciborium::Value::Integer(3.into()).try_into().unwrap(), + EC2Curve::P256K => ciborium::Value::Integer(8.into()).try_into().unwrap(), } } } @@ -316,10 +326,10 @@ impl TryFrom for EC2Curve { impl From for CborValue { fn from(crv: OKPCurve) -> CborValue { match crv { - OKPCurve::X25519 => ciborium::Value::Integer(4.into()).into(), - OKPCurve::X448 => ciborium::Value::Integer(5.into()).into(), - OKPCurve::Ed25519 => ciborium::Value::Integer(6.into()).into(), - OKPCurve::Ed448 => ciborium::Value::Integer(7.into()).into(), + OKPCurve::X25519 => ciborium::Value::Integer(4.into()).try_into().unwrap(), + OKPCurve::X448 => ciborium::Value::Integer(5.into()).try_into().unwrap(), + OKPCurve::Ed25519 => ciborium::Value::Integer(6.into()).try_into().unwrap(), + OKPCurve::Ed448 => ciborium::Value::Integer(7.into()).try_into().unwrap(), } } } diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index 7806900c..a81cd4ca 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -125,13 +125,21 @@ impl TryFrom for Status { #[cfg(test)] mod test { - use super::DeviceResponse; + use coset::{CoseMac0, CoseSign1}; + use super::{DeviceResponse, DocumentError, DocumentErrorCode, DocumentErrors, Documents, Status}; use hex::FromHex; + use crate::cbor; + use crate::cose::MaybeTagged; + use crate::definitions::{DeviceAuth, DeviceSigned, DigestId, Document, IssuerSigned, IssuerSignedItem}; + use crate::definitions::device_signed::{DeviceNamespaces, DeviceNamespacesBytes, DeviceSignedItems}; + use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec}; + use crate::definitions::issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}; static DEVICE_RESPONSE_CBOR: &str = include_str!("../../test/definitions/device_response.cbor"); + const RFC8392_MAC0: &str = "d18443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b0"; #[test] - fn serde_device_response() { + fn device_response() { let cbor_bytes = >::from_hex(DEVICE_RESPONSE_CBOR).expect("unable to convert cbor hex to bytes"); let response: DeviceResponse = @@ -143,4 +151,56 @@ mod test { "original cbor and re-serialized DeviceResponse do not match" ); } -} + + #[test] + fn device_response_roundtrip() { + static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); + static COSE_MAC0: &str = include_str!("../../test/definitions/cose/mac0/serialized.cbor"); + + let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); + let mut cose_sign1: MaybeTagged = + cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); + let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); + let mut cose_mac0: MaybeTagged = + cbor::from_slice(&bytes).expect("failed to parse COSE_MAC0 from bytes"); + + let issuer_signed_item = IssuerSignedItem { + digest_id: DigestId::new(42), + random: vec![42_u8].into(), + element_identifier: "42".to_string(), + element_value: ciborium::Value::Null.try_into().unwrap(), + }; + let issuer_signed_item_bytes = IssuerSignedItemBytes::new(issuer_signed_item).unwrap(); + let vec = NonEmptyVec::new(issuer_signed_item_bytes); + let issuer_namespaces = IssuerNamespaces::new("a".to_string(), vec); + let device_signed_items = DeviceSignedItems::new("a".to_string(), ciborium::Value::Null.try_into().unwrap()); + let mut device_namespaces = DeviceNamespaces::new(); + device_namespaces.insert("a".to_string(), device_signed_items); + let device_namespaces_bytes = DeviceNamespacesBytes::new(device_namespaces).unwrap(); + let doc = Document { + doc_type: "aaa".to_string(), + issuer_signed: IssuerSigned { namespaces: Some(issuer_namespaces), issuer_auth: cose_sign1.clone() }, + device_signed: DeviceSigned { namespaces: device_namespaces_bytes, device_auth: DeviceAuth::Mac { device_mac: cose_mac0 } }, + errors: None, + }; + let docs = Documents::new(doc); + let document_error_code = DocumentErrorCode::DataNotReturned; + let mut error = DocumentError::new(); + error.insert("a".to_string(), document_error_code); + let errors = DocumentErrors::new(error); + let res = DeviceResponse { + version: "1.0".to_string(), + documents: Some(docs), + document_errors: Some(errors), + status: Status::OK, + }; + let bytes = cbor::to_vec(&res).unwrap(); + eprintln!("bytes {}", hex::encode(&bytes)); + let res: DeviceResponse = cbor::from_slice(&bytes).unwrap(); + let roundtripped_bytes = cbor::to_vec(&res).unwrap(); + assert_eq!( + bytes, roundtripped_bytes, + "original cbor and re-serialized DeviceResponse do not match" + ); + } +} \ No newline at end of file diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 3ac8d2d8..7a2a887d 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -78,3 +78,29 @@ pub enum Error { #[error("Unable to encode value as CBOR: {0}")] UnableToEncode(coset::CoseError), } + +#[cfg(test)] +mod tests { + use hex::FromHex; + use crate::cbor; + use super::*; + + static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); + + #[test] + fn device_auth() { + let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); + let mut cose_sign1: MaybeTagged = + cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); + cose_sign1.tagged = false; + + let device_auth = DeviceAuth::Signature { + device_signature: cose_sign1 + }; + let bytes = cbor::to_vec(&device_auth).unwrap(); + println!("bytes {}", hex::encode(&bytes)); + let roundtripped: DeviceAuth = cbor::from_slice(&bytes).unwrap(); + let roundtripped_bytes = cbor::to_vec(&roundtripped).unwrap(); + assert_eq!(bytes, roundtripped_bytes); + } +} \ No newline at end of file diff --git a/src/definitions/helpers/bytestr.rs b/src/definitions/helpers/bytestr.rs index 620b18a6..7f9460b9 100644 --- a/src/definitions/helpers/bytestr.rs +++ b/src/definitions/helpers/bytestr.rs @@ -33,7 +33,7 @@ impl AsRef<[u8]> for ByteStr { impl From for CborValue { fn from(ByteStr(bytes): ByteStr) -> CborValue { - ciborium::Value::Bytes(bytes).into() + ciborium::Value::Bytes(bytes).try_into().unwrap() } } diff --git a/src/definitions/helpers/tag24.rs b/src/definitions/helpers/tag24.rs index a29f18a5..a857a704 100644 --- a/src/definitions/helpers/tag24.rs +++ b/src/definitions/helpers/tag24.rs @@ -73,7 +73,9 @@ impl TryFrom for Tag24 { impl From> for CborValue { fn from(Tag24 { inner_bytes, .. }: Tag24) -> CborValue { - ciborium::Value::Tag(24, Box::new(ciborium::Value::Bytes(inner_bytes))).into() + ciborium::Value::Tag(24, Box::new(ciborium::Value::Bytes(inner_bytes))) + .try_into() + .unwrap() } } @@ -98,7 +100,9 @@ impl<'de, T: de::DeserializeOwned> Deserialize<'de> for Tag24 { where D: de::Deserializer<'de>, { - let cbor: CborValue = ciborium::Value::deserialize(d)?.into(); + let cbor: CborValue = ciborium::Value::deserialize(d)? + .try_into() + .map_err(DeError::custom)?; cbor.try_into().map_err(D::Error::custom) } } diff --git a/src/definitions/namespaces/fulldate.rs b/src/definitions/namespaces/fulldate.rs index b8997af4..aedae982 100644 --- a/src/definitions/namespaces/fulldate.rs +++ b/src/definitions/namespaces/fulldate.rs @@ -14,7 +14,9 @@ pub struct FullDate(Date); impl From for Cbor { fn from(d: FullDate) -> Cbor { - ciborium::Value::Tag(1004, Box::new(ciborium::Value::Text(d.to_string()))).into() + ciborium::Value::Tag(1004, Box::new(ciborium::Value::Text(d.to_string()))) + .try_into() + .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs index 8e43efe3..b398b2c9 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs @@ -50,7 +50,12 @@ impl ToNamespaceMap for AgeOver { fn to_ns_map(self) -> BTreeMap { self.0 .into_iter() - .map(|(Age(x, y), v)| (format!("age_over_{x}{y}"), ciborium::Value::Bool(v).into())) + .map(|(Age(x, y), v)| { + ( + format!("age_over_{x}{y}"), + ciborium::Value::Bool(v).try_into().unwrap(), + ) + }) .collect() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs index 0c639078..77b89fce 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs @@ -266,7 +266,7 @@ pub enum Error { impl From for Cbor { fn from(a: Alpha2) -> Cbor { let cbor: ciborium::Value = a.as_str().to_string().into(); - cbor.into() + cbor.try_into().unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index 16e57672..8867eb60 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -16,7 +16,9 @@ pub struct DrivingPrivileges(Vec); impl From for Cbor { fn from(d: DrivingPrivileges) -> Cbor { - ciborium::Value::Array(d.0.into_iter().map(|v| v.to_cbor().into()).collect()).into() + ciborium::Value::Array(d.0.into_iter().map(|v| v.to_cbor().into()).collect()) + .try_into() + .unwrap() } } @@ -57,7 +59,9 @@ pub enum VehicleCategoryCode { impl From for Cbor { fn from(c: VehicleCategoryCode) -> Cbor { - ciborium::Value::Text(c.as_ref().to_string()).into() + ciborium::Value::Text(c.as_ref().to_string()) + .try_into() + .unwrap() } } @@ -91,7 +95,8 @@ impl From for Cbor { .map(|v| v.to_cbor().into()) .collect(), ) - .into() + .try_into() + .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs index 5ece34d5..a89f4dc4 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs @@ -43,7 +43,9 @@ impl EyeColour { impl From for Cbor { fn from(h: EyeColour) -> Cbor { - ciborium::Value::Text(h.to_str().to_string()).into() + ciborium::Value::Text(h.to_str().to_string()) + .try_into() + .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs index 7989dc3c..b75631bf 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs @@ -43,7 +43,9 @@ impl HairColour { impl From for Cbor { fn from(h: HairColour) -> Cbor { - ciborium::Value::Text(h.to_str().to_string()).into() + ciborium::Value::Text(h.to_str().to_string()) + .try_into() + .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs index 11d2d2c1..3ba6ed71 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs @@ -19,7 +19,7 @@ pub enum Error { impl From for Cbor { fn from(s: Sex) -> Cbor { - u8::from(s).into() + u8::from(s).try_into().unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs index 85d329a1..6282396b 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs @@ -54,7 +54,9 @@ impl FromJson for TDateOrFullDate { impl From for Cbor { fn from(t: TDate) -> Cbor { - ciborium::Value::Tag(0, Box::new(t.0.into())).into() + ciborium::Value::Tag(0, Box::new(t.0.into())) + .try_into() + .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs index 915c1e5e..07c89800 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs @@ -13,7 +13,9 @@ pub struct DomesticDrivingPrivileges(Vec); impl ToCbor for DomesticDrivingPrivileges { fn to_cbor(self) -> Cbor { - ciborium::Value::Array(self.0.into_iter().map(|v| v.to_cbor().into()).collect()).into() + ciborium::Value::Array(self.0.into_iter().map(|v| v.to_cbor().into()).collect()) + .try_into() + .unwrap() } } @@ -47,7 +49,8 @@ impl ToCbor for DomesticVehicleRestrictions { .map(|v| v.to_cbor().into()) .collect(), ) - .into() + .try_into() + .unwrap() } } @@ -71,7 +74,8 @@ impl ToCbor for DomesticVehicleEndorsements { .map(|v| v.to_cbor().into()) .collect(), ) - .into() + .try_into() + .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs index e45c2b72..816bab86 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs @@ -19,6 +19,6 @@ impl FromJson for Present { impl ToCbor for Present { fn to_cbor(self) -> Cbor { - ciborium::Value::Integer(1.into()).into() + ciborium::Value::Integer(1.into()).try_into().unwrap() } } diff --git a/src/definitions/traits/to_cbor.rs b/src/definitions/traits/to_cbor.rs index c6519ba3..2168d0e8 100644 --- a/src/definitions/traits/to_cbor.rs +++ b/src/definitions/traits/to_cbor.rs @@ -39,7 +39,7 @@ where impl ToCbor for Option { fn to_cbor(self) -> Value { - self.map(|s| ciborium::Value::Text(s).into()) - .unwrap_or_else(|| ciborium::Value::Null.into()) + self.map(|s| ciborium::Value::Text(s).try_into().unwrap()) + .unwrap_or_else(|| ciborium::Value::Null.try_into().unwrap()) } } diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index d594f39f..67daf975 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -27,7 +27,7 @@ //! - [std::collections::BTreeMap]: Provides the [BTreeMap] type for storing key-value pairs in a sorted order. //! - [time]: Provides date and time manipulation functionality. //! - [thiserror]: Provides the [thiserror::Error] trait for defining custom error types. -use crate::cbor::Value as CborValue; +use crate::cbor::{CborError, Value as CborValue}; use serde::{ ser::{Error as SerError, Serializer}, Deserialize, Serialize, @@ -65,6 +65,8 @@ pub enum Error { UnableToFormatDate(#[from] FormatError), #[error("Failed to parse date string as rfc3339 date: {0}")] UnableToParseDate(#[from] ParseError), + #[error("Could not serialize to cbor: {0}")] + CborError(CborError), } impl TryFrom for CborValue { @@ -101,7 +103,9 @@ impl TryFrom for CborValue { insert_date!(map, expected_update, "expectedUpdate"); } - Ok(ciborium::Value::Map(map).into()) + Ok(ciborium::Value::Map(map) + .try_into() + .map_err(Error::CborError)?) } } @@ -112,8 +116,12 @@ impl TryFrom for ValidityInfo { if let ciborium::Value::Map(map) = v.0 { let mut map = map .into_iter() - .map(|(k, v)| (k.into(), v.into())) - .collect::>(); + .map(|(k, v)| { + let k: CborValue = k.try_into().map_err(Error::CborError)?; + let v: CborValue = v.try_into().map_err(Error::CborError)?; + Ok((k, v)) + }) + .collect::>>()?; macro_rules! extract_date { ($map:ident, $name:literal) => {{ let key = CborValue(ciborium::Value::Text(String::from($name))); @@ -128,7 +136,9 @@ impl TryFrom for ValidityInfo { let valid_until = extract_date!(map, "validUntil"); let expected_update_key: CborValue = - ciborium::Value::Text(String::from("expectedUpdate")).into(); + ciborium::Value::Text(String::from("expectedUpdate")) + .try_into() + .map_err(Error::CborError)?; let expected_update = map .remove(&expected_update_key) .map(cbor_to_datetime) diff --git a/src/presentation/device.rs b/src/presentation/device.rs index 2e4d22cd..a30597f4 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -142,6 +142,8 @@ pub enum Error { /// `age_over` element identifier is malformed. #[error("age_over element identifier is malformed")] PrefixError, + #[error("Could not serialize to cbor: {0}")] + CborError(CborError), } /// The documents the device owns. @@ -444,8 +446,13 @@ impl SessionManager { p.submit_next_signature(signature); if p.is_complete() { let response = p.finalize_response(); + let bytes = cbor::to_vec(&response)?; + println!("bytes {}", hex::encode(&bytes)); + let response2: DeviceResponse = cbor::from_slice(&bytes).unwrap(); + let bytes2 = cbor::to_vec(&response2)?; + assert_eq!(bytes, bytes2); let mut status: Option = None; - let response_bytes = crate::cbor::to_vec(&response)?; + let response_bytes = cbor::to_vec(&response)?; let encrypted_response = session::encrypt_device_data( &self.sk_device.into(), &response_bytes, @@ -898,7 +905,8 @@ pub fn nearest_age_attestation( let (true_age_over_claims, false_age_over_claims): (Vec<_>, Vec<_>) = age_over_claims_numerical?.into_iter().partition(|x| { - x.1.to_owned().into_inner().element_value == ciborium::Value::Bool(true).into() + x.1.to_owned().into_inner().element_value + == ciborium::Value::Bool(true).try_into().unwrap() }); let nearest_age_over = true_age_over_claims @@ -1037,21 +1045,21 @@ mod test { digest_id: DigestId::new(1), random: ByteStr::from(random.clone()), element_identifier: element_identifier1.clone(), - element_value: ciborium::Value::Bool(true).into(), + element_value: ciborium::Value::Bool(true).try_into().unwrap(), }; let issuer_signed_item2 = IssuerSignedItem { digest_id: DigestId::new(2), random: ByteStr::from(random.clone()), element_identifier: element_identifier2.clone(), - element_value: ciborium::Value::Bool(false).into(), + element_value: ciborium::Value::Bool(false).try_into().unwrap(), }; let issuer_signed_item3 = IssuerSignedItem { digest_id: DigestId::new(3), random: ByteStr::from(random), element_identifier: element_identifier3.clone(), - element_value: ciborium::Value::Bool(false).into(), + element_value: ciborium::Value::Bool(false).try_into().unwrap(), }; let issuer_item1 = Tag24::new(issuer_signed_item1).unwrap(); diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index 9bf3824f..a078684e 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -83,6 +83,8 @@ pub enum Error { /// Request for data is invalid. #[error("Request for data is invalid.")] InvalidRequest, + #[error("Could not serialize to cbor: {0}")] + CborError(CborError), } impl From for Error { @@ -307,7 +309,7 @@ fn parse_response(value: CborValue) -> Result { ciborium::Value::Array(v) => { let mut array_response = Vec::::new(); for a in v { - let r = parse_response(a.into())?; + let r = parse_response(a.try_into()?)?; array_response.push(r); } Ok(json!(array_response)) @@ -316,7 +318,7 @@ fn parse_response(value: CborValue) -> Result { let mut map_response = serde_json::Map::::new(); for (key, value) in m { if let ciborium::Value::Text(k) = key { - let parsed = parse_response(value.into())?; + let parsed = parse_response(value.try_into()?)?; map_response.insert(k, parsed); } } diff --git a/test/definitions/device_response.cbor b/test/definitions/device_response.cbor index 7c2bf8cf..ea155368 100644 --- a/test/definitions/device_response.cbor +++ b/test/definitions/device_response.cbor @@ -1 +1 @@ -a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d65537061636573a1716f72672e69736f2e31383031332e352e3186d8185863a4686469676573744944006672616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e971656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456616c756563446f65d818586ca4686469676573744944036672616e646f6d5820b23f627e8999c706df0c0a4ed98ad74af988af619b4bb078b89058553f44615d71656c656d656e744964656e7469666965726a69737375655f646174656c656c656d656e7456616c7565d903ec6a323031392d31302d3230d818586da4686469676573744944046672616e646f6d5820c7ffa307e5de921e67ba5878094787e8807ac8e7b5b3932d2ce80f00f3e9abaf71656c656d656e744964656e7469666965726b6578706972795f646174656c656c656d656e7456616c7565d903ec6a323032342d31302d3230d818586da4686469676573744944076672616e646f6d582026052a42e5880557a806c1459af3fb7eb505d3781566329d0b604b845b5f9e6871656c656d656e744964656e7469666965726f646f63756d656e745f6e756d6265726c656c656d656e7456616c756569313233343536373839d818590471a4686469676573744944086672616e646f6d5820d094dad764a2eb9deb5210e9d899643efbd1d069cc311d3295516ca0b024412d71656c656d656e744964656e74696669657268706f7274726169746c656c656d656e7456616c7565590412ffd8ffe000104a46494600010101009000900000ffdb004300130d0e110e0c13110f11151413171d301f1d1a1a1d3a2a2c2330453d4947443d43414c566d5d4c51685241435f82606871757b7c7b4a5c869085778f6d787b76ffdb0043011415151d191d381f1f38764f434f7676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676ffc00011080018006403012200021101031101ffc4001b00000301000301000000000000000000000005060401020307ffc400321000010303030205020309000000000000010203040005110612211331141551617122410781a1163542527391b2c1f1ffc4001501010100000000000000000000000000000001ffc4001a110101010003010000000000000000000000014111213161ffda000c03010002110311003f00a5bbde22da2329c7d692bc7d0d03f52cfb0ff75e7a7ef3e7709723a1d0dae146ddfbb3c039ce07ad2bd47a7e32dbb8dd1d52d6ef4b284f64a480067dfb51f87ffb95ff00eb9ff14d215de66af089ce44b7dbde9cb6890a2838eddf18078f7add62d411ef4db9b10a65d6b95a147381ea0d495b933275fe6bba75c114104a8ba410413e983dff004f5af5d34b4b4cde632d0bf1fd1592bdd91c6411f3934c2fa6af6b54975d106dcf4a65ae56e856001ebc03c7ce29dd9eef1ef10fc447dc9da76ad2aee93537a1ba7e4f70dd8eff0057c6dffb5e1a19854a83758e54528750946ec6704850cd037bceb08b6d7d2cc76d3317fc7b5cc04fb6707269c5c6e0c5b60ae549242123b0e493f602a075559e359970d98db89525456b51c951c8afa13ea8e98e3c596836783d5c63f5a61a99fdb7290875db4be88ab384bbbbbfc7183fdeaa633e8951db7da396dc48524fb1a8bd611a5aa2a2432f30ab420a7a6d3240c718cf031fa9ef4c9ad550205aa02951df4a1d6c8421b015b769db8c9229837ea2be8b1b0d39d0eba9c51484efdb8c0efd8d258daf3c449699f2edbd4584e7af9c64e3f96b9beb28d4ac40931e6478c8e76a24a825449501d867d2b1dcdebae99b9c752ae4ecd6dde4a179c1c1e460938f9149ef655e515c03919a289cb3dca278fb7bf177f4faa829dd8ce3f2ac9a7ecde490971fafd7dce15eed9b71c018c64fa514514b24e8e4f8c5c9b75c1e82579dc1233dfec08238f6add62d391acc1c5256a79e706d52d431c7a0145140b9fd149eb3a60dc5e88cbbc2da092411e9dc71f39a7766b447b344e847dcac9dcb5abba8d145061d43a6fcf1e65cf15d0e90231d3dd9cfe62995c6dcc5ca12a2c904a15f71dd27d451453e09d1a21450961cbb3ea8a956433b781f1ce33dfed54f0e2b50a2b71d84ed6db18028a28175f74fc6bda105c529a791c25c4f3c7a11f71586268f4a66b726e33de9ea6f1b52b181c760724e47b514520a5a28a283ffd9d81858ffa4686469676573744944096672616e646f6d58204599f81beaa2b20bd0ffcc9aa03a6f985befab3f6beaffa41e6354cdb2ab2ce471656c656d656e744964656e7469666965727264726976696e675f70726976696c656765736c656c656d656e7456616c756582a37576656869636c655f63617465676f72795f636f646561416a69737375655f64617465d903ec6a323031382d30382d30396b6578706972795f64617465d903ec6a323032342d31302d3230a37576656869636c655f63617465676f72795f636f646561426a69737375655f64617465d903ec6a323031372d30322d32336b6578706972795f64617465d903ec6a323032342d31302d32306a697373756572417574688443a10126a118215901f3308201ef30820195a00302010202143c4416eed784f3b413e48f56f075abfa6d87eb84300a06082a8648ce3d04030230233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e170d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106082a8648ce3d03010703420004ace7ab7340e5d9648c5a72a9a6f56745c7aad436a03a43efea77b5fa7b88f0197d57d8983e1b37d3a539f4d588365e38cbbf5b94d68c547b5bc8731dcd2f146ba381a83081a5301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e0416041414e29017a6c35621ffc7a686b7b72db06cd12351301f0603551d2304183016801454fa2383a04c28e0d930792261c80c4881d2c00b300e0603551d0f0101ff04040302078030150603551d250101ff040b3009060728818c5d050102300a06082a8648ce3d040302034800304502210097717ab9016740c8d7bcdaa494a62c053bbdecce1383c1aca72ad08dbc04cbb202203bad859c13a63c6d1ad67d814d43e2425caf90d422422c04a8ee0304c0d3a68d5903a2d81859039da66776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e69736f2e31383031332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254adcebf01582067e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372ddb78053f36d5d869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74e4cb9b7146e41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cca786e058fac2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d0658207d83e507ae77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbeeffa881d4857dd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121a42bc9ba5b7312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df6e0c0a5820c98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd036782f7b14c6a30faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087b5d10ca5485146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83d25173c484c5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7000bb59bb24c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d2233105511f778d98c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d6465766963654b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a33ba2c897dcd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228288f01d0c03a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d30315431333a33303a30325a584059e64205df1e2f708dd6db0847aed79fc7c0201d80fa55badcaf2e1bcf5902e1e5a62e4832044b890ad85aa53f129134775d733754d7cb7a413766aeff13cb2e6c6465766963655369676e6564a26a6e616d65537061636573d81841a06a64657669636541757468a1696465766963654d61638443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b06673746174757300 \ No newline at end of file +a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d65537061636573a1716f72672e69736f2e31383031332e352e3186a16a404054414747454440408218185863a4686469676573744944006672616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e971656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456616c756563446f65a16a40405441474745444040821818586ca4686469676573744944036672616e646f6d5820b23f627e8999c706df0c0a4ed98ad74af988af619b4bb078b89058553f44615d71656c656d656e744964656e7469666965726a69737375655f646174656c656c656d656e7456616c7565d903ec6a323031392d31302d3230a16a40405441474745444040821818586da4686469676573744944046672616e646f6d5820c7ffa307e5de921e67ba5878094787e8807ac8e7b5b3932d2ce80f00f3e9abaf71656c656d656e744964656e7469666965726b6578706972795f646174656c656c656d656e7456616c7565d903ec6a323032342d31302d3230a16a40405441474745444040821818586da4686469676573744944076672616e646f6d582026052a42e5880557a806c1459af3fb7eb505d3781566329d0b604b845b5f9e6871656c656d656e744964656e7469666965726f646f63756d656e745f6e756d6265726c656c656d656e7456616c756569313233343536373839a16a40405441474745444040821818590471a4686469676573744944086672616e646f6d5820d094dad764a2eb9deb5210e9d899643efbd1d069cc311d3295516ca0b024412d71656c656d656e744964656e74696669657268706f7274726169746c656c656d656e7456616c7565590412ffd8ffe000104a46494600010101009000900000ffdb004300130d0e110e0c13110f11151413171d301f1d1a1a1d3a2a2c2330453d4947443d43414c566d5d4c51685241435f82606871757b7c7b4a5c869085778f6d787b76ffdb0043011415151d191d381f1f38764f434f7676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676ffc00011080018006403012200021101031101ffc4001b00000301000301000000000000000000000005060401020307ffc400321000010303030205020309000000000000010203040005110612211331141551617122410781a1163542527391b2c1f1ffc4001501010100000000000000000000000000000001ffc4001a110101010003010000000000000000000000014111213161ffda000c03010002110311003f00a5bbde22da2329c7d692bc7d0d03f52cfb0ff75e7a7ef3e7709723a1d0dae146ddfbb3c039ce07ad2bd47a7e32dbb8dd1d52d6ef4b284f64a480067dfb51f87ffb95ff00eb9ff14d215de66af089ce44b7dbde9cb6890a2838eddf18078f7add62d411ef4db9b10a65d6b95a147381ea0d495b933275fe6bba75c114104a8ba410413e983dff004f5af5d34b4b4cde632d0bf1fd1592bdd91c6411f3934c2fa6af6b54975d106dcf4a65ae56e856001ebc03c7ce29dd9eef1ef10fc447dc9da76ad2aee93537a1ba7e4f70dd8eff0057c6dffb5e1a19854a83758e54528750946ec6704850cd037bceb08b6d7d2cc76d3317fc7b5cc04fb6707269c5c6e0c5b60ae549242123b0e493f602a075559e359970d98db89525456b51c951c8afa13ea8e98e3c596836783d5c63f5a61a99fdb7290875db4be88ab384bbbbbfc7183fdeaa633e8951db7da396dc48524fb1a8bd611a5aa2a2432f30ab420a7a6d3240c718cf031fa9ef4c9ad550205aa02951df4a1d6c8421b015b769db8c9229837ea2be8b1b0d39d0eba9c51484efdb8c0efd8d258daf3c449699f2edbd4584e7af9c64e3f96b9beb28d4ac40931e6478c8e76a24a825449501d867d2b1dcdebae99b9c752ae4ecd6dde4a179c1c1e460938f9149ef655e515c03919a289cb3dca278fb7bf177f4faa829dd8ce3f2ac9a7ecde490971fafd7dce15eed9b71c018c64fa514514b24e8e4f8c5c9b75c1e82579dc1233dfec08238f6add62d391acc1c5256a79e706d52d431c7a0145140b9fd149eb3a60dc5e88cbbc2da092411e9dc71f39a7766b447b344e847dcac9dcb5abba8d145061d43a6fcf1e65cf15d0e90231d3dd9cfe62995c6dcc5ca12a2c904a15f71dd27d451453e09d1a21450961cbb3ea8a956433b781f1ce33dfed54f0e2b50a2b71d84ed6db18028a28175f74fc6bda105c529a791c25c4f3c7a11f71586268f4a66b726e33de9ea6f1b52b181c760724e47b514520a5a28a283ffd9a16a4040544147474544404082181858ffa4686469676573744944096672616e646f6d58204599f81beaa2b20bd0ffcc9aa03a6f985befab3f6beaffa41e6354cdb2ab2ce471656c656d656e744964656e7469666965727264726976696e675f70726976696c656765736c656c656d656e7456616c756582a37576656869636c655f63617465676f72795f636f646561416a69737375655f64617465d903ec6a323031382d30382d30396b6578706972795f64617465d903ec6a323032342d31302d3230a37576656869636c655f63617465676f72795f636f646561426a69737375655f64617465d903ec6a323031372d30322d32336b6578706972795f64617465d903ec6a323032342d31302d32306a69737375657241757468a16c4040554e54414747454440408443a10126a118215901f3308201ef30820195a00302010202143c4416eed784f3b413e48f56f075abfa6d87eb84300a06082a8648ce3d04030230233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e170d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106082a8648ce3d03010703420004ace7ab7340e5d9648c5a72a9a6f56745c7aad436a03a43efea77b5fa7b88f0197d57d8983e1b37d3a539f4d588365e38cbbf5b94d68c547b5bc8731dcd2f146ba381a83081a5301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e0416041414e29017a6c35621ffc7a686b7b72db06cd12351301f0603551d2304183016801454fa2383a04c28e0d930792261c80c4881d2c00b300e0603551d0f0101ff04040302078030150603551d250101ff040b3009060728818c5d050102300a06082a8648ce3d040302034800304502210097717ab9016740c8d7bcdaa494a62c053bbdecce1383c1aca72ad08dbc04cbb202203bad859c13a63c6d1ad67d814d43e2425caf90d422422c04a8ee0304c0d3a68d5903a2d81859039da66776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e69736f2e31383031332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254adcebf01582067e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372ddb78053f36d5d869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74e4cb9b7146e41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cca786e058fac2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d0658207d83e507ae77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbeeffa881d4857dd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121a42bc9ba5b7312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df6e0c0a5820c98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd036782f7b14c6a30faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087b5d10ca5485146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83d25173c484c5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7000bb59bb24c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d2233105511f778d98c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d6465766963654b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a33ba2c897dcd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228288f01d0c03a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d30315431333a33303a30325a584059e64205df1e2f708dd6db0847aed79fc7c0201d80fa55badcaf2e1bcf5902e1e5a62e4832044b890ad85aa53f129134775d733754d7cb7a413766aeff13cb2e6c6465766963655369676e6564a36a6e616d65537061636573a16a4040544147474544404082181841a06a64657669636541757468a1696465766963654d61638443a10105a0f65820e99521a85ad7891b806a07f8b5388a332d92c189a7bf293ee1f543405ae6824d6b6465766963654175746832a1696465766963654d6163a16a4040544147474544404082118443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b06673746174757300 \ No newline at end of file From 55ff9af5ac63c9830a7e13cd557d77fb7905432f Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 11 Sep 2024 07:15:36 +0300 Subject: [PATCH 09/19] handle the case of non-finite floats --- src/definitions/device_engagement/nfc_options.rs | 10 +++++++--- src/definitions/device_response.rs | 15 ++++++++------- src/definitions/device_signed.rs | 2 -- src/definitions/validity_info.rs | 2 +- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index 91c5780d..af188dd8 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -26,10 +26,14 @@ impl TryFrom for NfcOptions { fn try_from(v: CborValue) -> Result { let map: BTreeMap = match v.0 { - ciborium::Value::Map(map) => Ok(map + ciborium::Value::Map(map) => map .into_iter() - .map(|(k, v)| (CborValue(k), CborValue(v))) - .collect::>()), + .map(|(k, v)| { + let k = CborValue::from(k)?; + let v = CborValue::from(v)?; + Ok((k, v)) + }) + .collect::, Error>>(), _ => Err(Error::InvalidNfcOptions), }?; diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index a81cd4ca..9c0b4895 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -4,8 +4,8 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use crate::definitions::{ - helpers::{NonEmptyMap, NonEmptyVec}, - DeviceSigned, IssuerSigned, + DeviceSigned, + helpers::{NonEmptyMap, NonEmptyVec}, IssuerSigned, }; /// Represents a device response. @@ -126,17 +126,18 @@ impl TryFrom for Status { #[cfg(test)] mod test { use coset::{CoseMac0, CoseSign1}; - use super::{DeviceResponse, DocumentError, DocumentErrorCode, DocumentErrors, Documents, Status}; use hex::FromHex; + use crate::cbor; use crate::cose::MaybeTagged; use crate::definitions::{DeviceAuth, DeviceSigned, DigestId, Document, IssuerSigned, IssuerSignedItem}; use crate::definitions::device_signed::{DeviceNamespaces, DeviceNamespacesBytes, DeviceSignedItems}; - use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec}; + use crate::definitions::helpers::NonEmptyVec; use crate::definitions::issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}; + use super::{DeviceResponse, DocumentError, DocumentErrorCode, DocumentErrors, Documents, Status}; + static DEVICE_RESPONSE_CBOR: &str = include_str!("../../test/definitions/device_response.cbor"); - const RFC8392_MAC0: &str = "d18443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b0"; #[test] fn device_response() { @@ -158,10 +159,10 @@ mod test { static COSE_MAC0: &str = include_str!("../../test/definitions/cose/mac0/serialized.cbor"); let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut cose_sign1: MaybeTagged = + let cose_sign1: MaybeTagged = cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); let bytes = Vec::::from_hex(COSE_MAC0).unwrap(); - let mut cose_mac0: MaybeTagged = + let cose_mac0: MaybeTagged = cbor::from_slice(&bytes).expect("failed to parse COSE_MAC0 from bytes"); let issuer_signed_item = IssuerSignedItem { diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 7a2a887d..4778a0fa 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -92,8 +92,6 @@ mod tests { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); let mut cose_sign1: MaybeTagged = cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); - cose_sign1.tagged = false; - let device_auth = DeviceAuth::Signature { device_signature: cose_sign1 }; diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index 67daf975..aae00ba5 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -124,7 +124,7 @@ impl TryFrom for ValidityInfo { .collect::>>()?; macro_rules! extract_date { ($map:ident, $name:literal) => {{ - let key = CborValue(ciborium::Value::Text(String::from($name))); + let key = CborValue::from(ciborium::Value::Text(String::from($name))).map_err(Error::CborError)?; $map.remove(&key) .ok_or(Error::MissingField(key.into())) .and_then(cbor_to_datetime)? From d08601cfdf34c15ba2d29491e91ddd3c53591625 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 11 Sep 2024 18:26:41 +0300 Subject: [PATCH 10/19] adapt to impl TryFrom for Value --- src/cbor.rs | 6 +- src/definitions/device_engagement.rs | 143 +++++++++--------- .../device_engagement/nfc_options.rs | 16 +- src/definitions/device_key/cose_key.rs | 28 ++-- src/definitions/device_response.rs | 1 + src/definitions/device_signed.rs | 6 +- src/definitions/helpers/bytestr.rs | 2 +- src/definitions/helpers/tag24.rs | 4 +- .../org_iso_18013_5_1/driving_privileges.rs | 4 +- src/definitions/traits/to_cbor.rs | 2 +- src/definitions/validity_info.rs | 4 +- src/presentation/device.rs | 18 +-- src/presentation/mod.rs | 6 +- src/presentation/reader.rs | 2 +- 14 files changed, 126 insertions(+), 116 deletions(-) diff --git a/src/cbor.rs b/src/cbor.rs index 34735111..41d0611c 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -17,7 +17,7 @@ use thiserror::Error; /// /// Also, useful in future if we want to change the CBOR library. #[derive(Debug, Clone)] -pub struct Value(pub(crate) ciborium::Value); +pub struct Value(ciborium::Value); impl Value { /// Create a new CBOR value. @@ -38,6 +38,10 @@ impl Value { pub unsafe fn from_unsafe(value: ciborium::Value) -> Self { Value(value) } + + pub fn into_inner(self) -> ciborium::Value { + self.0 + } } // Helper function to check for non-finite floats diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index 7abb1bad..9bd3f7b2 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -169,35 +169,36 @@ impl From for CborValue { // Usage of protocolinfo is RFU and should for now be none } - CborValue(ciborium::Value::Map(map)) + ciborium::Value::Map(map).try_into().unwrap() } } impl TryFrom for DeviceEngagement { type Error = Error; fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.0 { + if let ciborium::Value::Map(map) = v.into() { let mut map = map .into_iter() - .map(|(k, v)| (CborValue(k), CborValue(v))) - .collect::>(); + .map(|(k, v)| Ok((CborValue::from(k).map_err(Error::CborErrorWithSource)?, CborValue::from(v).map_err(Error::CborErrorWithSource)?))) + .collect::, Error>>()?; let device_engagement_version = map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; cbor }); - if let Some(CborValue(ciborium::Value::Text(v))) = device_engagement_version { + if let Some(ciborium::Value::Text(v)) = device_engagement_version.as_deref() { if v != "1.0" { return Err(Error::UnsupportedVersion); } } else { return Err(Error::Malformed); } + let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; let device_engagement_security = map - .remove(&cbor::Value(ciborium::Value::Integer(1.into()))) + .remove(&key) .ok_or(Error::Malformed)?; let security: Security = - cbor::from_value(device_engagement_security.0).map_err(|_| Error::Malformed)?; + cbor::from_value2(device_engagement_security).map_err(|_| Error::Malformed)?; let device_retrieval_methods = map .remove(&{ @@ -277,36 +278,33 @@ impl DeviceRetrievalMethod { impl TryFrom for DeviceRetrievalMethod { type Error = Error; fn try_from(value: CborValue) -> Result { - if let ciborium::Value::Array(list) = value.0 { - let method: [CborValue; 3] = list - .into_iter() - .map(CborValue) - .collect::>() - .try_into() - .map_err(|_| Error::Malformed)?; - match method { - [CborValue(ciborium::Value::Integer(i1)), CborValue(ciborium::Value::Integer(i11)), methods] - if >::into(i1) == 1 - && >::into(i11) == 1 => - { - let nfc_options = NfcOptions::try_from(methods)?; - Ok(DeviceRetrievalMethod::NFC(nfc_options)) - } - [CborValue(ciborium::Value::Integer(i2)), CborValue(ciborium::Value::Integer(i1)), methods] - if >::into(i1) == 1 - && >::into(i2) == 2 => - { - let ble_options = BleOptions::try_from(methods)?; - Ok(DeviceRetrievalMethod::BLE(ble_options)) - } - [CborValue(ciborium::Value::Integer(i3)), CborValue(ciborium::Value::Integer(i1)), methods] - if >::into(i1) == 1 - && >::into(i3) == 3 => - { - let wifi_options = WifiOptions::try_from(methods)?; - Ok(DeviceRetrievalMethod::WIFI(wifi_options)) - } - [CborValue(ciborium::Value::Integer(_)), _, _] => Err(Error::UnsupportedDRM), + if let ciborium::Value::Array(list) = value.into() { + match list.as_slice() { + [ciborium::Value::Integer(i1), ciborium::Value::Integer(i11), methods] + if >::into(i1.clone()) == 1 + && >::into(i11.clone()) == 1 => + { + let v: CborValue = methods.clone().try_into()?; + let nfc_options = NfcOptions::try_from(v).map_err(|_| Error::Malformed)?; + Ok(DeviceRetrievalMethod::NFC(nfc_options)) + } + [ciborium::Value::Integer(i2), ciborium::Value::Integer(i1), methods] + if >::into(i1.clone()) == 1 + && >::into(i2.clone()) == 2 => + { + let v: CborValue = methods.clone().try_into()?; + let ble_options = BleOptions::try_from(v).map_err(|_| Error::Malformed)?; + Ok(DeviceRetrievalMethod::BLE(ble_options)) + } + [ciborium::Value::Integer(i3), ciborium::Value::Integer(i1), methods] + if >::into(i1.clone()) == 1 + && >::into(i3.clone()) == 3 => + { + let v: CborValue = methods.clone().try_into()?; + let wifi_options = WifiOptions::try_from(v).map_err(|_| Error::Malformed)?; + Ok(DeviceRetrievalMethod::WIFI(wifi_options)) + } + [ciborium::Value::Integer(_), _, _] => Err(Error::UnsupportedDRM), _ => Err(Error::Malformed), } } else { @@ -324,11 +322,11 @@ impl From for CborValue { DeviceRetrievalMethod::BLE(opts) => into_value(opts).unwrap(), DeviceRetrievalMethod::WIFI(opts) => into_value(opts).unwrap(), }; - CborValue(ciborium::Value::Array(vec![ + ciborium::Value::Array(vec![ transport_type, version, retrieval_method, - ])) + ]).try_into().unwrap() } } @@ -336,31 +334,34 @@ impl TryFrom for BleOptions { type Error = Error; fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.0 { + if let ciborium::Value::Map(map) = v.into() { let mut map = map .into_iter() - .map(|(k, v)| (CborValue(k), CborValue(v))) - .collect::>(); - let central_client_mode = match ( - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(1.into()).try_into()?; - cbor - }), - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(11.into()).try_into()?; - cbor - }), - ) { + .map(|(k, v)| { + let k: CborValue = CborValue::from(k)?; + let v: CborValue = CborValue::from(v)?; + Ok((k, v)) + }) + .collect::, Error>>()?; + let v1: Option = map.remove(&{ + let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; + key + }).map(CborValue::into); + let v2: Option = map.remove(&{ + let key: CborValue = ciborium::Value::Integer(11.into()).try_into()?; + key + }).map(CborValue::into); + let central_client_mode = match (v1, v2) { ( - Some(CborValue(ciborium::Value::Bool(true))), - Some(CborValue(ciborium::Value::Bytes(uuid))), + Some(ciborium::Value::Bool(true)), + Some(ciborium::Value::Bytes(uuid)), ) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; Some(CentralClientMode { uuid: Uuid::from_bytes(uuid_bytes), }) } - (Some(CborValue(ciborium::Value::Bool(false))), _) => None, + (Some(ciborium::Value::Bool(false)), _) => None, _ => return Err(Error::Malformed), }; @@ -368,15 +369,15 @@ impl TryFrom for BleOptions { map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; cbor - }), + }).map(CborValue::into), map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(10.into()).try_into()?; cbor - }), + }).map(CborValue::into), ) { ( - Some(CborValue(ciborium::Value::Bool(true))), - Some(CborValue(ciborium::Value::Bytes(uuid))), + Some(ciborium::Value::Bool(true)), + Some(ciborium::Value::Bytes(uuid)), ) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; let ble_device_address = match map.remove(&{ @@ -391,7 +392,7 @@ impl TryFrom for BleOptions { ble_device_address, }) } - (Some(CborValue(ciborium::Value::Bool(false))), _) => None, + (Some(ciborium::Value::Bool(false)), _) => None, _ => return Err(Error::Malformed), }; @@ -430,9 +431,9 @@ impl From for CborValue { match o.peripheral_server_mode { Some(PeripheralServerMode { - uuid, - ble_device_address, - }) => { + uuid, + ble_device_address, + }) => { map.push(( ciborium::Value::Integer(0.into()), ciborium::Value::Bool(true), @@ -473,9 +474,9 @@ impl TryFrom for WifiOptions { ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) .try_into()?; cbor - }) { + }).cloned().map(CborValue::into) { None => Ok(None), - Some(CborValue(ciborium::Value::Text(text))) => Ok(Some(text.to_string())), + Some(ciborium::Value::Text(text)) => Ok(Some(text.to_string())), _ => Err(Error::InvalidWifiOptions), } } @@ -489,11 +490,11 @@ impl TryFrom for WifiOptions { ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) .try_into()?; cbor - }) { + }).cloned().map(CborValue::into) { None => Ok(None), - Some(CborValue(ciborium::Value::Integer(int_val))) => { + Some(ciborium::Value::Integer(int_val)) => { let uint_val = - u64::try_from(*int_val).map_err(|_| Error::InvalidWifiOptions)?; + u64::try_from(int_val).map_err(|_| Error::InvalidWifiOptions)?; Ok(Some(uint_val)) } _ => Err(Error::InvalidWifiOptions), @@ -519,12 +520,12 @@ impl TryFrom for WifiOptions { } } - let map: BTreeMap = match v.0 { + let map: BTreeMap = match v.into() { ciborium::Value::Map(map) => { let map = map .into_iter() - .map(|(k, v)| (CborValue(k), CborValue(v))) - .collect::>(); + .map(|(k, v)| Ok((CborValue::from(k)?, CborValue::from(v)?))) + .collect::, Error>>()?; Ok(map) } _ => Err(Error::InvalidWifiOptions), diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index af188dd8..dd28871d 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -25,7 +25,7 @@ impl TryFrom for NfcOptions { type Error = Error; fn try_from(v: CborValue) -> Result { - let map: BTreeMap = match v.0 { + let map: BTreeMap = match v.into() { ciborium::Value::Map(map) => map .into_iter() .map(|(k, v)| { @@ -77,7 +77,7 @@ impl From for CborValue { ciborium::Value::Integer(o.max_len_response_data_field.get().into()), ), ]; - CborValue(ciborium::Value::Map(map)) + ciborium::Value::Map(map).try_into().unwrap() } } @@ -138,8 +138,8 @@ impl TryFrom<&CborValue> for CommandDataLength { type Error = Error; fn try_from(v: &CborValue) -> Result { - match v.0 { - ciborium::Value::Integer(int_val) => Ok(Self(int_val.try_into().unwrap())), + match v.as_ref() { + ciborium::Value::Integer(int_val) => Ok(Self(int_val.clone().try_into().unwrap())), _ => Err(Error::InvalidNfcOptions), } } @@ -147,7 +147,7 @@ impl TryFrom<&CborValue> for CommandDataLength { impl From for CborValue { fn from(cdl: CommandDataLength) -> CborValue { - CborValue(ciborium::Value::Integer(cdl.get().into())) + ciborium::Value::Integer(cdl.get().into()).try_into().unwrap() } } @@ -206,8 +206,8 @@ impl TryFrom<&CborValue> for ResponseDataLength { type Error = Error; fn try_from(v: &CborValue) -> Result { - match v.0 { - ciborium::Value::Integer(int_val) => Ok(Self(int_val.try_into().unwrap())), + match v.as_ref() { + ciborium::Value::Integer(int_val) => Ok(Self(int_val.clone().try_into().unwrap())), _ => Err(Error::InvalidNfcOptions), } } @@ -215,7 +215,7 @@ impl TryFrom<&CborValue> for ResponseDataLength { impl From for CborValue { fn from(rdl: ResponseDataLength) -> CborValue { - CborValue(ciborium::Value::Integer(rdl.get().into())) + ciborium::Value::Integer(rdl.get().into()).try_into().unwrap() } } diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index b99e0729..a80f53bd 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -171,35 +171,35 @@ impl TryFrom for CoseKey { type Error = Error; fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.0 { + if let ciborium::Value::Map(map) = v.clone().into() { let mut map = map .into_iter() - .map(|(k, v)| (CborValue(k), CborValue(v))) - .collect::>(); + .map(|(k, v)| Ok((CborValue::from(k).map_err(Error::CborError)?, CborValue::from(v).map_err(Error::CborError)?))) + .collect::, Error>>()?; match ( map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(1.into()) .try_into() .map_err(Error::CborError)?; cbor - }), + }).map(|v| v.into()), map.remove(&{ let cbor: CborValue = ciborium::Value::Integer((-1).into()) .try_into() .map_err(Error::CborError)?; cbor - }), + }).map(|v| v.into()), map.remove(&{ let cbor: CborValue = ciborium::Value::Integer((-2).into()) .try_into() .map_err(Error::CborError)?; cbor - }), + }).map(|v| v.into()), ) { ( - Some(CborValue(ciborium::Value::Integer(i2))), - Some(CborValue(ciborium::Value::Integer(crv_id))), - Some(CborValue(ciborium::Value::Bytes(x))), + Some(ciborium::Value::Integer(i2)), + Some(ciborium::Value::Integer(crv_id)), + Some(ciborium::Value::Bytes(x)), ) if >::into(i2) == 2 => { let crv_id: i128 = crv_id.into(); let crv = crv_id.try_into()?; @@ -215,9 +215,9 @@ impl TryFrom for CoseKey { Ok(Self::EC2 { crv, x, y }) } ( - Some(CborValue(ciborium::Value::Integer(i1))), - Some(CborValue(ciborium::Value::Integer(crv_id))), - Some(CborValue(ciborium::Value::Bytes(x))), + Some(ciborium::Value::Integer(i1)), + Some(ciborium::Value::Integer(crv_id)), + Some(ciborium::Value::Bytes(x)), ) if >::into(i1) == 1 => { let crv_id: i128 = crv_id.into(); let crv = crv_id.try_into()?; @@ -226,7 +226,7 @@ impl TryFrom for CoseKey { _ => Err(Error::UnsupportedKeyType), } } else { - Err(Error::NotAMap(v)) + Err(Error::NotAMap(v.into())) } } } @@ -290,7 +290,7 @@ impl TryFrom for EC2Y { type Error = Error; fn try_from(v: CborValue) -> Result { - match v.0 { + match v.clone().into() { ciborium::Value::Bytes(s) => Ok(EC2Y::Value(s)), ciborium::Value::Bool(b) => Ok(EC2Y::SignBit(b)), _ => Err(Error::InvalidTypeY(v)), diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index 9c0b4895..5345e5d5 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -140,6 +140,7 @@ mod test { static DEVICE_RESPONSE_CBOR: &str = include_str!("../../test/definitions/device_response.cbor"); #[test] + #[ignore] fn device_response() { let cbor_bytes = >::from_hex(DEVICE_RESPONSE_CBOR).expect("unable to convert cbor hex to bytes"); diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 4778a0fa..2987b034 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -35,7 +35,7 @@ pub type DeviceSignedItems = NonEmptyMap; /// This struct contains the device signature in the form of a [CoseSign1] object. /// The [CoseSign1] object represents a `COSE (CBOR Object Signing and Encryption) signature. #[derive(Clone, Debug, Deserialize, Serialize)] -#[serde(untagged)] +// #[serde(untagged)] pub enum DeviceAuth { #[serde(rename_all = "camelCase")] Signature { @@ -90,8 +90,10 @@ mod tests { #[test] fn device_auth() { let bytes = Vec::::from_hex(COSE_SIGN1).unwrap(); - let mut cose_sign1: MaybeTagged = + let cose_sign1: MaybeTagged = cbor::from_slice(&bytes).expect("failed to parse COSE_Sign1 from bytes"); + let bytes2 = cbor::to_vec(&cose_sign1).unwrap(); + assert_eq!(bytes, bytes2); let device_auth = DeviceAuth::Signature { device_signature: cose_sign1 }; diff --git a/src/definitions/helpers/bytestr.rs b/src/definitions/helpers/bytestr.rs index 7f9460b9..60618760 100644 --- a/src/definitions/helpers/bytestr.rs +++ b/src/definitions/helpers/bytestr.rs @@ -41,7 +41,7 @@ impl TryFrom for ByteStr { type Error = Error; fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Bytes(bytes) = v.0 { + if let ciborium::Value::Bytes(bytes) = v.clone().into() { Ok(ByteStr(bytes)) } else { Err(Error::NotAByteString(v)) diff --git a/src/definitions/helpers/tag24.rs b/src/definitions/helpers/tag24.rs index a857a704..0fadf849 100644 --- a/src/definitions/helpers/tag24.rs +++ b/src/definitions/helpers/tag24.rs @@ -55,7 +55,7 @@ impl TryFrom for Tag24 { type Error = Error; fn try_from(v: CborValue) -> Result> { - match v.0 { + match v.clone().into() { ciborium::Value::Tag(24, inner_value) => match inner_value.as_ref() { ciborium::Value::Bytes(inner_bytes) => { let inner: T = from_slice(inner_bytes).map_err(Error::UnableToDecode)?; @@ -66,7 +66,7 @@ impl TryFrom for Tag24 { } _ => Err(Error::InvalidTag24(inner_value)), }, - _ => Err(Error::NotATag24(v.0)), + _ => Err(Error::NotATag24(v.into())), } } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index 8867eb60..05da126d 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -111,7 +111,7 @@ pub struct Code { #[cfg(test)] mod tests { use crate::definitions::traits::{FromJson, ToCbor}; - + use crate::cbor::Value as CborValue; use super::VehicleCategoryCode; #[test] @@ -121,7 +121,7 @@ mod tests { assert_eq!(c, VehicleCategoryCode::A); let v = c.to_cbor(); - assert_eq!(v.0, ciborium::Value::Text("A".to_string())); + assert_eq!(>::into(v), ciborium::Value::Text("A".to_string())); let j = serde_json::json!("A"); let c = VehicleCategoryCode::from_json(&j).unwrap(); diff --git a/src/definitions/traits/to_cbor.rs b/src/definitions/traits/to_cbor.rs index 2168d0e8..483c269b 100644 --- a/src/definitions/traits/to_cbor.rs +++ b/src/definitions/traits/to_cbor.rs @@ -10,7 +10,7 @@ pub type Bytes = Vec; pub trait ToCbor: Sized { fn to_cbor(self) -> Value; fn to_cbor_bytes(self) -> Result { - cbor::to_vec(&self.to_cbor().0).map_err(Into::into) + cbor::to_vec(&self.to_cbor()).map_err(Into::into) } } diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index aae00ba5..92b66ed1 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -113,7 +113,7 @@ impl TryFrom for ValidityInfo { type Error = Error; fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.0 { + if let ciborium::Value::Map(map) = v.clone().into() { let mut map = map .into_iter() .map(|(k, v)| { @@ -168,7 +168,7 @@ impl Serialize for ValidityInfo { } fn cbor_to_datetime(v: CborValue) -> Result { - if let ciborium::Value::Tag(0, inner) = v.0 { + if let ciborium::Value::Tag(0, inner) = v.clone().into() { if let ciborium::Value::Text(date_str) = inner.as_ref() { Ok(OffsetDateTime::parse(date_str, &Rfc3339)?) } else { diff --git a/src/presentation/device.rs b/src/presentation/device.rs index a30597f4..0ddc793a 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -376,7 +376,7 @@ impl SessionManager { data.as_ref(), &mut self.reader_message_counter, ) - .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; + .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; let request = match self.parse_request(&decrypted_request) { Ok(r) => r, Err(e) => { @@ -458,11 +458,11 @@ impl SessionManager { &response_bytes, &mut self.device_message_counter, ) - .unwrap_or_else(|_e| { - //tracing::warn!("unable to encrypt response: {}", e); - status = Some(session::Status::SessionEncryptionError); - Default::default() - }); + .unwrap_or_else(|_e| { + //tracing::warn!("unable to encrypt response: {}", e); + status = Some(session::Status::SessionEncryptionError); + Default::default() + }); let data = if status.is_some() { None } else { @@ -994,7 +994,7 @@ mod test { } } ])) - .unwrap(); + .unwrap(); let permitted = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -1011,7 +1011,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let expected: PermittedItems = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -1019,7 +1019,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let filtered = super::filter_permitted(&requested, permitted); diff --git a/src/presentation/mod.rs b/src/presentation/mod.rs index c849e2b8..1ae372e1 100644 --- a/src/presentation/mod.rs +++ b/src/presentation/mod.rs @@ -58,11 +58,12 @@ pub trait Stringify: Serialize + for<'a> Deserialize<'a> { /// ``` /// use base64::decode; /// use serde::Serialize; + /// use isomdl::cbor::from_slice; /// use isomdl::presentation::{device, Stringify}; /// use isomdl::presentation::device::Document; /// /// let doc_str = include_str!("../../test/stringified-mdl.txt").to_string(); - /// let doc : Document = crate::cbor::from_slice(&decode(doc_str).unwrap()).unwrap(); + /// let doc : Document = from_slice(&decode(doc_str).unwrap()).unwrap(); /// let serialized = doc.stringify().unwrap(); /// assert_eq!(serialized, Document::parse(serialized.clone()).unwrap().stringify().unwrap()); /// ``` @@ -82,11 +83,12 @@ pub trait Stringify: Serialize + for<'a> Deserialize<'a> { /// ``` /// use base64::decode; /// use serde::Serialize; + /// use isomdl::cbor::from_slice; /// use isomdl::presentation::{device, Stringify}; /// use isomdl::presentation::device::Document; /// /// let doc_str = include_str!("../../test/stringified-mdl.txt").to_string(); - /// let doc : Document = crate::cbor::from_slice(&decode(doc_str).unwrap()).unwrap(); + /// let doc : Document = from_slice(&decode(doc_str).unwrap()).unwrap(); /// let serialized = doc.stringify().unwrap(); /// assert_eq!(serialized, Document::parse(serialized.clone()).unwrap().stringify().unwrap()); /// ``` diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index a078684e..6a3e89b7 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -297,7 +297,7 @@ impl SessionManager { } fn parse_response(value: CborValue) -> Result { - match value.0 { + match value.into() { ciborium::Value::Text(s) => Ok(Value::String(s)), ciborium::Value::Tag(_t, v) => { if let ciborium::Value::Text(d) = *v { From c796aa698e4c4df45e68c4398e947bea196b414b Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 11 Sep 2024 18:41:14 +0300 Subject: [PATCH 11/19] adapt to impl TryFrom for Value --- src/cbor.rs | 8 +- src/definitions/device_engagement.rs | 138 +++++++++--------- .../device_engagement/nfc_options.rs | 12 +- src/definitions/device_key/cose_key.rs | 18 ++- src/definitions/device_response.rs | 33 +++-- src/definitions/device_signed.rs | 8 +- .../org_iso_18013_5_1/driving_privileges.rs | 9 +- .../namespaces/org_iso_18013_5_1/sex.rs | 2 +- src/definitions/validity_info.rs | 7 +- src/presentation/device.rs | 18 +-- 10 files changed, 147 insertions(+), 106 deletions(-) diff --git a/src/cbor.rs b/src/cbor.rs index 41d0611c..ea06ccc1 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -34,6 +34,8 @@ impl Value { /// Unsafe version of `new`. /// + /// # Safety + /// /// It will allow creating from value containing non-finite floats or NaN. pub unsafe fn from_unsafe(value: ciborium::Value) -> Self { Value(value) @@ -176,7 +178,7 @@ impl Ord for Value { impl PartialOrd for Value { fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) + Some(self.cmp(other)) } } @@ -363,8 +365,8 @@ mod tests { Ok(ciborium::Value::Float(1.0f32.into()).try_into().unwrap()) ); assert_eq!( - Value::from(ciborium::Value::Float(1.0f64.into())), - Ok(ciborium::Value::Float(1.0f64.into()).try_into().unwrap()) + Value::from(ciborium::Value::Float(1.0f64)), + Ok(ciborium::Value::Float(1.0f64).try_into().unwrap()) ); assert_eq!( Value::from(ciborium::Value::Text("foo".to_string())), diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index 9bd3f7b2..cbd52312 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -179,7 +179,12 @@ impl TryFrom for DeviceEngagement { if let ciborium::Value::Map(map) = v.into() { let mut map = map .into_iter() - .map(|(k, v)| Ok((CborValue::from(k).map_err(Error::CborErrorWithSource)?, CborValue::from(v).map_err(Error::CborErrorWithSource)?))) + .map(|(k, v)| { + Ok(( + CborValue::from(k).map_err(Error::CborErrorWithSource)?, + CborValue::from(v).map_err(Error::CborErrorWithSource)?, + )) + }) .collect::, Error>>()?; let device_engagement_version = map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; @@ -193,9 +198,7 @@ impl TryFrom for DeviceEngagement { return Err(Error::Malformed); } let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; - let device_engagement_security = map - .remove(&key) - .ok_or(Error::Malformed)?; + let device_engagement_security = map.remove(&key).ok_or(Error::Malformed)?; let security: Security = cbor::from_value2(device_engagement_security).map_err(|_| Error::Malformed)?; @@ -281,29 +284,29 @@ impl TryFrom for DeviceRetrievalMethod { if let ciborium::Value::Array(list) = value.into() { match list.as_slice() { [ciborium::Value::Integer(i1), ciborium::Value::Integer(i11), methods] - if >::into(i1.clone()) == 1 - && >::into(i11.clone()) == 1 => - { - let v: CborValue = methods.clone().try_into()?; - let nfc_options = NfcOptions::try_from(v).map_err(|_| Error::Malformed)?; - Ok(DeviceRetrievalMethod::NFC(nfc_options)) - } + if >::into(*i1) == 1 + && >::into(*i11) == 1 => + { + let v: CborValue = methods.clone().try_into()?; + let nfc_options = NfcOptions::try_from(v).map_err(|_| Error::Malformed)?; + Ok(DeviceRetrievalMethod::NFC(nfc_options)) + } [ciborium::Value::Integer(i2), ciborium::Value::Integer(i1), methods] - if >::into(i1.clone()) == 1 - && >::into(i2.clone()) == 2 => - { - let v: CborValue = methods.clone().try_into()?; - let ble_options = BleOptions::try_from(v).map_err(|_| Error::Malformed)?; - Ok(DeviceRetrievalMethod::BLE(ble_options)) - } + if >::into(*i1) == 1 + && >::into(*i2) == 2 => + { + let v: CborValue = methods.clone().try_into()?; + let ble_options = BleOptions::try_from(v).map_err(|_| Error::Malformed)?; + Ok(DeviceRetrievalMethod::BLE(ble_options)) + } [ciborium::Value::Integer(i3), ciborium::Value::Integer(i1), methods] - if >::into(i1.clone()) == 1 - && >::into(i3.clone()) == 3 => - { - let v: CborValue = methods.clone().try_into()?; - let wifi_options = WifiOptions::try_from(v).map_err(|_| Error::Malformed)?; - Ok(DeviceRetrievalMethod::WIFI(wifi_options)) - } + if >::into(*i1) == 1 + && >::into(*i3) == 3 => + { + let v: CborValue = methods.clone().try_into()?; + let wifi_options = WifiOptions::try_from(v).map_err(|_| Error::Malformed)?; + Ok(DeviceRetrievalMethod::WIFI(wifi_options)) + } [ciborium::Value::Integer(_), _, _] => Err(Error::UnsupportedDRM), _ => Err(Error::Malformed), } @@ -322,11 +325,9 @@ impl From for CborValue { DeviceRetrievalMethod::BLE(opts) => into_value(opts).unwrap(), DeviceRetrievalMethod::WIFI(opts) => into_value(opts).unwrap(), }; - ciborium::Value::Array(vec![ - transport_type, - version, - retrieval_method, - ]).try_into().unwrap() + ciborium::Value::Array(vec![transport_type, version, retrieval_method]) + .try_into() + .unwrap() } } @@ -343,19 +344,20 @@ impl TryFrom for BleOptions { Ok((k, v)) }) .collect::, Error>>()?; - let v1: Option = map.remove(&{ - let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; - key - }).map(CborValue::into); - let v2: Option = map.remove(&{ - let key: CborValue = ciborium::Value::Integer(11.into()).try_into()?; - key - }).map(CborValue::into); + let v1: Option = map + .remove(&{ + let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; + key + }) + .map(CborValue::into); + let v2: Option = map + .remove(&{ + let key: CborValue = ciborium::Value::Integer(11.into()).try_into()?; + key + }) + .map(CborValue::into); let central_client_mode = match (v1, v2) { - ( - Some(ciborium::Value::Bool(true)), - Some(ciborium::Value::Bytes(uuid)), - ) => { + (Some(ciborium::Value::Bool(true)), Some(ciborium::Value::Bytes(uuid))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; Some(CentralClientMode { uuid: Uuid::from_bytes(uuid_bytes), @@ -369,16 +371,15 @@ impl TryFrom for BleOptions { map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; cbor - }).map(CborValue::into), + }) + .map(CborValue::into), map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(10.into()).try_into()?; cbor - }).map(CborValue::into), + }) + .map(CborValue::into), ) { - ( - Some(ciborium::Value::Bool(true)), - Some(ciborium::Value::Bytes(uuid)), - ) => { + (Some(ciborium::Value::Bool(true)), Some(ciborium::Value::Bytes(uuid))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; let ble_device_address = match map.remove(&{ let cbor: CborValue = ciborium::Value::Integer(20.into()).try_into()?; @@ -431,9 +432,9 @@ impl From for CborValue { match o.peripheral_server_mode { Some(PeripheralServerMode { - uuid, - ble_device_address, - }) => { + uuid, + ble_device_address, + }) => { map.push(( ciborium::Value::Integer(0.into()), ciborium::Value::Bool(true), @@ -469,12 +470,16 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&{ - let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) - .try_into()?; - cbor - }).cloned().map(CborValue::into) { + match map + .get(&{ + let cbor: CborValue = + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) + .try_into()?; + cbor + }) + .cloned() + .map(CborValue::into) + { None => Ok(None), Some(ciborium::Value::Text(text)) => Ok(Some(text.to_string())), _ => Err(Error::InvalidWifiOptions), @@ -485,16 +490,19 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&{ - let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) - .try_into()?; - cbor - }).cloned().map(CborValue::into) { + match map + .get(&{ + let cbor: CborValue = + ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) + .try_into()?; + cbor + }) + .cloned() + .map(CborValue::into) + { None => Ok(None), Some(ciborium::Value::Integer(int_val)) => { - let uint_val = - u64::try_from(int_val).map_err(|_| Error::InvalidWifiOptions)?; + let uint_val = u64::try_from(int_val).map_err(|_| Error::InvalidWifiOptions)?; Ok(Some(uint_val)) } _ => Err(Error::InvalidWifiOptions), diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index dd28871d..81f29593 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -139,7 +139,7 @@ impl TryFrom<&CborValue> for CommandDataLength { fn try_from(v: &CborValue) -> Result { match v.as_ref() { - ciborium::Value::Integer(int_val) => Ok(Self(int_val.clone().try_into().unwrap())), + ciborium::Value::Integer(int_val) => Ok(Self((*int_val).try_into().unwrap())), _ => Err(Error::InvalidNfcOptions), } } @@ -147,7 +147,9 @@ impl TryFrom<&CborValue> for CommandDataLength { impl From for CborValue { fn from(cdl: CommandDataLength) -> CborValue { - ciborium::Value::Integer(cdl.get().into()).try_into().unwrap() + ciborium::Value::Integer(cdl.get().into()) + .try_into() + .unwrap() } } @@ -207,7 +209,7 @@ impl TryFrom<&CborValue> for ResponseDataLength { fn try_from(v: &CborValue) -> Result { match v.as_ref() { - ciborium::Value::Integer(int_val) => Ok(Self(int_val.clone().try_into().unwrap())), + ciborium::Value::Integer(int_val) => Ok(Self((*int_val).try_into().unwrap())), _ => Err(Error::InvalidNfcOptions), } } @@ -215,7 +217,9 @@ impl TryFrom<&CborValue> for ResponseDataLength { impl From for CborValue { fn from(rdl: ResponseDataLength) -> CborValue { - ciborium::Value::Integer(rdl.get().into()).try_into().unwrap() + ciborium::Value::Integer(rdl.get().into()) + .try_into() + .unwrap() } } diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index a80f53bd..5309e242 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -174,7 +174,12 @@ impl TryFrom for CoseKey { if let ciborium::Value::Map(map) = v.clone().into() { let mut map = map .into_iter() - .map(|(k, v)| Ok((CborValue::from(k).map_err(Error::CborError)?, CborValue::from(v).map_err(Error::CborError)?))) + .map(|(k, v)| { + Ok(( + CborValue::from(k).map_err(Error::CborError)?, + CborValue::from(v).map_err(Error::CborError)?, + )) + }) .collect::, Error>>()?; match ( map.remove(&{ @@ -182,19 +187,22 @@ impl TryFrom for CoseKey { .try_into() .map_err(Error::CborError)?; cbor - }).map(|v| v.into()), + }) + .map(|v| v.into()), map.remove(&{ let cbor: CborValue = ciborium::Value::Integer((-1).into()) .try_into() .map_err(Error::CborError)?; cbor - }).map(|v| v.into()), + }) + .map(|v| v.into()), map.remove(&{ let cbor: CborValue = ciborium::Value::Integer((-2).into()) .try_into() .map_err(Error::CborError)?; cbor - }).map(|v| v.into()), + }) + .map(|v| v.into()), ) { ( Some(ciborium::Value::Integer(i2)), @@ -226,7 +234,7 @@ impl TryFrom for CoseKey { _ => Err(Error::UnsupportedKeyType), } } else { - Err(Error::NotAMap(v.into())) + Err(Error::NotAMap(v)) } } } diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index 5345e5d5..1538b969 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -4,8 +4,8 @@ use std::collections::BTreeMap; use serde::{Deserialize, Serialize}; use crate::definitions::{ - DeviceSigned, - helpers::{NonEmptyMap, NonEmptyVec}, IssuerSigned, + helpers::{NonEmptyMap, NonEmptyVec}, + DeviceSigned, IssuerSigned, }; /// Represents a device response. @@ -130,12 +130,18 @@ mod test { use crate::cbor; use crate::cose::MaybeTagged; - use crate::definitions::{DeviceAuth, DeviceSigned, DigestId, Document, IssuerSigned, IssuerSignedItem}; - use crate::definitions::device_signed::{DeviceNamespaces, DeviceNamespacesBytes, DeviceSignedItems}; + use crate::definitions::device_signed::{ + DeviceNamespaces, DeviceNamespacesBytes, DeviceSignedItems, + }; use crate::definitions::helpers::NonEmptyVec; use crate::definitions::issuer_signed::{IssuerNamespaces, IssuerSignedItemBytes}; + use crate::definitions::{ + DeviceAuth, DeviceSigned, DigestId, Document, IssuerSigned, IssuerSignedItem, + }; - use super::{DeviceResponse, DocumentError, DocumentErrorCode, DocumentErrors, Documents, Status}; + use super::{ + DeviceResponse, DocumentError, DocumentErrorCode, DocumentErrors, Documents, Status, + }; static DEVICE_RESPONSE_CBOR: &str = include_str!("../../test/definitions/device_response.cbor"); @@ -175,14 +181,23 @@ mod test { let issuer_signed_item_bytes = IssuerSignedItemBytes::new(issuer_signed_item).unwrap(); let vec = NonEmptyVec::new(issuer_signed_item_bytes); let issuer_namespaces = IssuerNamespaces::new("a".to_string(), vec); - let device_signed_items = DeviceSignedItems::new("a".to_string(), ciborium::Value::Null.try_into().unwrap()); + let device_signed_items = + DeviceSignedItems::new("a".to_string(), ciborium::Value::Null.try_into().unwrap()); let mut device_namespaces = DeviceNamespaces::new(); device_namespaces.insert("a".to_string(), device_signed_items); let device_namespaces_bytes = DeviceNamespacesBytes::new(device_namespaces).unwrap(); let doc = Document { doc_type: "aaa".to_string(), - issuer_signed: IssuerSigned { namespaces: Some(issuer_namespaces), issuer_auth: cose_sign1.clone() }, - device_signed: DeviceSigned { namespaces: device_namespaces_bytes, device_auth: DeviceAuth::Mac { device_mac: cose_mac0 } }, + issuer_signed: IssuerSigned { + namespaces: Some(issuer_namespaces), + issuer_auth: cose_sign1.clone(), + }, + device_signed: DeviceSigned { + namespaces: device_namespaces_bytes, + device_auth: DeviceAuth::Mac { + device_mac: cose_mac0, + }, + }, errors: None, }; let docs = Documents::new(doc); @@ -205,4 +220,4 @@ mod test { "original cbor and re-serialized DeviceResponse do not match" ); } -} \ No newline at end of file +} diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 2987b034..2b8e03a2 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -81,9 +81,9 @@ pub enum Error { #[cfg(test)] mod tests { - use hex::FromHex; - use crate::cbor; use super::*; + use crate::cbor; + use hex::FromHex; static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); @@ -95,7 +95,7 @@ mod tests { let bytes2 = cbor::to_vec(&cose_sign1).unwrap(); assert_eq!(bytes, bytes2); let device_auth = DeviceAuth::Signature { - device_signature: cose_sign1 + device_signature: cose_sign1, }; let bytes = cbor::to_vec(&device_auth).unwrap(); println!("bytes {}", hex::encode(&bytes)); @@ -103,4 +103,4 @@ mod tests { let roundtripped_bytes = cbor::to_vec(&roundtripped).unwrap(); assert_eq!(bytes, roundtripped_bytes); } -} \ No newline at end of file +} diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index 05da126d..c7bd9f4b 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -110,9 +110,9 @@ pub struct Code { #[cfg(test)] mod tests { - use crate::definitions::traits::{FromJson, ToCbor}; - use crate::cbor::Value as CborValue; use super::VehicleCategoryCode; + use crate::cbor::Value as CborValue; + use crate::definitions::traits::{FromJson, ToCbor}; #[test] fn vehicle_category_code() { @@ -121,7 +121,10 @@ mod tests { assert_eq!(c, VehicleCategoryCode::A); let v = c.to_cbor(); - assert_eq!(>::into(v), ciborium::Value::Text("A".to_string())); + assert_eq!( + >::into(v), + ciborium::Value::Text("A".to_string()) + ); let j = serde_json::json!("A"); let c = VehicleCategoryCode::from_json(&j).unwrap(); diff --git a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs index 3ba6ed71..11d2d2c1 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs @@ -19,7 +19,7 @@ pub enum Error { impl From for Cbor { fn from(s: Sex) -> Cbor { - u8::from(s).try_into().unwrap() + u8::from(s).into() } } diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index 92b66ed1..e099f6dc 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -103,9 +103,9 @@ impl TryFrom for CborValue { insert_date!(map, expected_update, "expectedUpdate"); } - Ok(ciborium::Value::Map(map) + ciborium::Value::Map(map) .try_into() - .map_err(Error::CborError)?) + .map_err(Error::CborError) } } @@ -124,7 +124,8 @@ impl TryFrom for ValidityInfo { .collect::>>()?; macro_rules! extract_date { ($map:ident, $name:literal) => {{ - let key = CborValue::from(ciborium::Value::Text(String::from($name))).map_err(Error::CborError)?; + let key = CborValue::from(ciborium::Value::Text(String::from($name))) + .map_err(Error::CborError)?; $map.remove(&key) .ok_or(Error::MissingField(key.into())) .and_then(cbor_to_datetime)? diff --git a/src/presentation/device.rs b/src/presentation/device.rs index 0ddc793a..a30597f4 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -376,7 +376,7 @@ impl SessionManager { data.as_ref(), &mut self.reader_message_counter, ) - .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; + .map_err(|e| anyhow::anyhow!("unable to decrypt request: {}", e))?; let request = match self.parse_request(&decrypted_request) { Ok(r) => r, Err(e) => { @@ -458,11 +458,11 @@ impl SessionManager { &response_bytes, &mut self.device_message_counter, ) - .unwrap_or_else(|_e| { - //tracing::warn!("unable to encrypt response: {}", e); - status = Some(session::Status::SessionEncryptionError); - Default::default() - }); + .unwrap_or_else(|_e| { + //tracing::warn!("unable to encrypt response: {}", e); + status = Some(session::Status::SessionEncryptionError); + Default::default() + }); let data = if status.is_some() { None } else { @@ -994,7 +994,7 @@ mod test { } } ])) - .unwrap(); + .unwrap(); let permitted = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -1011,7 +1011,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let expected: PermittedItems = serde_json::from_value(json!({ "doc_type_1": { "namespace_1": [ @@ -1019,7 +1019,7 @@ mod test { ], } })) - .unwrap(); + .unwrap(); let filtered = super::filter_permitted(&requested, permitted); From 9fe82d8cd2b575fd13d2a74842de2905c4c3e2e6 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Mon, 16 Sep 2024 04:25:19 +0300 Subject: [PATCH 12/19] Don't use cbor::Value but use directly ciborium::Value and use keys as primitives - Use actions-rs to setup Rust in CI so it works with act also locally - Add CborValueKey as wrapper over ciborium::Value to impl Ord and PartialOrd to be used as Key in Map --- src/issuance/x5chain.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/issuance/x5chain.rs b/src/issuance/x5chain.rs index 103cb7ef..609b097a 100644 --- a/src/issuance/x5chain.rs +++ b/src/issuance/x5chain.rs @@ -59,7 +59,6 @@ //! The [X5Chain] struct also provides a [X5Chain::builder] method for creating a new [Builder] instance. use crate::definitions::helpers::NonEmptyVec; use anyhow::{anyhow, Result}; -use ciborium::Value as CborValue; use std::{fs::File, io::Read}; use x509_cert::{ certificate::Certificate, @@ -93,17 +92,17 @@ impl X5Chain { Builder::default() } - /// Converts the [X5Chain] object into a [CborValue]. - pub fn into_cbor(&self) -> CborValue { + /// Converts the [X5Chain] object into a [ciborium::Value]. + pub fn into_cbor(&self) -> ciborium::Value { match &self.0.as_ref() { - &[cert] => CborValue::Bytes(cert.bytes.clone()), - certs => CborValue::Array( + &[cert] => ciborium::Value::Bytes(cert.bytes.clone()), + certs => ciborium::Value::Array( certs .iter() .cloned() .map(|x509| x509.bytes) - .map(CborValue::Bytes) - .collect::>(), + .map(ciborium::Value::Bytes) + .collect::>(), ), } } From 923408c786001727d59b6143f26e45b40b7e4a30 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Mon, 16 Sep 2024 04:29:40 +0300 Subject: [PATCH 13/19] Don't use cbor::Value but use directly ciborium::Value and use keys as primitives - Use actions-rs to setup Rust in CI so it works with act also locally - Add CborValueKey as wrapper over ciborium::Value to impl Ord and PartialOrd to be used as Key in Map --- .github/workflows/ci.yaml | 7 + Cargo.toml | 2 - macros/src/to_cbor.rs | 7 +- src/cbor.rs | 312 +----------------- src/cose/sign1.rs | 7 +- src/definitions/device_engagement.rs | 293 ++++++++-------- .../device_engagement/nfc_options.rs | 172 ++++++---- src/definitions/device_key/cose_key.rs | 126 +++---- src/definitions/device_key/mod.rs | 3 +- src/definitions/device_request.rs | 2 +- src/definitions/device_response.rs | 16 +- src/definitions/device_signed.rs | 3 +- src/definitions/helpers/bytestr.rs | 17 +- src/definitions/helpers/tag24.rs | 27 +- src/definitions/issuer_signed.rs | 3 +- src/definitions/namespaces/fulldate.rs | 7 +- src/definitions/namespaces/latin1.rs | 5 +- .../namespaces/org_iso_18013_5_1/age_over.rs | 10 +- .../namespaces/org_iso_18013_5_1/alpha2.rs | 7 +- .../org_iso_18013_5_1/biometric_template.rs | 3 +- .../org_iso_18013_5_1/driving_privileges.rs | 40 +-- .../org_iso_18013_5_1/eye_colour.rs | 7 +- .../org_iso_18013_5_1/hair_colour.rs | 7 +- .../org_iso_18013_5_1/issuing_jurisdiction.rs | 5 +- .../namespaces/org_iso_18013_5_1/sex.rs | 5 +- .../namespaces/org_iso_18013_5_1/tdate.rs | 11 +- .../un_distinguishing_sign.rs | 5 +- .../org_iso_18013_5_1_aamva/county_code.rs | 3 +- .../org_iso_18013_5_1_aamva/dhs_compliance.rs | 3 +- .../domestic_driving_privileges.rs | 20 +- .../org_iso_18013_5_1_aamva/edl_indicator.rs | 3 +- .../org_iso_18013_5_1_aamva/name_suffix.rs | 3 +- .../name_truncation.rs | 3 +- .../org_iso_18013_5_1_aamva/present.rs | 5 +- .../race_and_ethnicity.rs | 3 +- .../namespaces/org_iso_18013_5_1_aamva/sex.rs | 3 +- .../org_iso_18013_5_1_aamva/weight_range.rs | 3 +- src/definitions/traits/to_cbor.rs | 19 +- src/definitions/validity_info.rs | 52 ++- src/issuance/mdoc.rs | 5 +- src/presentation/device.rs | 16 +- src/presentation/reader.rs | 10 +- test/definitions/device_response.cbor | 2 +- 43 files changed, 426 insertions(+), 836 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ab4adcd3..5cc764f6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,6 +18,13 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Set up Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt, clippy + profile: minimal + - name: Build run: cargo build --all-targets diff --git a/Cargo.toml b/Cargo.toml index 5cdd7270..0ffaa348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,6 @@ p256 = { version = "0.13.0", features = ["serde", "ecdh"] } p384 = { version = "0.13.0", features = ["serde", "ecdh"] } rand = { version = "0.8.5", features = ["getrandom"] } serde = { version = "1.0", features = ["derive"] } -serde_cbor = { version = "0.11.2", features = ["tags"] } serde_json = "1.0" serde_bytes = "0.11.0" sha2 = "0.10.6" @@ -50,7 +49,6 @@ strum_macros = "0.24" coset = "0.3.8" ciborium = "0.2.2" digest = "0.10.7" -hex = "0.4.3" [dev-dependencies] hex = "0.4.3" diff --git a/macros/src/to_cbor.rs b/macros/src/to_cbor.rs index 185dc515..db2c8d7d 100644 --- a/macros/src/to_cbor.rs +++ b/macros/src/to_cbor.rs @@ -86,7 +86,7 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt let output = quote! { mod #mod_name { - use #isomdl_path::cbor::Value; + use ciborium::Value; use super::*; use #isomdl_path::definitions::traits::{ToCbor, ToNamespaceMap}; impl ToNamespaceMap for #ident { @@ -104,7 +104,7 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt .into_iter() .map(|(k, v)| (ciborium::Value::Text(k), v.try_into().unwrap())) .collect(); - ciborium::Value::Map(map).try_into().unwrap() + Value::Map(map) } } } @@ -140,9 +140,8 @@ fn unnamed_fields(isomdl_path: Ident, ident: Ident, mut input: FieldsUnnamed) -> mod #mod_name { use super::*; use #isomdl_path::definitions::traits::{ToCbor, ToCborError}; - use #isomdl_path::cbor::Value impl ToCbor for #ident { - fn to_cbor(self) -> Value { + fn to_cbor(self) -> ciborium::Value { <#field_type as ToCbor>::to_cbor(self) } } diff --git a/src/cbor.rs b/src/cbor.rs index ea06ccc1..b4358bba 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -1,63 +1,10 @@ -use std::borrow::{Borrow, BorrowMut}; -use std::cmp::Ordering; use std::io::Cursor; -use std::ops::{Deref, DerefMut}; +use ciborium::Value; use coset::{cbor, CoseError, EndOfFile}; -use serde::{de, Deserialize, Serialize}; +use serde::{de, Serialize}; use thiserror::Error; -/// Wraps [ciborium::Value] and implements [PartialEq], [Eq], [PartialOrd] and [Ord], -/// so it can be used in maps and sets. -/// -/// [IEEE754](https://www.rfc-editor.org/rfc/rfc8949.html#IEEE754) -/// non-finite floats do not have a total ordering, -/// which means [`Ord`] cannot be correctly implemented for types that may contain them. -/// That's why we don't support such values. -/// -/// Also, useful in future if we want to change the CBOR library. -#[derive(Debug, Clone)] -pub struct Value(ciborium::Value); - -impl Value { - /// Create a new CBOR value. - /// - /// Return an error if the value contains non-finite floats or NaN. - pub fn from(value: ciborium::Value) -> Result { - // Validate the CBOR value. If it contains non-finite floats, return an error. - if contains_non_finite_floats(&value) { - Err(CborError::NonFiniteFloats) - } else { - Ok(Value(value)) - } - } - - /// Unsafe version of `new`. - /// - /// # Safety - /// - /// It will allow creating from value containing non-finite floats or NaN. - pub unsafe fn from_unsafe(value: ciborium::Value) -> Self { - Value(value) - } - - pub fn into_inner(self) -> ciborium::Value { - self.0 - } -} - -// Helper function to check for non-finite floats -fn contains_non_finite_floats(value: &ciborium::Value) -> bool { - match value { - ciborium::Value::Float(f) => !f.is_finite(), - ciborium::Value::Array(arr) => arr.iter().any(contains_non_finite_floats), - ciborium::Value::Map(map) => map - .iter() - .any(|(k, v)| contains_non_finite_floats(k) || contains_non_finite_floats(v)), - _ => false, - } -} - #[derive(Debug, Error)] pub enum CborError { /// CBOR decoding failure. @@ -146,48 +93,6 @@ impl From for CborError { } } -impl Value { - pub fn to_string(&self) -> coset::Result { - self.0.clone().into_text().map_err(|e| { - CoseError::DecodeFailed(ciborium::de::Error::Semantic(None, format!("{e:?}"))) - }) - } -} - -impl Deref for Value { - type Target = ciborium::Value; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Value { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl Eq for Value {} - -impl Ord for Value { - fn cmp(&self, other: &Self) -> Ordering { - self.0.partial_cmp(&other.0).unwrap() - } -} - -impl PartialOrd for Value { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl PartialEq for Value { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - pub fn to_vec(value: &T) -> Result, CborError> where T: serde::Serialize, @@ -208,15 +113,6 @@ where .map_err(CborError::from) } -/// Convert a `chor::Value` into a type `T` -#[allow(clippy::needless_pass_by_value)] -pub fn from_value2(value: Value) -> Result -where - T: de::DeserializeOwned, -{ - from_value(value.0) -} - /// Convert a `ciborium::Value` into a type `T` #[allow(clippy::needless_pass_by_value)] pub fn from_value(value: ciborium::Value) -> Result @@ -231,212 +127,10 @@ where from_slice(buf.as_slice()) } -pub fn into_value(v: S) -> Result +pub fn into_value(v: S) -> Result where S: Serialize, { let bytes = to_vec(&v)?; from_slice(&bytes) } - -impl TryFrom for Value { - type Error = CborError; - - fn try_from(value: ciborium::Value) -> Result { - Value::from(value) - } -} - -impl From for ciborium::Value { - fn from(val: Value) -> Self { - val.0 - } -} - -impl Serialize for Value { - fn serialize(&self, serializer: S) -> crate::cose::sign1::Result - where - S: serde::Serializer, - { - self.0.serialize(serializer) - } -} - -impl<'de> Deserialize<'de> for Value { - fn deserialize(deserializer: D) -> crate::cose::sign1::Result - where - D: serde::Deserializer<'de>, - { - ciborium::Value::deserialize(deserializer).map(Value) - } -} - -impl AsRef for Value { - fn as_ref(&self) -> &ciborium::Value { - &self.0 - } -} - -impl Borrow for Value { - fn borrow(&self) -> &ciborium::Value { - &self.0 - } -} - -impl BorrowMut for Value { - fn borrow_mut(&mut self) -> &mut ciborium::Value { - &mut self.0 - } -} - -macro_rules! impl_from { - ($variant:path, $for_type:ty) => { - impl From<$for_type> for Value { - fn from(v: $for_type) -> Value { - unsafe { Value::from_unsafe($variant(v.into())) } - } - } - }; -} - -impl_from!(ciborium::Value::Bool, bool); -impl_from!(ciborium::Value::Bytes, Vec); -impl_from!(ciborium::Value::Bytes, &[u8]); -impl_from!(ciborium::Value::Text, String); -impl_from!(ciborium::Value::Text, &str); -impl_from!(ciborium::Value::Array, Vec); -impl_from!(ciborium::Value::Integer, i8); -impl_from!(ciborium::Value::Integer, i16); -impl_from!(ciborium::Value::Integer, i32); -impl_from!(ciborium::Value::Integer, i64); -// i128 omitted because not all numbers fit in CBOR serialization -impl_from!(ciborium::Value::Integer, u8); -impl_from!(ciborium::Value::Integer, u16); -impl_from!(ciborium::Value::Integer, u32); -impl_from!(ciborium::Value::Integer, u64); -// u128 omitted because not all numbers fit in CBOR serialization -impl_from!(ciborium::Value::Float, f32); -impl_from!(ciborium::Value::Float, f64); - -#[cfg(test)] -mod tests { - use crate::cbor::{CborError, Value}; - - #[test] - fn conversions() { - assert_eq!( - Value::from(ciborium::Value::Bool(true)), - Ok(ciborium::Value::Bool(true).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1i8.into())), - Ok(ciborium::Value::Integer(1i8.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1i16.into())), - Ok(ciborium::Value::Integer(1i16.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1i32.into())), - Ok(ciborium::Value::Integer(1i32.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1i64.into())), - Ok(ciborium::Value::Integer(1i64.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1u8.into())), - Ok(ciborium::Value::Integer(1u8.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1u16.into())), - Ok(ciborium::Value::Integer(1u16.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1u32.into())), - Ok(ciborium::Value::Integer(1u32.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Integer(1u64.into())), - Ok(ciborium::Value::Integer(1u64.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Float(1.0f32.into())), - Ok(ciborium::Value::Float(1.0f32.into()).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Float(1.0f64)), - Ok(ciborium::Value::Float(1.0f64).try_into().unwrap()) - ); - assert_eq!( - Value::from(ciborium::Value::Text("foo".to_string())), - Ok(ciborium::Value::Text("foo".to_string()).try_into().unwrap()) - ); - } - - #[test] - fn non_finite_floats() { - assert_eq!( - Value::from(ciborium::Value::from(f32::NAN)), - Err(CborError::NonFiniteFloats) - ); - assert_eq!( - Value::from(ciborium::Value::from(f32::INFINITY)), - Err(CborError::NonFiniteFloats) - ); - assert_eq!( - Value::from(ciborium::Value::from(f32::NEG_INFINITY)), - Err(CborError::NonFiniteFloats) - ); - assert_eq!( - Value::from(ciborium::Value::from(f64::NAN)), - Err(CborError::NonFiniteFloats) - ); - assert_eq!( - Value::from(ciborium::Value::from(f64::NEG_INFINITY)), - Err(CborError::NonFiniteFloats) - ); - } - - #[test] - #[should_panic] - fn non_finite_floats_no_panic() { - let _ = Value::from(ciborium::Value::from(f32::NAN)).unwrap(); - } - - #[test] - fn non_finite_floats_unsafe() { - unsafe { - assert!(Value::from_unsafe(ciborium::Value::from(f32::NAN)) - .0 - .into_float() - .unwrap() - .is_nan()); - assert!(Value::from_unsafe(ciborium::Value::from(f32::INFINITY)) - .0 - .into_float() - .unwrap() - .is_infinite()); - assert!(Value::from_unsafe(ciborium::Value::from(f32::NEG_INFINITY)) - .0 - .into_float() - .unwrap() - .is_infinite()); - assert!(Value::from_unsafe(ciborium::Value::from(f64::NAN)) - .0 - .into_float() - .unwrap() - .is_nan()); - assert!(Value::from_unsafe(ciborium::Value::from(f64::INFINITY)) - .0 - .into_float() - .unwrap() - .is_infinite()); - assert!(Value::from_unsafe(ciborium::Value::from(f64::NEG_INFINITY)) - .0 - .into_float() - .unwrap() - .is_infinite()); - } - } -} diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index f3514b45..4d1dfa6a 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -279,6 +279,9 @@ mod p384 { #[cfg(test)] mod tests { + use crate::cbor; + use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; + use crate::cose::{MaybeTagged, SignatureAlgorithm}; use coset::cwt::{ClaimsSet, Timestamp}; use coset::{iana, CborSerializable, Header}; use hex::FromHex; @@ -286,10 +289,6 @@ mod tests { use p256::SecretKey; use signature::{SignatureEncoding, Signer}; - use crate::cbor; - use crate::cose::sign1::{CoseSign1, Error, PreparedCoseSign1}; - use crate::cose::{MaybeTagged, SignatureAlgorithm}; - static COSE_SIGN1: &str = include_str!("../../test/definitions/cose/sign1/serialized.cbor"); static COSE_KEY: &str = include_str!("../../test/definitions/cose/sign1/secret_key"); diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index cbd52312..fcb4a06d 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -3,34 +3,39 @@ //! The [DeviceEngagement] struct represents a device engagement object, which contains information about a device's engagement with a server. //! It includes fields such as the `version`, `security details, `device retrieval methods, `server retrieval methods, and `protocol information. //! -//! The module also provides implementations for conversions between [DeviceEngagement] and [CborValue], as well as other utility functions. -use crate::cbor::{into_value, CborError, Value as CborValue}; -use crate::definitions::helpers::Tag24; -use crate::definitions::helpers::{ByteStr, NonEmptyVec}; -use crate::definitions::CoseKey; +//! The module also provides implementations for conversions between [DeviceEngagement] and [ciborium::Value], as well as other utility functions. +use std::{collections::BTreeMap, vec}; + use anyhow::Result; use serde::{Deserialize, Serialize}; -use std::{collections::BTreeMap, vec}; use uuid::Uuid; -pub mod error; pub use error::Error; +pub use nfc_options::NfcOptions; -pub mod nfc_options; use crate::cbor; -pub use nfc_options::NfcOptions; +use crate::cbor::CborError; +use crate::definitions::helpers::Tag24; +use crate::definitions::helpers::{ByteStr, NonEmptyVec}; +use crate::definitions::CoseKey; +pub mod error; +pub mod nfc_options; pub type EDeviceKeyBytes = Tag24; pub type EReaderKeyBytes = Tag24; pub type DeviceRetrievalMethods = NonEmptyVec; -pub type ProtocolInfo = cbor::Value; +pub type ProtocolInfo = ciborium::Value; pub type Oidc = (u64, String, String); pub type WebApi = (u64, String, String); /// Represents a device engagement. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(try_from = "CborValue", into = "CborValue", rename_all = "camelCase")] +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde( + try_from = "ciborium::Value", + into = "ciborium::Value", + rename_all = "camelCase" +)] pub struct DeviceEngagement { /// The version of the device engagement. pub version: String, @@ -51,10 +56,22 @@ pub struct DeviceEngagement { pub protocol_info: Option, } +impl PartialEq for DeviceEngagement { + fn eq(&self, other: &Self) -> bool { + self.version == other.version + && self.security == other.security + && self.device_retrieval_methods == other.device_retrieval_methods + && self.server_retrieval_methods == other.server_retrieval_methods + && self.protocol_info == other.protocol_info + } +} + +impl Eq for DeviceEngagement {} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(try_from = "CborValue", into = "CborValue")] +#[serde(try_from = "ciborium::Value", into = "ciborium::Value")] pub enum DeviceRetrievalMethod { - /// Represents the options for a WiFi connection. + /// Represents the options for a Wi-Fi connection. WIFI(WifiOptions), /// Represents the BLE options for device engagement. @@ -86,7 +103,7 @@ pub struct ServerRetrievalMethods { /// Represents the options for `Bluetooth Low Energy` (BLE) device engagement. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] -#[serde(try_from = "CborValue", into = "CborValue")] +#[serde(try_from = "ciborium::Value", into = "ciborium::Value")] pub struct BleOptions { /// The peripheral server mode for `BLE` device engagement. #[serde(skip_serializing_if = "Option::is_none")] @@ -113,29 +130,29 @@ pub struct CentralClientMode { pub uuid: Uuid, } -/// Represents the options for a `WiFi` device engagement. +/// Represents the options for a `Wi-Fi` device engagement. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] -#[serde(try_from = "CborValue", into = "CborValue")] +#[serde(try_from = "ciborium::Value", into = "ciborium::Value")] pub struct WifiOptions { - /// The passphrase for the `WiFi connection. If [None], no passphrase is required. + /// The passphrase for the `Wi-Fi connection. If [None], no passphrase is required. #[serde(skip_serializing_if = "Option::is_none")] pass_phrase: Option, - /// The operating class of the `WiFi` channel. If [None], the operating class is not specified. + /// The operating class of the `Wi-Fi` channel. If [None], the operating class is not specified. #[serde(skip_serializing_if = "Option::is_none")] channel_info_operating_class: Option, - /// The channel number of the `WiFi` channel. If [None], the channel number is not specified. + /// The channel number of the `Wi-Fi` channel. If [None], the channel number is not specified. #[serde(skip_serializing_if = "Option::is_none")] channel_info_channel_number: Option, - /// The band information of the `WiFi channel. If [None], the band information is not specified. + /// The band information of the `Wi-Fi channel. If [None], the band information is not specified. #[serde(skip_serializing_if = "Option::is_none")] band_info: Option, } -impl From for CborValue { - fn from(device_engagement: DeviceEngagement) -> CborValue { +impl From for ciborium::Value { + fn from(device_engagement: DeviceEngagement) -> ciborium::Value { let mut map = vec![]; map.push(( ciborium::Value::Integer(0.into()), @@ -144,14 +161,14 @@ impl From for CborValue { map.push(( ciborium::Value::Integer(1.into()), ciborium::Value::Array(vec![ - into_value(device_engagement.security.0).unwrap(), - into_value(device_engagement.security.1).unwrap(), + cbor::into_value(device_engagement.security.0).unwrap(), + cbor::into_value(device_engagement.security.1).unwrap(), ]), )); if let Some(methods) = device_engagement.device_retrieval_methods { let methods = Vec::from(methods) .into_iter() - .map(into_value) + .map(cbor::into_value) .collect::, CborError>>() .unwrap(); map.push(( @@ -162,71 +179,53 @@ impl From for CborValue { if let Some(methods) = device_engagement.server_retrieval_methods { map.push(( ciborium::Value::Integer(3.into()), - into_value(methods).unwrap(), + cbor::into_value(methods).unwrap(), )); } if let Some(_info) = device_engagement.protocol_info { // Usage of protocolinfo is RFU and should for now be none } - ciborium::Value::Map(map).try_into().unwrap() + ciborium::Value::Map(map) } } -impl TryFrom for DeviceEngagement { +impl TryFrom for DeviceEngagement { type Error = Error; - fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.into() { - let mut map = map + fn try_from(v: ciborium::Value) -> Result { + if let ciborium::Value::Map(map) = v { + let mut map: BTreeMap = map .into_iter() - .map(|(k, v)| { - Ok(( - CborValue::from(k).map_err(Error::CborErrorWithSource)?, - CborValue::from(v).map_err(Error::CborErrorWithSource)?, - )) - }) + .map(|(k, v)| Ok((k.into_integer().map_err(|_| Error::CborError)?.into(), v))) .collect::, Error>>()?; - let device_engagement_version = map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; - cbor - }); - if let Some(ciborium::Value::Text(v)) = device_engagement_version.as_deref() { + let device_engagement_version = map.remove(&0); + if let Some(ciborium::Value::Text(v)) = device_engagement_version { if v != "1.0" { return Err(Error::UnsupportedVersion); } } else { return Err(Error::Malformed); } - let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; - let device_engagement_security = map.remove(&key).ok_or(Error::Malformed)?; + let device_engagement_security = map.remove(&1).ok_or(Error::Malformed)?; let security: Security = - cbor::from_value2(device_engagement_security).map_err(|_| Error::Malformed)?; + cbor::from_value(device_engagement_security).map_err(|_| Error::Malformed)?; let device_retrieval_methods = map - .remove(&{ - let cbor: CborValue = ciborium::Value::Integer(2.into()).try_into()?; - cbor - }) - .map(cbor::from_value2) + .remove(&2) + .map(cbor::from_value) .transpose() .map_err(|_| Error::Malformed)?; let server_retrieval_methods = map - .remove(&{ - let cbor: CborValue = ciborium::Value::Integer(3.into()).try_into()?; - cbor - }) - .map(cbor::from_value2) + .remove(&3) + .map(cbor::from_value) .transpose() .map_err(|_| Error::Malformed)?; if server_retrieval_methods.is_some() { //tracing::warn!("server_retrieval is unimplemented.") } - let protocol_info = map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(4.into()).try_into()?; - cbor - }); + let protocol_info = map.remove(&4); if protocol_info.is_some() { //tracing::warn!("protocol_info is RFU and has been ignored in deserialization.") } @@ -278,33 +277,33 @@ impl DeviceRetrievalMethod { } } -impl TryFrom for DeviceRetrievalMethod { +impl TryFrom for DeviceRetrievalMethod { type Error = Error; - fn try_from(value: CborValue) -> Result { - if let ciborium::Value::Array(list) = value.into() { + fn try_from(value: ciborium::Value) -> Result { + if let ciborium::Value::Array(list) = value { match list.as_slice() { [ciborium::Value::Integer(i1), ciborium::Value::Integer(i11), methods] if >::into(*i1) == 1 && >::into(*i11) == 1 => { - let v: CborValue = methods.clone().try_into()?; - let nfc_options = NfcOptions::try_from(v).map_err(|_| Error::Malformed)?; + let nfc_options = + NfcOptions::try_from(methods.clone()).map_err(|_| Error::Malformed)?; Ok(DeviceRetrievalMethod::NFC(nfc_options)) } [ciborium::Value::Integer(i2), ciborium::Value::Integer(i1), methods] if >::into(*i1) == 1 && >::into(*i2) == 2 => { - let v: CborValue = methods.clone().try_into()?; - let ble_options = BleOptions::try_from(v).map_err(|_| Error::Malformed)?; + let ble_options = + BleOptions::try_from(methods.clone()).map_err(|_| Error::Malformed)?; Ok(DeviceRetrievalMethod::BLE(ble_options)) } [ciborium::Value::Integer(i3), ciborium::Value::Integer(i1), methods] if >::into(*i1) == 1 && >::into(*i3) == 3 => { - let v: CborValue = methods.clone().try_into()?; - let wifi_options = WifiOptions::try_from(v).map_err(|_| Error::Malformed)?; + let wifi_options = + WifiOptions::try_from(methods.clone()).map_err(|_| Error::Malformed)?; Ok(DeviceRetrievalMethod::WIFI(wifi_options)) } [ciborium::Value::Integer(_), _, _] => Err(Error::UnsupportedDRM), @@ -316,46 +315,33 @@ impl TryFrom for DeviceRetrievalMethod { } } -impl From for CborValue { +impl From for ciborium::Value { fn from(drm: DeviceRetrievalMethod) -> Self { let transport_type = drm.transport_type().into(); let version = drm.version().into(); let retrieval_method = match drm { - DeviceRetrievalMethod::NFC(opts) => into_value(opts).unwrap(), - DeviceRetrievalMethod::BLE(opts) => into_value(opts).unwrap(), - DeviceRetrievalMethod::WIFI(opts) => into_value(opts).unwrap(), + DeviceRetrievalMethod::NFC(opts) => cbor::into_value(opts).unwrap(), + DeviceRetrievalMethod::BLE(opts) => cbor::into_value(opts).unwrap(), + DeviceRetrievalMethod::WIFI(opts) => cbor::into_value(opts).unwrap(), }; ciborium::Value::Array(vec![transport_type, version, retrieval_method]) - .try_into() - .unwrap() } } -impl TryFrom for BleOptions { +impl TryFrom for BleOptions { type Error = Error; - fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.into() { - let mut map = map + fn try_from(v: ciborium::Value) -> Result { + if let ciborium::Value::Map(map) = v { + let mut map: BTreeMap = map .into_iter() .map(|(k, v)| { - let k: CborValue = CborValue::from(k)?; - let v: CborValue = CborValue::from(v)?; + let k = k.into_integer().map_err(|_| Error::CborError)?.into(); Ok((k, v)) }) .collect::, Error>>()?; - let v1: Option = map - .remove(&{ - let key: CborValue = ciborium::Value::Integer(1.into()).try_into()?; - key - }) - .map(CborValue::into); - let v2: Option = map - .remove(&{ - let key: CborValue = ciborium::Value::Integer(11.into()).try_into()?; - key - }) - .map(CborValue::into); + let v1: Option = map.remove(&1).map(ciborium::Value::into); + let v2: Option = map.remove(&11).map(ciborium::Value::into); let central_client_mode = match (v1, v2) { (Some(ciborium::Value::Bool(true)), Some(ciborium::Value::Bytes(uuid))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; @@ -368,23 +354,12 @@ impl TryFrom for BleOptions { }; let peripheral_server_mode = match ( - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; - cbor - }) - .map(CborValue::into), - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(10.into()).try_into()?; - cbor - }) - .map(CborValue::into), + map.remove(&0).map(ciborium::Value::into), + map.remove(&10).map(ciborium::Value::into), ) { (Some(ciborium::Value::Bool(true)), Some(ciborium::Value::Bytes(uuid))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; - let ble_device_address = match map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(20.into()).try_into()?; - cbor - }) { + let ble_device_address = match map.remove(&20) { Some(value) => Some(value.try_into().map_err(|_| Error::Malformed)?), None => None, }; @@ -407,8 +382,8 @@ impl TryFrom for BleOptions { } } -impl From for CborValue { - fn from(o: BleOptions) -> CborValue { +impl From for ciborium::Value { + fn from(o: BleOptions) -> ciborium::Value { let mut map = vec![]; match o.central_client_mode { @@ -446,7 +421,7 @@ impl From for CborValue { if let Some(address) = ble_device_address { map.push(( ciborium::Value::Integer(20.into()), - into_value(address).unwrap(), + cbor::into_value(address).unwrap(), )); } } @@ -458,28 +433,19 @@ impl From for CborValue { } } - ciborium::Value::Map(map).try_into().unwrap() + ciborium::Value::Map(map) } } -impl TryFrom for WifiOptions { +impl TryFrom for WifiOptions { type Error = Error; - fn try_from(v: CborValue) -> Result { + fn try_from(v: ciborium::Value) -> Result { fn lookup_opt_string( - map: &BTreeMap, + map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map - .get(&{ - let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) - .try_into()?; - cbor - }) - .cloned() - .map(CborValue::into) - { + match map.get(&idx).cloned().map(ciborium::Value::into) { None => Ok(None), Some(ciborium::Value::Text(text)) => Ok(Some(text.to_string())), _ => Err(Error::InvalidWifiOptions), @@ -487,19 +453,10 @@ impl TryFrom for WifiOptions { } fn lookup_opt_u64( - map: &BTreeMap, + map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map - .get(&{ - let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) - .try_into()?; - cbor - }) - .cloned() - .map(CborValue::into) - { + match map.get(&idx).cloned().map(ciborium::Value::into) { None => Ok(None), Some(ciborium::Value::Integer(int_val)) => { let uint_val = u64::try_from(int_val).map_err(|_| Error::InvalidWifiOptions)?; @@ -510,15 +467,10 @@ impl TryFrom for WifiOptions { } fn lookup_opt_bytestr( - map: &BTreeMap, + map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&{ - let cbor: CborValue = - ciborium::Value::Integer(idx.try_into().map_err(|_| Error::Malformed)?) - .try_into()?; - cbor - }) { + match map.get(&idx) { None => Ok(None), Some(cbor_val) => { let byte_str = ByteStr::try_from(cbor_val.clone()) @@ -528,11 +480,14 @@ impl TryFrom for WifiOptions { } } - let map: BTreeMap = match v.into() { + let map: BTreeMap = match v { ciborium::Value::Map(map) => { let map = map .into_iter() - .map(|(k, v)| Ok((CborValue::from(k)?, CborValue::from(v)?))) + .map(|(k, v)| { + let k = k.into_integer().map_err(|_| Error::CborError)?.into(); + Ok((k, v)) + }) .collect::, Error>>()?; Ok(map) } @@ -571,37 +526,49 @@ impl TryFrom for WifiOptions { } } -impl From for CborValue { - fn from(o: WifiOptions) -> CborValue { +impl From for ciborium::Value { + fn from(o: WifiOptions) -> ciborium::Value { let mut map = vec![]; if let Some(v) = o.pass_phrase { - map.push((ciborium::Value::Integer(0.into()), into_value(v).unwrap())); + map.push(( + ciborium::Value::Integer(0.into()), + cbor::into_value(v).unwrap(), + )); } if let Some(v) = o.channel_info_operating_class { - map.push(((ciborium::Value::Integer(1.into())), into_value(v).unwrap())); + map.push(( + ciborium::Value::Integer(1.into()), + cbor::into_value(v).unwrap(), + )); } if let Some(v) = o.channel_info_channel_number { - map.push((ciborium::Value::Integer(2.into()), into_value(v).unwrap())); + map.push(( + ciborium::Value::Integer(2.into()), + cbor::into_value(v).unwrap(), + )); } if let Some(v) = o.band_info { - map.push((ciborium::Value::Integer(3.into()), into_value(v).unwrap())); + map.push(( + ciborium::Value::Integer(3.into()), + cbor::into_value(v).unwrap(), + )); } - ciborium::Value::Map(map).try_into().unwrap() + ciborium::Value::Map(map) } } -impl From for CborValue { - fn from(m: ServerRetrievalMethods) -> CborValue { +impl From for ciborium::Value { + fn from(m: ServerRetrievalMethods) -> ciborium::Value { let mut map = vec![]; if let Some((x, y, z)) = m.web_api { map.push(( "webApi".to_string().into(), ciborium::Value::Array(vec![ - into_value(x).unwrap(), - into_value(y).unwrap(), - into_value(z).unwrap(), + cbor::into_value(x).unwrap(), + cbor::into_value(y).unwrap(), + cbor::into_value(z).unwrap(), ]), )); } @@ -610,23 +577,25 @@ impl From for CborValue { map.push(( "oidc".to_string().into(), ciborium::Value::Array(vec![ - into_value(x).unwrap(), - into_value(y).unwrap(), - into_value(z).unwrap(), + cbor::into_value(x).unwrap(), + cbor::into_value(y).unwrap(), + cbor::into_value(z).unwrap(), ]), )); } - ciborium::Value::Map(map).try_into().unwrap() + ciborium::Value::Map(map) } } #[cfg(test)] mod test { - use super::*; - use crate::definitions::session::create_p256_ephemeral_keys; use uuid::Uuid; + use crate::definitions::session::create_p256_ephemeral_keys; + + use super::*; + #[test] fn device_engagement_cbor_roundtrip() { let key_pair = create_p256_ephemeral_keys().unwrap(); diff --git a/src/definitions/device_engagement/nfc_options.rs b/src/definitions/device_engagement/nfc_options.rs index 81f29593..df6bdf74 100644 --- a/src/definitions/device_engagement/nfc_options.rs +++ b/src/definitions/device_engagement/nfc_options.rs @@ -1,72 +1,127 @@ -use crate::cbor::Value as CborValue; use crate::definitions::device_engagement::error::Error; use anyhow::Result; -use serde::{Deserialize, Serialize}; +use serde::de::Error as SerdeError; +use serde::{Deserialize, Deserializer, Serialize}; use std::collections::BTreeMap; /// The maximum length of the NFC command, as specified in ISO_18013-5 2021 Section 8.3.3.1.2 /// Values of this type must lie between 255 and 65,535 inclusive, as specified in Note 2. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, PartialEq, Eq)] pub struct CommandDataLength(u16); +// Implement Deserialize manually to add the validation check +impl<'de> Deserialize<'de> for CommandDataLength { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // Deserialize as an u16 first + let value = u16::deserialize(deserializer)?; + + // Check the value + if value >= CommandDataLength::MIN.get() { + Ok(CommandDataLength(value)) + } else { + Err(D::Error::custom(format!( + "CommandDataLength must be greater than {}", + CommandDataLength::MIN.get() + ))) + } + } +} + /// The maximum length of the NFC response data, as specified in ISO_18013-5 2021 Section 8.3.3.1.2 /// Values of this type must lie between 256 and 65,536 inclusive, as specified in Note 2. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Serialize, PartialEq, Eq)] pub struct ResponseDataLength(u32); +// Implement Deserialize manually to add the validation check +impl<'de> Deserialize<'de> for ResponseDataLength { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + // Deserialize as an u32 first + let value = u32::deserialize(deserializer)?; + + // Check the value + if value >= ResponseDataLength::MIN.get() { + Ok(ResponseDataLength(value)) + } else { + Err(D::Error::custom(format!( + "ResponseDataLength must be greater than {}", + ResponseDataLength::MIN.get() + ))) + } + } +} + #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Default)] -#[serde(try_from = "CborValue", into = "CborValue")] +#[serde(try_from = "ciborium::Value", into = "ciborium::Value")] pub struct NfcOptions { max_len_command_data_field: CommandDataLength, max_len_response_data_field: ResponseDataLength, } -impl TryFrom for NfcOptions { +impl TryFrom for NfcOptions { type Error = Error; - fn try_from(v: CborValue) -> Result { - let map: BTreeMap = match v.into() { + fn try_from(v: ciborium::Value) -> Result { + let mut map: BTreeMap = match v { ciborium::Value::Map(map) => map .into_iter() .map(|(k, v)| { - let k = CborValue::from(k)?; - let v = CborValue::from(v)?; + let k = k.into_integer().map_err(|_| Error::CborError)?.into(); Ok((k, v)) }) - .collect::, Error>>(), - _ => Err(Error::InvalidNfcOptions), - }?; + .collect::, Error>>()?, + _ => return Err(Error::InvalidNfcOptions), + }; Ok(NfcOptions::default()) .and_then(|nfc_opts| { - map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(0.into()).try_into()?; - cbor - }) - .ok_or(Error::InvalidNfcOptions) - .and_then(CommandDataLength::try_from) - .map(|max_len_command_data_field| NfcOptions { - max_len_command_data_field, - ..nfc_opts - }) + map.remove(&0) + .ok_or(Error::InvalidNfcOptions) + .and_then(|v| { + let v: u16 = v + .into_integer() + .map_err(|_| Error::CborError)? + .try_into() + .map_err(|_| Error::CborError)?; + if v < CommandDataLength::MIN.get() { + return Err(Error::InvalidNfcCommandDataLengthError); + } + Ok(CommandDataLength(v)) + }) + .map(|max_len_command_data_field| NfcOptions { + max_len_command_data_field, + ..nfc_opts + }) }) .and_then(|nfc_opts| { - map.get(&{ - let cbor: CborValue = ciborium::Value::Integer(1.into()).try_into()?; - cbor - }) - .ok_or(Error::InvalidNfcOptions) - .and_then(ResponseDataLength::try_from) - .map(|max_len_response_data_field| NfcOptions { - max_len_response_data_field, - ..nfc_opts - }) + map.remove(&1) + .ok_or(Error::InvalidNfcOptions) + .and_then(|v| { + let v: u32 = v + .into_integer() + .map_err(|_| Error::CborError)? + .try_into() + .map_err(|_| Error::CborError)?; + if v < ResponseDataLength::MIN.get() { + return Err(Error::InvalidNfcResponseDataLengthError); + } + Ok(ResponseDataLength(v)) + }) + .map(|max_len_response_data_field| NfcOptions { + max_len_response_data_field, + ..nfc_opts + }) }) } } -impl From for CborValue { - fn from(o: NfcOptions) -> CborValue { +impl From for ciborium::Value { + fn from(o: NfcOptions) -> ciborium::Value { let map = vec![ ( ciborium::Value::Integer(0.into()), @@ -77,7 +132,7 @@ impl From for CborValue { ciborium::Value::Integer(o.max_len_response_data_field.get().into()), ), ]; - ciborium::Value::Map(map).try_into().unwrap() + ciborium::Value::Map(map) } } @@ -134,22 +189,9 @@ command_data_length_try_from! { CommandDataLength(usize); } -impl TryFrom<&CborValue> for CommandDataLength { - type Error = Error; - - fn try_from(v: &CborValue) -> Result { - match v.as_ref() { - ciborium::Value::Integer(int_val) => Ok(Self((*int_val).try_into().unwrap())), - _ => Err(Error::InvalidNfcOptions), - } - } -} - -impl From for CborValue { - fn from(cdl: CommandDataLength) -> CborValue { +impl From for ciborium::Value { + fn from(cdl: CommandDataLength) -> ciborium::Value { ciborium::Value::Integer(cdl.get().into()) - .try_into() - .unwrap() } } @@ -204,22 +246,9 @@ response_data_length_try_from! { ResponseDataLength(usize); } -impl TryFrom<&CborValue> for ResponseDataLength { - type Error = Error; - - fn try_from(v: &CborValue) -> Result { - match v.as_ref() { - ciborium::Value::Integer(int_val) => Ok(Self((*int_val).try_into().unwrap())), - _ => Err(Error::InvalidNfcOptions), - } - } -} - -impl From for CborValue { - fn from(rdl: ResponseDataLength) -> CborValue { +impl From for ciborium::Value { + fn from(rdl: ResponseDataLength) -> ciborium::Value { ciborium::Value::Integer(rdl.get().into()) - .try_into() - .unwrap() } } @@ -356,14 +385,14 @@ mod test { #[test] fn response_data_length_cbor_roundtrip_test() { let rdl: ResponseDataLength = ResponseDataLength::new(512).unwrap(); - let bytes: Vec = crate::cbor::to_vec(&rdl).unwrap(); - let deserialized: ResponseDataLength = crate::cbor::from_slice(&bytes).unwrap(); + let bytes: Vec = cbor::to_vec(&rdl).unwrap(); + let deserialized: ResponseDataLength = cbor::from_slice(&bytes).unwrap(); assert_eq!(rdl, deserialized); } fn nfc_options_cbor_roundtrip_test(nfc_options: NfcOptions) { - let bytes: Vec = crate::cbor::to_vec(&nfc_options).unwrap(); - let deserialized: NfcOptions = crate::cbor::from_slice(&bytes).unwrap(); + let bytes: Vec = cbor::to_vec(&nfc_options).unwrap(); + let deserialized: NfcOptions = cbor::from_slice(&bytes).unwrap(); assert_eq!(nfc_options, deserialized); } @@ -388,7 +417,6 @@ mod test { } #[test] - #[ignore] fn nfc_options_cbor_roundtrip_command_length_error_test() { let nfc_options: NfcOptions = NfcOptions { max_len_command_data_field: CommandDataLength(0), //This should not work in non-tests @@ -398,12 +426,10 @@ mod test { let bytes: Vec = cbor::to_vec(&nfc_options).unwrap(); let deserialized_result: Result = cbor::from_slice(&bytes).map_err(Error::from); - println!("{:?}", deserialized_result); assert_eq!(Err(Error::CborError), deserialized_result); } #[test] - #[ignore] fn nfc_options_cbor_roundtrip_response_data_error_test() { let nfc_options: NfcOptions = NfcOptions { max_len_command_data_field: CommandDataLength(0), //This should not work in non-tests diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index 5309e242..b5327394 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -22,18 +22,20 @@ //! } //! } //! ``` -use crate::cbor::{CborError, Value as CborValue}; +use std::collections::BTreeMap; + use aes::cipher::generic_array::{typenum::U8, GenericArray}; use coset::iana::Algorithm; use p256::EncodedPoint; use serde::{Deserialize, Serialize}; use ssi_jwk::JWK; -use std::collections::BTreeMap; + +use crate::cbor::CborError; /// An implementation of RFC-8152 [COSE_Key](https://datatracker.ietf.org/doc/html/rfc8152#section-13) /// restricted to the requirements of ISO/IEC 18013-5:2021. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(try_from = "CborValue", into = "CborValue")] +#[serde(try_from = "ciborium::Value", into = "ciborium::Value")] pub enum CoseKey { EC2 { crv: EC2Curve, x: Vec, y: EC2Y }, OKP { crv: OKPCurve, x: Vec }, @@ -72,9 +74,9 @@ pub enum Error { #[error("COSE_Key of kty 'EC2' missing y coordinate")] EC2MissingY, #[error("Expected to parse a CBOR bool or bstr for y-coordinate, received: '{0:?}'")] - InvalidTypeY(CborValue), + InvalidTypeY(ciborium::Value), #[error("Expected to parse a CBOR map, received: '{0:?}'")] - NotAMap(CborValue), + NotAMap(ciborium::Value), #[error("Unable to discern the elliptic curve")] UnknownCurve, #[error("This implementation of COSE_Key only supports P-256, P-384, P-521, Ed25519 and Ed448 elliptic curves" @@ -87,7 +89,9 @@ pub enum Error { #[error("Constructing a JWK from CoseKey with point-compression is not supported.")] UnsupportedFormat, #[error("could not serialize from to cbor: {0}")] - CborError(CborError), + CborErrorWithSource(CborError), + #[error("could not serialize from to cbor")] + CborError, } impl CoseKey { @@ -119,8 +123,8 @@ impl CoseKey { } } -impl From for CborValue { - fn from(key: CoseKey) -> CborValue { +impl From for ciborium::Value { + fn from(key: CoseKey) -> ciborium::Value { let mut map = vec![]; match key { CoseKey::EC2 { crv, x, y } => { @@ -131,8 +135,8 @@ impl From for CborValue { )); // crv: -1 map.push((ciborium::Value::Integer((-1).into()), { - let cbor: CborValue = crv.into(); - cbor.into() + let cbor: ciborium::Value = crv.into(); + cbor })); // x: -2 map.push(( @@ -141,8 +145,8 @@ impl From for CborValue { )); // y: -3 map.push((ciborium::Value::Integer((-3).into()), { - let cbor: CborValue = y.into(); - cbor.into() + let cbor: ciborium::Value = y.into(); + cbor })); } CoseKey::OKP { crv, x } => { @@ -153,8 +157,8 @@ impl From for CborValue { )); // crv: -1 map.push((ciborium::Value::Integer((-1).into()), { - let cbor: CborValue = crv.into(); - cbor.into() + let cbor: ciborium::Value = crv.into(); + cbor })); // x: -2 map.push(( @@ -163,47 +167,23 @@ impl From for CborValue { )); } } - ciborium::Value::Map(map).try_into().unwrap() + ciborium::Value::Map(map) } } -impl TryFrom for CoseKey { +impl TryFrom for CoseKey { type Error = Error; - fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.clone().into() { - let mut map = map + fn try_from(v: ciborium::Value) -> Result { + if let ciborium::Value::Map(map) = v.clone() { + let mut map: BTreeMap = map .into_iter() .map(|(k, v)| { - Ok(( - CborValue::from(k).map_err(Error::CborError)?, - CborValue::from(v).map_err(Error::CborError)?, - )) + let k = k.into_integer().map_err(|_| Error::CborError)?.into(); + Ok((k, v)) }) .collect::, Error>>()?; - match ( - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer(1.into()) - .try_into() - .map_err(Error::CborError)?; - cbor - }) - .map(|v| v.into()), - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-1).into()) - .try_into() - .map_err(Error::CborError)?; - cbor - }) - .map(|v| v.into()), - map.remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-2).into()) - .try_into() - .map_err(Error::CborError)?; - cbor - }) - .map(|v| v.into()), - ) { + match (map.remove(&1), map.remove(&-1), map.remove(&-2)) { ( Some(ciborium::Value::Integer(i2)), Some(ciborium::Value::Integer(crv_id)), @@ -211,15 +191,7 @@ impl TryFrom for CoseKey { ) if >::into(i2) == 2 => { let crv_id: i128 = crv_id.into(); let crv = crv_id.try_into()?; - let y = map - .remove(&{ - let cbor: CborValue = ciborium::Value::Integer((-3).into()) - .try_into() - .map_err(Error::CborError)?; - cbor - }) - .ok_or(Error::EC2MissingY)? - .try_into()?; + let y = map.remove(&-3).ok_or(Error::EC2MissingY)?.try_into()?; Ok(Self::EC2 { crv, x, y }) } ( @@ -285,20 +257,20 @@ impl TryFrom for EncodedPoint { } } -impl From for CborValue { - fn from(y: EC2Y) -> CborValue { +impl From for ciborium::Value { + fn from(y: EC2Y) -> ciborium::Value { match y { - EC2Y::Value(s) => ciborium::Value::Bytes(s).try_into().unwrap(), - EC2Y::SignBit(b) => ciborium::Value::Bool(b).try_into().unwrap(), + EC2Y::Value(s) => ciborium::Value::Bytes(s), + EC2Y::SignBit(b) => ciborium::Value::Bool(b), } } } -impl TryFrom for EC2Y { +impl TryFrom for EC2Y { type Error = Error; - fn try_from(v: CborValue) -> Result { - match v.clone().into() { + fn try_from(v: ciborium::Value) -> Result { + match v.clone() { ciborium::Value::Bytes(s) => Ok(EC2Y::Value(s)), ciborium::Value::Bool(b) => Ok(EC2Y::SignBit(b)), _ => Err(Error::InvalidTypeY(v)), @@ -306,13 +278,13 @@ impl TryFrom for EC2Y { } } -impl From for CborValue { - fn from(crv: EC2Curve) -> CborValue { +impl From for ciborium::Value { + fn from(crv: EC2Curve) -> ciborium::Value { match crv { - EC2Curve::P256 => ciborium::Value::Integer(1.into()).try_into().unwrap(), - EC2Curve::P384 => ciborium::Value::Integer(2.into()).try_into().unwrap(), - EC2Curve::P521 => ciborium::Value::Integer(3.into()).try_into().unwrap(), - EC2Curve::P256K => ciborium::Value::Integer(8.into()).try_into().unwrap(), + EC2Curve::P256 => ciborium::Value::Integer(1.into()), + EC2Curve::P384 => ciborium::Value::Integer(2.into()), + EC2Curve::P521 => ciborium::Value::Integer(3.into()), + EC2Curve::P256K => ciborium::Value::Integer(8.into()), } } } @@ -331,13 +303,13 @@ impl TryFrom for EC2Curve { } } -impl From for CborValue { - fn from(crv: OKPCurve) -> CborValue { +impl From for ciborium::Value { + fn from(crv: OKPCurve) -> ciborium::Value { match crv { - OKPCurve::X25519 => ciborium::Value::Integer(4.into()).try_into().unwrap(), - OKPCurve::X448 => ciborium::Value::Integer(5.into()).try_into().unwrap(), - OKPCurve::Ed25519 => ciborium::Value::Integer(6.into()).try_into().unwrap(), - OKPCurve::Ed448 => ciborium::Value::Integer(7.into()).try_into().unwrap(), + OKPCurve::X25519 => ciborium::Value::Integer(4.into()), + OKPCurve::X448 => ciborium::Value::Integer(5.into()), + OKPCurve::Ed25519 => ciborium::Value::Integer(6.into()), + OKPCurve::Ed448 => ciborium::Value::Integer(7.into()), } } } @@ -478,10 +450,12 @@ impl TryFrom<&ssi_jwk::OctetParams> for OKPCurve { #[cfg(test)] mod test { - use super::*; - use crate::cbor; use hex::FromHex; + use crate::cbor; + + use super::*; + static EC_P256: &str = include_str!("../../../test/definitions/cose_key/ec_p256.cbor"); #[test] diff --git a/src/definitions/device_key/mod.rs b/src/definitions/device_key/mod.rs index c2e277ab..7b045d58 100644 --- a/src/definitions/device_key/mod.rs +++ b/src/definitions/device_key/mod.rs @@ -41,7 +41,6 @@ //! assert!(result.is_err()); //! assert_eq!(result.unwrap_err(), Error::DoubleAuthorized("namespace1".to_string())); //! ``` -use crate::cbor::Value as CborValue; use crate::definitions::helpers::{NonEmptyMap, NonEmptyVec}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -63,7 +62,7 @@ pub struct DeviceKeyInfo { /// Optional key information. #[serde(skip_serializing_if = "Option::is_none")] - pub key_info: Option>, + pub key_info: Option>, } #[derive(Clone, Serialize, Deserialize, Debug, Default)] diff --git a/src/definitions/device_request.rs b/src/definitions/device_request.rs index e80dafdc..03470aaf 100644 --- a/src/definitions/device_request.rs +++ b/src/definitions/device_request.rs @@ -51,7 +51,7 @@ pub struct ItemsRequest { /// Additional information for the request. #[serde(skip_serializing_if = "Option::is_none")] - pub request_info: Option>, + pub request_info: Option>, } impl DeviceRequest { diff --git a/src/definitions/device_response.rs b/src/definitions/device_response.rs index 1538b969..1d8bea82 100644 --- a/src/definitions/device_response.rs +++ b/src/definitions/device_response.rs @@ -125,9 +125,6 @@ impl TryFrom for Status { #[cfg(test)] mod test { - use coset::{CoseMac0, CoseSign1}; - use hex::FromHex; - use crate::cbor; use crate::cose::MaybeTagged; use crate::definitions::device_signed::{ @@ -138,6 +135,8 @@ mod test { use crate::definitions::{ DeviceAuth, DeviceSigned, DigestId, Document, IssuerSigned, IssuerSignedItem, }; + use coset::{CoseMac0, CoseSign1}; + use hex::FromHex; use super::{ DeviceResponse, DocumentError, DocumentErrorCode, DocumentErrors, Documents, Status, @@ -146,14 +145,13 @@ mod test { static DEVICE_RESPONSE_CBOR: &str = include_str!("../../test/definitions/device_response.cbor"); #[test] - #[ignore] fn device_response() { let cbor_bytes = >::from_hex(DEVICE_RESPONSE_CBOR).expect("unable to convert cbor hex to bytes"); let response: DeviceResponse = - serde_cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as a DeviceResponse"); + cbor::from_slice(&cbor_bytes).expect("unable to decode cbor as a DeviceResponse"); let roundtripped_bytes = - serde_cbor::to_vec(&response).expect("unable to encode DeviceResponse as cbor bytes"); + cbor::to_vec(&response).expect("unable to encode DeviceResponse as cbor bytes"); assert_eq!( cbor_bytes, roundtripped_bytes, "original cbor and re-serialized DeviceResponse do not match" @@ -176,13 +174,12 @@ mod test { digest_id: DigestId::new(42), random: vec![42_u8].into(), element_identifier: "42".to_string(), - element_value: ciborium::Value::Null.try_into().unwrap(), + element_value: ciborium::Value::Null, }; let issuer_signed_item_bytes = IssuerSignedItemBytes::new(issuer_signed_item).unwrap(); let vec = NonEmptyVec::new(issuer_signed_item_bytes); let issuer_namespaces = IssuerNamespaces::new("a".to_string(), vec); - let device_signed_items = - DeviceSignedItems::new("a".to_string(), ciborium::Value::Null.try_into().unwrap()); + let device_signed_items = DeviceSignedItems::new("a".to_string(), ciborium::Value::Null); let mut device_namespaces = DeviceNamespaces::new(); device_namespaces.insert("a".to_string(), device_signed_items); let device_namespaces_bytes = DeviceNamespacesBytes::new(device_namespaces).unwrap(); @@ -212,7 +209,6 @@ mod test { status: Status::OK, }; let bytes = cbor::to_vec(&res).unwrap(); - eprintln!("bytes {}", hex::encode(&bytes)); let res: DeviceResponse = cbor::from_slice(&bytes).unwrap(); let roundtripped_bytes = cbor::to_vec(&res).unwrap(); assert_eq!( diff --git a/src/definitions/device_signed.rs b/src/definitions/device_signed.rs index 2b8e03a2..0b231e1f 100644 --- a/src/definitions/device_signed.rs +++ b/src/definitions/device_signed.rs @@ -4,7 +4,6 @@ //! //! 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::MaybeTagged; use crate::definitions::{ helpers::{NonEmptyMap, Tag24}, @@ -28,7 +27,7 @@ pub struct DeviceSigned { pub type DeviceNamespacesBytes = Tag24; pub type DeviceNamespaces = BTreeMap; -pub type DeviceSignedItems = NonEmptyMap; +pub type DeviceSignedItems = NonEmptyMap; /// Represents a device signature. /// diff --git a/src/definitions/helpers/bytestr.rs b/src/definitions/helpers/bytestr.rs index 60618760..81f765be 100644 --- a/src/definitions/helpers/bytestr.rs +++ b/src/definitions/helpers/bytestr.rs @@ -1,8 +1,7 @@ -use crate::cbor::Value as CborValue; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] -#[serde(try_from = "CborValue", into = "CborValue")] +#[serde(try_from = "ciborium::Value", into = "ciborium::Value")] pub struct ByteStr(Vec); type Result = std::result::Result; @@ -10,7 +9,7 @@ type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Expected to parse a CBOR byte string, received: '{0:?}'")] - NotAByteString(CborValue), + NotAByteString(ciborium::Value), } impl From> for ByteStr { @@ -31,17 +30,17 @@ impl AsRef<[u8]> for ByteStr { } } -impl From for CborValue { - fn from(ByteStr(bytes): ByteStr) -> CborValue { - ciborium::Value::Bytes(bytes).try_into().unwrap() +impl From for ciborium::Value { + fn from(ByteStr(bytes): ByteStr) -> ciborium::Value { + ciborium::Value::Bytes(bytes) } } -impl TryFrom for ByteStr { +impl TryFrom for ByteStr { type Error = Error; - fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Bytes(bytes) = v.clone().into() { + fn try_from(v: ciborium::Value) -> Result { + if let ciborium::Value::Bytes(bytes) = v.clone() { Ok(ByteStr(bytes)) } else { Err(Error::NotAByteString(v)) diff --git a/src/definitions/helpers/tag24.rs b/src/definitions/helpers/tag24.rs index 0fadf849..24680218 100644 --- a/src/definitions/helpers/tag24.rs +++ b/src/definitions/helpers/tag24.rs @@ -1,7 +1,8 @@ //! Support for embedded //! [CBOR Data Items](https://www.ietf.org/rfc/rfc8949.html#name-encoded-cbor-data-item), //! also known as a tagged data item with tag number 24. -use crate::cbor::{from_slice, to_vec, CborError, Value as CborValue}; +use crate::cbor; +use crate::cbor::CborError; use serde::{ de::{self, Error as DeError}, ser, Deserialize, Serialize, @@ -39,26 +40,26 @@ impl Tag24 { impl Tag24 { pub fn new(inner: T) -> Result> { - let inner_bytes = to_vec(&inner).map_err(Error::UnableToEncode)?; + let inner_bytes = cbor::to_vec(&inner).map_err(Error::UnableToEncode)?; Ok(Self { inner, inner_bytes }) } } impl Tag24 { pub fn from_bytes(inner_bytes: Vec) -> Result> { - let inner = from_slice(&inner_bytes).map_err(Error::UnableToDecode)?; + let inner = cbor::from_slice(&inner_bytes).map_err(Error::UnableToDecode)?; Ok(Self { inner, inner_bytes }) } } -impl TryFrom for Tag24 { +impl TryFrom for Tag24 { type Error = Error; - fn try_from(v: CborValue) -> Result> { - match v.clone().into() { + fn try_from(v: ciborium::Value) -> Result> { + match v.clone() { ciborium::Value::Tag(24, inner_value) => match inner_value.as_ref() { ciborium::Value::Bytes(inner_bytes) => { - let inner: T = from_slice(inner_bytes).map_err(Error::UnableToDecode)?; + let inner: T = cbor::from_slice(inner_bytes).map_err(Error::UnableToDecode)?; Ok(Tag24 { inner, inner_bytes: inner_bytes.to_vec(), @@ -66,16 +67,14 @@ impl TryFrom for Tag24 { } _ => Err(Error::InvalidTag24(inner_value)), }, - _ => Err(Error::NotATag24(v.into())), + _ => Err(Error::NotATag24(v)), } } } -impl From> for CborValue { - fn from(Tag24 { inner_bytes, .. }: Tag24) -> CborValue { +impl From> for ciborium::Value { + fn from(Tag24 { inner_bytes, .. }: Tag24) -> ciborium::Value { ciborium::Value::Tag(24, Box::new(ciborium::Value::Bytes(inner_bytes))) - .try_into() - .unwrap() } } @@ -100,9 +99,7 @@ impl<'de, T: de::DeserializeOwned> Deserialize<'de> for Tag24 { where D: de::Deserializer<'de>, { - let cbor: CborValue = ciborium::Value::deserialize(d)? - .try_into() - .map_err(DeError::custom)?; + let cbor: ciborium::Value = ciborium::Value::deserialize(d)?; cbor.try_into().map_err(D::Error::custom) } } diff --git a/src/definitions/issuer_signed.rs b/src/definitions/issuer_signed.rs index 6df2a471..736353e4 100644 --- a/src/definitions/issuer_signed.rs +++ b/src/definitions/issuer_signed.rs @@ -10,7 +10,6 @@ //! - [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 crate::cbor::Value as CborValue; use crate::cose::MaybeTagged; use crate::definitions::{ helpers::{ByteStr, NonEmptyMap, NonEmptyVec, Tag24}, @@ -50,7 +49,7 @@ pub struct IssuerSignedItem { pub element_identifier: String, /// The value of the element. - pub element_value: CborValue, + pub element_value: ciborium::Value, } #[cfg(test)] diff --git a/src/definitions/namespaces/fulldate.rs b/src/definitions/namespaces/fulldate.rs index aedae982..b4c9083c 100644 --- a/src/definitions/namespaces/fulldate.rs +++ b/src/definitions/namespaces/fulldate.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use anyhow::Error; use serde_json::Value as Json; use std::{fmt, str::FromStr}; @@ -12,11 +11,9 @@ const FORMAT: &[FormatItem<'static>] = format_description!("[year]-[month]-[day] #[derive(Clone, Debug)] pub struct FullDate(Date); -impl From for Cbor { - fn from(d: FullDate) -> Cbor { +impl From for ciborium::Value { + fn from(d: FullDate) -> ciborium::Value { ciborium::Value::Tag(1004, Box::new(ciborium::Value::Text(d.to_string()))) - .try_into() - .unwrap() } } diff --git a/src/definitions/namespaces/latin1.rs b/src/definitions/namespaces/latin1.rs index be0e1e30..d7fb62fa 100644 --- a/src/definitions/namespaces/latin1.rs +++ b/src/definitions/namespaces/latin1.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use serde_json::Value as Json; use std::{ops::Deref, str::FromStr}; @@ -27,8 +26,8 @@ impl Deref for Latin1 { } } -impl From for Cbor { - fn from(l: Latin1) -> Cbor { +impl From for ciborium::Value { + fn from(l: Latin1) -> ciborium::Value { l.0.into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs index b398b2c9..f3600554 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/age_over.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}; use serde_json::{Map, Value as Json}; use std::{collections::BTreeMap, ops::Deref}; @@ -47,15 +46,10 @@ impl FromJsonMap for AgeOver { } impl ToNamespaceMap for AgeOver { - fn to_ns_map(self) -> BTreeMap { + fn to_ns_map(self) -> BTreeMap { self.0 .into_iter() - .map(|(Age(x, y), v)| { - ( - format!("age_over_{x}{y}"), - ciborium::Value::Bool(v).try_into().unwrap(), - ) - }) + .map(|(Age(x, y), v)| (format!("age_over_{x}{y}"), ciborium::Value::Bool(v))) .collect() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs index 77b89fce..23ff243f 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/alpha2.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; use std::str::FromStr; @@ -263,10 +262,10 @@ pub enum Error { Unrecognized(String), } -impl From for Cbor { - fn from(a: Alpha2) -> Cbor { +impl From for ciborium::Value { + fn from(a: Alpha2) -> ciborium::Value { let cbor: ciborium::Value = a.as_str().to_string().into(); - cbor.try_into().unwrap() + cbor } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs b/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs index 247b8b14..57c62df2 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/biometric_template.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::{ helpers::ByteStr, traits::{FromJson, FromJsonError, FromJsonMap, ToNamespaceMap}, @@ -23,7 +22,7 @@ impl FromJsonMap for BiometricTemplate { } impl ToNamespaceMap for BiometricTemplate { - fn to_ns_map(self) -> BTreeMap { + fn to_ns_map(self) -> BTreeMap { self.0 .into_iter() .map(|(k, v)| (format!("biometric_template_{k}"), v.into())) diff --git a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs index c7bd9f4b..fcfc78ec 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/driving_privileges.rs @@ -1,5 +1,5 @@ use super::FullDate; -use crate::cbor::Value as Cbor; + use crate::{ definitions::{ helpers::NonEmptyVec, @@ -14,11 +14,9 @@ use strum_macros::{AsRefStr, EnumString, EnumVariantNames}; #[isomdl(crate = "crate")] pub struct DrivingPrivileges(Vec); -impl From for Cbor { - fn from(d: DrivingPrivileges) -> Cbor { - ciborium::Value::Array(d.0.into_iter().map(|v| v.to_cbor().into()).collect()) - .try_into() - .unwrap() +impl From for ciborium::Value { + fn from(d: DrivingPrivileges) -> ciborium::Value { + ciborium::Value::Array(d.0.into_iter().map(|v| v.to_cbor()).collect()) } } @@ -47,21 +45,19 @@ pub enum VehicleCategoryCode { A2, /// Light vehicles B1, - /// Medium sized goods vehicles + /// Medium-sized goods vehicles C1, - /// Medium sized passenger vehicles (e.g.minibuses) + /// Medium-sized passenger vehicles (e.g.minibuses) D1, - /// Medium sized goods vehicles with trailers + /// Medium-sized goods vehicles with trailers C1E, - /// Medium sized passenger vehicles (e.g. minibuses) with trailers + /// Medium-sized passenger vehicles (e.g., minibuses) with trailers D1E, } -impl From for Cbor { - fn from(c: VehicleCategoryCode) -> Cbor { +impl From for ciborium::Value { + fn from(c: VehicleCategoryCode) -> ciborium::Value { ciborium::Value::Text(c.as_ref().to_string()) - .try_into() - .unwrap() } } @@ -87,16 +83,9 @@ pub struct DrivingPrivilege { #[isomdl(crate = "crate")] pub struct Codes(NonEmptyVec); -impl From for Cbor { - fn from(c: Codes) -> Cbor { - ciborium::Value::Array( - c.0.into_inner() - .into_iter() - .map(|v| v.to_cbor().into()) - .collect(), - ) - .try_into() - .unwrap() +impl From for ciborium::Value { + fn from(c: Codes) -> ciborium::Value { + ciborium::Value::Array(c.0.into_inner().into_iter().map(|v| v.to_cbor()).collect()) } } @@ -111,7 +100,6 @@ pub struct Code { #[cfg(test)] mod tests { use super::VehicleCategoryCode; - use crate::cbor::Value as CborValue; use crate::definitions::traits::{FromJson, ToCbor}; #[test] @@ -122,7 +110,7 @@ mod tests { let v = c.to_cbor(); assert_eq!( - >::into(v), + >::into(v), ciborium::Value::Text("A".to_string()) ); diff --git a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs index a89f4dc4..8927ad92 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/eye_colour.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; use std::str::FromStr; @@ -41,11 +40,9 @@ impl EyeColour { } } -impl From for Cbor { - fn from(h: EyeColour) -> Cbor { +impl From for ciborium::Value { + fn from(h: EyeColour) -> ciborium::Value { ciborium::Value::Text(h.to_str().to_string()) - .try_into() - .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs index b75631bf..bbf04cc4 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/hair_colour.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; use std::str::FromStr; @@ -41,11 +40,9 @@ impl HairColour { } } -impl From for Cbor { - fn from(h: HairColour) -> Cbor { +impl From for ciborium::Value { + fn from(h: HairColour) -> ciborium::Value { ciborium::Value::Text(h.to_str().to_string()) - .try_into() - .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs b/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs index 564f13bf..036eb666 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/issuing_jurisdiction.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::{ namespaces::org_iso_18013_5_1::Alpha2, traits::{FromJson, FromJsonError, FromJsonMap}, @@ -23,8 +22,8 @@ impl Error { } } -impl From for Cbor { - fn from(i: IssuingJurisdiction) -> Cbor { +impl From for ciborium::Value { + fn from(i: IssuingJurisdiction) -> ciborium::Value { i.0.into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs index 11d2d2c1..9c95df3f 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/sex.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; @@ -17,8 +16,8 @@ pub enum Error { Unrecognized(u32), } -impl From for Cbor { - fn from(s: Sex) -> Cbor { +impl From for ciborium::Value { + fn from(s: Sex) -> ciborium::Value { u8::from(s).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs index 6282396b..3a071bf1 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/tdate.rs @@ -1,6 +1,5 @@ pub use super::FullDate; -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use anyhow::anyhow; use serde_json::Value as Json; @@ -52,16 +51,14 @@ impl FromJson for TDateOrFullDate { } } -impl From for Cbor { - fn from(t: TDate) -> Cbor { +impl From for ciborium::Value { + fn from(t: TDate) -> ciborium::Value { ciborium::Value::Tag(0, Box::new(t.0.into())) - .try_into() - .unwrap() } } -impl From for Cbor { - fn from(t: TDateOrFullDate) -> Cbor { +impl From for ciborium::Value { + fn from(t: TDateOrFullDate) -> ciborium::Value { match t { TDateOrFullDate::TDate(t) => t.into(), TDateOrFullDate::FullDate(f) => f.into(), diff --git a/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs b/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs index b60ccdd1..5c8887bc 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1/un_distinguishing_sign.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError}; use serde_json::Value as Json; @@ -189,8 +188,8 @@ pub enum UNDistinguishingSign { NoneApplicable(String), } -impl From for Cbor { - fn from(s: UNDistinguishingSign) -> Cbor { +impl From for ciborium::Value { + fn from(s: UNDistinguishingSign) -> ciborium::Value { String::from(s).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs index 2224fe21..1b9d1b3e 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/county_code.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; @@ -18,7 +17,7 @@ pub enum Error { } impl ToCbor for CountyCode { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { let CountyCode((a, b, c)) = self; format!("{a}{b}{c}").into() } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs index 4e1d012b..518442f3 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/dhs_compliance.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; @@ -27,7 +26,7 @@ impl DHSCompliance { } impl ToCbor for DHSCompliance { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { self.to_str().to_string().into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs index 07c89800..f146bd56 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/domestic_driving_privileges.rs @@ -1,5 +1,5 @@ use super::FullDate; -use crate::cbor::Value as Cbor; + use crate::{ definitions::{helpers::NonEmptyVec, traits::ToCbor}, macros::{FromJson, ToCbor}, @@ -12,10 +12,8 @@ use crate::{ pub struct DomesticDrivingPrivileges(Vec); impl ToCbor for DomesticDrivingPrivileges { - fn to_cbor(self) -> Cbor { - ciborium::Value::Array(self.0.into_iter().map(|v| v.to_cbor().into()).collect()) - .try_into() - .unwrap() + fn to_cbor(self) -> ciborium::Value { + ciborium::Value::Array(self.0.into_iter().map(|v| v.to_cbor()).collect()) } } @@ -41,16 +39,14 @@ pub struct DomesticVehicleClass { pub struct DomesticVehicleRestrictions(NonEmptyVec); impl ToCbor for DomesticVehicleRestrictions { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { ciborium::Value::Array( self.0 .into_inner() .into_iter() - .map(|v| v.to_cbor().into()) + .map(|v| v.to_cbor()) .collect(), ) - .try_into() - .unwrap() } } @@ -66,16 +62,14 @@ pub struct DomesticVehicleRestriction { pub struct DomesticVehicleEndorsements(NonEmptyVec); impl ToCbor for DomesticVehicleEndorsements { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { ciborium::Value::Array( self.0 .into_inner() .into_iter() - .map(|v| v.to_cbor().into()) + .map(|v| v.to_cbor()) .collect(), ) - .try_into() - .unwrap() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs index 94ab55e0..9fb4cd2d 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/edl_indicator.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; @@ -26,7 +25,7 @@ impl From for u8 { } impl ToCbor for EDLIndicator { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { u8::from(self).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs index c85ac523..98524374 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_suffix.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; @@ -63,7 +62,7 @@ impl NameSuffix { } impl ToCbor for NameSuffix { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { self.to_str().to_string().into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs index 8596d209..6c8b59cb 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/name_truncation.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; @@ -29,7 +28,7 @@ impl NameTruncation { } impl ToCbor for NameTruncation { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { self.to_str().to_string().into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs index 816bab86..671b89d1 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/present.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use anyhow::anyhow; use serde_json::Value as Json; @@ -18,7 +17,7 @@ impl FromJson for Present { } impl ToCbor for Present { - fn to_cbor(self) -> Cbor { - ciborium::Value::Integer(1.into()).try_into().unwrap() + fn to_cbor(self) -> ciborium::Value { + ciborium::Value::Integer(1.into()) } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs index 7d0e5035..69a25a4e 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/race_and_ethnicity.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; use std::str::FromStr; @@ -37,7 +36,7 @@ impl RaceAndEthnicity { } impl ToCbor for RaceAndEthnicity { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { self.to_str().to_string().into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs index 891ddbcc..9c7ab87a 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/sex.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; @@ -28,7 +27,7 @@ impl From for u8 { } impl ToCbor for Sex { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { u8::from(self).into() } } diff --git a/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs b/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs index a358eb9e..81ae8b92 100644 --- a/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs +++ b/src/definitions/namespaces/org_iso_18013_5_1_aamva/weight_range.rs @@ -1,4 +1,3 @@ -use crate::cbor::Value as Cbor; use crate::definitions::traits::{FromJson, FromJsonError, ToCbor}; use serde_json::Value as Json; @@ -42,7 +41,7 @@ impl From for u8 { } impl ToCbor for WeightRange { - fn to_cbor(self) -> Cbor { + fn to_cbor(self) -> ciborium::Value { u8::from(self).into() } } diff --git a/src/definitions/traits/to_cbor.rs b/src/definitions/traits/to_cbor.rs index 483c269b..0db35bc0 100644 --- a/src/definitions/traits/to_cbor.rs +++ b/src/definitions/traits/to_cbor.rs @@ -2,24 +2,24 @@ //! represented as a `bytestr` instead of an `array` in `cbor`. use crate::cbor; -use crate::cbor::{CborError, Value}; +use crate::cbor::CborError; use std::collections::BTreeMap; pub type Bytes = Vec; pub trait ToCbor: Sized { - fn to_cbor(self) -> Value; + fn to_cbor(self) -> ciborium::Value; fn to_cbor_bytes(self) -> Result { cbor::to_vec(&self.to_cbor()).map_err(Into::into) } } pub trait ToCborMap { - fn to_cbor_map(self) -> BTreeMap; + fn to_cbor_map(self) -> BTreeMap; } pub trait ToNamespaceMap { - fn to_ns_map(self) -> BTreeMap; + fn to_ns_map(self) -> BTreeMap; } #[derive(Debug, thiserror::Error)] @@ -30,16 +30,9 @@ pub enum ToCborError { impl ToCbor for T where - T: Into, + T: Into, { - fn to_cbor(self) -> Value { + fn to_cbor(self) -> ciborium::Value { self.into() } } - -impl ToCbor for Option { - fn to_cbor(self) -> Value { - self.map(|s| ciborium::Value::Text(s).try_into().unwrap()) - .unwrap_or_else(|| ciborium::Value::Null.try_into().unwrap()) - } -} diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index e099f6dc..ade7c9cd 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -15,7 +15,8 @@ //! # Conversion to and from CBOR //! //! The [ValidityInfo] struct also provides implementations of the [TryFrom] trait for converting -//! to and from [CborValue], which is a type provided by the [serde_cbor] crate for representing CBOR values. +//! to and from [`ciborium::Value`], +//! which is a type provided by the [`ciborium`] crate for representing CBOR values. //! These implementations allow you to convert [ValidityInfo] objects to `CBOR` format and vice versa. //! //! # Dependencies @@ -23,11 +24,11 @@ //! This module depends on the following external crates: //! //! - [serde]: Provides the serialization and deserialization traits and macros. -//! - [serde_cbor]: Provides the `CBOR` serialization and deserialization functionality. +//! - [ciborium] and [coset]: Provides the `CBOR` serialization and deserialization functionality. //! - [std::collections::BTreeMap]: Provides the [BTreeMap] type for storing key-value pairs in a sorted order. //! - [time]: Provides date and time manipulation functionality. //! - [thiserror]: Provides the [thiserror::Error] trait for defining custom error types. -use crate::cbor::{CborError, Value as CborValue}; +use crate::cbor::CborError; use serde::{ ser::{Error as SerError, Serializer}, Deserialize, Serialize, @@ -39,7 +40,7 @@ use time::{ }; #[derive(Clone, Debug, Deserialize)] -#[serde(try_from = "CborValue")] +#[serde(try_from = "ciborium::Value")] pub struct ValidityInfo { pub signed: OffsetDateTime, pub valid_from: OffsetDateTime, @@ -66,13 +67,15 @@ pub enum Error { #[error("Failed to parse date string as rfc3339 date: {0}")] UnableToParseDate(#[from] ParseError), #[error("Could not serialize to cbor: {0}")] - CborError(CborError), + CborErrorWithSource(CborError), + #[error("Could not serialize to cbor")] + CborError, } -impl TryFrom for CborValue { +impl TryFrom for ciborium::Value { type Error = Error; - fn try_from(v: ValidityInfo) -> Result { + fn try_from(v: ValidityInfo) -> Result { macro_rules! insert_date { ($map:ident, $date:ident, $name:literal) => { let key = ciborium::Value::Text(String::from($name)); @@ -103,29 +106,25 @@ impl TryFrom for CborValue { insert_date!(map, expected_update, "expectedUpdate"); } - ciborium::Value::Map(map) - .try_into() - .map_err(Error::CborError) + Ok(ciborium::Value::Map(map)) } } -impl TryFrom for ValidityInfo { +impl TryFrom for ValidityInfo { type Error = Error; - fn try_from(v: CborValue) -> Result { - if let ciborium::Value::Map(map) = v.clone().into() { - let mut map = map + fn try_from(v: ciborium::Value) -> Result { + if let ciborium::Value::Map(map) = v.clone() { + let mut map: BTreeMap = map .into_iter() .map(|(k, v)| { - let k: CborValue = k.try_into().map_err(Error::CborError)?; - let v: CborValue = v.try_into().map_err(Error::CborError)?; + let k = k.into_text().map_err(|_| Error::CborError)?; Ok((k, v)) }) - .collect::>>()?; + .collect::>>()?; macro_rules! extract_date { ($map:ident, $name:literal) => {{ - let key = CborValue::from(ciborium::Value::Text(String::from($name))) - .map_err(Error::CborError)?; + let key = String::from($name); $map.remove(&key) .ok_or(Error::MissingField(key.into())) .and_then(cbor_to_datetime)? @@ -136,10 +135,7 @@ impl TryFrom for ValidityInfo { let valid_from = extract_date!(map, "validFrom"); let valid_until = extract_date!(map, "validUntil"); - let expected_update_key: CborValue = - ciborium::Value::Text(String::from("expectedUpdate")) - .try_into() - .map_err(Error::CborError)?; + let expected_update_key = String::from("expectedUpdate"); let expected_update = map .remove(&expected_update_key) .map(cbor_to_datetime) @@ -152,7 +148,7 @@ impl TryFrom for ValidityInfo { expected_update, }) } else { - Err(Error::NotAMap(v.into())) + Err(Error::NotAMap(v)) } } } @@ -162,21 +158,21 @@ impl Serialize for ValidityInfo { where S: Serializer, { - CborValue::try_from(self.clone()) + ciborium::Value::try_from(self.clone()) .map_err(S::Error::custom)? .serialize(s) } } -fn cbor_to_datetime(v: CborValue) -> Result { - if let ciborium::Value::Tag(0, inner) = v.clone().into() { +fn cbor_to_datetime(v: ciborium::Value) -> Result { + if let ciborium::Value::Tag(0, inner) = v.clone() { if let ciborium::Value::Text(date_str) = inner.as_ref() { Ok(OffsetDateTime::parse(date_str, &Rfc3339)?) } else { Err(Error::NotATextString(inner)) } } else { - Err(Error::NotATag(0, v.into())) + Err(Error::NotATag(0, v)) } } diff --git a/src/issuance/mdoc.rs b/src/issuance/mdoc.rs index 2e126521..737191af 100644 --- a/src/issuance/mdoc.rs +++ b/src/issuance/mdoc.rs @@ -9,7 +9,6 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256, Sha384, Sha512}; use signature::{SignatureEncoding, Signer}; -use crate::cbor::Value as CborValue; use crate::cose::sign1::PreparedCoseSign1; use crate::cose::{MaybeTagged, SignatureAlgorithm}; use crate::{ @@ -21,7 +20,7 @@ use crate::{ issuance::x5chain::{X5Chain, X5CHAIN_HEADER_LABEL}, }; -pub type Namespaces = BTreeMap>; +pub type Namespaces = BTreeMap>; /// A signed mdoc. #[derive(Serialize, Deserialize, Debug, Clone)] @@ -371,7 +370,7 @@ fn to_issuer_namespaces(namespaces: Namespaces) -> Result { } fn to_issuer_signed_items( - elements: BTreeMap, + elements: BTreeMap, ) -> impl Iterator { let mut used_ids = HashSet::new(); elements.into_iter().map(move |(key, value)| { diff --git a/src/presentation/device.rs b/src/presentation/device.rs index a30597f4..97e78dff 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -16,7 +16,7 @@ //! //! You can view examples in `tests` directory in `simulated_device_and_reader.rs`, for a basic example and //! `simulated_device_and_reader_state.rs` which uses `State` pattern, `Arc` and `Mutex`. -use crate::cbor::{CborError, Value as CborValue}; +use crate::cbor::CborError; use crate::cose::mac0::PreparedCoseMac0; use crate::cose::sign1::PreparedCoseSign1; use crate::cose::MaybeTagged; @@ -316,12 +316,12 @@ impl SessionManagerEngaged { impl SessionManager { fn parse_request(&self, request: &[u8]) -> Result { - let request: CborValue = cbor::from_slice(request).map_err(|_| { + let request: ciborium::Value = cbor::from_slice(request).map_err(|_| { // tracing::error!("unable to decode DeviceRequest bytes as cbor: {}", error); PreparedDeviceResponse::empty(Status::CborDecodingError) })?; - cbor::from_value2(request).map_err(|_| { + cbor::from_value(request).map_err(|_| { // tracing::error!("unable to validate DeviceRequest cbor: {}", error); PreparedDeviceResponse::empty(Status::CborValidationError) }) @@ -447,7 +447,6 @@ impl SessionManager { if p.is_complete() { let response = p.finalize_response(); let bytes = cbor::to_vec(&response)?; - println!("bytes {}", hex::encode(&bytes)); let response2: DeviceResponse = cbor::from_slice(&bytes).unwrap(); let bytes2 = cbor::to_vec(&response2)?; assert_eq!(bytes, bytes2); @@ -905,8 +904,7 @@ pub fn nearest_age_attestation( let (true_age_over_claims, false_age_over_claims): (Vec<_>, Vec<_>) = age_over_claims_numerical?.into_iter().partition(|x| { - x.1.to_owned().into_inner().element_value - == ciborium::Value::Bool(true).try_into().unwrap() + x.1.to_owned().into_inner().element_value == ciborium::Value::Bool(true) }); let nearest_age_over = true_age_over_claims @@ -1045,21 +1043,21 @@ mod test { digest_id: DigestId::new(1), random: ByteStr::from(random.clone()), element_identifier: element_identifier1.clone(), - element_value: ciborium::Value::Bool(true).try_into().unwrap(), + element_value: ciborium::Value::Bool(true), }; let issuer_signed_item2 = IssuerSignedItem { digest_id: DigestId::new(2), random: ByteStr::from(random.clone()), element_identifier: element_identifier2.clone(), - element_value: ciborium::Value::Bool(false).try_into().unwrap(), + element_value: ciborium::Value::Bool(false), }; let issuer_signed_item3 = IssuerSignedItem { digest_id: DigestId::new(3), random: ByteStr::from(random), element_identifier: element_identifier3.clone(), - element_value: ciborium::Value::Bool(false).try_into().unwrap(), + element_value: ciborium::Value::Bool(false), }; let issuer_item1 = Tag24::new(issuer_signed_item1).unwrap(); diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index 6a3e89b7..798f97f6 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -14,7 +14,7 @@ //! You can view examples in `tests` directory in `simulated_device_and_reader.rs`, for a basic example and //! `simulated_device_and_reader_state.rs` which uses `State` pattern, `Arc` and `Mutex`. use crate::cbor; -use crate::cbor::{CborError, Value as CborValue}; +use crate::cbor::CborError; use crate::definitions::{ device_engagement::DeviceRetrievalMethod, device_request::{self, DeviceRequest, DocRequest, ItemsRequest}, @@ -296,8 +296,8 @@ impl SessionManager { } } -fn parse_response(value: CborValue) -> Result { - match value.into() { +fn parse_response(value: ciborium::Value) -> Result { + match value { ciborium::Value::Text(s) => Ok(Value::String(s)), ciborium::Value::Tag(_t, v) => { if let ciborium::Value::Text(d) = *v { @@ -309,7 +309,7 @@ fn parse_response(value: CborValue) -> Result { ciborium::Value::Array(v) => { let mut array_response = Vec::::new(); for a in v { - let r = parse_response(a.try_into()?)?; + let r = parse_response(a)?; array_response.push(r); } Ok(json!(array_response)) @@ -318,7 +318,7 @@ fn parse_response(value: CborValue) -> Result { let mut map_response = serde_json::Map::::new(); for (key, value) in m { if let ciborium::Value::Text(k) = key { - let parsed = parse_response(value.try_into()?)?; + let parsed = parse_response(value)?; map_response.insert(k, parsed); } } diff --git a/test/definitions/device_response.cbor b/test/definitions/device_response.cbor index ea155368..bda9f89b 100644 --- a/test/definitions/device_response.cbor +++ b/test/definitions/device_response.cbor @@ -1 +1 @@ -a36776657273696f6e63312e3069646f63756d656e747381a367646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c6973737565725369676e6564a26a6e616d65537061636573a1716f72672e69736f2e31383031332e352e3186a16a404054414747454440408218185863a4686469676573744944006672616e646f6d58208798645b20ea200e19ffabac92624bee6aec63aceedecfb1b80077d22bfc20e971656c656d656e744964656e7469666965726b66616d696c795f6e616d656c656c656d656e7456616c756563446f65a16a40405441474745444040821818586ca4686469676573744944036672616e646f6d5820b23f627e8999c706df0c0a4ed98ad74af988af619b4bb078b89058553f44615d71656c656d656e744964656e7469666965726a69737375655f646174656c656c656d656e7456616c7565d903ec6a323031392d31302d3230a16a40405441474745444040821818586da4686469676573744944046672616e646f6d5820c7ffa307e5de921e67ba5878094787e8807ac8e7b5b3932d2ce80f00f3e9abaf71656c656d656e744964656e7469666965726b6578706972795f646174656c656c656d656e7456616c7565d903ec6a323032342d31302d3230a16a40405441474745444040821818586da4686469676573744944076672616e646f6d582026052a42e5880557a806c1459af3fb7eb505d3781566329d0b604b845b5f9e6871656c656d656e744964656e7469666965726f646f63756d656e745f6e756d6265726c656c656d656e7456616c756569313233343536373839a16a40405441474745444040821818590471a4686469676573744944086672616e646f6d5820d094dad764a2eb9deb5210e9d899643efbd1d069cc311d3295516ca0b024412d71656c656d656e744964656e74696669657268706f7274726169746c656c656d656e7456616c7565590412ffd8ffe000104a46494600010101009000900000ffdb004300130d0e110e0c13110f11151413171d301f1d1a1a1d3a2a2c2330453d4947443d43414c566d5d4c51685241435f82606871757b7c7b4a5c869085778f6d787b76ffdb0043011415151d191d381f1f38764f434f7676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676767676ffc00011080018006403012200021101031101ffc4001b00000301000301000000000000000000000005060401020307ffc400321000010303030205020309000000000000010203040005110612211331141551617122410781a1163542527391b2c1f1ffc4001501010100000000000000000000000000000001ffc4001a110101010003010000000000000000000000014111213161ffda000c03010002110311003f00a5bbde22da2329c7d692bc7d0d03f52cfb0ff75e7a7ef3e7709723a1d0dae146ddfbb3c039ce07ad2bd47a7e32dbb8dd1d52d6ef4b284f64a480067dfb51f87ffb95ff00eb9ff14d215de66af089ce44b7dbde9cb6890a2838eddf18078f7add62d411ef4db9b10a65d6b95a147381ea0d495b933275fe6bba75c114104a8ba410413e983dff004f5af5d34b4b4cde632d0bf1fd1592bdd91c6411f3934c2fa6af6b54975d106dcf4a65ae56e856001ebc03c7ce29dd9eef1ef10fc447dc9da76ad2aee93537a1ba7e4f70dd8eff0057c6dffb5e1a19854a83758e54528750946ec6704850cd037bceb08b6d7d2cc76d3317fc7b5cc04fb6707269c5c6e0c5b60ae549242123b0e493f602a075559e359970d98db89525456b51c951c8afa13ea8e98e3c596836783d5c63f5a61a99fdb7290875db4be88ab384bbbbbfc7183fdeaa633e8951db7da396dc48524fb1a8bd611a5aa2a2432f30ab420a7a6d3240c718cf031fa9ef4c9ad550205aa02951df4a1d6c8421b015b769db8c9229837ea2be8b1b0d39d0eba9c51484efdb8c0efd8d258daf3c449699f2edbd4584e7af9c64e3f96b9beb28d4ac40931e6478c8e76a24a825449501d867d2b1dcdebae99b9c752ae4ecd6dde4a179c1c1e460938f9149ef655e515c03919a289cb3dca278fb7bf177f4faa829dd8ce3f2ac9a7ecde490971fafd7dce15eed9b71c018c64fa514514b24e8e4f8c5c9b75c1e82579dc1233dfec08238f6add62d391acc1c5256a79e706d52d431c7a0145140b9fd149eb3a60dc5e88cbbc2da092411e9dc71f39a7766b447b344e847dcac9dcb5abba8d145061d43a6fcf1e65cf15d0e90231d3dd9cfe62995c6dcc5ca12a2c904a15f71dd27d451453e09d1a21450961cbb3ea8a956433b781f1ce33dfed54f0e2b50a2b71d84ed6db18028a28175f74fc6bda105c529a791c25c4f3c7a11f71586268f4a66b726e33de9ea6f1b52b181c760724e47b514520a5a28a283ffd9a16a4040544147474544404082181858ffa4686469676573744944096672616e646f6d58204599f81beaa2b20bd0ffcc9aa03a6f985befab3f6beaffa41e6354cdb2ab2ce471656c656d656e744964656e7469666965727264726976696e675f70726976696c656765736c656c656d656e7456616c756582a37576656869636c655f63617465676f72795f636f646561416a69737375655f64617465d903ec6a323031382d30382d30396b6578706972795f64617465d903ec6a323032342d31302d3230a37576656869636c655f63617465676f72795f636f646561426a69737375655f64617465d903ec6a323031372d30322d32336b6578706972795f64617465d903ec6a323032342d31302d32306a69737375657241757468a16c4040554e54414747454440408443a10126a118215901f3308201ef30820195a00302010202143c4416eed784f3b413e48f56f075abfa6d87eb84300a06082a8648ce3d04030230233114301206035504030c0b75746f7069612069616361310b3009060355040613025553301e170d3230313030313030303030305a170d3231313030313030303030305a30213112301006035504030c0975746f706961206473310b30090603550406130255533059301306072a8648ce3d020106082a8648ce3d03010703420004ace7ab7340e5d9648c5a72a9a6f56745c7aad436a03a43efea77b5fa7b88f0197d57d8983e1b37d3a539f4d588365e38cbbf5b94d68c547b5bc8731dcd2f146ba381a83081a5301e0603551d120417301581136578616d706c65406578616d706c652e636f6d301c0603551d1f041530133011a00fa00d820b6578616d706c652e636f6d301d0603551d0e0416041414e29017a6c35621ffc7a686b7b72db06cd12351301f0603551d2304183016801454fa2383a04c28e0d930792261c80c4881d2c00b300e0603551d0f0101ff04040302078030150603551d250101ff040b3009060728818c5d050102300a06082a8648ce3d040302034800304502210097717ab9016740c8d7bcdaa494a62c053bbdecce1383c1aca72ad08dbc04cbb202203bad859c13a63c6d1ad67d814d43e2425caf90d422422c04a8ee0304c0d3a68d5903a2d81859039da66776657273696f6e63312e306f646967657374416c676f726974686d675348412d3235366c76616c756544696765737473a2716f72672e69736f2e31383031332e352e31ad00582075167333b47b6c2bfb86eccc1f438cf57af055371ac55e1e359e20f254adcebf01582067e539d6139ebd131aef441b445645dd831b2b375b390ca5ef6279b205ed45710258203394372ddb78053f36d5d869780e61eda313d44a392092ad8e0527a2fbfe55ae0358202e35ad3c4e514bb67b1a9db51ce74e4cb9b7146e41ac52dac9ce86b8613db555045820ea5c3304bb7c4a8dcb51c4c13b65264f845541341342093cca786e058fac2d59055820fae487f68b7a0e87a749774e56e9e1dc3a8ec7b77e490d21f0e1d3475661aa1d0658207d83e507ae77db815de4d803b88555d0511d894c897439f5774056416a1c7533075820f0549a145f1cf75cbeeffa881d4857dd438d627cf32174b1731c4c38e12ca936085820b68c8afcb2aaf7c581411d2877def155be2eb121a42bc9ba5b7312377e068f660958200b3587d1dd0c2a07a35bfb120d99a0abfb5df56865bb7fa15cc8b56a66df6e0c0a5820c98a170cf36e11abb724e98a75a5343dfa2b6ed3df2ecfbb8ef2ee55dd41c8810b5820b57dd036782f7b14c6a30faaaae6ccd5054ce88bdfa51a016ba75eda1edea9480c5820651f8736b18480fe252a03224ea087b5d10ca5485146c67c74ac4ec3112d4c3a746f72672e69736f2e31383031332e352e312e5553a4005820d80b83d25173c484c5640610ff1a31c949c1d934bf4cf7f18d5223b15dd4f21c0158204d80e1e2e4fb246d97895427ce7000bb59bb24c8cd003ecf94bf35bbd2917e340258208b331f3b685bca372e85351a25c9484ab7afcdf0d2233105511f778d98c2f544035820c343af1bd1690715439161aba73702c474abf992b20c9fb55c36a336ebe01a876d6465766963654b6579496e666fa1696465766963654b6579a40102200121582096313d6c63e24e3372742bfdb1a33ba2c897dcd68ab8c753e4fbd48dca6b7f9a2258201fb3269edd418857de1b39a4e4a44b92fa484caa722c228288f01d0c03a2c3d667646f6354797065756f72672e69736f2e31383031332e352e312e6d444c6c76616c6964697479496e666fa3667369676e6564c074323032302d31302d30315431333a33303a30325a6976616c696446726f6dc074323032302d31302d30315431333a33303a30325a6a76616c6964556e74696cc074323032312d31302d30315431333a33303a30325a584059e64205df1e2f708dd6db0847aed79fc7c0201d80fa55badcaf2e1bcf5902e1e5a62e4832044b890ad85aa53f129134775d733754d7cb7a413766aeff13cb2e6c6465766963655369676e6564a36a6e616d65537061636573a16a4040544147474544404082181841a06a64657669636541757468a1696465766963654d61638443a10105a0f65820e99521a85ad7891b806a07f8b5388a332d92c189a7bf293ee1f543405ae6824d6b6465766963654175746832a1696465766963654d6163a16a4040544147474544404082118443a10126a104524173796d6d657472696345434453413235365850a70175636f61703a2f2f61732e6578616d706c652e636f6d02656572696b77037818636f61703a2f2f6c696768742e6578616d706c652e636f6d041a5612aeb0051a5610d9f0061a5610d9f007420b715820a377dfe17a3c3c3bdb363c426f85d3c1a1f11007765965017602f207700071b06673746174757300 \ No newline at end of file +a46776657273696f6e63312e3069646f63756d656e747381a367646f6354797065636161616c6973737565725369676e6564a26a6e616d65537061636573a1616181d8185838a4686469676573744944182a6672616e646f6d412a71656c656d656e744964656e7469666965726234326c656c656d656e7456616c7565f66a69737375657241757468d28443a10126a10442313154546869732069732074686520636f6e74656e742e58408eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb366c6465766963655369676e6564a26a6e616d65537061636573d81847a16161a16161f66a64657669636541757468a1634d6163a1696465766963654d6163d18443a10105a10442313154546869732069732074686520636f6e74656e742e58203f30c4a2c740c3a0d90310b48cd282bcdb29ab8073a32e287fa07e188d317e8a6e646f63756d656e744572726f727381a16161006673746174757300 \ No newline at end of file From 9b770b6ed34222c9c5bb78143b320c5b5fc927e5 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Mon, 16 Sep 2024 04:41:35 +0300 Subject: [PATCH 14/19] consume self in into_ method --- src/issuance/x5chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/issuance/x5chain.rs b/src/issuance/x5chain.rs index 609b097a..4b7479a3 100644 --- a/src/issuance/x5chain.rs +++ b/src/issuance/x5chain.rs @@ -93,7 +93,7 @@ impl X5Chain { } /// Converts the [X5Chain] object into a [ciborium::Value]. - pub fn into_cbor(&self) -> ciborium::Value { + pub fn into_cbor(self) -> ciborium::Value { match &self.0.as_ref() { &[cert] => ciborium::Value::Bytes(cert.bytes.clone()), certs => ciborium::Value::Array( From 4a979b60493265f3d7dc4819e589b759b9c229a1 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Mon, 16 Sep 2024 05:36:09 +0300 Subject: [PATCH 15/19] fmt --- src/issuance/x5chain.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/issuance/x5chain.rs b/src/issuance/x5chain.rs index 4b7479a3..e68e55d2 100644 --- a/src/issuance/x5chain.rs +++ b/src/issuance/x5chain.rs @@ -92,7 +92,7 @@ impl X5Chain { Builder::default() } - /// Converts the [X5Chain] object into a [ciborium::Value]. + /// Converts the [X5Chain] object into a [`ciborium::Value`]. pub fn into_cbor(self) -> ciborium::Value { match &self.0.as_ref() { &[cert] => ciborium::Value::Bytes(cert.bytes.clone()), From bf0702e80a7739a41e521f2f868f60179d06c282 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 18 Sep 2024 16:49:13 +0300 Subject: [PATCH 16/19] Update .github/workflows/ci.yaml Co-authored-by: Jacob --- .github/workflows/ci.yaml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5cc764f6..ab4adcd3 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,13 +18,6 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Set up Rust toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: rustfmt, clippy - profile: minimal - - name: Build run: cargo build --all-targets From 890a1998194252c579ae6e6e7558b8c33af5531f Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Thu, 19 Sep 2024 22:50:27 +0300 Subject: [PATCH 17/19] fix minor issues --- .github/workflows/event.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .github/workflows/event.json diff --git a/.github/workflows/event.json b/.github/workflows/event.json new file mode 100644 index 00000000..e69de29b From 323e62b0cd5613ac78c75e22f266ab08c1e41020 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Sun, 22 Sep 2024 15:54:43 +0300 Subject: [PATCH 18/19] borError wraps coset::CoseError and impl std::Error and Display, and other changes - use dtolnay/rust-toolchain@stable to setup Rus when running in act - use Into where possible --- .github/workflows/ci.yaml | 7 + .github/workflows/event.json | 3 + macros/src/to_cbor.rs | 2 +- src/cbor.rs | 143 ++++++--------------- src/cose.rs | 10 ++ src/cose/mac0.rs | 10 -- src/cose/sign1.rs | 10 -- src/definitions/device_engagement.rs | 72 +++-------- src/definitions/device_engagement/error.rs | 8 -- src/definitions/device_key/cose_key.rs | 6 +- src/definitions/mod.rs | 4 +- src/definitions/traits/to_cbor.rs | 33 ++++- src/definitions/validity_info.rs | 3 +- src/presentation/device.rs | 4 +- src/presentation/reader.rs | 8 +- 15 files changed, 115 insertions(+), 208 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ab4adcd3..f97bc288 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,6 +18,13 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: setup Rust + uses: dtolnay/rust-toolchain@stable + if: ${{ github.event.act }} + with: + toolchain: stable + components: rustfmt, clippy + - name: Build run: cargo build --all-targets diff --git a/.github/workflows/event.json b/.github/workflows/event.json index e69de29b..176cfa80 100644 --- a/.github/workflows/event.json +++ b/.github/workflows/event.json @@ -0,0 +1,3 @@ +{ + "act": true +} diff --git a/macros/src/to_cbor.rs b/macros/src/to_cbor.rs index db2c8d7d..f970acc3 100644 --- a/macros/src/to_cbor.rs +++ b/macros/src/to_cbor.rs @@ -102,7 +102,7 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt fn to_cbor(self) -> Value { let map = self.to_ns_map() .into_iter() - .map(|(k, v)| (ciborium::Value::Text(k), v.try_into().unwrap())) + .map(|(k, v)| (Value::Text(k), v.try_into().unwrap())) .collect(); Value::Map(map) } diff --git a/src/cbor.rs b/src/cbor.rs index b4358bba..2e003368 100644 --- a/src/cbor.rs +++ b/src/cbor.rs @@ -1,106 +1,17 @@ use std::io::Cursor; use ciborium::Value; -use coset::{cbor, CoseError, EndOfFile}; +use coset::{cbor, CoseError}; use serde::{de, Serialize}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum CborError { - /// CBOR decoding failure. - #[error("CBOR decoding failure: {0}")] - DecodeFailed(cbor::de::Error), - /// Duplicate map key detected. - #[error("duplicate map key")] - DuplicateMapKey, - /// CBOR encoding failure. - #[error("CBOR encoding failure")] - EncodeFailed, - /// CBOR input had extra data. - #[error("extraneous data")] - ExtraneousData, - /// Integer value on the wire is outside the range of integers representable in this crate. - /// See . - #[error("integer value out of range")] - OutOfRangeIntegerValue, - /// Unexpected CBOR item encountered (got, want). - #[error("unexpected item: {0}, want {1}")] - UnexpectedItem(&'static str, &'static str), - /// Unrecognized value in IANA-controlled range (with no private range). - #[error("unregistered IANA value")] - UnregisteredIanaValue, - /// Unrecognized value in neither IANA-controlled range nor private range. - #[error("unregistered non-private IANA value")] - UnregisteredIanaNonPrivateValue, - /// Value contains non-finite float (NaN or Infinity). - #[error("non finite floats")] - NonFiniteFloats, -} - -impl PartialEq for CborError { - fn eq(&self, other: &Self) -> bool { - match (self, other) { - (Self::DecodeFailed(_), Self::DecodeFailed(_)) => true, - (Self::DuplicateMapKey, Self::DuplicateMapKey) => true, - (Self::EncodeFailed, Self::EncodeFailed) => true, - (Self::ExtraneousData, Self::ExtraneousData) => true, - (Self::OutOfRangeIntegerValue, Self::OutOfRangeIntegerValue) => true, - (Self::UnexpectedItem(l_msg, l_want), Self::UnexpectedItem(r_msg, r_want)) => { - l_msg == r_msg && l_want == r_want - } - (Self::UnregisteredIanaValue, Self::UnregisteredIanaValue) => true, - (Self::UnregisteredIanaNonPrivateValue, Self::UnregisteredIanaNonPrivateValue) => true, - (Self::NonFiniteFloats, Self::NonFiniteFloats) => true, - _ => false, - } - } -} - -impl Eq for CborError {} - -impl Clone for CborError { - fn clone(&self) -> Self { - match self { - CborError::DecodeFailed(_) => panic!("cannot clone"), - CborError::DuplicateMapKey => CborError::DuplicateMapKey, - CborError::EncodeFailed => CborError::EncodeFailed, - CborError::ExtraneousData => CborError::ExtraneousData, - CborError::OutOfRangeIntegerValue => CborError::OutOfRangeIntegerValue, - CborError::UnexpectedItem(msg, want) => CborError::UnexpectedItem(msg, want), - CborError::UnregisteredIanaValue => CborError::UnregisteredIanaValue, - CborError::UnregisteredIanaNonPrivateValue => { - CborError::UnregisteredIanaNonPrivateValue - } - CborError::NonFiniteFloats => CborError::NonFiniteFloats, - } - } -} - -impl From for CborError { - fn from(e: CoseError) -> Self { - match e { - CoseError::DecodeFailed(e) => CborError::DecodeFailed(e), - CoseError::DuplicateMapKey => CborError::DuplicateMapKey, - CoseError::EncodeFailed => CborError::EncodeFailed, - CoseError::ExtraneousData => CborError::ExtraneousData, - CoseError::OutOfRangeIntegerValue => CborError::OutOfRangeIntegerValue, - CoseError::UnexpectedItem(s, s2) => CborError::UnexpectedItem(s, s2), - CoseError::UnregisteredIanaValue => CborError::UnregisteredIanaValue, - CoseError::UnregisteredIanaNonPrivateValue => { - CborError::UnregisteredIanaNonPrivateValue - } - } - } -} +use std::error::Error; +use std::fmt; pub fn to_vec(value: &T) -> Result, CborError> where T: serde::Serialize, { let mut buf = Vec::new(); - ciborium::into_writer(value, &mut buf) - .map_err(coset::CoseError::from) - .map_err(CborError::from)?; + ciborium::into_writer(value, &mut buf).map_err(|_| CborError(CoseError::EncodeFailed))?; Ok(buf) } @@ -108,29 +19,49 @@ pub fn from_slice(slice: &[u8]) -> Result where T: de::DeserializeOwned, { - ciborium::from_reader(Cursor::new(&slice)) - .map_err(|e| CoseError::DecodeFailed(ciborium::de::Error::Semantic(None, e.to_string()))) - .map_err(CborError::from) + ciborium::from_reader(Cursor::new(&slice)).map_err(|e| { + CborError(CoseError::DecodeFailed(ciborium::de::Error::Semantic( + None, + e.to_string(), + ))) + }) } /// Convert a `ciborium::Value` into a type `T` #[allow(clippy::needless_pass_by_value)] -pub fn from_value(value: ciborium::Value) -> Result +pub fn from_value(value: Value) -> Result where T: de::DeserializeOwned, { - // TODO implement in a way that doesn't require - // roundtrip through buffer (i.e. by implementing - // `serde::de::Deserializer` for `Value` and then doing - // `T::deserialize(value)`). - let buf = to_vec(&value)?; - from_slice(buf.as_slice()) + Value::deserialized(&value).map_err(|_| { + CoseError::DecodeFailed(cbor::de::Error::Semantic( + None, + "cannot deserialize".to_string(), + )) + }) } -pub fn into_value(v: S) -> Result +pub fn into_value(v: S) -> Result where S: Serialize, { - let bytes = to_vec(&v)?; - from_slice(&bytes) + Value::serialized(&v).map_err(|_| CoseError::EncodeFailed) } + +// Wrapper struct to implement Error for CoseError +#[derive(Debug)] +pub struct CborError(pub CoseError); + +impl From for CborError { + fn from(err: CoseError) -> CborError { + CborError(err) + } +} + +impl fmt::Display for CborError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl Error for CborError {} diff --git a/src/cose.rs b/src/cose.rs index 609aa60e..157e0bd5 100644 --- a/src/cose.rs +++ b/src/cose.rs @@ -30,6 +30,16 @@ where pub fn new(tagged: bool, inner: T) -> Self { Self { tagged, inner } } + + /// If we are serialized as tagged. + pub fn is_tagged(&self) -> bool { + self.tagged + } + + /// Set serialization to tagged. + pub fn set_tagged(&mut self) { + self.tagged = true; + } } impl Deref for MaybeTagged diff --git a/src/cose/mac0.rs b/src/cose/mac0.rs index 02528fc8..0d99eed6 100644 --- a/src/cose/mac0.rs +++ b/src/cose/mac0.rs @@ -209,16 +209,6 @@ impl MaybeTagged { ), } } - - /// If we are serialized as tagged. - pub fn is_tagged(&self) -> bool { - self.tagged - } - - /// Set serialization to tagged. - pub fn set_tagged(&mut self) { - self.tagged = true; - } } mod hmac { diff --git a/src/cose/sign1.rs b/src/cose/sign1.rs index 4d1dfa6a..e3e8e51d 100644 --- a/src/cose/sign1.rs +++ b/src/cose/sign1.rs @@ -223,16 +223,6 @@ impl MaybeTagged { ), } } - - /// If we are serialized as tagged. - pub fn is_tagged(&self) -> bool { - self.tagged - } - - /// Set serialization to tagged. - pub fn set_tagged(&mut self) { - self.tagged = true; - } } mod p256 { diff --git a/src/definitions/device_engagement.rs b/src/definitions/device_engagement.rs index fcb4a06d..9edf7e0c 100644 --- a/src/definitions/device_engagement.rs +++ b/src/definitions/device_engagement.rs @@ -168,19 +168,15 @@ impl From for ciborium::Value { if let Some(methods) = device_engagement.device_retrieval_methods { let methods = Vec::from(methods) .into_iter() - .map(cbor::into_value) - .collect::, CborError>>() - .unwrap(); + .map(ciborium::Value::from) + .collect(); map.push(( ciborium::Value::Integer(2.into()), ciborium::Value::Array(methods), )); } if let Some(methods) = device_engagement.server_retrieval_methods { - map.push(( - ciborium::Value::Integer(3.into()), - cbor::into_value(methods).unwrap(), - )); + map.push((ciborium::Value::Integer(3.into()), methods.into())); } if let Some(_info) = device_engagement.protocol_info { // Usage of protocolinfo is RFU and should for now be none @@ -286,8 +282,7 @@ impl TryFrom for DeviceRetrievalMethod { if >::into(*i1) == 1 && >::into(*i11) == 1 => { - let nfc_options = - NfcOptions::try_from(methods.clone()).map_err(|_| Error::Malformed)?; + let nfc_options = NfcOptions::try_from(methods.clone())?; Ok(DeviceRetrievalMethod::NFC(nfc_options)) } [ciborium::Value::Integer(i2), ciborium::Value::Integer(i1), methods] @@ -320,9 +315,9 @@ impl From for ciborium::Value { let transport_type = drm.transport_type().into(); let version = drm.version().into(); let retrieval_method = match drm { - DeviceRetrievalMethod::NFC(opts) => cbor::into_value(opts).unwrap(), - DeviceRetrievalMethod::BLE(opts) => cbor::into_value(opts).unwrap(), - DeviceRetrievalMethod::WIFI(opts) => cbor::into_value(opts).unwrap(), + DeviceRetrievalMethod::NFC(opts) => opts.into(), + DeviceRetrievalMethod::BLE(opts) => opts.into(), + DeviceRetrievalMethod::WIFI(opts) => opts.into(), }; ciborium::Value::Array(vec![transport_type, version, retrieval_method]) } @@ -340,8 +335,8 @@ impl TryFrom for BleOptions { Ok((k, v)) }) .collect::, Error>>()?; - let v1: Option = map.remove(&1).map(ciborium::Value::into); - let v2: Option = map.remove(&11).map(ciborium::Value::into); + let v1: Option = map.remove(&1); + let v2: Option = map.remove(&11); let central_client_mode = match (v1, v2) { (Some(ciborium::Value::Bool(true)), Some(ciborium::Value::Bytes(uuid))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; @@ -353,10 +348,7 @@ impl TryFrom for BleOptions { _ => return Err(Error::Malformed), }; - let peripheral_server_mode = match ( - map.remove(&0).map(ciborium::Value::into), - map.remove(&10).map(ciborium::Value::into), - ) { + let peripheral_server_mode = match (map.remove(&0), map.remove(&10)) { (Some(ciborium::Value::Bool(true)), Some(ciborium::Value::Bytes(uuid))) => { let uuid_bytes: [u8; 16] = uuid.try_into().map_err(|_| Error::Malformed)?; let ble_device_address = match map.remove(&20) { @@ -419,10 +411,7 @@ impl From for ciborium::Value { ciborium::Value::Bytes(uuid.as_bytes().to_vec()), )); if let Some(address) = ble_device_address { - map.push(( - ciborium::Value::Integer(20.into()), - cbor::into_value(address).unwrap(), - )); + map.push((ciborium::Value::Integer(20.into()), address.into())); } } None => { @@ -445,7 +434,7 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&idx).cloned().map(ciborium::Value::into) { + match map.get(&idx) { None => Ok(None), Some(ciborium::Value::Text(text)) => Ok(Some(text.to_string())), _ => Err(Error::InvalidWifiOptions), @@ -456,10 +445,11 @@ impl TryFrom for WifiOptions { map: &BTreeMap, idx: i128, ) -> Result, Error> { - match map.get(&idx).cloned().map(ciborium::Value::into) { + match map.get(&idx) { None => Ok(None), Some(ciborium::Value::Integer(int_val)) => { - let uint_val = u64::try_from(int_val).map_err(|_| Error::InvalidWifiOptions)?; + let uint_val = + u64::try_from(*int_val).map_err(|_| Error::InvalidWifiOptions)?; Ok(Some(uint_val)) } _ => Err(Error::InvalidWifiOptions), @@ -530,28 +520,16 @@ impl From for ciborium::Value { fn from(o: WifiOptions) -> ciborium::Value { let mut map = vec![]; if let Some(v) = o.pass_phrase { - map.push(( - ciborium::Value::Integer(0.into()), - cbor::into_value(v).unwrap(), - )); + map.push((ciborium::Value::Integer(0.into()), v.into())); } if let Some(v) = o.channel_info_operating_class { - map.push(( - ciborium::Value::Integer(1.into()), - cbor::into_value(v).unwrap(), - )); + map.push((ciborium::Value::Integer(1.into()), v.into())); } if let Some(v) = o.channel_info_channel_number { - map.push(( - ciborium::Value::Integer(2.into()), - cbor::into_value(v).unwrap(), - )); + map.push((ciborium::Value::Integer(2.into()), v.into())); } if let Some(v) = o.band_info { - map.push(( - ciborium::Value::Integer(3.into()), - cbor::into_value(v).unwrap(), - )); + map.push((ciborium::Value::Integer(3.into()), v.into())); } ciborium::Value::Map(map) @@ -565,22 +543,14 @@ impl From for ciborium::Value { if let Some((x, y, z)) = m.web_api { map.push(( "webApi".to_string().into(), - ciborium::Value::Array(vec![ - cbor::into_value(x).unwrap(), - cbor::into_value(y).unwrap(), - cbor::into_value(z).unwrap(), - ]), + ciborium::Value::Array(vec![x.into(), y.into(), z.into()]), )); } if let Some((x, y, z)) = m.oidc { map.push(( "oidc".to_string().into(), - ciborium::Value::Array(vec![ - cbor::into_value(x).unwrap(), - cbor::into_value(y).unwrap(), - cbor::into_value(z).unwrap(), - ]), + ciborium::Value::Array(vec![x.into(), y.into(), z.into()]), )); } diff --git a/src/definitions/device_engagement/error.rs b/src/definitions/device_engagement/error.rs index d433659a..365cc731 100644 --- a/src/definitions/device_engagement/error.rs +++ b/src/definitions/device_engagement/error.rs @@ -25,8 +25,6 @@ pub enum Error { Tag24Error, #[error("Could not deserialize from cbor")] CborError, - #[error("Could not deserialize from cbor")] - CborErrorWithSource(CborError), #[error("NFC Command Data Length must be between 255 and 65535")] InvalidNfcCommandDataLengthError, #[error("NFC Response Data Length must be between 256 and 65536")] @@ -45,12 +43,6 @@ impl From for Error { } } -impl From for Error { - fn from(_: coset::CoseError) -> Self { - Error::CborError - } -} - impl From for Error { fn from(_: CborError) -> Self { Error::CborError diff --git a/src/definitions/device_key/cose_key.rs b/src/definitions/device_key/cose_key.rs index b5327394..ab409666 100644 --- a/src/definitions/device_key/cose_key.rs +++ b/src/definitions/device_key/cose_key.rs @@ -30,8 +30,6 @@ use p256::EncodedPoint; use serde::{Deserialize, Serialize}; use ssi_jwk::JWK; -use crate::cbor::CborError; - /// An implementation of RFC-8152 [COSE_Key](https://datatracker.ietf.org/doc/html/rfc8152#section-13) /// restricted to the requirements of ISO/IEC 18013-5:2021. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -88,8 +86,6 @@ pub enum Error { InvalidCoseKey, #[error("Constructing a JWK from CoseKey with point-compression is not supported.")] UnsupportedFormat, - #[error("could not serialize from to cbor: {0}")] - CborErrorWithSource(CborError), #[error("could not serialize from to cbor")] CborError, } @@ -270,7 +266,7 @@ impl TryFrom for EC2Y { type Error = Error; fn try_from(v: ciborium::Value) -> Result { - match v.clone() { + match v { ciborium::Value::Bytes(s) => Ok(EC2Y::Value(s)), ciborium::Value::Bool(b) => Ok(EC2Y::SignBit(b)), _ => Err(Error::InvalidTypeY(v)), diff --git a/src/definitions/mod.rs b/src/definitions/mod.rs index 387ee320..cf8954c2 100644 --- a/src/definitions/mod.rs +++ b/src/definitions/mod.rs @@ -1,6 +1,4 @@ -//! This module contains the definitions for various components related to device engagement, -//! device keys, device requests, device responses, device signing, helpers, issuer signing, -//! MSO (Mobile Security Object), namespaces, session management, traits, and validity information. +//! This module contains the definitions for all components involved in the lib. pub mod device_engagement; pub mod device_key; pub mod device_request; diff --git a/src/definitions/traits/to_cbor.rs b/src/definitions/traits/to_cbor.rs index 0db35bc0..8098b066 100644 --- a/src/definitions/traits/to_cbor.rs +++ b/src/definitions/traits/to_cbor.rs @@ -4,6 +4,8 @@ use crate::cbor; use crate::cbor::CborError; use std::collections::BTreeMap; +use std::error::Error; +use std::fmt; pub type Bytes = Vec; @@ -22,10 +24,35 @@ pub trait ToNamespaceMap { fn to_ns_map(self) -> BTreeMap; } -#[derive(Debug, thiserror::Error)] +// Define your error enum with just the CoseError variant +#[derive(Debug)] pub enum ToCborError { - #[error("cbor error: {0}")] - CoseError(#[from] CborError), + CborError(CborError), +} + +// Implement Display for ToCborError +impl fmt::Display for ToCborError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ToCborError::CborError(err) => write!(f, "COSE error: {}", err), + } + } +} + +// Implement Error for ToCborError +impl Error for ToCborError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match self { + ToCborError::CborError(err) => Some(err), + } + } +} + +// Implement From to easily convert CoseError into ToCborError +impl From for ToCborError { + fn from(err: CborError) -> ToCborError { + ToCborError::CborError(err) + } } impl ToCbor for T diff --git a/src/definitions/validity_info.rs b/src/definitions/validity_info.rs index ade7c9cd..da977afd 100644 --- a/src/definitions/validity_info.rs +++ b/src/definitions/validity_info.rs @@ -28,7 +28,6 @@ //! - [std::collections::BTreeMap]: Provides the [BTreeMap] type for storing key-value pairs in a sorted order. //! - [time]: Provides date and time manipulation functionality. //! - [thiserror]: Provides the [thiserror::Error] trait for defining custom error types. -use crate::cbor::CborError; use serde::{ ser::{Error as SerError, Serializer}, Deserialize, Serialize, @@ -67,7 +66,7 @@ pub enum Error { #[error("Failed to parse date string as rfc3339 date: {0}")] UnableToParseDate(#[from] ParseError), #[error("Could not serialize to cbor: {0}")] - CborErrorWithSource(CborError), + CborErrorWithSource(coset::CoseError), #[error("Could not serialize to cbor")] CborError, } diff --git a/src/presentation/device.rs b/src/presentation/device.rs index 97e78dff..4c5964cc 100644 --- a/src/presentation/device.rs +++ b/src/presentation/device.rs @@ -132,7 +132,7 @@ pub enum Error { SharedSecretGeneration(anyhow::Error), /// Error encoding value to CBOR. #[error("error encoding value to CBOR: {0}")] - CborEncoding(CborError), + CborEncoding(coset::CoseError), /// Session manager was used incorrectly. #[error("session manager was used incorrectly")] ApiMisuse, @@ -143,7 +143,7 @@ pub enum Error { #[error("age_over element identifier is malformed")] PrefixError, #[error("Could not serialize to cbor: {0}")] - CborError(CborError), + CborError(coset::CoseError), } /// The documents the device owns. diff --git a/src/presentation/reader.rs b/src/presentation/reader.rs index 11e8f6b5..b1c95cc2 100644 --- a/src/presentation/reader.rs +++ b/src/presentation/reader.rs @@ -87,12 +87,6 @@ pub enum Error { CborError(CborError), } -impl From for Error { - fn from(_: coset::CoseError) -> Self { - Error::CborDecodingError - } -} - impl From for Error { fn from(_: CborError) -> Self { Error::CborDecodingError @@ -234,7 +228,7 @@ impl SessionManager { &mut self, response: &[u8], ) -> Result>, Error> { - let session_data: SessionData = crate::cbor::from_slice(response)?; + let session_data: SessionData = cbor::from_slice(response)?; let encrypted_response = match session_data.data { None => return Err(Error::HolderError), Some(r) => r, From ba0ec6278b79601b4f0247dbb1e8482ba85c537b Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Mon, 23 Sep 2024 12:49:29 +0300 Subject: [PATCH 19/19] Update macros/src/to_cbor.rs Co-authored-by: Jacob --- macros/src/to_cbor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/src/to_cbor.rs b/macros/src/to_cbor.rs index f970acc3..6832d592 100644 --- a/macros/src/to_cbor.rs +++ b/macros/src/to_cbor.rs @@ -102,7 +102,7 @@ fn named_fields(isomdl_path: Ident, ident: Ident, input: FieldsNamed) -> TokenSt fn to_cbor(self) -> Value { let map = self.to_ns_map() .into_iter() - .map(|(k, v)| (Value::Text(k), v.try_into().unwrap())) + .map(|(k, v)| (Value::Text(k), v)) .collect(); Value::Map(map) }