Skip to content

encoding.cose #

encoding.cose

CBOR Object Signing and Encryption — pure-V implementation of the signing and MAC subset of [RFC 9052][rfc9052] and [RFC 9053][rfc9053].

[rfc9052]: https://www.rfc-editor.org/rfc/rfc9052 [rfc9053]: https://www.rfc-editor.org/rfc/rfc9053

What it covers

Message type Tag Status
COSE_Sign1 18 ✅
COSE_Sign 98 ✅
COSE_Mac0 17 ✅
COSE_Mac 97 ✅ (direct mode, single recipient)
COSE_Encrypt0 16 ❌ (not implemented)
COSE_Encrypt 96 ❌ (idem)
Algorithm IANA Status
ES256 -7 ✅
ES384 -35 ✅
ES512 -36 ✅ (P-521 + SHA-512)
EdDSA -8 ✅ Ed25519
HMAC 256/64 4 ✅
HMAC 256/256 5 ✅
HMAC 384/384 6 ✅
HMAC 512/512 7 ✅

Quick examples

Sign and verify a payload (COSE_Sign1, ES256)

The most common pattern — single-signer ECDSA P-256 over SHA-256.

import encoding.cose
import encoding.hex

fn main() {
    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)

    signed := cose.sign1('hello'.bytes(), priv,
        protected: cose.Headers{
            algorithm: .es256
        }
    )!
    payload := cose.verify1(signed, pub_key)!
    assert payload == 'hello'.bytes()
}

Sign and verify with EdDSA (Ed25519)

EdDSA is deterministic — the same payload + key always produces the same signature, useful for caching and reproducible builds.

import encoding.cose
import encoding.hex

fn main() {
    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'.bytes(), priv,
        protected: cose.Headers{
            algorithm: .eddsa
        }
    )!
    payload := cose.verify1(signed, pub_key)!
    assert payload == 'hello'.bytes()
}

MAC a payload (COSE_Mac0, HMAC-SHA256)

import encoding.cose

fn main() {
    key := cose.Key.symmetric([u8(0x42)].repeat(32))
    tag := cose.mac0('payload'.bytes(), key,
        protected: cose.Headers{
            algorithm: .hmac_256_256
        }
    )!
    got := cose.verify_mac0(tag, key)!
    assert got == 'payload'.bytes()
}

Multi-signer (COSE_Sign)

a := cose.Signer{
    key:       alice_key
    protected: cose.Headers{ algorithm: .eddsa }
}
b := cose.Signer{
    key:       bob_key
    protected: cose.Headers{ algorithm: .es256 }
}
msg := cose.sign('payload'.bytes(), [a, b])!

Cookbook

Verifying a webhook payload

import encoding.cose

fn handle_webhook(body []u8, pub_key cose.Key) ! {
    payload := cose.verify1(body, pub_key) or {
        match err {
            cose.VerificationFailed { return error('webhook signature invalid') }
            cose.MalformedMessage   { return error('webhook bytes malformed') }
            else                    { return err }
        }
    }
    process(payload)!
}

Setting kid (key identifier) so the verifier can pick the right key

The kid lives in the unprotected header — it's just routing info, not security-critical, and can change without breaking the signature.

signed := cose.sign1(body, priv,
    protected:   cose.Headers{ algorithm: .es256 }
    unprotected: cose.Headers{ kid: 'k-2026-01'.bytes() }
)!

On the verify side, decode first, look up the key by kid, then verify:

msg := cose.Sign1Message.decode(signed)!
key := key_lookup(msg.unprotected.kid or { return error('no kid') })!
msg.verify(key, msg.payload or { []u8{} }, []u8{})!

Detached payload (sign without embedding the bytes)

Useful when the payload is large or already transmitted out of band:

sig_only := cose.sign1([]u8{}, priv,
    protected:        cose.Headers{ algorithm: .es256 }
    detached_payload: large_blob
)!
// Later, on the receiving side:
cose.verify1(sig_only, pub_key, detached_payload: large_blob)!

