Cybersecurity research · Tokyo

ba-cybersecurity.comResearch by Nobuo Otoi

Reviewing widely used software · python-cwt

python-cwt: a COSE message with a repeated protected-header label was accepted

Status · FixedReported · 8 Nov 2026Fixed and released · 9 Nov 2026 (4.0.0)

COSE (RFC 9052) is the signature and encryption format underneath CWT, the CBOR form of the web token used in constrained and IoT settings. A COSE message carries a protected header: a CBOR map whose labels name parameters such as the algorithm. RFC 9052 requires those labels to be unique and a message that repeats one to be rejected. python-cwt accepted such a message.

The problem

python-cwt decoded the protected header with the default CBOR loader, which keeps the last value when a map key appears more than once. A protected header that gave the algorithm label 1 twice — once with a value a verifier would reject and once with one it accepts — was therefore reduced to a single value, and the message was accepted and verified. RFC 9052 §3 states that "Labels in each of the maps MUST be unique" and that, when processing a message, "if a label appears multiple times, the message MUST be rejected as malformed."

Check it yourself

The whole report is a few lines of CBOR. Build a COSE_Mac0 (tag 17) whose protected header is the map {1:4, 1:5} — label 1 twice — and MAC the message over those bytes as written:

# protected-header bytes = CBOR map {1:4, 1:5}: label 1 appears twice inner = bytes([0xa2]) + dumps(1) + dumps(4) + dumps(1) + dumps(5) assert loads(inner) == {1: 5} # default CBOR decode keeps the last value tag = key.sign(dumps(["MAC0", inner, b"", b"payload"])) wire = dumps(CBORTag(17, [inner, {}, b"payload", tag])) p, u, pl = COSE.new().decode_with_headers(wire, key) # python-cwt 3.3.0: accepted and verified. 4.0.0: raises "Duplicate map key: 1".

Two independent COSE implementations reject the identical bytes: go-cose reports "found duplicate map key" and coset reports "duplicate map key". The same loader decodes CWT claim sets, so the last-value-wins behaviour reached those too.

Full report and reproduction: issue #710

Response

The maintainer confirmed the report and fixed it the same day in pull request #718, which rejects CBOR maps with duplicate keys on decode; the fix raises the minimum cbor2 version to 6.1.0. It shipped in release 4.0.0 (9 Nov 2026), whose notes list "Reject CBOR maps with duplicate keys on decoding" as a breaking change. On 4.0.0 the message above raises Duplicate map key: 1; on 3.3.0 it is still accepted. The reported severity was Low.

Cite as

Nobuo Otoi (ba-cybersecurity.com). python-cwt: a COSE Message with a Repeated Protected-Header Label Was Accepted (report and fix). 2026. https://ba-cybersecurity.com/research/cose-duplicate-label/.

@misc{otoi2026pythoncwt,
  author       = {Otoi, Nobuo},
  title        = {python-cwt: a COSE Message with a Repeated Protected-Header Label Was Accepted (report and fix)},
  year         = {2026},
  howpublished = {\url{https://ba-cybersecurity.com/research/cose-duplicate-label/}},
  note         = {Report: https://github.com/dajiaji/python-cwt/issues/710 - Fix: https://github.com/dajiaji/python-cwt/pull/718}
}

Reported 8 Nov 2026 - Fixed and released 9 Nov 2026 (4.0.0) · This page: updated 10 Nov 2026 · Permanent address: https://ba-cybersecurity.com/research/cose-duplicate-label/