CBOR Object Signing and Encryption (COSE): Structures and Process
obsoletes
- rfc-8152 — CBOR Object Signing and Encryption (COSE)
updated by
- rfc-9338 — CBOR Object Signing and Encryption (COSE): Countersignatures
also
- std-96
Extracted elements (31)
COSE deliberately does not define a digest structure, unlike CMS. Different protocols need different fields in a digest structure, so each application protocol should define its own (e.g., RFC 9054). A single COSE-defined structure would be too rigid for the diversity of use cases.
COSE uses native CBOR binary (bstr) encodings rather than base64url encodings as used in JOSE/JSON. This exploits CBOR's native binary data support, reducing message size and code complexity, which is important for the constrained IoT devices COSE targets.
COSE uses the same COSE_recipient structure for both content encryption layers and key encryption layers, unlike CMS and JWE which use separate structures. This enables common processing code and supports recursive nesting for complex multi-party key distribution (Appendix B).
The protected header bucket is encoded as a bstr (CBOR byte string wrapping the encoded map) rather than an inline map. This ensures the protected bytes are transported without alteration and avoids requiring canonical map encoding by all parties for cryptographic computation.
RFC 9052 (together with RFC 9053) obsoletes RFC 8152. The original document was split: RFC 9052 covers structures and process, RFC 9053 covers algorithms. Implementations targeting RFC 8152 must also understand that the counter signature algorithm (label 7 from RFC 8152) must still be recognized for backwards compatibility with RFC 8152 senders.
If the 'alg' parameter is present in a COSE_Key, the application MUST verify it matches the algorithm for which the key is being used. If algorithms do not match, the key object MUST NOT be used for the cryptographic operation.
Labels in each header map MUST be unique; if a label appears multiple times, the message MUST be rejected as malformed. Attributes MUST be obtained from the protected bucket first; only if not found there may they be obtained from the unprotected bucket.
The 'alg' header parameter MUST be authenticated where possible, either by placing it in the protected-header-parameters bucket or as part of externally supplied data. This applies to AEAD algorithms and constructions such as COSE_Sign and COSE_Mac0.
The 'crit' header parameter MUST be placed in the protected-header-parameters bucket and its array MUST contain at least one value. If a label listed in 'crit' does not have a corresponding header parameter in the protected bucket, this is a fatal processing error.
The 'IV' and 'Partial IV' header parameters MUST NOT both be present in the same security layer. When Partial IV is used, the message IV is computed by left-padding the Partial IV with zeros to the full IV length, then XORing with the Context IV.
The 'kty' parameter MUST be present in a COSE_Key object. Implementations MUST verify that the key type is appropriate for the algorithm being processed, and the key type MUST be included as part of the trust-decision process.
The message content MUST NOT be used if AEAD decryption does not validate. Most AEAD algorithms logically return plaintext only upon successful authentication; implementations must enforce this even when the underlying cryptographic library may not.
When direct-encryption mode is used, it MUST be the only recipient mode used on the message. The COSE_Recipient 'protected' field MUST be a zero-length byte string (unless used in key computation), 'ciphertext' MUST be zero-length, and 'recipients' MUST be absent.
Common COSE header parameters are: alg (label 1, int/tstr), crit (label 2, array of labels), content type (label 3, tstr/uint), kid (label 4, bstr key identifier), IV (label 5, bstr full initialization vector), and Partial IV (label 6, bstr partial IV). These appear in both protected and unprotected buckets across all COSE message types.
COSE defines six message structures, each identified by a CBOR tag: COSE_Sign (tag 98, multi-signer), COSE_Sign1 (tag 18, single-signer), COSE_Encrypt (tag 96, enveloped), COSE_Encrypt0 (tag 16, single-recipient), COSE_Mac (tag 97, MACed with recipients), and COSE_Mac0 (tag 17, implicit-key MAC). All are CBOR arrays sharing a common protected/unprotected/content structure.
COSE_Encrypt is a four-element CBOR array [protected, unprotected, ciphertext (bstr/nil), recipients ([+COSE_recipient])]. COSE_recipient uses the same structure with an optional recursive recipients field to enable nested key distribution. CBOR tag 96 identifies COSE_Encrypt.
COSE_Encrypt0 is a three-element CBOR array [protected, unprotected, ciphertext (bstr/nil)] used when the recipient implicitly knows the CEK. It does not support recipient structures; if key identification is needed, COSE_Encrypt should be used instead. CBOR tag 16 identifies COSE_Encrypt0.
COSE_Key is a CBOR map with required field kty (label 1, identifies key family) and optional fields: kid (label 2, bstr), alg (label 3, restricts usage to one algorithm), key_ops (label 4, array of permitted operations from ten defined values), and Base IV (label 5, bstr). COSE_KeySet is a CBOR array of one or more COSE_Key objects.
COSE_Mac is a five-element array [protected, unprotected, payload (bstr/nil), tag (bstr), recipients ([+COSE_recipient])]; COSE_Mac0 is a four-element array [protected, unprotected, payload (bstr/nil), tag (bstr)] for implicit-key MAC. CBOR tags 97 and 17 identify them, respectively.
COSE_Sign is a four-element CBOR array [protected, unprotected, payload (bstr/nil), signatures ([+COSE_Signature])]. COSE_Signature is a three-element array [protected, unprotected, signature (bstr)]. The two structures cannot be converted to each other because the signature computation encodes which structure type is in use.
COSE_Sign1 is a four-element CBOR array [protected, unprotected, payload (bstr/nil), signature (bstr)] used when only one signature is applied. Header parameters for both content and signature share a single pair of buckets, unlike COSE_Sign. CBOR tag 18 identifies a tagged COSE_Sign1 structure.
This document establishes the 'COSE Header Parameters' IANA registry containing label/value-type pairs for parameters used in COSE header buckets. Labels are divided into a standard range, a private use range, and an algorithm-dependent range.
This document establishes the 'COSE Key Common Parameters' IANA registry defining map labels for COSE_Key objects (kty=1, kid=2, alg=3, key_ops=4, Base IV=5). Key-type-specific parameters are in the separate 'COSE Key Type Parameters' registry defined by RFC 9053.
This document registers eight CBOR tags in the IANA CBOR Tags registry: 98 (COSE_Sign), 18 (COSE_Sign1), 96 (COSE_Encrypt), 16 (COSE_Encrypt0), 97 (COSE_Mac), 17 (COSE_Mac0), 101 (COSE_Key), and 102 (COSE_KeySet). It also registers CoAP Content-Formats and media types for these structures.
After signature or MAC verification, the application MUST perform additional checks to ensure the key is correctly paired with the signing identity and that the signing identity is authorized before performing actions. COSE cryptographic verification alone does not constitute authorization.
All countersignature text was removed from this document because the security properties described for COSE_Sign1 countersignatures were incorrect. A separate document [COSE-COUNTERSIGN] deprecates the old countersignature algorithm and header parameters and defines corrected ones.
Extreme care must be taken when using Base IV in a COSE_Key, as many encryption algorithms lose security if the same IV is used twice. When the same key is shared among multiple senders, the application must partition the IV space between senders or provide different base points.
All COSE message structures are CBOR arrays whose first two fields are: (1) the protected header parameters encoded as a bstr (CBOR-encoded map wrapped in a byte string), and (2) the unprotected header parameters as a CBOR map. An empty protected header MUST be encoded as a zero-length bstr, not a zero-length map (h'a0').
The Enc_structure is the canonical AAD input for AEAD encryption, a CBOR array: [context ('Encrypt', 'Encrypt0', 'Enc_Recipient', 'Mac_Recipient', or 'Rec_Recipient'), protected (bstr), external_aad (bstr)]. The AAD passed to the AEAD algorithm is the CBOR encoding of this structure.
The MAC_structure is the canonical input for MAC computation, a CBOR array: [context ('MAC' for COSE_Mac or 'MAC0' for COSE_Mac0), protected (bstr), external_aad (bstr), payload (bstr)]. ToBeMaced is the CBOR encoding of this structure per Section 9.
The Sig_structure is the canonical signing input, a CBOR array: [context ('Signature' or 'Signature1'), body_protected (bstr), optional sign_protected (bstr, omitted for COSE_Sign1), external_aad (bstr), payload (bstr)]. ToBeSigned is the byte-string encoding of this structure per Section 9.