API surface

  • cose.Algorithm — typed enum mapped to the IANA registry.
  • cose.Key — COSE_Key with constructors Key.ec2_*, Key.okp_*, Key.symmetric. CBOR encode/decode via key.encode() / Key.decode().
  • cose.Headers — typed protected/unprotected header bag. Well-known parameters as fields, others via extra_int_labels / extra_text_labels; mixed integer/text crit entries are retained in critical / critical_text. Serialised in canonical CBOR order, except for the protected bucket of a decoded message: RFC 9052 §4.4 and §6.3 build the Sig_structure / MAC_structure from the protected bytes as they were received, so those are kept verbatim and re-emitted as-is by encode(). Messages whose protected header uses a legal but non-canonical encoding therefore verifies. The one exception is an empty protected bucket: §4.4 and §6.3 require a zero-length bstr in the structures when there are no protected attributes, so a bucket spelled h'a0' — which §3 requires recipients to accept — contributes nothing to the signature while still being re-emitted as it arrived. Mutating protected on a decoded message makes encoding and verification fail until it is signed again. Labels 2-6 stay reserved for their typed fields and are rejected in extra_int_labels; label 1 is the one exception, accepted there so that an alg this module does not model — an unknown integer identifier, or a text one — survives a decode/encode round-trip. A numeric content type must fit the CoAP registry range; a text one must follow the type-name/subtype-name syntax of RFC 9052 §3.1, with no leading or trailing whitespace. Media type parameters (; charset=utf-8) are carried through unvalidated, since implementations in the wild do send them.
  • cose.sign1 / cose.verify1 — single-signer convenience helpers.
  • cose.sign / cose.SignMessage — multi-signer.
  • cose.mac0 / cose.verify_mac0 — single-recipient MAC.
  • cose.mac / cose.verify_mac — COSE_Mac, direct mode with exactly one recipient.

Error variants: VerificationFailed, MalformedMessage, AlgorithmMismatch, UnsupportedAlgorithm. Use if err is X to discriminate.

See also

Constants #

const tag_sign = u64(98) // COSE_Sign  — RFC 9052 §4

CBOR tag numbers identifying COSE message types. A COSE message MAY be emitted untagged (the recipient already knows the structure) or tagged with one of these values to make the message self-describing.

const tag_sign1 = u64(18) // COSE_Sign1 — RFC 9052 §4.2

COSE_Sign — RFC 9052 §4

const tag_mac = u64(97) // COSE_Mac   — RFC 9052 §6

COSE_Sign1 — RFC 9052 §4.2

const tag_mac0 = u64(17) // COSE_Mac0  — RFC 9052 §6.2

COSE_Mac — RFC 9052 §6

fn algorithm_from_int #

fn algorithm_from_int(code i64) !Algorithm

algorithm_from_int converts an IANA algorithm code to a typed Algorithm. It returns an error if the code is not supported by this module.

fn mac #

fn mac(payload []u8, key Key, opts MacOptions) ![]u8

mac produces a tagged COSE_Mac message. The MAC tag is computed once, over the body — recipients are descriptive routing only in "direct" mode. The body algorithm in opts.protected.algorithm drives the MAC computation and the symmetric key is the shared secret named by the recipients' kid.

fn mac0 #

fn mac0(payload []u8, key Key, opts Mac0Options) ![]u8

mac0 produces a tagged COSE_Mac0 message in one call.

fn parse_headers_map #

fn parse_headers_map(data []u8) !Headers

parse_headers_map decodes a CBOR map (already extracted from the surrounding message) into a Headers value. Unknown labels go into extra_int_labels / extra_text_labels instead of being dropped, so round-trips preserve the original parameters.

fn parse_protected #

fn parse_protected(data []u8) !Headers

parse_protected decodes a protected header bstr (the unwrapped bytes, not the bstr itself). An empty buffer maps to an empty Headers.

fn sign #

fn sign(payload []u8, signers []Signer, opts SignOptions) ![]u8

sign produces a tagged COSE_Sign message in one call. Each Signer in signers adds one entry to the signatures array; their per-signer protected headers must include the algorithm to use.

fn sign1 #

fn sign1(payload []u8, key Key, opts Sign1Options) ![]u8

sign1 produces a tagged COSE_Sign1 message in one call. The algorithm in opts.protected.algorithm selects the signing routine and is integrity-protected by the signature.

fn verify1 #

fn verify1(message []u8, key Key, opts Verify1Options) ![]u8

