Skip to content

node:crypto: uppercase X509 serialNumber and modulus hex to match Node#31519

Merged
Jarred-Sumner merged 8 commits into
mainfrom
ali/x509-serial-uppercase-hex
May 29, 2026
Merged

node:crypto: uppercase X509 serialNumber and modulus hex to match Node#31519
Jarred-Sumner merged 8 commits into
mainfrom
ali/x509-serial-uppercase-hex

Conversation

@alii

@alii alii commented May 28, 2026

Copy link
Copy Markdown
Member

X509Certificate.serialNumber, .toLegacyObject().serialNumber/.modulus, and tls.getPeerCertificate().serialNumber/.modulus were lowercase hex in Bun, but Node/OpenSSL (and openssl x509 -serial) emit uppercase — breaking literal serial-string cert pinning / allowlists.

BoringSSL's BN_bn2hex/BN_print emit lowercase; OpenSSL (which Node's ncrypto targets) emits uppercase. Bun already uppercases the fingerprint path but missed serialNumber/modulus. ASCII-uppercase them in computeSerialNumber and the two toLegacyObject modulus sites (getPeerCertificate routes through the same C++ toLegacyObject, so it's covered). exponent is left lowercase (Node emits it lowercase too), and BignumPointer::toHex is untouched (its other callers re-parse via parseInt).

Updates the existing tests that asserted the old lowercase (node-tls-connect/node-tls-server, regression #21274) to the uppercase values Node produces, and adds an X509 uppercase-hex regression test. Not a port regression — 1.3.14 is also lowercase; Node is the reference.

@alii

alii commented May 28, 2026

Copy link
Copy Markdown
Member Author

@robobun adopt

@robobun

robobun commented May 28, 2026

Copy link
Copy Markdown
Collaborator
Updated 9:05 PM PT - May 28th, 2026

@robobun, your commit 9bd3a86 has some failures in Build #58794 (All Failures)


🧪   To try this PR locally:

bunx bun-pr 31519

That installs a local version of the PR into your bun-31519 executable, so you can run:

bun-31519 --bun

@robobun

robobun commented May 28, 2026

Copy link
Copy Markdown
Collaborator

✅ Adopted & addressed review. X509 serialNumber/modulus and tls.getPeerCertificate() now emit uppercase hex matching Node/OpenSSL (openssl x509 -serial on agent1-cert.pem147D36C1…; baked Bun returned lowercase). Uppercasing is done in a single allocation per @Jarred-Sumner's review.

Rebased onto main; debug + release build clean and the crypto/TLS tests pass. On build #58794, 284 jobs passed with 0 real failures — the build is only red because the two darwin-aarch64-test-bun lanes expired waiting for a macOS arm64 agent (never ran; infra, not this diff). The diff is green and the PR is mergeable — it just needs a maintainer to re-run those two lanes or merge.

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Actionable comments posted: 0

@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@robobun, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 36 minutes and 19 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 531f3da8-f2b2-40e5-b7c3-5dca6fad6848

📥 Commits

Reviewing files that changed from the base of the PR and between ffe44fd and 9bd3a86.

📒 Files selected for processing (5)
  • src/jsc/bindings/JSX509Certificate.cpp
  • test/js/node/crypto/x509-subclass.test.ts
  • test/js/node/tls/node-tls-connect.test.ts
  • test/js/node/tls/node-tls-server.test.ts
  • test/regression/issue/21274.test.ts

Walkthrough

This PR normalizes X509 certificate hexadecimal output to uppercase. The native C++ binding uppercases serial numbers and RSA public key moduli; a new regression test verifies uppercase formatting and several existing tests update expectations to match.

Changes

X509 Certificate Hex Uppercase Normalization

Layer / File(s) Summary
Serial number and modulus uppercase in C++ bindings
src/jsc/bindings/JSX509Certificate.cpp
Adds ASCII-uppercase helpers; computeSerialNumber now returns uppercase hex; both toLegacyObject RSA paths assign an uppercased modulus string.
New uppercase formatting regression test
test/js/node/crypto/x509-subclass.test.ts
Adds a regression test asserting serialNumber and legacy.modulus are uppercase hex and that toLegacyObject() returns consistent uppercase values.
Updated TLS/connect tests and regression expectations
test/js/node/tls/node-tls-connect.test.ts, test/js/node/tls/node-tls-server.test.ts, test/regression/issue/21274.test.ts
Live-host TLS tests gain conditional network gating; expected cert.modulus and cert.serialNumber assertions updated to uppercase hex.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: uppercasing X509 serialNumber and modulus hex to match Node.js behavior.
Description check ✅ Passed The description comprehensively explains what was changed, why (to match Node/OpenSSL behavior and fix cert pinning), and how it was verified with test updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM — straightforward Node compat fix uppercasing BoringSSL hex output for serialNumber/modulus.

Extended reasoning...

Overview

Three one-line changes in src/jsc/bindings/JSX509Certificate.cpp appending .convertToASCIIUppercase() to the hex strings produced for serialNumber (in computeSerialNumber) and modulus (in both toLegacyObject overloads). The remaining diff is test fixture updates flipping expected values from lowercase to uppercase, plus one new regression test asserting /^[0-9A-F]+$/.

Security risks

None. This is a presentation-only change to hex strings that already contain only [0-9a-f]; uppercasing cannot alter semantics or introduce injection. It actually improves compat for users doing literal serial-string cert pinning against Node-produced values.

Level of scrutiny

Low. The change is mechanical, the rationale (BoringSSL's BN_bn2hex/BN_print emit lowercase vs OpenSSL's uppercase) is well-documented and correct, and the PR explicitly justifies leaving exponent lowercase (Node uses %x printf there too) and not touching BignumPointer::toHex. The fingerprint path was already uppercased, so this brings serialNumber/modulus in line.

Other factors

No CODEOWNERS for these paths. The bug-hunting system found nothing. Existing tests that previously locked in the (incorrect) lowercase behavior are updated rather than deleted, and a new dedicated regression test is added. getPeerCertificate routes through the same toLegacyObject, so TLS socket output is covered without separate changes.

Comment thread src/jsc/bindings/JSX509Certificate.cpp Outdated
Comment thread src/jsc/bindings/JSX509Certificate.cpp Outdated

@Jarred-Sumner Jarred-Sumner left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Comments

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM — focused Node-compat string-casing fix with Jarred's review feedback addressed and CI green.

Extended reasoning...

Overview

This PR normalizes X509Certificate.serialNumber and the modulus field of toLegacyObject()/getPeerCertificate() to uppercase hex, matching Node.js/OpenSSL output (BoringSSL emits lowercase). The C++ change adds a small toUppercaseASCIIWTFString helper (single-allocation via createUninitialized + toASCIIUpper) and applies it at three call sites in JSX509Certificate.cpp. Four test files update lowercase→uppercase hex literals and a new regression test pins the uppercase behavior.

Security risks

None. While the file is in the crypto bindings, the change is pure presentation-layer string casing of already-computed hex values. No verification, key handling, parsing, or trust decisions are touched. toASCIIUpper only affects a-z, so non-hex bytes (none expected from BN_bn2hex/BN_print) would pass through unchanged.

Level of scrutiny

Low–medium. The diff is ~20 net lines of straightforward C++ plus mechanical test-literal updates. The helper follows an established WTF pattern (createUninitialized + fill) already used elsewhere in the bindings. The BIO overload mirrors the existing toWTFString(BIOPointer&) shape minus the UTF-8 branch, which is unnecessary since BN hex is always ASCII.

Other factors

  • Jarred-Sumner reviewed the original revision and requested the single-allocation approach; that feedback was addressed in 083a7af and both inline comments are resolved.
  • CI passed (Build #58670).
  • The bug-hunting system found no issues.
  • No CODEOWNERS coverage for the touched paths.
  • Behavior change is intentional and Node-compat-driven; the updated tests now match what Node.js produces.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/node/tls/node-tls-connect.test.ts`:
- Around line 491-495: Remove the watchdog setTimeout block (the const timer =
setTimeout(...) that calls socket.end() and done(new Error(...))) from the test;
instead rely on the socket's 'timeout' event and the test framework's timeout to
fail if needed, and ensure any existing socket timeout handler calls
done()/rejects the test promise appropriately (adjust the socket timeout handler
used in this test so it produces the failure path formerly enforced by the
watchdog, then delete references to timer, socket.end() from that setTimeout).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0224c08c-9def-445e-a5df-a9bd5512c82a

📥 Commits

Reviewing files that changed from the base of the PR and between f51433d and ffe44fd.

📒 Files selected for processing (2)
  • src/jsc/bindings/JSX509Certificate.cpp
  • test/js/node/tls/node-tls-connect.test.ts

Comment thread test/js/node/tls/node-tls-connect.test.ts Outdated
Comment thread test/js/node/tls/node-tls-connect.test.ts
alii and others added 8 commits May 28, 2026 22:09
The serialNumber/modulus hex is always ASCII, so build the uppercased
string in one pass via createUninitialized + toASCIIUpper instead of
creating the lowercase string and then allocating a second copy with
convertToASCIIUppercase(). The modulus path also no longer creates the
intermediate external string.
The node-tls-connect suite has several tests that dial the live bun.sh
host. In offline/sandboxed environments these fail with
DNSException/getaddrinfo errors unrelated to the code under test. Gate
them behind a one-time reachability probe so the suite passes offline
while still exercising the local self-signed-cert assertions (which
cover the X509 serialNumber/modulus uppercase behavior).
Rely on the test's existing 10s timeout budget as the failsafe instead
of a manual setTimeout watchdog, per the repo's no-setTimeout-in-tests
rule.
The module-load probe waited only on secureConnect/error, so a stalled
TLS handshake or black-holed connection could hang module evaluation
indefinitely (per-test timeouts don't apply before any test registers).
Bound it with a 5s timer that resolves false and always destroy the
socket, so the live tests skip cleanly instead of hanging.
…tests

The listen tests raced a 100ms setTimeout watchdog against the actual
bind; under load (ASAN) a legitimate listen could exceed 100ms and trip
mustNotCall, failing a passing test. Rely on the server 'error' handler
(real bind failures still fail fast) and the per-test timeout instead,
per the repo's no-setTimeout-in-tests rule.
@robobun robobun force-pushed the ali/x509-serial-uppercase-hex branch from a2d2ca2 to 9bd3a86 Compare May 28, 2026 22:17
@Jarred-Sumner Jarred-Sumner merged commit 3a79bd7 into main May 29, 2026
75 of 77 checks passed
@Jarred-Sumner Jarred-Sumner deleted the ali/x509-serial-uppercase-hex branch May 29, 2026 19:34
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.

3 participants