Skip to content

Commit

Permalink
sign: Allow serde to deserialize tagged values (#12)
Browse files Browse the repository at this point in the history
Add the capability to use plain serde_cbor::from_slice to read tagged
COSESign1 objects.
This manually implements Deserialize so that if we hit the Tagged value
(a newtype struct), it doesn't crash, and just returns the inner value.

Signed-off-by: Patrick Uiterwijk <[email protected]>

Co-authored-by: Petre Eftime <[email protected]>
  • Loading branch information
2 people authored and petreeftime committed Jul 14, 2021
1 parent 0557c16 commit 3d16c91
Showing 1 changed file with 81 additions and 2 deletions.
83 changes: 81 additions & 2 deletions src/sign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use openssl::hash::{hash, MessageDigest};
use openssl::nid::Nid;
use openssl::pkey::PKeyRef;
use openssl::pkey::{Private, Public};
use serde::{ser::SerializeSeq, Deserialize, Serialize, Serializer};
use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
use serde_bytes::ByteBuf;
use serde_cbor::Error as CborError;
use serde_cbor::Value as CborValue;
Expand Down Expand Up @@ -194,7 +194,7 @@ impl SigStructure {
/// the spec and the only way to achieve this is to add the token at the
/// start of the serialized object, since the serde_cbor library doesn't
/// support custom tags.
#[derive(Debug, Clone, Deserialize)]
#[derive(Debug, Clone)]
pub struct CoseSign1 {
/// protected: empty_or_serialized_map,
protected: ByteBuf,
Expand Down Expand Up @@ -222,6 +222,67 @@ impl Serialize for CoseSign1 {
}
}

impl<'de> Deserialize<'de> for CoseSign1 {
fn deserialize<D>(deserializer: D) -> Result<CoseSign1, D::Error>
where
D: Deserializer<'de>,
{
use serde::de::{Error, SeqAccess, Visitor};
use std::fmt;

struct CoseSign1Visitor;

impl<'de> Visitor<'de> for CoseSign1Visitor {
type Value = CoseSign1;

fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a possibly tagged CoseSign1 structure")
}

fn visit_seq<A>(self, mut seq: A) -> Result<CoseSign1, A::Error>
where
A: SeqAccess<'de>,
{
// This is the untagged version
let protected = match seq.next_element()? {
Some(v) => v,
None => return Err(A::Error::missing_field("protected")),
};

let unprotected = match seq.next_element()? {
Some(v) => v,
None => return Err(A::Error::missing_field("unprotected")),
};
let payload = match seq.next_element()? {
Some(v) => v,
None => return Err(A::Error::missing_field("payload")),
};
let signature = match seq.next_element()? {
Some(v) => v,
None => return Err(A::Error::missing_field("signature")),
};

Ok(CoseSign1 {
protected,
unprotected,
payload,
signature,
})
}

fn visit_newtype_struct<D>(self, deserializer: D) -> Result<CoseSign1, D::Error>
where
D: Deserializer<'de>,
{
// This is the tagged version: we ignore the tag part, and just go into it
deserializer.deserialize_seq(CoseSign1Visitor)
}
}

deserializer.deserialize_any(CoseSign1Visitor)
}
}

impl CoseSign1 {
/// Follows the recommandations put in place by the RFC and doesn't deal with potential
/// mismatches: https://tools.ietf.org/html/rfc8152#section-8.1.
Expand Down Expand Up @@ -855,6 +916,24 @@ mod tests {
);
}

#[test]
fn cose_sign1_ec256_text_tagged_serde() {
let (ec_private, ec_public) = generate_ec256_test_key();
let mut map = HeaderMap::new();
map.insert(CborValue::Integer(4), CborValue::Bytes(b"11".to_vec()));

let cose_doc1 = CoseSign1::new(TEXT, &map, &ec_private).unwrap();
let tagged_bytes = cose_doc1.as_bytes(true).unwrap();
// Tag 6.18 should be present
assert_eq!(tagged_bytes[0], 6 << 5 | 18);
let cose_doc2: CoseSign1 = serde_cbor::from_slice(&tagged_bytes).unwrap();

assert_eq!(
cose_doc1.get_payload(None).unwrap(),
cose_doc2.get_payload(Some(&ec_public)).unwrap()
);
}

#[test]
fn cose_sign1_ec256_text_with_extra_protected() {
let (ec_private, ec_public) = generate_ec256_test_key();
Expand Down

0 comments on commit 3d16c91

Please sign in to comment.