verify1 parses a (tagged or untagged) COSE_Sign1, verifies the signature against key, and returns the payload. For detached payloads, the caller passes the bytes via opts.detached_payload.

fn verify_mac #

fn verify_mac(message []u8, key Key, opts VerifyMacOptions) ![]u8

verify_mac parses a COSE_Mac, recomputes the MAC tag with key and checks it. Returns the payload bytes. An algorithm in the unprotected body bucket is accepted only when bound by key.alg.

fn verify_mac0 #

fn verify_mac0(message []u8, key Key, opts VerifyMac0Options) ![]u8

verify_mac0 parses a (tagged or untagged) COSE_Mac0, verifies the tag against key, and returns the payload.

fn Key.decode #

fn Key.decode(data []u8) !Key

Key.decode parses a CBOR-encoded COSE_Key.

fn Key.ec2_private #

fn Key.ec2_private(crv Curve, x []u8, y []u8, d []u8) Key

Key.ec2_private builds an EC2 private key from raw coordinates and scalar. x and y are the public point components (big-endian, no leading 0x00 padding required), d is the private scalar.

fn Key.ec2_public #

fn Key.ec2_public(crv Curve, x []u8, y []u8) Key

Key.ec2_public builds an EC2 public key (no private scalar).

fn Key.okp_private #

fn Key.okp_private(crv Curve, x []u8, d []u8) Key

Key.okp_private builds an OKP private key. For Ed25519, x is the 32-byte public key and d is the 32-byte private seed.

fn Key.okp_public #

fn Key.okp_public(crv Curve, x []u8) Key

Key.okp_public builds an OKP public key.

fn Key.symmetric #

fn Key.symmetric(k []u8) Key

Key.symmetric builds a Symmetric key from raw key material.

fn Mac0Message.decode #

fn Mac0Message.decode(data []u8) !Mac0Message

Mac0Message.decode parses a CBOR-encoded COSE_Mac0.

fn MacMessage.decode #

fn MacMessage.decode(data []u8) !MacMessage

MacMessage.decode parses a CBOR-encoded COSE_Mac.

fn Sign1Message.decode #

fn Sign1Message.decode(data []u8) !Sign1Message

Sign1Message.decode parses a CBOR-encoded COSE_Sign1. Both the tagged (tag 18) and untagged forms are accepted.

fn SignMessage.decode #

fn SignMessage.decode(data []u8) !SignMessage

SignMessage.decode parses a CBOR-encoded COSE_Sign.

enum Algorithm #

enum Algorithm {
	// Signature algorithms (RFC 9053 §2)
	es256 = -7 // ECDSA w/ SHA-256, curve P-256
	es384 = -35 // ECDSA w/ SHA-384, curve P-384
	es512 = -36 // ECDSA w/ SHA-512, curve P-521
	eddsa = -8 // EdDSA (Ed25519 in this module)

	// MAC algorithms (RFC 9053 §3)
	hmac_256_64  = 4 // HMAC w/ SHA-256, truncated to 64 bits
	hmac_256_256 = 5 // HMAC w/ SHA-256
	hmac_384_384 = 6 // HMAC w/ SHA-384
	hmac_512_512 = 7 // HMAC w/ SHA-512
}

Algorithm is a COSE algorithm identifier as registered in the IANA "COSE Algorithms" registry. Values match the integer codes defined by RFC 9053.

fn (Algorithm) is_signature #

fn (a Algorithm) is_signature() bool

is_signature reports whether the algorithm is a signature algorithm (used with COSE_Sign and COSE_Sign1).

fn (Algorithm) is_mac #

fn (a Algorithm) is_mac() bool

is_mac reports whether the algorithm is a MAC algorithm (used with COSE_Mac and COSE_Mac0).

fn (Algorithm) name #

fn (a Algorithm) name() string

name returns the IANA registered name of the algorithm (e.g. "ES256"). Useful for error messages and logging; the wire format always uses the integer code.

enum Curve #

enum Curve {
	p_256   = 1 // EC2, ES256
	p_384   = 2 // EC2, ES384
	p_521   = 3 // EC2, ES512 (note: 521-bit, not 512)
	ed25519 = 6 // OKP, EdDSA
}

