cose2 0.1.0

A Rust library for CBOR Object Signing and Encryption (COSE, RFC 9052) and CBOR Web Token (CWT, RFC 8392), built on cbor2.
Documentation

cose2

CBOR Object Signing and Encryption (COSE, RFC 9052) and CBOR Web Token (CWT, RFC 8392) for Rust, built on cbor2 and its #[derive(cbor2::Cbor)] macro.

crates.io docs.rs

cose2 models the COSE wire structures and CWT claims and leaves the cryptography to you: signing, verification, MAC and content encryption are supplied through the Signer, Verifier, Macer and Encryptor traits, so the crate carries no cryptographic dependencies of its own. Pick any crypto library (e.g. ed25519-dalek, p256, aes-gcm, hmac) and implement the relevant trait.

Features

  • MessagesCOSE_Sign1, COSE_Sign, COSE_Mac0, COSE_Mac, COSE_Encrypt0, COSE_Encrypt, COSE_recipient.
  • KeysCOSE_Key objects (Key) and key sets (KeySet) with typed accessors for kty, kid, alg, key_ops and Base IV.
  • Headers — protected/unprotected Header maps with integer or text labels and the full IANA parameter registry under iana.
  • CWT — typed cwt::Claims, a label-keyed cwt::ClaimsMap, and a cwt::Validator for expiry, not-before, issued-at, issuer and audience.
  • KDF contextKdfContext, PartyInfo, SuppPubInfo (RFC 9053 §5.2).
  • Tagging — tagged or untagged messages, with optional CWT and self-described CBOR prefixes handled transparently.

Quick start

use cose2::{iana, Sign1Message, Signer, Verifier, Error};

// Plug in your own crypto. (This toy "signer" is for illustration only.)
struct MySigner;
impl Signer for MySigner {
    fn alg(&self) -> i64 { iana::AlgorithmEdDSA }
    fn kid(&self) -> &[u8] { b"key-1" }
    fn sign(&self, data: &[u8]) -> Result<Vec<u8>, Error> { /* ... */ Ok(data.to_vec()) }
}
struct MyVerifier;
impl Verifier for MyVerifier {
    fn alg(&self) -> i64 { iana::AlgorithmEdDSA }
    fn verify(&self, data: &[u8], sig: &[u8]) -> Result<(), Error> {
        if sig == data { Ok(()) } else { Err(Error::verify("bad signature")) }
    }
}

let mut msg = Sign1Message::new(Some(b"This is the content".to_vec()));
let encoded = msg.sign_and_encode(&MySigner, None)?;

let verified = Sign1Message::verify_and_decode(&MyVerifier, &encoded, None)?;
assert_eq!(verified.payload.as_deref(), Some(&b"This is the content"[..]));
# Ok::<(), cose2::Error>(())

Design notes

  • Header, key and claim maps share one ordered type, CoseMap, keyed by Label (int / tstr). Values are [cbor2::Value].
  • The protected header is captured as raw bytes on decode and reused verbatim in the Sig_structure/MAC_structure/Enc_structure, so signatures made with non-canonical encodings still verify.
  • Newly built protected headers and keys serialize with canonical (deterministic) CBOR (RFC 8949 §4.2.1).
  • IVs are taken from the unprotected header; this crate generates no randomness.

Testing

cargo test runs the unit, integration and doc tests, including a byte-exact reproduction of the RFC 9052 Appendix C.4.1 COSE_Encrypt0 vector. Coverage measured with cargo llvm-cov is 100% of lines and functions; the remaining uncovered regions are unreachable error-propagation arms on serialization that cannot fail.

License

Dual-licensed under MIT or the UNLICENSE.