coset
Rust types for building and parsing CBOR Object Signing and Encryption (COSE) messages
Repository Health
Technical Analysis
coset is a Google-maintained Rust crate providing a set of types for working with CBOR Object Signing and Encryption (COSE) objects as defined in RFC 8152 (and its successor RFC 9052/9053). It covers the full family of COSE message structures — CoseSign1, CoseSign, CoseEncrypt0, CoseEncrypt, CoseMac0, CoseMac, and CoseKey — along with COSE headers, algorithm/key-type IANA registries, and CBOR Web Token (CWT) claim structures.
The crate builds on ciborium for the underlying CBOR encoding, is no_std-compatible (using only alloc), and deliberately stays cryptography-agnostic: callers supply their own signing, verification, encryption, and decryption operations as closures, so coset handles only the message framing, header encoding, and signature/MAC construction mandated by the COSE spec.
What You Get
- Builder types (
CoseSign1Builder,CoseSignBuilder,CoseEncrypt0Builder,CoseEncryptBuilder,CoseMac0Builder,CoseMacBuilder,CoseKeyBuilder,HeaderBuilder) for constructing every core COSE message shape - A
CborSerializabletrait giving every COSE typeto_vec()/from_slice()round-tripping to and from CBOR bytes - Closure-based
create_signature/verify_signature(and MAC/encrypt equivalents) so any signing or crypto library can be plugged in without coset depending on it - A comprehensive
ianamodule mirroring the IANA COSE algorithm, key-type, header-parameter, and elliptic-curve registries as typed Rust enums - CBOR Web Token (
cwt) claim-set types for building and parsing COSE-secured CWTs no_std+allocsupport so it can be used in embedded and constrained environments
Common Use Cases
- Signing and verifying CBOR-encoded payloads (e.g. firmware manifests, attestation statements) using COSE_Sign1/COSE_Sign structures
- Building CBOR Web Tokens (CWTs) for constrained IoT and embedded authentication flows
- Implementing COSE-based encryption (COSE_Encrypt0/COSE_Encrypt) for confidential CBOR message exchange
- Representing and serializing cryptographic keys with
CoseKeyfor interoperable key exchange (e.g. WebAuthn/FIDO2, Android Keystore attestation)
Under The Hood
Architecture: coset organizes each COSE message family (sign, encrypt, mac, key, header, cwt, context) into its own module under src/, each exposing a plain data struct plus a matching *Builder. Wire-format conversion is centralized through the AsCborValue trait (to_cbor_value/from_cbor_value) and the CborSerializable marker trait, which together give every public type to_vec()/from_slice() round-tripping without duplicating CBOR array/map handling in each module; lib.rs re-exports these modules flatly so coset::CoseSign1Builder etc. form the public surface. Cryptographic operations are never implemented directly — sign/verify/encrypt/decrypt/MAC all take caller-supplied closures, keeping the crate a pure message-framing layer over RFC 8152.
Tech Stack: Pure Rust, edition 2018, MSRV 1.81, no_std with alloc. The only runtime dependencies are ciborium (re-exported as coset::cbor) for CBOR encoding and ciborium-io for the alloc-based I/O traits; hex is a dev-dependency for examples/tests. No build step beyond cargo build; CI (GitHub Actions) runs tests, Clippy, rustfmt, and cargo-deny per CONTRIBUTING.md.
Code Quality: Each module (sign, encrypt, mac, key, header, common, context, cwt, util, iana) ships a co-located tests.rs (10 test files across the crate, ~11.4k total lines of source+tests), and the project enforces house rules via CI: builds must be Clippy-warning-free, rustfmt-formatted per .rustfmt.toml, and every panic!/unwrap/expect call must carry a // safe: reason comment justifying it — an unusually disciplined convention for panic-safety auditing. Fuzz targets exist under fuzz/, and CoseError is a explicit, enumerated error type rather than stringly-typed errors.
API Design: The builder pattern (CoseSign1Builder::new().protected(...).payload(...).create_signature(aad, |pt| signer.sign(pt)).build()) reads close to the RFC’s own structure, and the crate documents a worked end-to-end sign/verify example directly in lib.rs doctests. The closure-based crypto injection keeps the API decoupled from any one crypto crate, at the cost of requiring callers to already understand COSE/CBOR concepts (protected vs. unprotected headers, AAD) — the README itself flags the crate as still evolving (“API…may change without warning”), which tempers an otherwise clean, idiomatic Rust surface.