Curve identifies an elliptic curve used by EC2 or OKP keys (label -1 of the type-specific parameters). Only the curves actually used by this module's algorithms are listed; others can still be parsed but are reported as unsupported when a key is converted to a signer/verifier.

enum KeyOp #

enum KeyOp {
	sign        = 1
	verify      = 2
	encrypt     = 3
	decrypt     = 4
	wrap_key    = 5
	unwrap_key  = 6
	derive_key  = 7
	derive_bits = 8
	mac_create  = 9
	mac_verify  = 10
}

KeyOp restricts the operations a key may be used for (label 4). Values match the IANA "COSE Key Operation Values" registry.

enum KeyType #

enum KeyType {
	okp       = 1 // Octet Key Pair (Ed25519, X25519…)
	ec2       = 2 // Elliptic Curve, two-coordinate
	rsa       = 3 // RSA — not yet supported by this module
	symmetric = 4
}

KeyType identifies the cryptographic family of a COSE_Key (label 1). Values match the IANA "COSE Key Types" registry.

struct AlgorithmMismatch #

struct AlgorithmMismatch {
	Error
pub:
	expected Algorithm // algorithm declared by the key
	got      Algorithm // algorithm requested for the operation
}

AlgorithmMismatch is returned when a key constrains itself to one algorithm via its alg parameter and the caller asks the module to use a different one. This catches the common mistake of passing e.g. an ES256-only key to an EdDSA signing call.

fn (AlgorithmMismatch) msg #

fn (e &AlgorithmMismatch) msg() string

msg formats an AlgorithmMismatch for IError.msg().

struct HeaderEntry #

struct HeaderEntry {
pub:
	label i64
	value cbor.Value
}

HeaderEntry is one (int label, value) pair. The value is held as a cbor.Value so any CBOR datum can be carried.

struct Headers #

struct Headers {
pub mut:
	// algorithm — label 1.
	algorithm ?Algorithm
	// critical — label 2. Lists labels that MUST appear in the protected
	// header and that the recipient MUST understand. Integer and text
	// labels are retained separately for convenient typed access.
	critical      []i64
	critical_text []string
	// content_type — label 3. RFC 9052 allows either a uint (CoAP
	// content-format) or a tstr (IANA media type). Set at most one of
	// these two fields; if both are set, the int form wins on encode.
	content_type_int  ?u64
	content_type_text ?string
	// kid — label 4. Application-level key identifier. Octet string.
	kid ?[]u8
	// iv — label 5. Full initialization vector for AEAD/MAC-with-IV
	// algorithms. Modelled here so messages produced by other COSE
	// implementations round-trip cleanly even though the current set of
	// supported algorithms (HMAC variants) does not consume it.
	iv ?[]u8
	// partial_iv — label 6. Partial IV; XOR'd with the context IV to
	// derive the effective IV.
	partial_iv ?[]u8
	// extra_int_labels carries integer-labelled parameters not covered
	// above. Order is preserved on encode so callers can produce stable
	// output, though COSE itself does not require any particular order
	// in *un*protected headers. Protected headers are always re-sorted
	// canonically on encode regardless.
	extra_int_labels []HeaderEntry
	// extra_text_labels carries text-labelled parameters (private use).
	extra_text_labels []TextHeaderEntry
}

Headers carries the header parameters of a COSE message. Each COSE message has two header buckets: the protected bucket (integrity- covered) and the unprotected bucket (informational only). Both use this type — the bucket they live in is decided by the message struct, not by Headers itself.

All well-known parameters are optional. To omit a parameter from the encoded output, leave its field as none (or empty for the slice fields). Use extra_int_labels / extra_text_labels for parameters not modelled here.

fn (Headers) is_empty #

fn (h Headers) is_empty() bool

is_empty reports whether the Headers contains no parameters at all. An empty protected Headers serialises to a zero-length bstr (0x40) per RFC 9052 §3.

fn (Headers) to_value #

fn (h Headers) to_value() cbor.Value

to_value returns the Headers as a cbor.Value (always a Map), suitable for embedding in another CBOR structure via Packer.pack_value. Pair order is the same as on the wire after canonical sorting (the Packer sorts when canonical = true).

fn (Headers) encode_map #

fn (h Headers) encode_map() ![]u8

