Skip to content

Follow RFC 9597 for CWT Claims in COSE headers - #1

Closed
achamayou wants to merge 1 commit into
mainfrom
achamayou-rfc9597-cwt-claims-plan
Closed

achamayou wants to merge 1 commit into
mainfrom
achamayou-rfc9597-cwt-claims-plan

Conversation

@achamayou

@achamayou achamayou commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

draft-ietf-cose-cwt-claims-in-headers-06, which this library implements, was published as RFC 9597 (Standards Track, June 2024). This PR brings the library into compliance with the final RFC.

Label reassignment (breaking change)

Label draft-06 IANA today
13 requested for CWT Claims kcwt (RFC 9528, EDHOC), value type COSE_Messages
15 – CWT Claims (RFC 9597 §2), map
  • COSEHeaders.CWT_CLAIMS changes from 13 to 15.
  • Label 13 is no longer read as CWT Claims. Interpreting it that way would be wrong now that it means kcwt. Before this change, CWT.decode() raised an uncaught AttributeError when label 13 held a kcwt-shaped (non-map) value.
  • Tokens issued with v3.3.0 or earlier that put claims under 13 are no longer cross-checked and should be re-issued with 15. The README and CHANGES document this.

Fixes for requirements already in the draft

The normative MUSTs are the same in draft-06 and RFC 9597, but the implementation only partly followed them:

RFC 9597 §2 Before After
Verify header claim values are identical to payload values Only the protected header of the innermost COSE layer was checked Protected and unprotected headers of every layer are checked. Nested Encrypt0(MAC0/Sign1(claims)) with claims in the outer header is the RFC's main use case
Parameter MUST occur only once (protected or unprotected) Not checked on decode ValueError
Value type is map AttributeError on non-map ValueError

The error message for a protected-header mismatch is unchanged. no_verify=True still skips all claim checks. A claim that appears only in the header, not in the payload, is still accepted.

Deliberately out of scope

  • Rejecting duplicate labels across header buckets for all labels in generic COSE.decode* (RFC 9052 §3). This is a broader behaviour change.
  • Named typ (16), kcwt (13) and kccs (14) support. The README mentions using typ through its integer label, as RFC 9597 recommends.
  • Header and payload claim values are compared as deterministic CBOR encodings, so 1, 1.0 and True are not identical (map key order does not matter).

Docs

  • README: RFC 9597 replaces draft-06 in both spec lists, and the "experimental" label is dropped. The section is rewritten to state the label, explain the reassignment, describe exactly what decode() checks, and add the RFC's recommendations: put the parameter in the protected header, header claims are not encrypted, don't act on headers before verification, detached payloads, and typ. It also notes that the parameter can be used with any COSE message.
  • CHANGES (Unreleased) and the CWT.decode docstring are updated.

Testing

  • New tests cover: label 15 on the wire; label 13 ignored (kcwt-shaped and legacy draft values); unprotected match and mismatch; duplicate across buckets; non-map values in both buckets; claims absent from the payload; structured values (aud list, cnf map); no_verify; nested outer/inner layers; and strict CBOR comparison (1, 1.0 and True differ; map key order does not matter).

  • Checked against the old code: 17 of the new tests fail there, and the positive cases pass.

  • Full suite: 1531 passed.

  • black, isort, flake8 and blacken-docs (versions from .pre-commit-config.yaml) pass. mypy reports the same 12 errors as before this change, none in the files touched here.

  • Headers of individual signers (COSE_Signature) and recipients are not checked; RFC 9597's identical-values rule is about the header of the CWT.

draft-ietf-cose-cwt-claims-in-headers-06 is now RFC 9597. IANA assigned
label 15 to the CWT Claims header parameter; the draft's requested label
13 went to kcwt (RFC 9528). COSEHeaders.CWT_CLAIMS is now 15 and 13 is
no longer interpreted as CWT Claims.

CWT.decode() now checks the CWT Claims header parameter in both the
protected and unprotected headers of every COSE layer of a nested CWT,
not only the protected header of the innermost layer. Header and payload
claim values must be identical CBOR data items. A parameter that is not
a map, or that occurs in both the protected and unprotected header, is
rejected with ValueError.

Update README and CHANGES accordingly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@achamayou
achamayou force-pushed the achamayou-rfc9597-cwt-claims-plan branch from 5a9d178 to 5cac2f4 Compare November 8, 2026 16:31
@achamayou

Copy link
Copy Markdown
Owner Author

Superseded by dajiaji#708.

@achamayou achamayou closed this Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant