Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions deps/ncrypto/ncrypto.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2797,6 +2797,13 @@ const EVP_MD* getDigestByName(const char* name) {
if (strcmp(name, "dss1") == 0 || strcmp(name, "DSS1") == 0) [[unlikely]] {
return EVP_sha1();
}
#if NCRYPTO_USE_BORINGSSL
// BoringSSL exposes SHA-3 through EVP_MD descriptors without registering
// their names or assigning them NIDs.
if (CaseInsensitiveNameEqual()(name, "sha3-256")) return EVP_sha3_256();
if (CaseInsensitiveNameEqual()(name, "sha3-384")) return EVP_sha3_384();
if (CaseInsensitiveNameEqual()(name, "sha3-512")) return EVP_sha3_512();
#endif
return EVP_get_digestbyname(name);
}

Expand Down Expand Up @@ -4998,11 +5005,7 @@ bool SSLPointer::isServer() const {
EVPKeyPointer SSLPointer::getPeerTempKey() const {
if (!ssl_) return {};
EVP_PKEY* raw_key = nullptr;
#ifndef OPENSSL_IS_BORINGSSL
if (!SSL_get_peer_tmp_key(get(), &raw_key)) return {};
#else
if (!SSL_get_server_tmp_key(get(), &raw_key)) return {};
#endif
return EVPKeyPointer(raw_key);
}

Expand Down Expand Up @@ -5100,13 +5103,57 @@ bool SSLCtxPointer::setGroups(const char* groups) {
}

bool SSLCtxPointer::setCipherSuites(const char* ciphers) {
#ifndef OPENSSL_IS_BORINGSSL
if (!ctx_) return false;
#ifndef OPENSSL_IS_BORINGSSL
return SSL_CTX_set_ciphersuites(ctx_.get(), ciphers);
#else
// BoringSSL does not allow API config of TLS 1.3 cipher suites.
// We treat this as a non-op.
return true;
constexpr std::array<uint16_t, 3> supported = {
SSL_CIPHER_AES_128_GCM_SHA256,
SSL_CIPHER_AES_256_GCM_SHA384,
SSL_CIPHER_CHACHA20_POLY1305_SHA256,
};
std::vector<uint16_t> cipher_ids;
std::string_view remaining(ciphers);
while (!remaining.empty()) {
const size_t end = remaining.find(':');
std::string_view name = remaining.substr(0, end);
const size_t first = name.find_first_not_of(" \t\n\r\f\v");
if (first != std::string_view::npos) {
name = name.substr(first);
name = name.substr(0, name.find_last_not_of(" \t\n\r\f\v") + 1);
for (const auto id : supported) {
const SSL_CIPHER* cipher = SSL_get_cipher_by_value(id);
if (name == SSL_CIPHER_standard_name(cipher)) {
// OpenSSL ignores unknown names and accepts duplicates, while
// BoringSSL requires unique protocol IDs.
if (std::find(cipher_ids.begin(), cipher_ids.end(), id) ==
cipher_ids.end()) {
cipher_ids.push_back(id);
}
break;
}
}
}
if (end == std::string_view::npos) break;
remaining.remove_prefix(end + 1);
}
if (*ciphers != '\0' && cipher_ids.empty()) {
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
return false;
}
std::vector<uint32_t> flags(cipher_ids.size());
if (!flags.empty() &&
(SSL_CTX_get_options(get()) & SSL_OP_CIPHER_SERVER_PREFERENCE) == 0) {
// TLS 1.3 otherwise always uses server preference in BoringSSL. An
// equal-preference group lets the client choose from the configured list.
std::fill(flags.begin(),
flags.end() - 1,
SSL_CIPHER_FLAG_EQUAL_PREFERENCE_WITH_NEXT);
}
// An empty input resets BoringSSL's built-in preference order. The public
// TLS API expands empty cipher strings to Node's defaults before this call.
return SSL_CTX_set1_tls13_ciphers(
get(), cipher_ids.data(), flags.data(), cipher_ids.size()) == 1;
#endif
}

Expand Down
15 changes: 11 additions & 4 deletions lib/internal/crypto/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const {
isKeyAlgorithmAvailable,
} = internalBinding('crypto');

const { openssl_is_boringssl: isBoringSSL } = process.features;

let fips;
let fipsGeneration;
function isFips() {
Expand Down Expand Up @@ -533,7 +535,7 @@ function getConditionalAlgorithms() {
'Argon2d': !!Argon2Job && !fips,
'Argon2i': !!Argon2Job && !fips,
'Argon2id': !!Argon2Job && !fips,
'ChaCha20-Poly1305': process.features.openssl_is_boringssl ||
'ChaCha20-Poly1305': isBoringSSL ||
has(ciphers, 'chacha20-poly1305'),
'cSHAKE128': has(hashes, 'shake128'),
'cSHAKE256': has(hashes, 'shake256'),
Expand All @@ -550,15 +552,15 @@ function getConditionalAlgorithms() {
'ML-KEM-768': mlKem768,
'ML-KEM-1024': mlKem1024,
'MLKEM768-P256': !fips && mlKem768 &&
(!process.features.openssl_is_boringssl ||
(!isBoringSSL ||
(has(hashes, 'sha3-256') &&
has(hashes, 'shake256'))),
'MLKEM768-X25519': !fips && mlKem768 &&
(!process.features.openssl_is_boringssl ||
(!isBoringSSL ||
(has(hashes, 'sha3-256') &&
has(hashes, 'shake256'))),
'MLKEM1024-P384': !fips && mlKem1024 &&
(!process.features.openssl_is_boringssl ||
(!isBoringSSL ||
(has(hashes, 'sha3-256') &&
has(hashes, 'shake256'))),
'SHA-1': has(hashes, 'sha1'),
Expand Down Expand Up @@ -847,6 +849,11 @@ function normalizeAlgorithm(algorithm, op) {
throw lazyDOMException(
`Only SHA hashes are supported in ${desiredType}`, 'NotSupportedError');
}
if (isBoringSSL && StringPrototypeStartsWith(hash.name, 'SHA3-')) {
throw lazyDOMException(
'SHA-3 is only supported for digest operations',
'NotSupportedError');
}
normalizedAlgorithm[member] = hash;
} else if (idlType === 'AlgorithmIdentifier') {
// This extension point is not used by any supported algorithm (yet?)
Expand Down
4 changes: 3 additions & 1 deletion lib/internal/crypto/webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ const {
TypedArrayPrototypeIncludes,
} = primordials;

const { openssl_is_boringssl: isBoringSSL } = process.features;

const {
lazyDOMException,
} = require('internal/util');
Expand Down Expand Up @@ -670,7 +672,7 @@ converters.ContextParams = createAlgorithmDictionaryConverter(
const validateLength = (V) =>
validateMaxBufferLength(V, 'ContextParams.context', 255);

if (process.features.openssl_is_boringssl) {
if (isBoringSSL) {
this.validator = validateLength;
} else {
let { 0: major, 1: minor } =
Expand Down
4 changes: 3 additions & 1 deletion lib/internal/tls/wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const {

assertCrypto();

const { openssl_is_boringssl: isBoringSSL } = process.features;

const { setImmediate } = require('timers');
const assert = require('internal/assert');
const crypto = require('crypto');
Expand Down Expand Up @@ -1126,7 +1128,7 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
this._handle.renegotiate();
} catch (err) {
const isBoringSSLRenegotiationUnsupported =
process.features.openssl_is_boringssl &&
isBoringSSL &&
err?.code === 'ERR_SSL_FUNCTION_SHOULD_NOT_HAVE_BEEN_CALLED';
const error = isBoringSSLRenegotiationUnsupported ?
new ERR_TLS_RENEGOTIATION_UNSUPPORTED() : err;
Expand Down
3 changes: 3 additions & 0 deletions src/crypto/crypto_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ MaybeLocal<Value> GetValidationErrorReason(Environment* env, int err) {
// Suggest --use-system-ca if the error indicates a certificate issue
bool suggest_system_ca =
(err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE) ||
#ifdef OPENSSL_IS_BORINGSSL
(err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) ||
#endif
(err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ||
((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) &&
!env->options()->use_system_ca);
Expand Down
6 changes: 6 additions & 0 deletions src/crypto/crypto_hash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ const std::vector<std::string>& GetSupportedHashAlgorithms(Environment* env) {
EVP_MD_do_all_provided(nullptr, SaveSupportedProviderHashAlgorithms, env);
#else
EVP_MD_do_all_sorted(SaveSupportedHashAlgorithms, env);
#endif
#if NCRYPTO_USE_BORINGSSL
// SHA-3 descriptors are not included in BoringSSL's name registry.
for (const char* name : {"sha3-256", "sha3-384", "sha3-512"}) {
env->supported_hash_algorithms.emplace_back(name);
}
#endif
}
const uint64_t current_generation = ncrypto::getFipsStateGeneration();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ if (!common.hasCrypto)
// https must be dynamically imported so that builds without crypto support
// can skip it.
const { default: https } = await import('node:https');
const { untrustedLeafErrorCode } = await import('../common/tls.js');

const server = https.createServer({
cert: fixtures.readKey('agent8-cert.pem'),
Expand All @@ -36,8 +37,8 @@ const { code, signal, stderr, stdout } = await runProxiedRequest({
HTTPS_PROXY: `http://localhost:${proxy.address().port}`,
});

// The proxy client should get a UNABLE_TO_VERIFY_LEAF_SIGNATURE during TLS handshake.
assert.match(stderr, /UNABLE_TO_VERIFY_LEAF_SIGNATURE/);
// The proxy client should report the untrusted leaf during the TLS handshake.
assert.match(stderr, new RegExp(untrustedLeafErrorCode));
assert.strictEqual(stdout, '');
assert.strictEqual(code, 0);
assert.strictEqual(signal, null);
Expand Down
28 changes: 0 additions & 28 deletions test/common/boringssl.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,33 +167,6 @@ function testEphemeralKeyInfo() {
}));
}

/**
* The protocol matrix tests cover OpenSSL behavior for legacy TLS protocols.
* For BoringSSL we only need to exhibit that a TLSv1-only client cannot connect
* to a server whose minimum protocol is TLS 1.2; the client receives the
* protocol-version alert instead of the OpenSSL version-specific matrix.
*/
function testLegacyProtocolUnsupported() {
const server = tls.createServer({
key: fixtures.readKey('agent2-key.pem'),
cert: fixtures.readKey('agent2-cert.pem'),
minVersion: 'TLSv1.2',
}, common.mustNotCall());

server.on('tlsClientError', common.mustCall());
server.listen(0, common.mustCall(() => {
const client = tls.connect({
port: server.address().port,
rejectUnauthorized: false,
secureProtocol: 'TLSv1_method',
}, common.mustNotCall());
client.on('error', common.mustCall((err) => {
assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION');
server.close();
}));
}));
}

/**
* BoringSSL can load a multi-PFX option well enough to serve the ECDSA
* identity, but it does not provide the same OpenSSL multi-identity selection
Expand Down Expand Up @@ -335,7 +308,6 @@ module.exports = {
assertNoCipherMatch,
assertOpenSSLSecurityLevelsUnsupported,
testEphemeralKeyInfo,
testLegacyProtocolUnsupported,
testMultiPfxSelectionDifference,
testPskTls13Unsupported,
testRenegotiationUnsupported,
Expand Down
6 changes: 6 additions & 0 deletions test/common/tls.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@
const crypto = require('crypto');
const net = require('net');
const assert = require('assert');
const { isBoringSSL } = require('./crypto');

// BoringSSL reports the missing issuer directly for a lone, non-self-signed
// leaf. OpenSSL replaces it with a leaf signature verification error.
exports.untrustedLeafErrorCode = isBoringSSL ?
'UNABLE_TO_GET_ISSUER_CERT_LOCALLY' : 'UNABLE_TO_VERIFY_LEAF_SIGNATURE';

exports.ccs = Buffer.from('140303000101', 'hex');

Expand Down
11 changes: 6 additions & 5 deletions test/fixtures/crypto/ecdsa.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

const { isBoringSSL } = require('../../common/crypto');

module.exports = function() {
module.exports = function({ includeUnsupported = false } = {}) {
const includeSha3 = !isBoringSSL || includeUnsupported;
const pkcs8 = {
'P-256': Buffer.from(
'308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b020101' +
Expand Down Expand Up @@ -74,7 +75,7 @@ module.exports = function() {
'b6a0a14d7e4bc6dd2eda82c9234f174b670b60c8f7d101f68fdf5889e02373b025' +
'dcbc4c82f2929b8e06c68535da98e38fe399c53a814b097935581ef21535eb',
'hex'),
...(!isBoringSSL ? {
...(includeSha3 ? {
'SHA3-256': Buffer.from(
'f6a48eb5557f484ed0c3e4b5c78a3cf497cbd346db06a4165d429248aa2cc51a69' +
'747d09f57af145469a8b607a9b8b9709629d74e8f5ca337c6ddc581b6f6103',
Expand Down Expand Up @@ -106,7 +107,7 @@ module.exports = function() {
'72fbdb369fd34c1c54264d07f4facd69b02e4206f8a8bb259b882a305c56fde2d3' +
'5107e493c53cd6b4af0b31306f4d03fd43cfc762a1030e17a3d775453a1212b142' +
'9f7b3d93066a5f42a10b138cd177dc09616e827d598822d78d4627b754e6', 'hex'),
...(!isBoringSSL ? {
...(includeSha3 ? {
'SHA3-256': Buffer.from(
'0b07c078be30fa5925a307d6fc559c5f398e63fb5d007d6b24a834847f2d3d18d5' +
'b5e840711c52a7bc6626c3ced93301e873c013a706f6b297c12cc6d47a71e0529e' +
Expand Down Expand Up @@ -146,7 +147,7 @@ module.exports = function() {
'01f0071e6a32867fa70f695cd39c4e87e142b9e4134d38740bd6fee354a575167e' +
'13524e94832637910fe11e53a85fb21b91adb81bb1779c4e2b8bc87c717dc35084',
'hex'),
...(!isBoringSSL ? {
...(includeSha3 ? {
'SHA3-256': Buffer.from(
'00463679f47a4c705e03447360dcf34d1743e0d4b2591cc66832a6bc80d92e538c' +
'169a1fd330f98e7235ca7fec7e16ac44fb13095b8edf2c76b75c4845177d59e425' +
Expand All @@ -172,7 +173,7 @@ module.exports = function() {
const curves = ['P-256', 'P-384', 'P-521'];
const hashes = ['SHA-1', 'SHA-256', 'SHA-384', 'SHA-512'];

if (!isBoringSSL) {
if (includeSha3) {
hashes.push('SHA3-256', 'SHA3-384', 'SHA3-512');
}

Expand Down
46 changes: 20 additions & 26 deletions test/fixtures/crypto/hmac.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';

const { isBoringSSL } = require('../../common/crypto');

module.exports = function () {
const plaintext = Buffer.from(
'5f4dba4f320c0ce876725afce5fbd25bf83e5a7125a08cafe73c3ebac421779df9d55d' +
Expand All @@ -24,18 +22,16 @@ module.exports = function () {
'5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' +
'0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9',
'hex'),
...(!isBoringSSL ? {
'SHA3-256': Buffer.from(
'e588ec0811463d767241df1074b47ae4071b51f2ce36537ba69ccdc3fdc2b7a8',
'hex'),
'SHA3-384': Buffer.from(
'6b1da28eab1f582ad9718effe05e23d5fd2c9877a2d9443f90bec093bece2ea7' +
'd2354cd0bdc5e147d2e9009373494488', 'hex'),
'SHA3-512': Buffer.from(
'5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' +
'0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9',
'hex')
} : {})
'SHA3-256': Buffer.from(
'e588ec0811463d767241df1074b47ae4071b51f2ce36537ba69ccdc3fdc2b7a8',
'hex'),
'SHA3-384': Buffer.from(
'6b1da28eab1f582ad9718effe05e23d5fd2c9877a2d9443f90bec093bece2ea7' +
'd2354cd0bdc5e147d2e9009373494488', 'hex'),
'SHA3-512': Buffer.from(
'5dcc359443aaf652fa1375d6b3e61fdcf29bb4a28bd5d3dcfa40f82f906bb280' +
'0455db03b5d31fb972a15a6d0103a24e56d156a119c0e5a1e92a44c3c5657cf9',
'hex')
}

const signatures = {
Expand All @@ -50,18 +46,16 @@ module.exports = function () {
'61fb278c3ffb0cce2bf1cf723ddfd8ef1f931c0c618c25907324605939e3f9a2' +
'c6f4af690bda3407dc2f5770f6a0a44b954d64a332e3ee0821abf82b7f3e99c1',
'hex'),
...(!isBoringSSL ? {
'SHA3-256': Buffer.from(
'c1ac5e11fcd50c48bf567f6e296632f5801c4eb07a8a47579b41dee971a3099b',
'hex'),
'SHA3-384': Buffer.from(
'ac8c97f6dd8d9e16101063077c16b23fe291a5e6d149653e9ac7002365159317' +
'adcfad511996578b0053a5c14b75f16c', 'hex'),
'SHA3-512': Buffer.from(
'2162c2a8907e6b2f68599a69e81a464d8f076b5eeb555d98b4d20330034df3c7' +
'cf35b1fa958a074ca12f0d242df39f0da3d4f1dbfb3629057798fe1f883974ee',
'hex')
} : {})
'SHA3-256': Buffer.from(
'c1ac5e11fcd50c48bf567f6e296632f5801c4eb07a8a47579b41dee971a3099b',
'hex'),
'SHA3-384': Buffer.from(
'ac8c97f6dd8d9e16101063077c16b23fe291a5e6d149653e9ac7002365159317' +
'adcfad511996578b0053a5c14b75f16c', 'hex'),
'SHA3-512': Buffer.from(
'2162c2a8907e6b2f68599a69e81a464d8f076b5eeb555d98b4d20330034df3c7' +
'cf35b1fa958a074ca12f0d242df39f0da3d4f1dbfb3629057798fe1f883974ee',
'hex')
}

const vectors = [];
Expand Down
Loading
Loading