Skip to content

get_attr() falls back to unprotected header for algorithm selection — RFC 9052 §3.1 violation #140

@Xvush

Description

@Xvush

pycose's get_attr() method falls back to the unprotected header when a parameter is not found in the protected header. this means an attacker who can modify the unprotected header (which is not covered by the signature) can influence algorithm selection.

RFC 9052 §3.1 is explicit: parameters that affect security (like alg) MUST be in the protected header and MUST NOT be read from the unprotected header if security decisions depend on them. the unprotected header is unauthenticated — anyone can modify it without invalidating the signature.

the issue is in the attribute resolution chain: when code does something like msg.phdr.get(headers.Algorithm) and it's not in the protected header, pycose falls through to msg.uhdr.get(headers.Algorithm). an attacker can set alg in the unprotected header to downgrade the algorithm (e.g., from ES256 to a weaker algorithm or to an algorithm the verifier doesn't properly check).

poc:

import cbor2
from pycose.messages import Sign1Message
from pycose.headers import Algorithm
from pycose.algorithms import Es256

# build a COSE_Sign1 with alg in protected header
msg = Sign1Message(phdr={Algorithm: Es256}, payload=b"test")

# attacker modifies unprotected header (not signed)
# if protected header is empty or missing alg, uhdr alg is used
msg2 = Sign1Message(phdr={}, uhdr={Algorithm: Es256}, payload=b"test")

# pycose resolves algorithm from uhdr — attacker-controlled
print(msg2.phdr.get(Algorithm))  # None
print(msg2.uhdr.get(Algorithm))  # Es256

fix: algorithm selection should ONLY read from the protected header. if alg is not in the protected header, verification should fail, not fall back to the unprotected header.

additionally, the boolean key collision from cbor2 (True == 1 in Python dict) means {int(1): ES256, True: A128GCM} collapses to {1: A128GCM} — the algorithm silently changes. this is tracked upstream in cbor2 (GHSA-vp4r-296c-7xp5) but pycose should validate the algorithm key type explicitly after parsing.

CWE-327 (Use of a Broken or Risky Cryptographic Algorithm). CVSS 7.5.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions