Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to coset and ciborium #94

Merged
merged 20 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e849575
seerialize cose objects with serde and ciborium for serde
radumarias Sep 6, 2024
8eac14f
remove serde_cbor and use coset and ciborium serialization and CborValue
radumarias Sep 7, 2024
369c1a3
remove serde_cbor and use coset and ciborium serialization and CborValue
radumarias Sep 7, 2024
178edf7
clippy
radumarias Sep 7, 2024
7d90847
Add MaybeTagged struct and wrap Cose objects in that
radumarias Sep 7, 2024
ccaae0b
Add MaybeTagged struct and wrap Cose objects in that
radumarias Sep 7, 2024
670bce2
use correct TAG in MaybeTagged when serializing
radumarias Sep 9, 2024
0c6efb5
handle the case of non-finite floats
radumarias Sep 11, 2024
55ff9af
handle the case of non-finite floats
radumarias Sep 11, 2024
d08601c
adapt to impl TryFrom<ciborium::Value> for Value
radumarias Sep 11, 2024
c796aa6
adapt to impl TryFrom<ciborium::Value> for Value
radumarias Sep 11, 2024
9fe82d8
Don't use cbor::Value but use directly ciborium::Value and use keys a…
radumarias Sep 16, 2024
923408c
Don't use cbor::Value but use directly ciborium::Value and use keys a…
radumarias Sep 16, 2024
9b770b6
consume self in into_ method
radumarias Sep 16, 2024
378ded9
Merge branch 'main' into coset
radumarias Sep 16, 2024
4a979b6
fmt
radumarias Sep 16, 2024
bf0702e
Update .github/workflows/ci.yaml
radumarias Sep 18, 2024
890a199
fix minor issues
radumarias Sep 19, 2024
323e62b
borError wraps coset::CoseError and impl std::Error and Display, and …
radumarias Sep 22, 2024
ba0ec62
Update macros/src/to_cbor.rs
radumarias Sep 23, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

radumarias marked this conversation as resolved.
Show resolved Hide resolved
- name: Build
run: cargo build --all-targets

Expand Down
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ 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"
thiserror = "1.0"
elliptic-curve = "0.13.1"
Expand Down Expand Up @@ -46,9 +46,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"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

7 changes: 3 additions & 4 deletions macros/src/to_cbor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 ciborium::Value;
use super::*;
use #isomdl_path::definitions::traits::{ToCbor, ToNamespaceMap};
impl ToNamespaceMap for #ident {
Expand All @@ -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))
.map(|(k, v)| (ciborium::Value::Text(k), v.try_into().unwrap()))
radumarias marked this conversation as resolved.
Show resolved Hide resolved
.collect();
Value::Map(map)
}
Expand Down Expand Up @@ -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 serde_cbor::Value;
impl ToCbor for #ident {
fn to_cbor(self) -> Value {
fn to_cbor(self) -> ciborium::Value {
<#field_type as ToCbor>::to_cbor(self)
}
}
Expand Down
136 changes: 136 additions & 0 deletions src/cbor.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
use std::io::Cursor;

use ciborium::Value;
use coset::{cbor, CoseError, EndOfFile};
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<EndOfFile>),
/// 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 <https://crates.io/crates/coset/#integer-ranges>.
#[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"),
radumarias marked this conversation as resolved.
Show resolved Hide resolved
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<CoseError> for CborError {
fn from(e: CoseError) -> Self {
match e {
CoseError::DecodeFailed(e) => CborError::DecodeFailed(e),
radumarias marked this conversation as resolved.
Show resolved Hide resolved
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
}
}
}
}

pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, 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<T>(slice: &[u8]) -> Result<T, CborError>
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 `ciborium::Value` into a type `T`
#[allow(clippy::needless_pass_by_value)]
pub fn from_value<T>(value: ciborium::Value) -> Result<T, CborError>
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())
radumarias marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn into_value<S>(v: S) -> Result<Value, CborError>
where
S: Serialize,
{
let bytes = to_vec(&v)?;
from_slice(&bytes)
radumarias marked this conversation as resolved.
Show resolved Hide resolved
}
117 changes: 117 additions & 0 deletions src/cose.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use std::borrow::{Borrow, BorrowMut};
use std::ops::{Deref, DerefMut};

use coset::{iana, AsCborValue, TaggedCborSerializable};

use crate::cose::serialized_as_cbor_value::SerializedAsCborValue;

pub mod mac0;
mod serialized_as_cbor_value;
pub mod sign1;

/// Trait to represent the signature algorithm of a signer or verifier.
pub trait SignatureAlgorithm {
fn algorithm(&self) -> iana::Algorithm;
}

#[derive(Debug, Clone)]
pub struct MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
pub tagged: bool,
pub inner: T,
}

impl<T> MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
pub fn new(tagged: bool, inner: T) -> Self {
Self { tagged, inner }
}
}

impl<T> Deref for MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
type Target = T;

fn deref(&self) -> &Self::Target {
&self.inner
}
}

impl<T> DerefMut for MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}

impl<T> Borrow<T> for MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
fn borrow(&self) -> &T {
&self.inner
}
}

impl<T> BorrowMut<T> for MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
fn borrow_mut(&mut self) -> &mut T {
&mut self.inner
}
}

impl<T> AsRef<T> for MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
fn as_ref(&self) -> &T {
&self.inner
}
}

/// Serialize manually using `ciborium::tag::Captured`, putting the tag if
/// necessary.
impl<T> serde::Serialize for MaybeTagged<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let tag = if self.tagged { Some(T::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<T>
where
T: AsCborValue + TaggedCborSerializable + Clone,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let ciborium::tag::Captured(tag, SerializedAsCborValue(inner)) =
ciborium::tag::Captured::deserialize(deserializer)?;
let tagged = match tag {
Some(tag) if tag == T::TAG => true,
Some(_) => return Err(serde::de::Error::custom("unexpected tag")),
None => false,
};

Ok(Self { tagged, inner })
}
}
Loading
Loading