Files
David LegrandandAlexander Medvednikov 1eb5373de0 encoding.cose: add COSE (RFC 9052) and CWT (RFC 8392) modules (#27111)
* encoding.cose: add COSE (RFC 9052) signing and MAC module

* encoding.cwt: add CBOR Web Token (RFC 8392) module

* encoding.cose: harden header validation

* encoding.cose: use the received protected bytes in Sig_structure/MAC_structure (RFC 9052)

* encoding.cose: harden key and message validation

* encoding.cose: validate recipient and critical metadata

* encoding.cose: validate key metadata invariants

* encoding.cose: validate private key consistency

* encoding.cose: validate message header metadata

* encoding.cose: preserve unsupported key constraints

* encoding.cose: enforce HMAC and claim constraints

* encoding.cose: bind external algorithms safely

* encoding.cose: enforce decoded header consistency

* encoding.cose: validate low-level message encoding

* encoding.cose: validate direct recipients before encoding

* encoding.cose: harden decoded metadata handling

* encoding.cose: disambiguate detached verification

* encoding.cose: commit message state transactionally

* encoding.cose: validate key and audience representations

* encoding.cose: normalize elliptic key encoding

* encoding.cose: bind low-level verification payloads

* encoding.cose: enforce attached payload and point widths

* encoding.cose: enforce direct recipient constraints

* encoding.cose: validate private key codec inputs

* encoding.cose: validate algorithm and date ranges

* encoding.cose: validate Ed25519 key consistency

* encoding.cose: reformat headers.v with the current vfmt

* encoding.cose: load EC public keys from raw coordinates

* encoding.cose: reserve modelled header labels in extra entries

* encoding.cwt: reserve standard claim labels in extra claims

* encoding.cose: correct scope statements in the module docs

* encoding.cose: validate text content types (RFC 9052 3.1)

* encoding.cose: reformat the remaining files with the current vfmt

* encoding.cose: use a zero-length structure bucket for an empty protected header

* encoding.cose: run the bundled cose-wg vectors

* encoding.cwt: run the bundled RFC 8392 vectors

---------

Co-authored-by: Alexander Medvednikov <alexander@medvednikov.com>
2026-09-29 17:35:16 +03:00

84 lines
2.6 KiB
V

// COSE / CWT example program: produces a signed payload, a MACed
// payload and a signed CBOR Web Token, then verifies each.
//
// Run with: v run examples/cose_cwt.v
import encoding.cose
import encoding.cwt
import encoding.hex
fn main() {
demo_sign1_eddsa()!
demo_mac0_hmac_256()!
demo_signed_cwt_es256()!
}
// COSE_Sign1 with EdDSA over Ed25519. EdDSA signatures are
// deterministic, so signing the same payload twice produces identical
// bytes — useful for caching and reproducible builds.
fn demo_sign1_eddsa() ! {
d := hex.decode('9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60')!
x := hex.decode('d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a')!
priv := cose.Key.okp_private(.ed25519, x, d)
pub_key := cose.Key.okp_public(.ed25519, x)
signed := cose.sign1('hello, COSE'.bytes(), priv,
protected: cose.Headers{
algorithm: .eddsa
}
unprotected: cose.Headers{
kid: 'demo-key'.bytes()
}
)!
println('Sign1 (${signed.len} bytes): ${hex.encode(signed)}')
payload := cose.verify1(signed, pub_key)!
assert payload == 'hello, COSE'.bytes()
println(' verified payload: ${payload.bytestr()}')
}
// COSE_Mac0 with HMAC-SHA256. Symmetric, useful when both ends share
// a secret out of band (e.g. a session key).
fn demo_mac0_hmac_256() ! {
key := cose.Key.symmetric([u8(0xA5)].repeat(32))
maced := cose.mac0('mac me'.bytes(), key,
protected: cose.Headers{
algorithm: .hmac_256_256
}
)!
println('Mac0 (${maced.len} bytes): ${hex.encode(maced)}')
got := cose.verify_mac0(maced, key)!
assert got == 'mac me'.bytes()
println(' verified payload: ${got.bytestr()}')
}
// Signed CBOR Web Token (RFC 8392) wrapped in a COSE_Sign1, with
// ECDSA P-256 + SHA-256. Key material taken from RFC 8392 Appendix A.
fn demo_signed_cwt_es256() ! {
x := hex.decode('143329cce7868e416927599cf65a34f3ce2ffda55a7eca69ed8919a394d42f0f')!
y := hex.decode('60f7f1a780d8a783bfb7a2dd6b2796e8128dbbcef9d3d168db9529971a36e7b9')!
d := hex.decode('6c1382765aec5358f117733d281c1c7bdc39884d04a45a1e6c67c858bc206c19')!
priv := cose.Key.ec2_private(.p_256, x, y, d)
pub_key := cose.Key.ec2_public(.p_256, x, y)
claims := cwt.ClaimsSet{
iss: 'coap://as.example.com'
sub: 'erikw'
aud: ['coap://light.example.com']
exp: 1444064944
iat: 1443944944
}
token := cwt.sign(claims, priv,
protected: cose.Headers{
algorithm: .es256
}
)!
println('CWT (${token.len} bytes): ${hex.encode(token)}')
verified := cwt.verify(token, pub_key)!
iss := verified.iss or { '' }
sub := verified.sub or { '' }
exp := verified.exp or { 0 }
println(' iss=${iss} sub=${sub} exp=${exp}')
}