Skip to content

Reject COSE structure tags that disagree with the algorithm on decode - #10

Open
zshenker wants to merge 2 commits into
mainfrom
cose-tag-alg-check
Open

zshenker wants to merge 2 commits into
mainfrom
cose-tag-alg-check

Conversation

@zshenker

@zshenker zshenker commented Oct 19, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

to_bytes picks the COSE structure tag from the algorithm's class: COSE_Mac0 (17) for MAC algorithms and COSE_Sign1 (18) for signature algorithms. from_bytes throws the tag away, so the encoder's class → tag rule is never checked on the way back in. A token tagged COSE_Mac0 that carries alg = ES256, or one tagged COSE_Sign1 that carries alg = HMAC, decodes and verifies without complaint. This was an open FOLLOWUPS.md item. It matters more now that asymmetric algorithms are supported, because consumers shouldn't have to parse raw bytes to check that the structure and the declared algorithm agree.

Change

  • from_bytes records any COSE_Mac0 or COSE_Sign1 tag among the (up to two) leading tags.
  • If the tag doesn't match alg.class(), decoding fails with Error::StructureTagMismatch { tag, algorithm, expected }, so callers can match on the mismatch without parsing message text.
  • These cases are accepted as before:
    • untagged tokens
    • tokens with only the CWT tag (61)
    • tokens with an unknown or missing alg (verify already rejects those)
  • The FOLLOWUPS item is removed and a CHANGELOG entry added.

This complements #6. #6 binds the algorithm to the caller's key; this PR makes the wire structure agree with the declared algorithm. It is based on main and independent of #6. A dry merge with #6, #8 and #9 shows no conflicts. The tests are in a new src/tests_cose.rs for that reason.

Tests

The new src/tests_cose.rs signs real HS256 and ES256 tokens and flips the tag byte. It covers:

  • tokens whose tag matches the algorithm are accepted
  • Mac0 + ES256 is rejected
  • Sign1 + HMAC is rejected
  • a mismatch is rejected even without the CWT tag
  • untagged and CWT-only tokens are still accepted

cargo fmt --check, cargo clippy --all-targets -- -D warnings and cargo test all pass: 60 unit tests and 73 doctests. The existing interop vector still verifies.

🤖 Generated with Claude Code

to_bytes writes COSE_Mac0 (17) for MAC algorithms and COSE_Sign1 (18)
for signature algorithms, but from_bytes discarded the tag, so a token
tagged COSE_Mac0 carrying ES256 (or COSE_Sign1 carrying HMAC) decoded
and verified. from_bytes now records any 17/18 tag among the leading
tags and rejects it with InvalidFormat if it doesn't match
alg.class(). Untagged and CWT-only (61) tokens are unchanged.

Resolves the FOLLOWUPS item.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace InvalidFormat(String) with Error::StructureTagMismatch { tag,
algorithm, expected }, so callers can distinguish a structure that
contradicts its declared algorithm from other malformed input without
matching on message text.

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