tls: drop NPN (next protocol negotiation) support

NPN has been superseded by ALPN.  Chrome and Firefox removed support for
NPN in 2016 and 2017 respectively to no ill effect.

Fixes: https://github.com/nodejs/node/issues/14602
PR-URL: https://github.com/nodejs/node/pull/19403
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
Ben Noordhuis 2018-03-17 05:13:47 +01:00
parent b3f23910a2
commit 5bfbe5ceae
16 changed files with 80 additions and 829 deletions

View File

@ -1268,6 +1268,9 @@
# the real driver but that poses a security liability when an attacker
# is able to create a malicious DLL in one of the default search paths.
'OPENSSL_NO_HW',
# Disable NPN (Next Protocol Negotiation), superseded by ALPN.
'OPENSSL_NO_NEXTPROTONEG',
],
'openssl_default_defines_win': [
'MK1MF_BUILD',

View File

@ -2412,10 +2412,6 @@ the `crypto`, `tls`, and `https` modules and are generally specific to OpenSSL.
<td><code>DH_NOT_SUITABLE_GENERATOR</code></td>
<td></td>
</tr>
<tr>
<td><code>NPN_ENABLED</code></td>
<td></td>
</tr>
<tr>
<td><code>ALPN_ENABLED</code></td>
<td></td>

View File

@ -104,22 +104,17 @@ not required and a default ECDHE curve will be used. The `ecdhCurve` property
can be used when creating a TLS Server to specify the list of names of supported
curves to use, see [`tls.createServer()`] for more info.
### ALPN, NPN, and SNI
### ALPN and SNI
<!-- type=misc -->
ALPN (Application-Layer Protocol Negotiation Extension), NPN (Next
Protocol Negotiation) and, SNI (Server Name Indication) are TLS
handshake extensions:
ALPN (Application-Layer Protocol Negotiation Extension) and
SNI (Server Name Indication) are TLS handshake extensions:
* ALPN/NPN - Allows the use of one TLS server for multiple protocols (HTTP,
SPDY, HTTP/2)
* ALPN - Allows the use of one TLS server for multiple protocols (HTTP, HTTP/2)
* SNI - Allows the use of one TLS server for multiple hostnames with different
SSL certificates.
Use of ALPN is recommended over NPN. The NPN extension has never been
formally defined or documented and generally not recommended for use.
### Client-initiated renegotiation attack mitigation
<!-- type=misc -->
@ -332,12 +327,9 @@ server. If `tlsSocket.authorized` is `false`, then `socket.authorizationError`
is set to describe how authorization failed. Note that depending on the settings
of the TLS server, unauthorized connections may still be accepted.
The `tlsSocket.npnProtocol` and `tlsSocket.alpnProtocol` properties are strings
that contain the selected NPN and ALPN protocols, respectively. When both NPN
and ALPN extensions are received, ALPN takes precedence over NPN and the next
protocol is selected by ALPN.
When ALPN has no selected protocol, `tlsSocket.alpnProtocol` returns `false`.
The `tlsSocket.alpnProtocol` property is a string that contains the selected
ALPN protocol. When ALPN has no selected protocol, `tlsSocket.alpnProtocol`
equals `false`.
The `tlsSocket.servername` property is a string containing the server name
requested via SNI.
@ -468,7 +460,6 @@ changes:
(`isServer` is true) may optionally set `requestCert` to true to request a
client certificate.
* `rejectUnauthorized`: Optional, see [`tls.createServer()`][]
* `NPNProtocols`: Optional, see [`tls.createServer()`][]
* `ALPNProtocols`: Optional, see [`tls.createServer()`][]
* `SNICallback`: Optional, see [`tls.createServer()`][]
* `session` {Buffer} An optional `Buffer` instance containing a TLS session.
@ -509,9 +500,9 @@ regardless of whether or not the server's certificate has been authorized. It
is the client's responsibility to check the `tlsSocket.authorized` property to
determine if the server certificate was signed by one of the specified CAs. If
`tlsSocket.authorized === false`, then the error can be found by examining the
`tlsSocket.authorizationError` property. If either ALPN or NPN was used,
the `tlsSocket.alpnProtocol` or `tlsSocket.npnProtocol` properties can be
checked to determine the negotiated protocol.
`tlsSocket.authorizationError` property. If ALPN was used, the
`tlsSocket.alpnProtocol` property can be checked to determine the negotiated
protocol.
### tlsSocket.address()
<!-- YAML
@ -841,8 +832,7 @@ changes:
description: The `lookup` option is supported now.
- version: v8.0.0
pr-url: https://github.com/nodejs/node/pull/11984
description: The `ALPNProtocols` and `NPNProtocols` options can
be `Uint8Array`s now.
description: The `ALPNProtocols` option can be a `Uint8Array` now.
- version: v5.3.0, v4.7.0
pr-url: https://github.com/nodejs/node/pull/4246
description: The `secureContext` option is supported now.
@ -869,12 +859,6 @@ changes:
verified against the list of supplied CAs. An `'error'` event is emitted if
verification fails; `err.code` contains the OpenSSL error code. Defaults to
`true`.
* `NPNProtocols` {string[]|Buffer[]|Uint8Array[]|Buffer|Uint8Array}
An array of strings, `Buffer`s or `Uint8Array`s, or a single `Buffer` or
`Uint8Array` containing supported NPN protocols. `Buffer`s should have the
format `[len][name][len][name]...` e.g. `0x05hello0x05world`, where the
first byte is the length of the next protocol name. Passing an array is
usually much simpler, e.g. `['hello', 'world']`.
* `ALPNProtocols`: {string[]|Buffer[]|Uint8Array[]|Buffer|Uint8Array}
An array of strings, `Buffer`s or `Uint8Array`s, or a single `Buffer` or
`Uint8Array` containing the supported ALPN protocols. `Buffer`s should have
@ -1116,8 +1100,7 @@ changes:
description: The `options` parameter can now include `clientCertEngine`.
- version: v8.0.0
pr-url: https://github.com/nodejs/node/pull/11984
description: The `ALPNProtocols` and `NPNProtocols` options can
be `Uint8Array`s now.
description: The `ALPNProtocols` option can be a `Uint8Array` now.
- version: v5.0.0
pr-url: https://github.com/nodejs/node/pull/2564
description: ALPN options are supported now.
@ -1136,13 +1119,6 @@ changes:
* `rejectUnauthorized` {boolean} If not `false` the server will reject any
connection which is not authorized with the list of supplied CAs. This
option only has an effect if `requestCert` is `true`. Defaults to `true`.
* `NPNProtocols` {string[]|Buffer[]|Uint8Array[]|Buffer|Uint8Array}
An array of strings, `Buffer`s or `Uint8Array`s, or a single `Buffer` or
`Uint8Array` containing supported NPN protocols. `Buffer`s should have the
format `[len][name][len][name]...` e.g. `0x05hello0x05world`, where the
first byte is the length of the next protocol name. Passing an array is
usually much simpler, e.g. `['hello', 'world']`.
(Protocols should be ordered by their priority.)
* `ALPNProtocols`: {string[]|Buffer[]|Uint8Array[]|Buffer|Uint8Array}
An array of strings, `Buffer`s or `Uint8Array`s, or a single `Buffer` or
`Uint8Array` containing the supported ALPN protocols. `Buffer`s should have
@ -1150,9 +1126,6 @@ changes:
first byte is the length of the next protocol name. Passing an array is
usually much simpler, e.g. `['hello', 'world']`.
(Protocols should be ordered by their priority.)
When the server receives both NPN and ALPN extensions from the client,
ALPN takes precedence over NPN and the server does not send an NPN
extension to the client.
* `SNICallback(servername, cb)` {Function} A function that will be called if
the client supports SNI TLS extension. Two arguments will be passed when
called: `servername` and `cb`. `SNICallback` should invoke `cb(null, ctx)`,
@ -1333,7 +1306,6 @@ changes:
* `server` {net.Server} An optional [`net.Server`][] instance
* `requestCert`: Optional, see [`tls.createServer()`][]
* `rejectUnauthorized`: Optional, see [`tls.createServer()`][]
* `NPNProtocols`: Optional, see [`tls.createServer()`][]
* `ALPNProtocols`: Optional, see [`tls.createServer()`][]
* `SNICallback`: Optional, see [`tls.createServer()`][]
* `session` {Buffer} An optional `Buffer` instance containing a TLS session.

View File

@ -294,8 +294,6 @@ function initRead(tls, wrapped) {
function TLSSocket(socket, opts) {
const tlsOptions = Object.assign({}, opts);
if (tlsOptions.NPNProtocols)
tls.convertNPNProtocols(tlsOptions.NPNProtocols, tlsOptions);
if (tlsOptions.ALPNProtocols)
tls.convertALPNProtocols(tlsOptions.ALPNProtocols, tlsOptions);
@ -306,7 +304,6 @@ function TLSSocket(socket, opts) {
this._controlReleased = false;
this._SNICallback = null;
this.servername = null;
this.npnProtocol = null;
this.alpnProtocol = null;
this.authorized = false;
this.authorizationError = null;
@ -529,9 +526,6 @@ TLSSocket.prototype._init = function(socket, wrap) {
ssl.enableCertCb();
}
if (process.features.tls_npn && options.NPNProtocols)
ssl.setNPNProtocols(options.NPNProtocols);
if (process.features.tls_alpn && options.ALPNProtocols) {
// keep reference in secureContext not to be GC-ed
ssl._secureContext.alpnBuffer = options.ALPNProtocols;
@ -630,10 +624,6 @@ TLSSocket.prototype._releaseControl = function() {
};
TLSSocket.prototype._finishInit = function() {
if (process.features.tls_npn) {
this.npnProtocol = this._handle.getNegotiatedProtocol();
}
if (process.features.tls_alpn) {
this.alpnProtocol = this._handle.getALPNNegotiatedProtocol();
}
@ -790,7 +780,6 @@ function tlsConnectionListener(rawSocket) {
requestCert: this.requestCert,
rejectUnauthorized: this.rejectUnauthorized,
handshakeTimeout: this[kHandshakeTimeout],
NPNProtocols: this.NPNProtocols,
ALPNProtocols: this.ALPNProtocols,
SNICallback: this[kSNICallback] || SNICallback
});
@ -982,7 +971,6 @@ Server.prototype.setOptions = function(options) {
else
this.honorCipherOrder = true;
if (secureOptions) this.secureOptions = secureOptions;
if (options.NPNProtocols) tls.convertNPNProtocols(options.NPNProtocols, this);
if (options.ALPNProtocols)
tls.convertALPNProtocols(options.ALPNProtocols, this);
if (options.sessionIdContext) {
@ -1149,7 +1137,6 @@ exports.connect = function(...args /* [port,] [host,] [options,] [cb] */) {
requestCert: true,
rejectUnauthorized: options.rejectUnauthorized !== false,
session: options.session,
NPNProtocols: options.NPNProtocols,
ALPNProtocols: options.ALPNProtocols,
requestOCSP: options.requestOCSP
});

View File

@ -49,10 +49,6 @@ function Server(opts, requestListener) {
}
opts = util._extend({}, opts);
if (process.features.tls_npn && !opts.NPNProtocols) {
opts.NPNProtocols = ['http/1.1', 'http/1.0'];
}
if (process.features.tls_alpn && !opts.ALPNProtocols) {
// http/1.0 is not defined as Protocol IDs in IANA
// http://www.iana.org/assignments/tls-extensiontype-values

View File

@ -586,8 +586,8 @@
'mkssldef_flags': [
# Categories to export.
'-CAES,BF,BIO,DES,DH,DSA,EC,ECDH,ECDSA,ENGINE,EVP,HMAC,MD4,MD5,'
'NEXTPROTONEG,PSK,RC2,RC4,RSA,SHA,SHA0,SHA1,SHA256,SHA512,SOCK,'
'STDIO,TLSEXT,FP_API',
'PSK,RC2,RC4,RSA,SHA,SHA0,SHA1,SHA256,SHA512,SOCK,STDIO,TLSEXT,'
'FP_API',
# Defines.
'-DWIN32',
# Symbols to filter from the export list.

View File

@ -103,8 +103,6 @@ struct PackageConfig {
V(contextify_context_private_symbol, "node:contextify:context") \
V(contextify_global_private_symbol, "node:contextify:global") \
V(decorated_private_symbol, "node:decorated") \
V(npn_buffer_private_symbol, "node:npnBuffer") \
V(selected_npn_buffer_private_symbol, "node:selectedNpnBuffer") \
V(napi_env, "node:napi:env") \
V(napi_wrapper, "node:napi:wrapper") \

View File

@ -2824,13 +2824,6 @@ static Local<Object> GetFeatures(Environment* env) {
// TODO(bnoordhuis) ping libuv
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "ipv6"), True(env->isolate()));
#ifndef OPENSSL_NO_NEXTPROTONEG
Local<Boolean> tls_npn = True(env->isolate());
#else
Local<Boolean> tls_npn = False(env->isolate());
#endif
obj->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "tls_npn"), tls_npn);
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
Local<Boolean> tls_alpn = True(env->isolate());
#else

View File

@ -971,11 +971,6 @@ void DefineOpenSSLConstants(Local<Object> target) {
NODE_DEFINE_CONSTANT(target, DH_NOT_SUITABLE_GENERATOR);
#endif
#ifndef OPENSSL_NO_NEXTPROTONEG
#define NPN_ENABLED 1
NODE_DEFINE_CONSTANT(target, NPN_ENABLED);
#endif
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
#define ALPN_ENABLED 1
NODE_DEFINE_CONSTANT(target, ALPN_ENABLED);

View File

@ -81,7 +81,6 @@ using v8::DontDelete;
using v8::EscapableHandleScope;
using v8::Exception;
using v8::External;
using v8::False;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::HandleScope;
@ -231,7 +230,7 @@ static X509_STORE* root_cert_store;
// Just to generate static methods
template void SSLWrap<TLSWrap>::AddMethods(Environment* env,
Local<FunctionTemplate> t);
template void SSLWrap<TLSWrap>::InitNPN(SecureContext* sc);
template void SSLWrap<TLSWrap>::ConfigureSecureContext(SecureContext* sc);
template void SSLWrap<TLSWrap>::SetSNIContext(SecureContext* sc);
template int SSLWrap<TLSWrap>::SetCACerts(SecureContext* sc);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
@ -253,21 +252,6 @@ template void SSLWrap<TLSWrap>::OnClientHello(
void* arg,
const ClientHelloParser::ClientHello& hello);
#ifndef OPENSSL_NO_NEXTPROTONEG
template int SSLWrap<TLSWrap>::AdvertiseNextProtoCallback(
SSL* s,
const unsigned char** data,
unsigned int* len,
void* arg);
template int SSLWrap<TLSWrap>::SelectNextProtoCallback(
SSL* s,
unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen,
void* arg);
#endif
#ifdef NODE__HAVE_TLSEXT_STATUS_CB
template int SSLWrap<TLSWrap>::TLSExtStatusCallback(SSL* s, void* arg);
#endif
@ -1593,31 +1577,13 @@ void SSLWrap<Base>::AddMethods(Environment* env, Local<FunctionTemplate> t) {
env->SetProtoMethod(t, "setMaxSendFragment", SetMaxSendFragment);
#endif // SSL_set_max_send_fragment
#ifndef OPENSSL_NO_NEXTPROTONEG
env->SetProtoMethod(t, "getNegotiatedProtocol", GetNegotiatedProto);
#endif // OPENSSL_NO_NEXTPROTONEG
#ifndef OPENSSL_NO_NEXTPROTONEG
env->SetProtoMethod(t, "setNPNProtocols", SetNPNProtocols);
#endif
env->SetProtoMethod(t, "getALPNNegotiatedProtocol", GetALPNNegotiatedProto);
env->SetProtoMethod(t, "setALPNProtocols", SetALPNProtocols);
}
template <class Base>
void SSLWrap<Base>::InitNPN(SecureContext* sc) {
#ifndef OPENSSL_NO_NEXTPROTONEG
// Server should advertise NPN protocols
SSL_CTX_set_next_protos_advertised_cb(sc->ctx_,
AdvertiseNextProtoCallback,
nullptr);
// Client should select protocol from list of advertised
// If server supports NPN
SSL_CTX_set_next_proto_select_cb(sc->ctx_, SelectNextProtoCallback, nullptr);
#endif // OPENSSL_NO_NEXTPROTONEG
void SSLWrap<Base>::ConfigureSecureContext(SecureContext* sc) {
#ifdef NODE__HAVE_TLSEXT_STATUS_CB
// OCSP stapling
SSL_CTX_set_tlsext_status_cb(sc->ctx_, TLSExtStatusCallback);
@ -2474,148 +2440,6 @@ void SSLWrap<Base>::GetProtocol(const FunctionCallbackInfo<Value>& args) {
}
#ifndef OPENSSL_NO_NEXTPROTONEG
template <class Base>
int SSLWrap<Base>::AdvertiseNextProtoCallback(SSL* s,
const unsigned char** data,
unsigned int* len,
void* arg) {
Base* w = static_cast<Base*>(SSL_get_app_data(s));
Environment* env = w->env();
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
auto npn_buffer =
w->object()->GetPrivate(
env->context(),
env->npn_buffer_private_symbol()).ToLocalChecked();
if (npn_buffer->IsUndefined()) {
// No initialization - no NPN protocols
*data = reinterpret_cast<const unsigned char*>("");
*len = 0;
} else {
CHECK(Buffer::HasInstance(npn_buffer));
*data = reinterpret_cast<const unsigned char*>(Buffer::Data(npn_buffer));
*len = Buffer::Length(npn_buffer);
}
return SSL_TLSEXT_ERR_OK;
}
template <class Base>
int SSLWrap<Base>::SelectNextProtoCallback(SSL* s,
unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen,
void* arg) {
Base* w = static_cast<Base*>(SSL_get_app_data(s));
Environment* env = w->env();
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
auto npn_buffer =
w->object()->GetPrivate(
env->context(),
env->npn_buffer_private_symbol()).ToLocalChecked();
if (npn_buffer->IsUndefined()) {
// We should at least select one protocol
// If server is using NPN
*out = reinterpret_cast<unsigned char*>(const_cast<char*>("http/1.1"));
*outlen = 8;
// set status: unsupported
CHECK(
w->object()->SetPrivate(
env->context(),
env->selected_npn_buffer_private_symbol(),
False(env->isolate())).FromJust());
return SSL_TLSEXT_ERR_OK;
}
CHECK(Buffer::HasInstance(npn_buffer));
const unsigned char* npn_protos =
reinterpret_cast<const unsigned char*>(Buffer::Data(npn_buffer));
size_t len = Buffer::Length(npn_buffer);
int status = SSL_select_next_proto(out, outlen, in, inlen, npn_protos, len);
Local<Value> result;
switch (status) {
case OPENSSL_NPN_UNSUPPORTED:
result = Null(env->isolate());
break;
case OPENSSL_NPN_NEGOTIATED:
result = OneByteString(env->isolate(), *out, *outlen);
break;
case OPENSSL_NPN_NO_OVERLAP:
result = False(env->isolate());
break;
default:
break;
}
CHECK(
w->object()->SetPrivate(
env->context(),
env->selected_npn_buffer_private_symbol(),
result).FromJust());
return SSL_TLSEXT_ERR_OK;
}
template <class Base>
void SSLWrap<Base>::GetNegotiatedProto(
const FunctionCallbackInfo<Value>& args) {
Base* w;
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
Environment* env = w->env();
if (w->is_client()) {
auto selected_npn_buffer =
w->object()->GetPrivate(
env->context(),
env->selected_npn_buffer_private_symbol()).ToLocalChecked();
args.GetReturnValue().Set(selected_npn_buffer);
return;
}
const unsigned char* npn_proto;
unsigned int npn_proto_len;
SSL_get0_next_proto_negotiated(w->ssl_, &npn_proto, &npn_proto_len);
if (!npn_proto)
return args.GetReturnValue().Set(false);
args.GetReturnValue().Set(
OneByteString(args.GetIsolate(), npn_proto, npn_proto_len));
}
template <class Base>
void SSLWrap<Base>::SetNPNProtocols(const FunctionCallbackInfo<Value>& args) {
Base* w;
ASSIGN_OR_RETURN_UNWRAP(&w, args.Holder());
Environment* env = w->env();
if (args.Length() < 1)
return env->ThrowTypeError("NPN protocols argument is mandatory");
THROW_AND_RETURN_IF_NOT_BUFFER(args[0], "NPN protocols");
CHECK(
w->object()->SetPrivate(
env->context(),
env->npn_buffer_private_symbol(),
args[0]).FromJust());
}
#endif // OPENSSL_NO_NEXTPROTONEG
#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
template <class Base>
int SSLWrap<Base>::SelectALPNCallback(SSL* s,
@ -2883,7 +2707,7 @@ void SSLWrap<Base>::DestroySSL() {
template <class Base>
void SSLWrap<Base>::SetSNIContext(SecureContext* sc) {
InitNPN(sc);
ConfigureSecureContext(sc);
CHECK_EQ(SSL_set_SSL_CTX(ssl_, sc->ctx_), sc->ctx_);
SetCACerts(sc);

View File

@ -249,7 +249,7 @@ class SSLWrap {
static const int64_t kExternalSize = 4448 + 1024 + 42 * 1024;
#endif
static void InitNPN(SecureContext* sc);
static void ConfigureSecureContext(SecureContext* sc);
static void AddMethods(Environment* env, v8::Local<v8::FunctionTemplate> t);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
@ -295,22 +295,6 @@ class SSLWrap {
const v8::FunctionCallbackInfo<v8::Value>& args);
#endif // SSL_set_max_send_fragment
#ifndef OPENSSL_NO_NEXTPROTONEG
static void GetNegotiatedProto(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetNPNProtocols(const v8::FunctionCallbackInfo<v8::Value>& args);
static int AdvertiseNextProtoCallback(SSL* s,
const unsigned char** data,
unsigned int* len,
void* arg);
static int SelectNextProtoCallback(SSL* s,
unsigned char** out,
unsigned char* outlen,
const unsigned char* in,
unsigned int inlen,
void* arg);
#endif // OPENSSL_NO_NEXTPROTONEG
static void GetALPNNegotiatedProto(
const v8::FunctionCallbackInfo<v8::Value>& args);
static void SetALPNProtocols(const v8::FunctionCallbackInfo<v8::Value>& args);

View File

@ -135,7 +135,7 @@ void TLSWrap::InitSSL() {
}
#endif // SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
InitNPN(sc_);
ConfigureSecureContext(sc_);
SSL_set_cert_cb(ssl_, SSLWrap<TLSWrap>::SSLCertCallback, this);

View File

@ -12,12 +12,13 @@ const dftProtocol = {};
// test for immutable `opts`
{
const opts = { foo: 'bar', NPNProtocols: [ 'http/1.1' ] };
const opts = { foo: 'bar', ALPNProtocols: [ 'http/1.1' ] };
const server = https.createServer(opts);
tls.convertNPNProtocols([ 'http/1.1' ], dftProtocol);
assert.deepStrictEqual(opts, { foo: 'bar', NPNProtocols: [ 'http/1.1' ] });
assert.strictEqual(server.NPNProtocols.compare(dftProtocol.NPNProtocols), 0);
tls.convertALPNProtocols([ 'http/1.1' ], dftProtocol);
assert.deepStrictEqual(opts, { foo: 'bar', ALPNProtocols: [ 'http/1.1' ] });
assert.strictEqual(server.ALPNProtocols.compare(dftProtocol.ALPNProtocols),
0);
}
@ -26,8 +27,9 @@ const dftProtocol = {};
const mustNotCall = common.mustNotCall();
const server = https.createServer(mustNotCall);
tls.convertNPNProtocols([ 'http/1.1', 'http/1.0' ], dftProtocol);
assert.strictEqual(server.NPNProtocols.compare(dftProtocol.NPNProtocols), 0);
tls.convertALPNProtocols([ 'http/1.1' ], dftProtocol);
assert.strictEqual(server.ALPNProtocols.compare(dftProtocol.ALPNProtocols),
0);
assert.strictEqual(server.listeners('request').length, 1);
assert.strictEqual(server.listeners('request')[0], mustNotCall);
}
@ -37,6 +39,7 @@ const dftProtocol = {};
{
const server = https.createServer();
assert.strictEqual(server.NPNProtocols.compare(dftProtocol.NPNProtocols), 0);
assert.strictEqual(server.ALPNProtocols.compare(dftProtocol.ALPNProtocols),
0);
assert.strictEqual(server.listeners('request').length, 0);
}

View File

@ -4,9 +4,9 @@ const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
if (!process.features.tls_alpn || !process.features.tls_npn) {
if (!process.features.tls_alpn) {
common.skip(
'Skipping because node compiled without NPN or ALPN feature of OpenSSL.');
'Skipping because node compiled without ALPN feature of OpenSSL.');
}
const assert = require('assert');
@ -21,9 +21,7 @@ const serverIP = common.localhostIPv4;
function checkResults(result, expected) {
assert.strictEqual(result.server.ALPN, expected.server.ALPN);
assert.strictEqual(result.server.NPN, expected.server.NPN);
assert.strictEqual(result.client.ALPN, expected.client.ALPN);
assert.strictEqual(result.client.NPN, expected.client.NPN);
}
function runTest(clientsOptions, serverOptions, cb) {
@ -32,7 +30,7 @@ function runTest(clientsOptions, serverOptions, cb) {
const results = [];
let index = 0;
const server = tls.createServer(serverOptions, function(c) {
results[index].server = { ALPN: c.alpnProtocol, NPN: c.npnProtocol };
results[index].server = { ALPN: c.alpnProtocol };
});
server.listen(0, serverIP, function() {
@ -47,8 +45,7 @@ function runTest(clientsOptions, serverOptions, cb) {
results[index] = {};
const client = tls.connect(opt, function() {
results[index].client = { ALPN: client.alpnProtocol,
NPN: client.npnProtocol };
results[index].client = { ALPN: client.alpnProtocol };
client.destroy();
if (options.length) {
index++;
@ -62,471 +59,109 @@ function runTest(clientsOptions, serverOptions, cb) {
}
// Server: ALPN/NPN, Client: ALPN/NPN
// Server: ALPN, Client: ALPN
function Test1() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0],
{ server: { ALPN: 'a', NPN: false },
client: { ALPN: 'a', NPN: undefined } });
{ server: { ALPN: 'a' },
client: { ALPN: 'a' } });
// 'b' is selected by ALPN
checkResults(results[1],
{ server: { ALPN: 'b', NPN: false },
client: { ALPN: 'b', NPN: undefined } });
{ server: { ALPN: 'b' },
client: { ALPN: 'b' } });
// nothing is selected by ALPN
checkResults(results[2],
{ server: { ALPN: false, NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
{ server: { ALPN: false },
client: { ALPN: false } });
// execute next test
Test2();
});
}
// Server: ALPN/NPN, Client: ALPN
// Server: ALPN, Client: Nothing
function Test2() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
const clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
// nothing is selected by ALPN
checkResults(results[0],
{ server: { ALPN: 'a', NPN: false },
client: { ALPN: 'a', NPN: undefined } });
// 'b' is selected by ALPN
{ server: { ALPN: false },
client: { ALPN: false } });
// nothing is selected by ALPN
checkResults(results[1],
{ server: { ALPN: 'b', NPN: false },
client: { ALPN: 'b', NPN: undefined } });
{ server: { ALPN: false },
client: { ALPN: false } });
// nothing is selected by ALPN
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
{ server: { ALPN: false },
client: { ALPN: false } });
// execute next test
Test3();
});
}
// Server: ALPN/NPN, Client: NPN
// Server: Nothing, Client: ALPN
function Test3() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
const serverOptions = {};
const clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
ALPNrotocols: ['a', 'b', 'c'],
}, {
NPPNProtocols: ['c', 'b', 'e']
ALPNProtocols: ['c', 'b', 'e'],
}, {
NPPNProtocols: ['first-priority-unsupported', 'x', 'y']
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by NPN
checkResults(results[0],
{ server: { ALPN: false, NPN: 'a' },
client: { ALPN: false, NPN: 'a' } });
// nothing is selected by ALPN
checkResults(results[1],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
// nothing is selected
checkResults(results[0], { server: { ALPN: false },
client: { ALPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false },
client: { ALPN: false } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
{ server: { ALPN: false },
client: { ALPN: false } });
// execute next test
Test4();
});
}
// Server: ALPN/NPN, Client: Nothing
function Test4() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected by ALPN
checkResults(results[0],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
checkResults(results[1],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// execute next test
Test5();
});
}
// Server: ALPN, Client: ALPN/NPN
function Test5() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0], { server: { ALPN: 'a', NPN: false },
client: { ALPN: 'a', NPN: undefined } });
// 'b' is selected by ALPN
checkResults(results[1], { server: { ALPN: 'b', NPN: false },
client: { ALPN: 'b', NPN: undefined } });
// nothing is selected by ALPN
checkResults(results[2], { server: { ALPN: false,
NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
// execute next test
Test6();
});
}
// Server: ALPN, Client: ALPN
function Test6() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by ALPN
checkResults(results[0], { server: { ALPN: 'a', NPN: false },
client: { ALPN: 'a', NPN: undefined } });
// 'b' is selected by ALPN
checkResults(results[1], { server: { ALPN: 'b', NPN: false },
client: { ALPN: 'b', NPN: undefined } });
// nothing is selected by ALPN
checkResults(results[2], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// execute next test
Test7();
});
}
// Server: ALPN, Client: NPN
function Test7() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPNProtocols: ['c', 'b', 'e']
}, {
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected by ALPN
checkResults(results[0], { server: { ALPN: false, NPN: 'a' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
checkResults(results[1], { server: { ALPN: false, NPN: 'c' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
checkResults(results[2],
{ server: { ALPN: false, NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
// execute next test
Test8();
});
}
// Server: ALPN, Client: Nothing
function Test8() {
const serverOptions = {
ALPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected by ALPN
checkResults(results[0], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
checkResults(results[1], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected by ALPN
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// execute next test
Test9();
});
}
// Server: NPN, Client: ALPN/NPN
function Test9() {
const serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
ALPNrotocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by NPN
checkResults(results[0], { server: { ALPN: false, NPN: 'a' },
client: { ALPN: false, NPN: 'a' } });
// 'b' is selected by NPN
checkResults(results[1], { server: { ALPN: false, NPN: 'b' },
client: { ALPN: false, NPN: 'b' } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
// execute next test
Test10();
});
}
// Server: NPN, Client: ALPN
function Test10() {
const serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
ALPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[2], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// execute next test
Test11();
});
}
// Server: NPN, Client: NPN
function Test11() {
const serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPNProtocols: ['c', 'b', 'e']
}, {
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// 'a' is selected by NPN
checkResults(results[0], { server: { ALPN: false, NPN: 'a' },
client: { ALPN: false, NPN: 'a' } });
// 'b' is selected by NPN
checkResults(results[1], { server: { ALPN: false, NPN: 'b' },
client: { ALPN: false, NPN: 'b' } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
// execute next test
Test12();
});
}
// Server: NPN, Client: Nothing
function Test12() {
const serverOptions = {
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// execute next test
Test13();
});
}
// Server: Nothing, Client: ALPN/NPN
function Test13() {
const serverOptions = {};
const clientsOptions = [{
ALPNrotocols: ['a', 'b', 'c'],
NPNProtocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e'],
NPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y'],
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], { server: { ALPN: false, NPN: 'a' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false, NPN: 'c' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
// execute next test
Test14();
});
}
// Server: Nothing, Client: ALPN
function Test14() {
const serverOptions = {};
const clientsOptions = [{
ALPNrotocols: ['a', 'b', 'c']
}, {
ALPNProtocols: ['c', 'b', 'e']
}, {
ALPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
// execute next test
Test15();
});
}
// Server: Nothing, Client: NPN
function Test15() {
const serverOptions = {};
const clientsOptions = [{
NPNProtocols: ['a', 'b', 'c']
}, {
NPNProtocols: ['c', 'b', 'e']
}, {
NPNProtocols: ['first-priority-unsupported', 'x', 'y']
}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], { server: { ALPN: false, NPN: 'a' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false, NPN: 'c' },
client: { ALPN: false, NPN: false } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'first-priority-unsupported' },
client: { ALPN: false, NPN: false } });
// execute next test
Test16();
});
}
// Server: Nothing, Client: Nothing
function Test16() {
function Test4() {
const serverOptions = {};
const clientsOptions = [{}, {}, {}];
runTest(clientsOptions, serverOptions, function(results) {
// nothing is selected
checkResults(results[0], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
checkResults(results[0], { server: { ALPN: false },
client: { ALPN: false } });
// nothing is selected
checkResults(results[1], { server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
checkResults(results[1], { server: { ALPN: false },
client: { ALPN: false } });
// nothing is selected
checkResults(results[2],
{ server: { ALPN: false, NPN: 'http/1.1' },
client: { ALPN: false, NPN: false } });
{ server: { ALPN: false },
client: { ALPN: false } });
});
}

View File

@ -1,118 +0,0 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict';
const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');
if (!process.features.tls_npn)
common.skip('Skipping because node compiled without NPN feature of OpenSSL.');
const assert = require('assert');
const tls = require('tls');
const fixtures = require('../common/fixtures');
function loadPEM(n) {
return fixtures.readKey(`${n}.pem`);
}
const serverOptions = {
key: loadPEM('agent2-key'),
cert: loadPEM('agent2-cert'),
crl: loadPEM('ca2-crl'),
SNICallback: function(servername, cb) {
cb(null, tls.createSecureContext({
key: loadPEM('agent2-key'),
cert: loadPEM('agent2-cert'),
crl: loadPEM('ca2-crl'),
}));
},
NPNProtocols: ['a', 'b', 'c']
};
const clientsOptions = [{
port: undefined,
key: serverOptions.key,
cert: serverOptions.cert,
crl: serverOptions.crl,
NPNProtocols: ['a', 'b', 'c'],
rejectUnauthorized: false
}, {
port: undefined,
key: serverOptions.key,
cert: serverOptions.cert,
crl: serverOptions.crl,
NPNProtocols: ['c', 'b', 'e'],
rejectUnauthorized: false
}, {
port: undefined,
key: serverOptions.key,
cert: serverOptions.cert,
crl: serverOptions.crl,
rejectUnauthorized: false
}, {
port: undefined,
key: serverOptions.key,
cert: serverOptions.cert,
crl: serverOptions.crl,
NPNProtocols: ['first-priority-unsupported', 'x', 'y'],
rejectUnauthorized: false
}];
const serverResults = [];
const clientsResults = [];
const server = tls.createServer(serverOptions, function(c) {
serverResults.push(c.npnProtocol);
});
server.listen(0, startTest);
function startTest() {
function connectClient(options, callback) {
options.port = server.address().port;
const client = tls.connect(options, function() {
clientsResults.push(client.npnProtocol);
client.destroy();
callback();
});
}
connectClient(clientsOptions[0], function() {
connectClient(clientsOptions[1], function() {
connectClient(clientsOptions[2], function() {
connectClient(clientsOptions[3], function() {
server.close();
});
});
});
});
}
process.on('exit', function() {
assert.strictEqual(serverResults[0], clientsResults[0]);
assert.strictEqual(serverResults[1], clientsResults[1]);
assert.strictEqual(serverResults[2], 'http/1.1');
assert.strictEqual(clientsResults[2], false);
assert.strictEqual(serverResults[3], 'first-priority-unsupported');
assert.strictEqual(clientsResults[3], false);
});

View File

@ -1,7 +1,6 @@
'use strict';
// Test that TLSSocket can take arrays of strings for ALPNProtocols and
// NPNProtocols.
// Test that TLSSocket can take arrays of strings for ALPNProtocols.
const common = require('../common');
@ -12,12 +11,8 @@ const tls = require('tls');
new tls.TLSSocket(null, {
ALPNProtocols: ['http/1.1'],
NPNProtocols: ['http/1.1']
});
if (!process.features.tls_npn)
common.skip('node compiled without NPN feature of OpenSSL');
if (!process.features.tls_alpn)
common.skip('node compiled without ALPN feature of OpenSSL');
@ -37,17 +32,15 @@ const server = net.createServer(common.mustCall((s) => {
key,
cert,
ALPNProtocols: ['http/1.1'],
NPNProtocols: ['http/1.1']
});
tlsSocket.on('secure', common.mustCall(() => {
protocols.push({
alpnProtocol: tlsSocket.alpnProtocol,
npnProtocol: tlsSocket.npnProtocol
});
tlsSocket.end();
}));
}, 2));
}));
server.listen(0, common.mustCall(() => {
const alpnOpts = {
@ -55,24 +48,14 @@ server.listen(0, common.mustCall(() => {
rejectUnauthorized: false,
ALPNProtocols: ['h2', 'http/1.1']
};
const npnOpts = {
port: server.address().port,
rejectUnauthorized: false,
NPNProtocols: ['h2', 'http/1.1']
};
tls.connect(alpnOpts, function() {
this.end();
tls.connect(npnOpts, function() {
this.end();
server.close();
server.close();
assert.deepStrictEqual(protocols, [
{ alpnProtocol: 'http/1.1', npnProtocol: false },
{ alpnProtocol: false, npnProtocol: 'http/1.1' }
]);
});
assert.deepStrictEqual(protocols, [
{ alpnProtocol: 'http/1.1' },
]);
});
}));