encode_map returns the canonical CBOR encoding of the Headers as a CBOR map (definite length, sorted keys per RFC 8949 §4.2.1). This is the form used inside both the protected bstr wrapper and the unprotected slot.

fn (Headers) encode_protected #

fn (h Headers) encode_protected() ![]u8

encode_protected returns the bstr-wrapped canonical CBOR encoding of the protected headers, as used in the wire message (a CBOR byte string containing either an empty buffer or the CBOR map). RFC 9052 §3: "the empty map is encoded as a zero-length string rather than as a h'A0'".

struct Key #

struct Key {
pub mut:
	kty     KeyType
	kid     ?[]u8
	alg     ?Algorithm
	key_ops []KeyOp
	base_iv ?[]u8

	// EC2 / OKP:
	crv ?Curve
	x   ?[]u8 // EC2 x-coordinate, or OKP public key
	y   ?[]u8 // EC2 y-coordinate
	d   ?[]u8 // private scalar (optional)

	// Symmetric:
	k ?[]u8

mut:
	// raw_algorithm preserves an unsupported label-3 value read from a
	// COSE_Key.  It prevents a decoded constrained key from silently being
	// treated as unconstrained while still allowing it to round-trip.
	raw_algorithm      ?i64
	raw_algorithm_text ?string
	// raw_curve preserves valid but unsupported integer/text curve identifiers.
	raw_curve      ?i64
	raw_curve_text ?string
	// raw_key_ops preserves valid text and future/private integer operations.
	raw_key_ops []cbor.Value
}

Key is the V representation of a COSE_Key. Fields applicable to the kty are populated; others stay none. Use the typed constructors (Key.ec2_*, Key.okp_*, Key.symmetric) rather than building instances by hand — they enforce the invariants of each key type.

fn (Key) encode #

fn (k Key) encode() ![]u8

encode returns the canonical CBOR encoding of the COSE_Key.

struct Mac0Message #

struct Mac0Message {
pub mut:
	protected   Headers
	unprotected Headers
	// payload — `none` means a detached payload (the actual payload is
	// transmitted out of band).
	payload ?[]u8
	tag     []u8
mut:
	// raw_protected holds the protected bstr exactly as it was read by
	// `decode`; see `protected_bytes_or`.
	raw_protected ?[]u8
}

Mac0Message is the V representation of a COSE_Mac0 message. The fields map 1:1 to the CBOR array slots defined by RFC 9052 §6.2.

fn (Mac0Message) compute #

fn (mut m Mac0Message) compute(key Key, payload []u8, external_aad []u8) !

compute computes the MAC tag and stores it in tag.

fn (Mac0Message) verify #

fn (m Mac0Message) verify(key Key, payload []u8, external_aad []u8) !

verify recomputes the MAC tag and checks it against the stored one.

fn (Mac0Message) encode #

fn (m Mac0Message) encode(tagged bool) ![]u8

encode serialises the message. When tagged is true the output is wrapped in CBOR tag 17 (RFC 9052 §2).

For a message that came from decode, the protected bucket is written back exactly as it was received rather than re-serialised, so the bytes under the MAC tag survive a decode/encode cycle. Every other part of the message is re-encoded canonically. Mutating protected on a decoded message is rejected until compute() replaces the stale bytes.

struct Mac0Options #

@[params]
struct Mac0Options {
pub:
	protected        Headers
	unprotected      Headers
	external_aad     []u8
	detached_payload ?[]u8
	untagged         bool
}

Mac0Options bundles the parameters that drive cose.mac0.

struct MacMessage #

struct MacMessage {
pub mut:
	protected   Headers
	unprotected Headers
	payload     ?[]u8
	tag         []u8
	recipients  []Recipient
mut:
	// raw_protected holds the body protected bstr exactly as it was read
	// by `decode`; see `protected_bytes_or`.
	raw_protected ?[]u8
}

MacMessage is the V representation of a COSE_Mac message.

fn (MacMessage) encode #

fn (m MacMessage) encode(tagged bool) ![]u8

encode serialises the MacMessage. When tagged is true the output is wrapped in CBOR tag 97.

For a message that came from decode, the protected bucket is written back exactly as it was received rather than re-serialised, so the bytes under the MAC tag survive a decode/encode cycle. Every other part of the message is re-encoded canonically. Mutating a decoded protected view is rejected until the message is produced again by mac().

