| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c05f382 commit 61b20f6
48 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,12 +18,12 @@ void GetSSLCtx(const v8::FunctionCallbackInfo<v8::Value>& args) { | |||
| 18 | 18 | return; | |
| 19 | 19 | } | |
| 20 | 20 | ||
| 21 | - // Verify the pointer is a valid SSL_CTX by calling an OpenSSL function. | ||
| 22 | - const SSL_METHOD* method = SSL_CTX_get_ssl_method(ctx); | ||
| 23 | - if (method == nullptr) { | ||
| 21 | + // Verify the pointer is a valid SSL_CTX by calling a function available | ||
| 22 | + // across OpenSSL-compatible TLS backends and checking context-owned state. | ||
| 23 | + STACK_OF(SSL_CIPHER)* ciphers = SSL_CTX_get_ciphers(ctx); | ||
| 24 | + if (ciphers == nullptr) { | ||
| 24 | 25 | isolate->ThrowException(v8::Exception::Error( | |
| 25 | - v8::String::NewFromUtf8(isolate, | ||
| 26 | - "SSL_CTX_get_ssl_method returned nullptr") | ||
| 26 | + v8::String::NewFromUtf8(isolate, "SSL_CTX_get_ciphers returned nullptr") | ||
| 27 | 27 | .ToLocalChecked())); | |
| 28 | 28 | return; | |
| 29 | 29 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,346 @@ | |||
| 1 | + /* eslint-disable node-core/crypto-check */ | ||
| 2 | + | ||
| 3 | + 'use strict'; | ||
| 4 | + const common = require('../common'); | ||
| 5 | + const assert = require('assert'); | ||
| 6 | + const fixtures = require('../common/fixtures'); | ||
| 7 | + const tls = require('tls'); | ||
| 8 | + | ||
| 9 | + // This module is for BoringSSL-specific branches in tests whose original | ||
| 10 | + // OpenSSL coverage cannot run unchanged. Each helper should assert the | ||
| 11 | + // observable BoringSSL behavior that explains why the OpenSSL-specific | ||
| 12 | + // assertions are bypassed. | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * BoringSSL exposes many removed or disabled TLS cipher suites as "no match" | ||
| 16 | + * at secure-context creation time. This is used for suites such as | ||
| 17 | + * finite-field DHE and anonymous ECDH that OpenSSL builds may still negotiate | ||
| 18 | + * in tests. | ||
| 19 | + * @param {Function} fn | ||
| 20 | + */ | ||
| 21 | + function assertNoCipherMatch(fn) { | ||
| 22 | + assert.throws(fn, { | ||
| 23 | + code: 'ERR_SSL_NO_CIPHER_MATCH', | ||
| 24 | + library: 'SSL routines', | ||
| 25 | + function: 'OPENSSL_internal', | ||
| 26 | + reason: 'NO_CIPHER_MATCH', | ||
| 27 | + }); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + /** | ||
| 31 | + * BoringSSL does not parse OpenSSL cipher-string commands such as `@SECLEVEL`. | ||
| 32 | + * Those are OpenSSL policy directives, not cipher names. | ||
| 33 | + * @param {Function} fn | ||
| 34 | + */ | ||
| 35 | + function assertInvalidCommand(fn) { | ||
| 36 | + assert.throws(fn, { | ||
| 37 | + code: 'ERR_SSL_INVALID_COMMAND', | ||
| 38 | + library: 'SSL routines', | ||
| 39 | + function: 'OPENSSL_internal', | ||
| 40 | + reason: 'INVALID_COMMAND', | ||
| 41 | + }); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * Node's DHE tests exercise OpenSSL's finite-field DHE cipher support and DH | ||
| 46 | + * parameter-size policy. BoringSSL does not offer these DHE cipher suites on | ||
| 47 | + * this surface, so creating a server context with a DHE-only cipher list fails | ||
| 48 | + * before a handshake can test DH parameter behavior. | ||
| 49 | + */ | ||
| 50 | + function assertFiniteFieldDheUnsupported() { | ||
| 51 | + assertNoCipherMatch(() => { | ||
| 52 | + tls.createServer({ | ||
| 53 | + key: fixtures.readKey('agent2-key.pem'), | ||
| 54 | + cert: fixtures.readKey('agent2-cert.pem'), | ||
| 55 | + ciphers: 'DHE-RSA-AES128-GCM-SHA256', | ||
| 56 | + }); | ||
| 57 | + }); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + /** | ||
| 61 | + * OpenSSL security levels reject small keys by policy and can be adjusted with | ||
| 62 | + * `@SECLEVEL` in the cipher string. BoringSSL does not implement those security | ||
| 63 | + * levels: the small-key server context is accepted, while the OpenSSL-specific | ||
| 64 | + * `@SECLEVEL` command is rejected as invalid cipher-string syntax. | ||
| 65 | + */ | ||
| 66 | + function assertOpenSSLSecurityLevelsUnsupported() { | ||
| 67 | + const options = { | ||
| 68 | + key: fixtures.readKey('agent11-key.pem'), | ||
| 69 | + cert: fixtures.readKey('agent11-cert.pem'), | ||
| 70 | + ciphers: 'DEFAULT', | ||
| 71 | + }; | ||
| 72 | + | ||
| 73 | + tls.createServer(options).close(); | ||
| 74 | + | ||
| 75 | + options.ciphers = 'DEFAULT:@SECLEVEL=0'; | ||
| 76 | + assertInvalidCommand(() => tls.createServer(options)); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * Node's multi-key tests rely on OpenSSL accepting an array of private keys and | ||
| 81 | + * matching them with an array of certificates. BoringSSL rejects this mixed | ||
| 82 | + * EC/RSA identity configuration while configuring the certificate chain, before | ||
| 83 | + * a client can negotiate either identity. | ||
| 84 | + */ | ||
| 85 | + function assertMultiKeyUnsupported() { | ||
| 86 | + assert.throws(() => { | ||
| 87 | + tls.createServer({ | ||
| 88 | + key: [ | ||
| 89 | + fixtures.readKey('ec10-key.pem'), | ||
| 90 | + fixtures.readKey('agent1-key.pem'), | ||
| 91 | + ], | ||
| 92 | + cert: [ | ||
| 93 | + fixtures.readKey('agent1-cert.pem'), | ||
| 94 | + fixtures.readKey('ec10-cert.pem'), | ||
| 95 | + ], | ||
| 96 | + }); | ||
| 97 | + }, { | ||
| 98 | + code: 'ERR_OSSL_X509_KEY_TYPE_MISMATCH', | ||
| 99 | + library: 'X.509 certificate routines', | ||
| 100 | + function: 'OPENSSL_internal', | ||
| 101 | + reason: 'KEY_TYPE_MISMATCH', | ||
| 102 | + }); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + /** | ||
| 106 | + * BoringSSL does not support caller-initiated renegotiation. Even on a TLS 1.2 | ||
| 107 | + * connection, TLSSocket#renegotiate() returns false and the callback receives | ||
| 108 | + * Node's BoringSSL-specific unsupported-renegotiation error instead of | ||
| 109 | + * entering the native binding or exercising Node's renegotiation-limit logic. | ||
| 110 | + */ | ||
| 111 | + function testRenegotiationUnsupported() { | ||
| 112 | + const server = tls.createServer({ | ||
| 113 | + key: fixtures.readKey('rsa_private.pem'), | ||
| 114 | + cert: fixtures.readKey('rsa_cert.crt'), | ||
| 115 | + maxVersion: 'TLSv1.2', | ||
| 116 | + }, (socket) => socket.resume()); | ||
| 117 | + | ||
| 118 | + server.listen(0, common.mustCall(() => { | ||
| 119 | + const client = tls.connect({ | ||
| 120 | + port: server.address().port, | ||
| 121 | + rejectUnauthorized: false, | ||
| 122 | + maxVersion: 'TLSv1.2', | ||
| 123 | + }, common.mustCall(() => { | ||
| 124 | + const ok = client.renegotiate({}, common.mustCall((err) => { | ||
| 125 | + assert.throws(() => { throw err; }, { | ||
| 126 | + code: 'ERR_TLS_RENEGOTIATION_UNSUPPORTED', | ||
| 127 | + message: 'TLS session renegotiation is unsupported by this TLS ' + | ||
| 128 | + 'implementation', | ||
| 129 | + }); | ||
| 130 | + client.destroy(); | ||
| 131 | + server.close(); | ||
| 132 | + })); | ||
| 133 | + assert.strictEqual(ok, false); | ||
| 134 | + })); | ||
| 135 | + client.on('error', common.mustNotCall()); | ||
| 136 | + })); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + /** | ||
| 140 | + * OpenSSL exposes the negotiated ephemeral key type, name, and size for TLS | ||
| 141 | + * clients. With BoringSSL the same ECDHE TLS 1.2 handshake succeeds, but | ||
| 142 | + * getEphemeralKeyInfo() returns null on the server side and an object whose | ||
| 143 | + * fields are undefined on the client side. | ||
| 144 | + */ | ||
| 145 | + function testEphemeralKeyInfoUnsupported() { | ||
| 146 | + const server = tls.createServer({ | ||
| 147 | + key: fixtures.readKey('agent2-key.pem'), | ||
| 148 | + cert: fixtures.readKey('agent2-cert.pem'), | ||
| 149 | + ciphers: 'ECDHE-RSA-AES256-GCM-SHA384', | ||
| 150 | + ecdhCurve: 'prime256v1', | ||
| 151 | + maxVersion: 'TLSv1.2', | ||
| 152 | + }, common.mustCall((socket) => { | ||
| 153 | + assert.strictEqual(socket.getEphemeralKeyInfo(), null); | ||
| 154 | + socket.end(); | ||
| 155 | + })); | ||
| 156 | + | ||
| 157 | + server.listen(0, common.mustCall(() => { | ||
| 158 | + const client = tls.connect({ | ||
| 159 | + port: server.address().port, | ||
| 160 | + rejectUnauthorized: false, | ||
| 161 | + maxVersion: 'TLSv1.2', | ||
| 162 | + }, common.mustCall(() => { | ||
| 163 | + assert.deepStrictEqual(client.getEphemeralKeyInfo(), { | ||
| 164 | + type: undefined, | ||
| 165 | + name: undefined, | ||
| 166 | + size: undefined, | ||
| 167 | + }); | ||
| 168 | + server.close(); | ||
| 169 | + })); | ||
| 170 | + })); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + /** | ||
| 174 | + * The protocol matrix tests cover OpenSSL behavior for legacy TLS protocols. | ||
| 175 | + * For BoringSSL we only need to exhibit that a TLSv1-only client cannot connect | ||
| 176 | + * to a server whose minimum protocol is TLS 1.2; the client receives the | ||
| 177 | + * protocol-version alert instead of the OpenSSL version-specific matrix. | ||
| 178 | + */ | ||
| 179 | + function testLegacyProtocolUnsupported() { | ||
| 180 | + const server = tls.createServer({ | ||
| 181 | + key: fixtures.readKey('agent2-key.pem'), | ||
| 182 | + cert: fixtures.readKey('agent2-cert.pem'), | ||
| 183 | + minVersion: 'TLSv1.2', | ||
| 184 | + }, common.mustNotCall()); | ||
| 185 | + | ||
| 186 | + server.on('tlsClientError', common.mustCall()); | ||
| 187 | + server.listen(0, common.mustCall(() => { | ||
| 188 | + const client = tls.connect({ | ||
| 189 | + port: server.address().port, | ||
| 190 | + rejectUnauthorized: false, | ||
| 191 | + secureProtocol: 'TLSv1_method', | ||
| 192 | + }, common.mustNotCall()); | ||
| 193 | + client.on('error', common.mustCall((err) => { | ||
| 194 | + assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_PROTOCOL_VERSION'); | ||
| 195 | + server.close(); | ||
| 196 | + })); | ||
| 197 | + })); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + /** | ||
| 201 | + * BoringSSL can load a multi-PFX option well enough to serve the ECDSA | ||
| 202 | + * identity, but it does not provide the same OpenSSL multi-identity selection | ||
| 203 | + * behavior. After the ECDSA handshake succeeds, an RSA-only client fails with | ||
| 204 | + * no shared cipher instead of selecting the RSA identity from the same PFX list. | ||
| 205 | + */ | ||
| 206 | + function testMultiPfxSelectionDifference() { | ||
| 207 | + const server = tls.createServer({ | ||
| 208 | + pfx: [ | ||
| 209 | + { | ||
| 210 | + buf: fixtures.readKey('agent1.pfx'), | ||
| 211 | + passphrase: 'sample', | ||
| 212 | + }, | ||
| 213 | + fixtures.readKey('ec.pfx'), | ||
| 214 | + ], | ||
| 215 | + }, common.mustCallAtLeast((socket) => socket.end(), 1)); | ||
| 216 | + | ||
| 217 | + server.listen(0, common.mustCall(() => { | ||
| 218 | + const ecdsa = tls.connect(server.address().port, { | ||
| 219 | + ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384', | ||
| 220 | + maxVersion: 'TLSv1.2', | ||
| 221 | + rejectUnauthorized: false, | ||
| 222 | + }, common.mustCall(() => { | ||
| 223 | + assert.strictEqual(ecdsa.getCipher().name, | ||
| 224 | + 'ECDHE-ECDSA-AES256-GCM-SHA384'); | ||
| 225 | + ecdsa.end(); | ||
| 226 | + | ||
| 227 | + server.once('tlsClientError', common.mustCall((err) => { | ||
| 228 | + assert.strictEqual(err.code, 'ERR_SSL_NO_SHARED_CIPHER'); | ||
| 229 | + })); | ||
| 230 | + const rsa = tls.connect(server.address().port, { | ||
| 231 | + ciphers: 'ECDHE-RSA-AES256-GCM-SHA384', | ||
| 232 | + maxVersion: 'TLSv1.2', | ||
| 233 | + rejectUnauthorized: false, | ||
| 234 | + }, common.mustNotCall()); | ||
| 235 | + rsa.on('error', common.mustCall((err) => { | ||
| 236 | + assert.strictEqual(err.code, 'ERR_SSL_SSLV3_ALERT_HANDSHAKE_FAILURE'); | ||
| 237 | + server.close(); | ||
| 238 | + })); | ||
| 239 | + })); | ||
| 240 | + })); | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + /** | ||
| 244 | + * PSK works for TLS 1.2 in BoringSSL, but Node's PSK tests also cover the | ||
| 245 | + * default TLS 1.3 path. In that path BoringSSL does not complete a certificate- | ||
| 246 | + * less PSK-only handshake through Node's current server setup: the server | ||
| 247 | + * reports NO_CERTIFICATE_SET and the client receives an internal-error alert. | ||
| 248 | + */ | ||
| 249 | + function testPskTls13Unsupported() { | ||
| 250 | + const key = Buffer.from('d731ef57be09e5204f0b205b60627028', 'hex'); | ||
| 251 | + let gotClientError = false; | ||
| 252 | + let gotServerError = false; | ||
| 253 | + function maybeClose(server) { | ||
| 254 | + if (gotClientError && gotServerError) | ||
| 255 | + server.close(); | ||
| 256 | + } | ||
| 257 | + | ||
| 258 | + const server = tls.createServer({ | ||
| 259 | + ciphers: 'PSK+HIGH', | ||
| 260 | + pskCallback() { return key; }, | ||
| 261 | + }, common.mustNotCall()); | ||
| 262 | + | ||
| 263 | + server.once('tlsClientError', common.mustCall((err) => { | ||
| 264 | + assert.strictEqual(err.code, 'ERR_SSL_NO_CERTIFICATE_SET'); | ||
| 265 | + gotServerError = true; | ||
| 266 | + maybeClose(server); | ||
| 267 | + })); | ||
| 268 | + | ||
| 269 | + server.listen(0, common.mustCall(() => { | ||
| 270 | + const client = tls.connect({ | ||
| 271 | + port: server.address().port, | ||
| 272 | + ciphers: 'PSK+HIGH', | ||
| 273 | + checkServerIdentity() {}, | ||
| 274 | + pskCallback() { | ||
| 275 | + return { psk: key, identity: 'TestUser' }; | ||
| 276 | + }, | ||
| 277 | + }, common.mustNotCall()); | ||
| 278 | + client.on('error', common.mustCall((err) => { | ||
| 279 | + assert.strictEqual(err.code, 'ERR_SSL_TLSV1_ALERT_INTERNAL_ERROR'); | ||
| 280 | + gotClientError = true; | ||
| 281 | + maybeClose(server); | ||
| 282 | + })); | ||
| 283 | + })); | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + /** | ||
| 287 | + * The OpenSSL ticket tests assume that once a TLS 1.3 session is reused, the | ||
| 288 | + * client will not necessarily receive a replacement session event before close. | ||
| 289 | + * BoringSSL emits new session tickets on both the initial and resumed TLS 1.3 | ||
| 290 | + * connections, so the resumed connection still emits at least one 'session' | ||
| 291 | + * event while isSessionReused() is true. | ||
| 292 | + */ | ||
| 293 | + function testTls13SessionTicketSemanticsDiffer() { | ||
| 294 | + const server = tls.createServer({ | ||
| 295 | + key: fixtures.readKey('agent1-key.pem'), | ||
| 296 | + cert: fixtures.readKey('agent1-cert.pem'), | ||
| 297 | + }, (socket) => socket.end()); | ||
| 298 | + | ||
| 299 | + let session; | ||
| 300 | + let secondSessionEvents = 0; | ||
| 301 | + | ||
| 302 | + server.listen(0, common.mustCall(() => { | ||
| 303 | + const first = tls.connect({ | ||
| 304 | + port: server.address().port, | ||
| 305 | + rejectUnauthorized: false, | ||
| 306 | + }, common.mustCall(() => { | ||
| 307 | + assert.strictEqual(first.isSessionReused(), false); | ||
| 308 | + })); | ||
| 309 | + first.on('session', common.mustCallAtLeast((sess) => { | ||
| 310 | + session = sess; | ||
| 311 | + }, 1)); | ||
| 312 | + first.on('close', common.mustCall(() => { | ||
| 313 | + assert(Buffer.isBuffer(session)); | ||
| 314 | + | ||
| 315 | + const second = tls.connect({ | ||
| 316 | + port: server.address().port, | ||
| 317 | + rejectUnauthorized: false, | ||
| 318 | + session, | ||
| 319 | + }, common.mustCall(() => { | ||
| 320 | + assert.strictEqual(second.isSessionReused(), true); | ||
| 321 | + })); | ||
| 322 | + second.on('session', common.mustCallAtLeast(() => { | ||
| 323 | + secondSessionEvents++; | ||
| 324 | + }, 1)); | ||
| 325 | + second.on('close', common.mustCall(() => { | ||
| 326 | + assert(secondSessionEvents > 0); | ||
| 327 | + server.close(); | ||
| 328 | + })); | ||
| 329 | + second.resume(); | ||
| 330 | + })); | ||
| 331 | + first.resume(); | ||
| 332 | + })); | ||
| 333 | + } | ||
| 334 | + | ||
| 335 | + module.exports = { | ||
| 336 | + assertFiniteFieldDheUnsupported, | ||
| 337 | + assertMultiKeyUnsupported, | ||
| 338 | + assertNoCipherMatch, | ||
| 339 | + assertOpenSSLSecurityLevelsUnsupported, | ||
| 340 | + testEphemeralKeyInfoUnsupported, | ||
| 341 | + testLegacyProtocolUnsupported, | ||
| 342 | + testMultiPfxSelectionDifference, | ||
| 343 | + testPskTls13Unsupported, | ||
| 344 | + testRenegotiationUnsupported, | ||
| 345 | + testTls13SessionTicketSemanticsDiffer, | ||
| 346 | + }; | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments