crypto,tls: do not ignore BN_get_word error#63895
Merged
Merged
Conversation
Collaborator
|
Review requested:
|
This changes `BignumPointer::GetWord` such that it does not hide errors from the caller. In the context of RSA keys within X.509 certificates, we should eventually compute the public exponent correctly regardless of its size. This patch, however, is designed to be a minimal change that prevents callers from using erroneous return values of `BN_get_word`. Signed-off-by: Tobias Nießen <tniessen@tnie.de>
27df418 to
3c157c5
Compare
panva
approved these changes
Jun 13, 2026
anonrig
approved these changes
Jun 13, 2026
This comment was marked as outdated.
This comment was marked as outdated.
Collaborator
jasnell
approved these changes
Jun 16, 2026
Collaborator
|
Landed in 21310cb |
aduh95
pushed a commit
that referenced
this pull request
Jun 20, 2026
This changes `BignumPointer::GetWord` such that it does not hide errors from the caller. In the context of RSA keys within X.509 certificates, we should eventually compute the public exponent correctly regardless of its size. This patch, however, is designed to be a minimal change that prevents callers from using erroneous return values of `BN_get_word`. Signed-off-by: Tobias Nießen <tniessen@tnie.de> PR-URL: #63895 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
luanmuniz
pushed a commit
to luanmuniz/node
that referenced
this pull request
Jun 25, 2026
This changes `BignumPointer::GetWord` such that it does not hide errors from the caller. In the context of RSA keys within X.509 certificates, we should eventually compute the public exponent correctly regardless of its size. This patch, however, is designed to be a minimal change that prevents callers from using erroneous return values of `BN_get_word`. Signed-off-by: Tobias Nießen <tniessen@tnie.de> PR-URL: nodejs#63895 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
aduh95
pushed a commit
that referenced
this pull request
Jun 25, 2026
This changes `BignumPointer::GetWord` such that it does not hide errors from the caller. In the context of RSA keys within X.509 certificates, we should eventually compute the public exponent correctly regardless of its size. This patch, however, is designed to be a minimal change that prevents callers from using erroneous return values of `BN_get_word`. Signed-off-by: Tobias Nießen <tniessen@tnie.de> PR-URL: #63895 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: James M Snell <jasnell@gmail.com>
nodejs-github-bot
pushed a commit
that referenced
this pull request
Jun 25, 2026
If the exponent does not fit into a single word, previous versions of Node.js would report an incorrect value or, recently, return `null`. Change `GetExponentString()` to handle arbitrarily large RSA public exponents properly. Signed-off-by: Tobias Nießen <tniessen@tnie.de> PR-URL: #64093 Refs: #63895 Reviewed-By: Filip Skokan <panva.ip@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
dylan-conway
added a commit
to oven-sh/bun
that referenced
this pull request
Jul 1, 2026
Bun's `src/jsc/bindings/ncrypto.{cpp,h}` is a port of
[nodejs/ncrypto@134ac40](nodejs/ncrypto@134ac40)
(February 2025), which maps to Node's `deps/ncrypto` from late January
2025. This ports the upstream correctness fixes made since then that
apply to code Bun already has, plus one Bun-local typo found while
auditing the drift.
### Upstream fixes
| Change | Upstream |
| --- | --- |
| `DataPointer::resize()`: handle `OPENSSL_realloc` failure instead of
returning a `DataPointer` with a null pointer and a nonzero length. Also
frees the old block on failure. Upstream's version calls a `free()`
helper after `release()` has already nulled `data_`, which is a no-op,
so the old block leaks; this keeps the intent without that. |
[nodejs/ncrypto#37](nodejs/ncrypto#37) |
| `CipherCtxPointer::{setIvLength, setAeadTag, setAeadTagLength,
getAeadTag}`: compare the `EVP_CIPHER_CTX_ctrl` return against `> 0`.
The raw `int` can be negative, which converts to `true`. BoringSSL
normalizes `-1` to `0`, so this is masked in Bun's build today, but it
is still the wrong check. |
[nodejs/ncrypto#36](nodejs/ncrypto#36) |
| `BignumPointer::{GetWord, getWord}` return `std::optional` so a
`BN_get_word` overflow (reported as the all-ones word) is
distinguishable from the real all-ones value. The optional wraps
`BN_ULONG`, not upstream's `unsigned long`: BoringSSL's `BN_ULONG` is
`uint64_t` on every 64-bit target, so `unsigned long` would still
silently truncate 33-to-64-bit values on LLP64 (Windows x64). |
[nodejs/node#63895](nodejs/node#63895) |
| `BIOPointer::New(method)`: return an empty pointer for a null method.
| [nodejs/node#61788](nodejs/node#61788) |
| `EVPKeyCtxPointer::publicCheck()`: remove an unconditional return that
made the `EVP_PKEY_public_check_quick` branch unreachable. The whole
block is non-BoringSSL, so it is dead in Bun's build. |
[nodejs/node#59471](nodejs/node#59471) |
| `X509Name::Iterator::operator*()`: free the buffer allocated by
`ASN1_STRING_to_UTF8`, and return early on a negative size (the
out-pointer is never written on failure). Dead code today:
`JSX509Certificate.cpp` has its own correct loop. |
[nodejs/node#60609](nodejs/node#60609) |
### `GetWord` callers updated
- `JSDiffieHellmanConstructor.cpp`: the generator-below-2 check becomes
`has_value() && *word < 2`. This is the one spot that needed care:
`std::optional<T> < 2` compiles and evaluates `nullopt < 2` as `true`,
so a naive translation would have started rejecting any generator wider
than a word.
- `JSX509Certificate.cpp` (both legacy-object sites): `exponent` is
reported as `null` when the RSA public exponent does not fit in a word,
matching Node. This branch is unreachable under BoringSSL, which rejects
RSA public exponents wider than 33 bits at SPKI parse time.
### Bun-local typo
`SSLCtxPointer::setCipherSuites` passed `ciphers.length()` where
`SSL_CTX_set_ciphersuites` expects the string. It is in the
non-BoringSSL `#ifndef` branch so it never compiles in Bun's build;
introduced by the original `WTF::StringView` port of the file.
### Testing
No test here can fail against the unmodified source on a Linux machine,
because nothing in this diff changes observable behavior on Linux. I
checked each one: `DataPointer::resize` only diverges under
`OPENSSL_realloc` failure, the `EVP_CIPHER_CTX_ctrl` returns are already
normalized to `0` or `1` by BoringSSL, `publicCheck` and
`setCipherSuites` are inside non-BoringSSL `#ifndef` branches,
`BIOPointer::New(method)` and `X509Name::Iterator` have no callers, and
`BN_ULONG` and `unsigned long` are the same 64-bit type on LP64. The one
real before-and-after is the `BN_ULONG` truncation, and it only exists
on Windows x64 (LLP64).
So the new tests pin invariants rather than prove a failure.
`node-crypto.test.js` gains three `DiffieHellman` cases:
- A 72-bit buffer generator is accepted. This is what a naive `optional
< 2` translation would have broken on every platform.
- A 33-bit (5-byte) buffer generator is accepted. Equivalent on
platforms with a 64-bit `unsigned long`; on Windows x64 the `unsigned
long` version of this change truncates it to `0` and wrongly rejects it
as below 2, so this is the real regression test for the `BN_ULONG`
return type on that platform.
- Generators below 2 are still rejected and exactly 2 is still accepted.
Node v26 agrees on all three.
<details>
<summary>Local verification against the debug build</summary>
-
`test/js/node/crypto/{crypto,node-crypto,crypto.key-objects,crypto-rsa,x509-subclass}.test.*`:
677 pass, 0 fail
-
`test/js/node/test/parallel/test-crypto-{x509,dh,dh-errors,dh-constructor,dh-odd-key,dh-generate-keys,dh-shared,dh-padding,cipheriv-decipheriv,gcm-explicit-short-tag,gcm-implicit-short-tag,aes-wrap,padding-aes256,getcipherinfo,rsa-dsa}.js`,
`test-webcrypto-encrypt-decrypt-aes.js`,
`test-crypto-webcrypto-aes-decrypt-tag-too-small.js`: all pass
</details>
### Deliberately not included
- `Cipher::MAX_AUTH_TAG_LENGTH`
([nodejs/node#57803](nodejs/node#57803)): of the
three macros upstream's `static_assert` uses, BoringSSL only defines
`EVP_GCM_TLS_TAG_LEN`, so the assert degenerates to `16 <= 16`.
- `ECKeyPointer::setPublicKeyRaw`
([nodejs/node#62396](nodejs/node#62396)): the
expensive check that rewrite avoids (the `order * Q == infinity` step of
`EC_KEY_check_key`) does not exist in BoringSSL's `EC_KEY_check_key`, so
there is no perf win for Bun.
- The BoringSSL build guards the standalone repo added
(`NCRYPTO_NO_DSA_KEYGEN`, `NCRYPTO_NO_EVP_DH`, rejecting DSA in
`NewFromID`, ...): those exist for vanilla BoringSSL CI, and Bun's DSA
and DH paths work today against its BoringSSL.
---------
Co-authored-by: Dylan Conway <dylan.conway567@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This changes
BignumPointer::GetWordsuch that it does not hide errors from the caller. In the context of RSA keys within X.509 certificates, we should eventually compute the public exponent correctly regardless of its size. This patch, however, is designed to be a minimal change that prevents callers from using erroneous return values ofBN_get_word.