Sitelet https://github.com/RustCrypto/formats/pull/2466
Skip to content

crmf: tag non-CHOICE fields implicitly, as RFC 4211 defines - #2466

Open
yuxi-liu-wired wants to merge 1 commit into
RustCrypto:masterfrom
yuxi-liu-wired:fix/crmf-implicit-tags
Open

yuxi-liu-wired wants to merge 1 commit into
RustCrypto:masterfrom
yuxi-liu-wired:fix/crmf-implicit-tags

Conversation

@yuxi-liu-wired

Copy link
Copy Markdown
Contributor

RFC 4211's ASN.1 module is DEFINITIONS IMPLICIT TAGS. A context tag is explicit only when the tagged type is a CHOICE (X.680 31.2.7), and crmf already does that for Name, Time, GeneralName and POPOPrivKey. Eight other tags are declared EXPLICIT although their types aren't CHOICEs:

Type Field Inner type
POPOPrivKey thisMessage [0], dhMAC [2] BIT STRING
POPOPrivKey subsequentMessage [1] INTEGER
POPOPrivKey agreeMAC [3] PKMACValue
POPOPrivKey encryptedKey [4] EnvelopedData
PKIArchiveOptions keyGenParameters [1] OCTET STRING
PKIArchiveOptions archiveRemGenPrivKey [2] BOOLEAN
EncryptedKey envelopedData [0] EnvelopedData

OpenSSL's crypto/crmf/crmf_asn.c uses ASN1_IMP for every one of these that it implements. In practice, crmf/cmpv2 can't decode a CMP ir from openssl cmp -popo 2 (key-encipherment proof of possession), whose POP is a2 03 81 01 00:

PkiMessage::from_der(ir)  // Err: unexpected ASN.1 DER tag: got CONTEXT-SPECIFIC [1] (primitive)

Encoding has the same problem: crmf writes these fields in a form OpenSSL rejects. The existing tests only round-trip crmf's own output, so they didn't notice.

The fix changes the eight tags to IMPLICIT (primitive for the BIT STRING, INTEGER, OCTET STRING and BOOLEAN alternatives).

Tests: tests/implicit_tags.rs decodes and re-encodes, byte for byte, each POPOPrivKey and PKIArchiveOptions alternative as encoded by pyasn1 from the RFC 4211 module, plus the key-encipherment POP from OpenSSL. All three tests fail on master. I also checked that OpenSSL 3.5 ir messages with POP RAVERIFIED, SIGNATURE and KEYENC (RSA and EC keys) all decode through cmpv2::message::PkiMessage and re-encode identically; two of the four fail on master. The crmf.yml matrix passed locally (powerset tests on stable and 1.85, thumbv7em powerset build, fmt, clippy), along with the cmpv2 all-features tests.

This PR was produced by AI agents (Claude) and reviewed by me before submission.

RFC 4211's ASN.1 module is DEFINITIONS IMPLICIT TAGS. A context tag is
explicit only when the tagged type is a CHOICE (X.680 31.2.7), which
crmf already does for Name, Time, GeneralName and POPOPrivKey. Eight
other tags were EXPLICIT although their types are not CHOICEs:

- POPOPrivKey: thisMessage [0] and dhMAC [2] BIT STRING,
  subsequentMessage [1] INTEGER, agreeMAC [3] PKMACValue,
  encryptedKey [4] EnvelopedData
- PKIArchiveOptions: keyGenParameters [1] OCTET STRING,
  archiveRemGenPrivKey [2] BOOLEAN
- EncryptedKey: envelopedData [0] EnvelopedData

So crmf/cmpv2 could not decode, e.g., an ir from OpenSSL's
`openssl cmp -popo 2` (key-encipherment POP `a2 03 81 01 00`: "got
CONTEXT-SPECIFIC [1] (primitive)"), and encoded these fields in a form
OpenSSL rejects. OpenSSL's crmf_asn.c uses IMPLICIT for all of them.

New tests decode and re-encode each alternative from pyasn1 encodings of
the RFC module, and OpenSSL's key-encipherment POP. OpenSSL 3.5 ir
messages with POP RAVERIFIED, SIGNATURE and KEYENC (RSA and EC) now all
decode via cmpv2 and re-encode byte for byte.

This branch has not been deployed

No deployments
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