struct MacOptions #

@[params]
struct MacOptions {
pub:
	protected        Headers
	unprotected      Headers
	external_aad     []u8
	detached_payload ?[]u8
	untagged         bool
	// recipients: exactly one entry, since only the direct mode is
	// supported. It SHOULD set its unprotected `kid` so that the
	// receiver can pick the right shared key. The `alg = direct (-6)`
	// parameter is auto-added if absent.
	recipients []Recipient
}

MacOptions bundles inputs to cose.mac.

struct MalformedMessage #

struct MalformedMessage {
	Error
pub:
	reason string
}

MalformedMessage indicates the input bytes do not form a valid COSE message of the expected type. The reason describes which check failed; callers should treat this as a permanent error.

fn (MalformedMessage) msg #

fn (e &MalformedMessage) msg() string

msg formats a MalformedMessage for IError.msg().

struct Recipient #

struct Recipient {
pub mut:
	protected     Headers
	unprotected   Headers
	encrypted_key []u8
mut:
	// raw_protected holds this recipient's protected bstr exactly as it
	// was read by `decode`; see `protected_bytes_or`.
	raw_protected ?[]u8
}

Recipient is one entry of the recipients array of a COSE_Mac message. In "direct" mode the recipient carries only routing info (typically kid plus alg = direct in the unprotected header) and an empty encrypted_key.

struct Sign1Message #

struct Sign1Message {
pub mut:
	protected   Headers
	unprotected Headers
	// payload — `none` means a detached payload (the actual payload is
	// transmitted out of band). The signature is still computed over the
	// real payload bytes; supply them via `Sign1Options.detached_payload`
	// when signing or via `Verify1Options.detached_payload` when
	// verifying.
	payload   ?[]u8
	signature []u8
mut:
	// raw_protected holds the protected bstr exactly as it was read by
	// `decode`. It is module-private so callers cannot make it disagree
	// with `protected`, which is simply its parsed view. See
	// `protected_bytes_or` for why the original bytes matter.
	raw_protected ?[]u8
}

Sign1Message is the V representation of a COSE_Sign1 message. The fields map 1:1 to the CBOR array slots defined by RFC 9052 §4.2.

Use the cose.sign1(...) shorthand for the common case (sign + encode in one step) and the type's methods (encode, sign, verify) when finer control over the message is needed (e.g. setting custom headers, doing a detached payload, or signing with an external AAD).

fn (Sign1Message) sign #

fn (mut m Sign1Message) sign(key Key, payload []u8, external_aad []u8) !

sign computes the signature over the Sig_structure built from the message's protected headers and the supplied payload. The result is stored in signature. The message itself isn't mutated outside of the signature; the caller is expected to have set the algorithm in protected before calling.

fn (Sign1Message) verify #

fn (m Sign1Message) verify(key Key, payload []u8, external_aad []u8) !

verify recomputes the Sig_structure from the message's protected headers and the supplied payload, then checks the signature. An algorithm in the unprotected bucket is accepted only when the key's alg constraint binds it to the same value.

fn (Sign1Message) encode #

fn (m Sign1Message) encode(tagged bool) ![]u8

encode serialises the message. When tagged is true the output is wrapped in CBOR tag 18 (RFC 9052 §2).

For a message that came from decode, the protected bucket is written back exactly as it was received rather than re-serialised, so the bytes under the signature survive a decode/encode cycle. Every other part of the message is re-encoded canonically. Mutating protected on a decoded message is rejected until sign() replaces the stale bytes.

struct Sign1Options #

@[params]
struct Sign1Options {
pub:
	protected   Headers
	unprotected Headers
	// external_aad is data that is included in the signature
	// computation but not transmitted in the message. Both signer and
	// verifier must supply the same value.
	external_aad []u8
	// detached_payload, if set, overrides `payload` for the purpose of
	// the signature input. When detached, the encoded message will
	// contain a CBOR `nil` in the payload slot rather than the bytes.
	detached_payload ?[]u8
	// untagged emits the message without the surrounding tag 18 wrapper.
	untagged bool
}

