Skip to content

crypto: throw on X509Certificate operation errors - #67408

Open
panva wants to merge 2 commits into
nodejs:mainfrom
panva:x509-major
Open

panva wants to merge 2 commits into
nodejs:mainfrom
panva:x509-major

Conversation

@panva

@panva panva commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

X509Certificate can report failed operations as missing fields or negative checks. Invalid validity times return "Bad time value" or Invalid Date, making relational date comparisons return false.

Throw when required fields cannot be converted, certificate encoding or fingerprint calculation fails, or a present AIA or EKU extension cannot be decoded. Apply this to getters and explicit toLegacyObject() calls. Report failed or unsupported private-key comparisons as exceptions.

For verify(), return true for 1 and false for 0. Also return false for a wrong key type or invalid unused bits in the signature BIT STRING. Throw for other negative results.

Use the existing conversion behavior for legacy TLS certificate objects, which are also constructed internally during handshakes. Formatting unrelated metadata must not cause an uncaught exception or reject an otherwise valid connection.

Fixes: #67285

@panva panva added the semver-major PRs that contain breaking changes and should be released in the next major version. label Nov 11, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/security-wg

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. labels Nov 11, 2026
@panva panva added the blocked PRs that are blocked by other issues or PRs. label Nov 11, 2026
@panva

This comment was marked as resolved.

@mcollina mcollina left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@codecov

codecov Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.67241% with 75 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.70%. Comparing base (c905bed) to head (7dee17d).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_x509.cc 64.59% 37 Missing and 37 partials ⚠️
lib/internal/crypto/x509.js 95.65% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #67408      +/-   ##
==========================================
- Coverage   90.71%   90.70%   -0.01%     
==========================================
  Files         802      802              
  Lines      282109   282211     +102     
  Branches    54357    54385      +28     
==========================================
+ Hits       255922   255987      +65     
- Misses      16311    16341      +30     
- Partials     9876     9883       +7     
Files with missing lines Coverage Δ
lib/internal/crypto/x509.js 96.00% <95.65%> (-0.13%) ⬇️
src/crypto/crypto_x509.cc 71.25% <64.59%> (-0.05%) ⬇️

... and 26 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@aduh95

This comment was marked as resolved.

@panva

This comment was marked as resolved.

@panva panva removed the blocked PRs that are blocked by other issues or PRs. label Nov 13, 2026
@panva panva added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Nov 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

@pimterry pimterry left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this has some problems. These methods are called during normal TLS, and this can add remotely triggerable crashes in normal handshake flows, if somebody uses a broken cert. That's because these are called within X509ToObject, which is called via getPeerCertificate, and normal TLS calls that in two common cases:

  • Clients call getPeerCertificate in onConnectSecure after they connect, for every successful fresh connection, but doesn't catch errors. Bad server certs throw errors only catchable via uncaughtException, not normal TLS error handling.
  • Servers doing mTLS call socket.getPeerCertificate() to read the client cert after it connects to get identity. This can now throw unexpectedly, even for successfully authorized certs, when it never did before.

Probably other cases too.

This needs something in the TLS flows alongside it, everything that can reach X509ToObject, to guard against this and properly expose failures there too. Either it should fail earlier & clearer (so if any part of the cert is unparseable then the whole connection fails, like any other invalid cert - though this might break some weird connections that do work today), or we should defer the failure somehow out of the normal simple flow (fail in the same way as the other APIs but only when you actually read the invalid field, but not when you initially parse the cert during TLS).

Interestingly, we do already have similar handling to avoid errors during SAN parsing within TLS verification:

if (malformed_name_mode ==
MalformedSubjectAltNameMode::kReturnLegacySentinel) {
// The legacy object is used by TLS hostname verification. Return an
// empty DNS name so verification cannot fall back to the common name,
// while keeping getPeerCertificate() usable for malformed certificates.
return OneByteString(env->isolate(), kMalformedSubjectAltName);
}

That should probably match whatever we do here too.

The main risk is AIA, since other invalid data (dates) will make the cert fail authorization anyway, while AIA is mostly ignored by everybody for simple TLS today but would now be strictly validated.

@panva panva removed the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Nov 13, 2026
X509Certificate can report failed operations as missing fields or
negative checks. Invalid validity times return "Bad time value" or
Invalid Date, making relational date comparisons return false.

Throw when required fields cannot be converted, certificate encoding or
fingerprint calculation fails, or a present AIA or EKU extension cannot
be decoded. Apply this to getters and explicit toLegacyObject() calls.
Report failed or unsupported private-key comparisons as exceptions.

For verify(), return true for 1 and false for 0. Also return false for a
wrong key type or invalid unused bits in the signature BIT STRING.
Throw for other negative results.

Use the existing conversion behavior for legacy TLS certificate objects,
which are also constructed internally during handshakes. Formatting
unrelated metadata must not cause an uncaught exception or reject an
otherwise valid connection.

Fixes: nodejs#67285
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex
@panva panva changed the title crypto: stop swallowing X.509 operation errors crypto: throw on X509Certificate operation errors Nov 13, 2026
@panva

panva commented Nov 13, 2026

Copy link
Copy Markdown
Member Author

@pimterry You're right. I reproduced the uncaught exception with a trusted certificate containing malformed AIA.

So I've separated the conversion paths. The new conversion exceptions apply to X509Certificate getters and explicit toLegacyObject() calls now while TLS getPeerCertificate() and getCertificate() use their existing field omissions and time diagnostics, so unrelated metadata does not become a new handshake rejection condition.

The custom inspector reads X509Certificate fields, so their new
exceptions can also make logging fail.

Retry conversion failures with native legacy formatting for inspection
only. Leave those fallbacks out of the getter cache so direct access
still reports the failure afterward.

Cover omitted AIA and EKU values, invalid-time diagnostics, and cached
Date and EKU mutations. Leave the existing SAN inspection errors intact.

Signed-off-by: Filip Skokan <panva.ip@gmail.com>
Assisted-by: Codex

@pimterry pimterry left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! LGTM with that change.

@panva panva added the author ready PRs with CI started, the required approvals, and no outstanding review comments. label Nov 13, 2026
@panva panva added the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Nov 13, 2026
@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. Only starts once the PR has an approving review. label Nov 13, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs with CI started, the required approvals, and no outstanding review comments. c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs involving general changes in the lib/ or src/ directories. needs-ci PRs that need a full CI run. semver-major PRs that contain breaking changes and should be released in the next major version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crypto: X509Certificate returns "Bad time value" instead of throwing for an unreadable validity time

5 participants