Files
suaveplan/packages/foundation/flow/cbor/src/cose/sign.md
2026-05-27 23:47:53 +01:00

2.1 KiB

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

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.