Skip to content

Harden COSE decoding: trailing bytes, crit, and integer ranges - #13

Open
zshenker wants to merge 1 commit into
alg-confusion-fixfrom
cose-hardening
Open

zshenker wants to merge 1 commit into
alg-confusion-fixfrom
cose-hardening

Conversation

@zshenker

Copy link
Copy Markdown
Collaborator

Stacked on #6 (base: alg-confusion-fix). It is based there for the verify_with_key test helpers.

Problem

Token::from_bytes accepted several inputs that RFC 9052 and RFC 8949 say to reject:

  • Trailing bytes. Data after the COSE structure, or after the encoded map inside the protected header or payload byte strings, was ignored. A token with junk appended still verified, even though the appended bytes aren't covered by the signature.
  • crit. The header parameter was ignored. RFC 9052 §3.1 requires a recipient to reject a message whose critical header parameters it doesn't understand.
  • Integer truncation. alg was converted with as i32, so an out-of-range value such as 2^32 + 5 aliased HMAC 256/256 (5). The catreplay value had the same truncation: 2^32 aliased 0 (replay permitted).

Change

  • Trailing bytes: from_bytes rejects bytes after the COSE structure, and after the map inside the protected and payload byte strings, with Error::InvalidFormat.
  • crit: a crit parameter must be a non-empty array in the protected header. Every label must be one this library processes (alg, kid); otherwise decoding fails with the new Error::UnsupportedCriticalHeader. crit in the unprotected header is rejected.
  • Integer conversion: alg and catreplay are converted with i32::try_from. An out-of-range alg is treated as unknown, and an out-of-range catreplay is an invalid claim value.

Tests

  • New inline tests at the end of src/token.rs build exact COSE_Mac0 bytes to cover:
    • trailing bytes after the structure and inside the payload
    • understood, unknown, malformed and unprotected crit
    • out-of-range alg and catreplay
  • The existing interop vector still verifies.
  • cargo fmt --check, cargo clippy --all-targets -- -D warnings and cargo test pass: 68 unit tests and 74 doctests.
  • A local merge with every other open PR has no conflicts.

🤖 Generated with Claude Code

- Reject bytes after the COSE structure, and after the encoded map
  inside the protected header and payload byte strings. Appended data
  is not covered by the signature and was silently ignored.
- Enforce the crit header parameter (RFC 9052 §3.1): it must be a
  non-empty array of labels in the protected header, and each label
  must be a parameter this library processes (alg, kid). Anything else
  fails with the new Error::UnsupportedCriticalHeader, as §3.1 requires
  for critical parameters the recipient does not understand.
- Convert alg with i32::try_from instead of `as i32`, so an out-of-range
  value like 2^32 + 5 no longer aliases HMAC 256/256 (5). Apply the same
  fix to the catreplay value, where 2^32 aliased 0 (replay permitted).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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