Sign1Options bundles the parameters that drive cose.sign1. Only the algorithm in protected.algorithm is mandatory; everything else has safe defaults.

struct SignMessage #

struct SignMessage {
pub mut:
	protected   Headers
	unprotected Headers
	payload     ?[]u8
	signatures  []Signature
mut:
	// raw_protected holds the body protected bstr exactly as it was read
	// by `decode`; see `protected_bytes_or`.
	raw_protected ?[]u8
}

SignMessage is the V representation of a COSE_Sign message.

fn (SignMessage) verify #

fn (m SignMessage) verify(signer_index int, key Key, opts VerifySignOptions) !

verify checks the signature at signer_index of the message against key. By default the payload is taken from m.payload; pass opts.detached_payload for the detached case. An algorithm in the unprotected signer bucket is accepted only when bound by key.alg.

fn (SignMessage) encode #

fn (m SignMessage) encode(tagged bool) ![]u8

encode serialises the SignMessage. When tagged is true the output is wrapped in CBOR tag 98.

For a message that came from decode, the protected bucket is written back exactly as it was received rather than re-serialised, so the bytes under the signature survive a decode/encode cycle. Every other part of the message is re-encoded canonically. Mutating a decoded protected view is rejected until the message is produced again by sign().

struct SignOptions #

@[params]
struct SignOptions {
pub:
	protected        Headers
	unprotected      Headers
	external_aad     []u8
	detached_payload ?[]u8
	untagged         bool
}

SignOptions bundles inputs to cose.sign.

struct Signature #

struct Signature {
pub mut:
	protected   Headers
	unprotected Headers
	signature   []u8
mut:
	// raw_protected holds this signer's protected bstr exactly as it was
	// read by `decode`; see `protected_bytes_or`.
	raw_protected ?[]u8
}

Signature is one entry of the signatures array of a COSE_Sign message. The algorithm belongs in the per-signer protected header; verification also accepts it from the unprotected header, but only when the verifying key carries a matching alg constraint.

struct Signer #

struct Signer {
pub:
	key         Key
	protected   Headers
	unprotected Headers
}

Signer bundles a signing key with the per-signer headers that go into the COSE_Sign message. Use a separate Signer per identity when producing a multi-signer message.

struct TextHeaderEntry #

struct TextHeaderEntry {
pub:
	label string
	value cbor.Value
}

TextHeaderEntry is one (text label, value) pair.

struct UnsupportedAlgorithm #

struct UnsupportedAlgorithm {
	Error
pub:
	algorithm Algorithm
	context   string // e.g. "signing", "MAC"
}

UnsupportedAlgorithm is returned when an operation is attempted with an algorithm that the called function does not handle (e.g. a MAC algorithm passed to a signing routine).

fn (UnsupportedAlgorithm) msg #

fn (e &UnsupportedAlgorithm) msg() string

msg formats an UnsupportedAlgorithm for IError.msg().

struct VerificationFailed #

struct VerificationFailed {
	Error
pub:
	// algorithm is the algorithm that was attempted, if known.
	algorithm ?Algorithm
}

VerificationFailed is returned by verify routines when the signature or MAC tag does not match. Distinct from MalformedMessage so callers can tell a tampered/wrong-key message apart from a structurally invalid one.

fn (VerificationFailed) msg #

fn (e &VerificationFailed) msg() string

msg formats a VerificationFailed for IError.msg().

struct Verify1Options #

@[params]
struct Verify1Options {
pub:
	external_aad     []u8
	detached_payload ?[]u8
}

Verify1Options bundles inputs to cose.verify1. Defaults are the usual case (tagged message, attached payload, no external AAD).

struct VerifyMac0Options #

@[params]
struct VerifyMac0Options {
pub:
	external_aad     []u8
	detached_payload ?[]u8
}

VerifyMac0Options bundles inputs to cose.verify_mac0.

struct VerifyMacOptions #

@[params]
struct VerifyMacOptions {
pub:
	external_aad     []u8
	detached_payload ?[]u8
}

VerifyMacOptions bundles inputs to cose.verify_mac.

struct VerifySignOptions #

@[params]
struct VerifySignOptions {
pub:
	external_aad     []u8
	detached_payload ?[]u8
}

VerifySignOptions bundles inputs to per-signer verification.