# `mac` ## Purpose `COSE_Mac` (RFC 9052 §6.1) — multi-recipient MAC message in CBOR tag 97. Each recipient gets a wrapped copy of the content-MAC key (CMK); the body itself carries one MAC computed over the payload using the CMK. This module ships encode/decode of the wire shape plus a kid-based recipient lookup. ## Features - `encodeCoseMac` builds the 5-tuple `[bodyProtected, bodyUnprotected, payload, tag, recipients]`. Per-recipient triples `[recipientProtected, recipientUnprotected, encryptedKey]` carry the wrapped-key bytes verbatim — wrap/unwrap is delegated to whatever scheme the recipient header declares (direct, KEK, ECDH-ES, etc.). - `decodeCoseMac` recovers the typed `CoseMac` view. Each recipient lands in `msg.recipients[]` with its own `protectedRaw` byte string ready for any per-recipient verification flow that needs it. - `findRecipientByKid` does a constant-time-ish byte compare over the recipient's `kid` (consulted in protected then unprotected headers). - Empty recipient arrays are rejected — RFC 9052 §6.1 requires at least one. ## Usage ```ts import { encodeCoseMac, decodeCoseMac, findRecipientByKid } from "@suaveplan/cbor/cose"; const wire = encodeCoseMac({ protectedHeader: { alg: "HS256" }, payload, tag: macTag, recipients: [ { unprotectedHeader: { kid: kidA }, encryptedKey: wrapped }, { unprotectedHeader: { kid: kidB }, encryptedKey: wrappedB }, ], }); const msg = decodeCoseMac(wire); const mine = findRecipientByKid(msg, kidA); ``` ## API - `encodeCoseMac(input)` / `decodeCoseMac(bytes)`. - `findRecipientByKid(msg, kid): CoseRecipient | undefined`. - `COSE_MAC_TAG = 97`. ## Notes The body's `tag` is computed over the canonical `MAC_structure` (RFC 9052 §6.3) using the CMK. This module does not implement the wrapping/unwrapping of the CMK to each recipient — that lives one layer up, where the recipient- specific algorithm (`A128KW`, `direct`, `ECDH-ES + A256KW`, …) is dispatched. The helper here gives a typed shape for callers to plug those wraps into.