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

x509-cert: CertificateBuilder: reject duplicate extensions - #2458

Open
yuxi-liu-wired wants to merge 1 commit into
RustCrypto:masterfrom
yuxi-liu-wired:fix/builder-reject-duplicate-extensions
Open

yuxi-liu-wired wants to merge 1 commit into
RustCrypto:masterfrom
yuxi-liu-wired:fix/builder-reject-duplicate-extensions

Conversation

@yuxi-liu-wired

Copy link
Copy Markdown
Contributor

RFC 5280 4.2 says: "A certificate MUST NOT include more than one instance of a particular extension." CertificateBuilder::finalize appends the profile's extensions to the ones added with add_extension without checking. So adding an extension that the profile also adds produces a certificate with two of them.

For example, giving a cabf::Root certificate a pathLenConstraint:

let mut builder = CertificateBuilder::new(profile::cabf::Root::new(false, subject)?, serial, validity, spki)?;
builder.add_extension(&BasicConstraints { ca: true, path_len_constraint: Some(1) })?;
let cert = builder.build::<_, DerSignature>(&signer)?; // Ok
// extensions: 2.5.29.19, 2.5.29.15, 2.5.29.35, 2.5.29.19, 2.5.29.15, 2.5.29.14
//             ^ BasicConstraints twice

Relying parties reject such certificates, and pyca/cryptography reports DuplicateExtension when reading their extensions.

This PR makes finalize return a new Error::DuplicateExtension { oid } (Error is #[non_exhaustive], so this isn't a breaking change). A follow-up could let user-supplied extensions replace the profile's defaults instead, but rejecting is the conservative fix.

Test: reject_duplicate_extension in x509-cert/tests/builder.rs. On master the build succeeds with two BasicConstraints.

This PR was produced by AI agents (Claude) while fuzzing x509-cert's builders against RFC 5280 rules and pyca/cryptography's parser.

RFC 5280 4.2: "A certificate MUST NOT include more than one instance of a
particular extension." `CertificateBuilder::finalize` appended the
profile's extensions to the ones added with `add_extension` without
checking, so adding an extension that the profile also adds produced a
certificate with two of them. For example, a `BasicConstraints` with a
`pathLenConstraint` on a `cabf::Root` certificate gives

    extensions: 2.5.29.19, 2.5.29.15, 2.5.29.35, 2.5.29.19, 2.5.29.15, 2.5.29.14

pyca/cryptography refuses to load such a certificate's extensions
(`DuplicateExtension`), and other verifiers reject it.

Return a new `Error::DuplicateExtension { oid }` from `finalize` instead
(`Error` is `#[non_exhaustive]`).

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