# `sign` ## Purpose `COSE_Sign` (RFC 9052 ยง4.1) โ€” multi-signer COSE message in CBOR tag 98. Use when a payload needs to be co-signed by multiple parties (e.g. multi- party attestation, escrowed signing). Each signer carries its own protected header (typically with its own `alg`) and produces an independent signature over the canonical `Sig_structure` for context `"Signature"`. ## Features - `encodeCoseSign` builds the 4-tuple `[bodyProtected, bodyUnprotected, payload, signatures]` with deterministic encoding and per-signer `[sigProtected, sigUnprotected, signature]` triples. - `decodeCoseSign` recovers the typed `CoseSign` view, including `protectedRaw` byte strings on every level needed to recompute the five-element `Sig_structure`. - `verifyCoseSignAny` walks the signers in order against a 1:1 candidate- key array. Returns the first index whose signature verifies, or `-1` on no match. Caller decides whether one valid signer is enough or whether all signers must verify (loop the array). - Currently dispatches to ES256 + EdDSA via `@suaveplan/crypto-curve`; unsupported algs return `false` (signer is skipped) rather than throwing so the helper can scan a heterogeneous array without aborting. ## Usage ```ts import { encodeCoseSign, decodeCoseSign, verifyCoseSignAny } from "@suaveplan/cbor/cose"; const wire = encodeCoseSign({ payload, signatures: [ { protectedHeader: { alg: "ES256" }, signature: sig1 }, { protectedHeader: { alg: "EdDSA" }, signature: sig2 }, ], }); const msg = decodeCoseSign(wire); const matchIndex = verifyCoseSignAny(msg, [keyA, keyB]); ``` ## API - `encodeCoseSign(input)` / `decodeCoseSign(bytes)`. - `verifyCoseSignAny(msg, keys, options?): number` โ€” returns matching index or `-1`. Length mismatch between `keys` and `msg.signatures` throws `UNEXPECTED_TYPE`. - `COSE_SIGN_TAG = 98`. ## Notes Sig_structure for multi-signer is 5-element: `[context, body_protected, sign_protected, external_aad, payload]` โ€” the extra `sign_protected` discriminates the signer's own protected header from the message-wide one. Both must be the SERIALISED byte strings, not the inner maps.