Skip to content

Commit 9301b8a

Browse files
committedJan 14, 2018
tls: make deprecated tls.createSecurePair() use public API
Make the deprecated `tls.createSecurePair()` method use other public APIs only (`TLSSocket` in particular). Since `tls.createSecurePair()` has been runtime-deprecated only since Node 8, it probably isn’t quite time to remove it yet, but this patch removes almost all of the code complexity that is retained by it. The API, as it is documented, is retained. However, it is very likely that some users have come to rely on parts of undocumented API of the `SecurePair` class, especially since some of the existing tests checked for those. Therefore, this should definitely be considered a breaking change. PR-URL: #17882 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Minwoo Jung <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
1 parent 02fef8a commit 9301b8a

15 files changed

+90
-1871
lines changed
 

‎lib/_tls_legacy.js

-956
This file was deleted.

‎lib/internal/streams/duplexpair.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
'use strict';
2+
const { Duplex } = require('stream');
3+
4+
const kCallback = Symbol('Callback');
5+
const kOtherSide = Symbol('Other');
6+
7+
class DuplexSocket extends Duplex {
8+
constructor() {
9+
super();
10+
this[kCallback] = null;
11+
this[kOtherSide] = null;
12+
}
13+
14+
_read() {
15+
const callback = this[kCallback];
16+
if (callback) {
17+
this[kCallback] = null;
18+
callback();
19+
}
20+
}
21+
22+
_write(chunk, encoding, callback) {
23+
this[kOtherSide][kCallback] = callback;
24+
this[kOtherSide].push(chunk);
25+
}
26+
27+
_final(callback) {
28+
this[kOtherSide].on('end', callback);
29+
this[kOtherSide].push(null);
30+
}
31+
}
32+
33+
class DuplexPair {
34+
constructor() {
35+
this.socket1 = new DuplexSocket();
36+
this.socket2 = new DuplexSocket();
37+
this.socket1[kOtherSide] = this.socket2;
38+
this.socket2[kOtherSide] = this.socket1;
39+
}
40+
}
41+
42+
module.exports = DuplexPair;

‎lib/tls.js

+35-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ const net = require('net');
3131
const url = require('url');
3232
const binding = process.binding('crypto');
3333
const Buffer = require('buffer').Buffer;
34+
const EventEmitter = require('events');
35+
const DuplexPair = require('internal/streams/duplexpair');
3436
const canonicalizeIP = process.binding('cares_wrap').canonicalizeIP;
3537

3638
// Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations
@@ -230,6 +232,33 @@ exports.checkServerIdentity = function checkServerIdentity(host, cert) {
230232
}
231233
};
232234

235+
236+
class SecurePair extends EventEmitter {
237+
constructor(secureContext = exports.createSecureContext(),
238+
isServer = false,
239+
requestCert = !isServer,
240+
rejectUnauthorized = false,
241+
options = {}) {
242+
super();
243+
const { socket1, socket2 } = new DuplexPair();
244+
245+
this.server = options.server;
246+
this.credentials = secureContext;
247+
248+
this.encrypted = socket1;
249+
this.cleartext = new exports.TLSSocket(socket2, Object.assign({
250+
secureContext, isServer, requestCert, rejectUnauthorized
251+
}, options));
252+
this.cleartext.once('secure', () => this.emit('secure'));
253+
}
254+
255+
destroy() {
256+
this.cleartext.destroy();
257+
this.encrypted.destroy();
258+
}
259+
}
260+
261+
233262
exports.parseCertString = internalUtil.deprecate(
234263
internalTLS.parseCertString,
235264
'tls.parseCertString() is deprecated. ' +
@@ -243,5 +272,9 @@ exports.Server = require('_tls_wrap').Server;
243272
exports.createServer = require('_tls_wrap').createServer;
244273
exports.connect = require('_tls_wrap').connect;
245274

246-
// Deprecated: DEP0064
247-
exports.createSecurePair = require('_tls_legacy').createSecurePair;
275+
exports.createSecurePair = internalUtil.deprecate(
276+
function createSecurePair(...args) {
277+
return new SecurePair(...args);
278+
},
279+
'tls.createSecurePair() is deprecated. Please use ' +
280+
'tls.TLSSocket instead.', 'DEP0064');

‎node.gyp

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
'lib/timers.js',
7070
'lib/tls.js',
7171
'lib/_tls_common.js',
72-
'lib/_tls_legacy.js',
7372
'lib/_tls_wrap.js',
7473
'lib/tty.js',
7574
'lib/url.js',
@@ -140,6 +139,7 @@
140139
'lib/internal/streams/lazy_transform.js',
141140
'lib/internal/streams/async_iterator.js',
142141
'lib/internal/streams/BufferList.js',
142+
'lib/internal/streams/duplexpair.js',
143143
'lib/internal/streams/legacy.js',
144144
'lib/internal/streams/destroy.js',
145145
'lib/internal/wrap_js_stream.js',

‎src/async_wrap.h

-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ namespace node {
6767

6868
#if HAVE_OPENSSL
6969
#define NODE_ASYNC_CRYPTO_PROVIDER_TYPES(V) \
70-
V(SSLCONNECTION) \
7170
V(PBKDF2REQUEST) \
7271
V(RANDOMBYTESREQUEST) \
7372
V(TLSWRAP)

‎src/env.h

-4
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,12 @@ class ModuleWrap;
193193
V(onheaders_string, "onheaders") \
194194
V(onmessage_string, "onmessage") \
195195
V(onnewsession_string, "onnewsession") \
196-
V(onnewsessiondone_string, "onnewsessiondone") \
197196
V(onocspresponse_string, "onocspresponse") \
198197
V(ongoawaydata_string, "ongoawaydata") \
199198
V(onpriority_string, "onpriority") \
200199
V(onread_string, "onread") \
201200
V(onreadstart_string, "onreadstart") \
202201
V(onreadstop_string, "onreadstop") \
203-
V(onselect_string, "onselect") \
204202
V(onsettings_string, "onsettings") \
205203
V(onshutdown_string, "onshutdown") \
206204
V(onsignal_string, "onsignal") \
@@ -224,15 +222,13 @@ class ModuleWrap;
224222
V(raw_string, "raw") \
225223
V(read_host_object_string, "_readHostObject") \
226224
V(readable_string, "readable") \
227-
V(received_shutdown_string, "receivedShutdown") \
228225
V(refresh_string, "refresh") \
229226
V(regexp_string, "regexp") \
230227
V(rename_string, "rename") \
231228
V(replacement_string, "replacement") \
232229
V(retry_string, "retry") \
233230
V(serial_string, "serial") \
234231
V(scopeid_string, "scopeid") \
235-
V(sent_shutdown_string, "sentShutdown") \
236232
V(serial_number_string, "serialNumber") \
237233
V(service_string, "service") \
238234
V(servername_string, "servername") \

‎src/node_crypto.cc

+1-631
Large diffs are not rendered by default.

‎src/node_crypto.h

-82
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,6 @@ class SSLWrap {
328328
void* arg);
329329
static int TLSExtStatusCallback(SSL* s, void* arg);
330330
static int SSLCertCallback(SSL* s, void* arg);
331-
static void SSLGetter(const v8::FunctionCallbackInfo<v8::Value>& info);
332331

333332
void DestroySSL();
334333
void WaitForCertCb(CertCb cb, void* arg);
@@ -364,87 +363,6 @@ class SSLWrap {
364363
friend class SecureContext;
365364
};
366365

367-
// Connection inherits from AsyncWrap because SSLWrap makes calls to
368-
// MakeCallback, but SSLWrap doesn't store the handle itself. Instead it
369-
// assumes that any args.This() called will be the handle from Connection.
370-
class Connection : public AsyncWrap, public SSLWrap<Connection> {
371-
public:
372-
~Connection() override {
373-
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
374-
sniObject_.Reset();
375-
servername_.Reset();
376-
#endif
377-
}
378-
379-
static void Initialize(Environment* env, v8::Local<v8::Object> target);
380-
void NewSessionDoneCb();
381-
382-
#ifndef OPENSSL_NO_NEXTPROTONEG
383-
v8::Persistent<v8::Object> npnProtos_;
384-
v8::Persistent<v8::Value> selectedNPNProto_;
385-
#endif
386-
387-
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
388-
v8::Persistent<v8::Object> sniObject_;
389-
v8::Persistent<v8::String> servername_;
390-
#endif
391-
392-
size_t self_size() const override { return sizeof(*this); }
393-
394-
protected:
395-
static void New(const v8::FunctionCallbackInfo<v8::Value>& args);
396-
static void EncIn(const v8::FunctionCallbackInfo<v8::Value>& args);
397-
static void ClearOut(const v8::FunctionCallbackInfo<v8::Value>& args);
398-
static void ClearPending(const v8::FunctionCallbackInfo<v8::Value>& args);
399-
static void EncPending(const v8::FunctionCallbackInfo<v8::Value>& args);
400-
static void EncOut(const v8::FunctionCallbackInfo<v8::Value>& args);
401-
static void ClearIn(const v8::FunctionCallbackInfo<v8::Value>& args);
402-
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
403-
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
404-
405-
#ifdef SSL_CTRL_SET_TLSEXT_SERVERNAME_CB
406-
// SNI
407-
static void GetServername(const v8::FunctionCallbackInfo<v8::Value>& args);
408-
static void SetSNICallback(const v8::FunctionCallbackInfo<v8::Value>& args);
409-
static int SelectSNIContextCallback_(SSL* s, int* ad, void* arg);
410-
#endif
411-
412-
static void OnClientHelloParseEnd(void* arg);
413-
414-
int HandleBIOError(BIO* bio, const char* func, int rv);
415-
416-
enum ZeroStatus {
417-
kZeroIsNotAnError,
418-
kZeroIsAnError
419-
};
420-
421-
enum SyscallStatus {
422-
kIgnoreSyscall,
423-
kSyscallError
424-
};
425-
426-
int HandleSSLError(const char* func, int rv, ZeroStatus zs, SyscallStatus ss);
427-
428-
void SetShutdownFlags();
429-
430-
Connection(Environment* env,
431-
v8::Local<v8::Object> wrap,
432-
SecureContext* sc,
433-
SSLWrap<Connection>::Kind kind);
434-
435-
private:
436-
static void SSLInfoCallback(const SSL *ssl, int where, int ret);
437-
438-
BIO *bio_read_;
439-
BIO *bio_write_;
440-
441-
uint8_t hello_data_[18432];
442-
size_t hello_offset_;
443-
444-
friend class ClientHelloParser;
445-
friend class SecureContext;
446-
};
447-
448366
class CipherBase : public BaseObject {
449367
public:
450368
~CipherBase() override {

‎test/async-hooks/test-connection.ssl.js

-86
This file was deleted.

‎test/async-hooks/test-graph.connection.js

-55
This file was deleted.

‎test/parallel/test-accessor-properties.js

-10
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,10 @@ const UDP = process.binding('udp_wrap').UDP;
5959
crypto.SecureContext.prototype._external;
6060
}, TypeError);
6161

62-
assert.throws(() => {
63-
crypto.Connection.prototype._external;
64-
}, TypeError);
65-
6662
assert.strictEqual(
6763
typeof Object.getOwnPropertyDescriptor(
6864
crypto.SecureContext.prototype, '_external'),
6965
'object'
7066
);
71-
72-
assert.strictEqual(
73-
typeof Object.getOwnPropertyDescriptor(
74-
crypto.Connection.prototype, '_external'),
75-
'object'
76-
);
7767
}
7868
}

‎test/parallel/test-tls-basic-validations.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ assert.throws(() => tls.createServer({ ticketKeys: Buffer.alloc(0) }),
4040
/TypeError: Ticket keys length must be 48 bytes/);
4141

4242
assert.throws(() => tls.createSecurePair({}),
43-
/Error: First argument must be a tls module SecureContext/);
43+
/TypeError: Second argument should be a SecureContext instance/);
4444

4545
{
4646
const buffer = Buffer.from('abcd');

‎test/parallel/test-tls-legacy-onselect.js

-27
This file was deleted.

‎test/parallel/test-tls-securepair-leak.js

+10-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if (!common.hasCrypto)
77

88
const assert = require('assert');
99
const { createSecureContext } = require('tls');
10-
const { createSecurePair } = require('_tls_legacy');
10+
const { createSecurePair } = require('tls');
1111

1212
const before = process.memoryUsage().external;
1313
{
@@ -16,11 +16,13 @@ const before = process.memoryUsage().external;
1616
for (let i = 0; i < 1e4; i += 1)
1717
createSecurePair(context, false, false, false, options).destroy();
1818
}
19-
global.gc();
20-
const after = process.memoryUsage().external;
19+
setImmediate(() => {
20+
global.gc();
21+
const after = process.memoryUsage().external;
2122

22-
// It's not an exact science but a SecurePair grows .external by about 45 kB.
23-
// Unless AdjustAmountOfExternalAllocatedMemory() is called on destruction,
24-
// 10,000 instances make it grow by well over 400 MB. Allow for some slop
25-
// because objects like buffers also affect the external limit.
26-
assert(after - before < 25 << 20);
23+
// It's not an exact science but a SecurePair grows .external by about 45 kB.
24+
// Unless AdjustAmountOfExternalAllocatedMemory() is called on destruction,
25+
// 10,000 instances make it grow by well over 400 MB. Allow for some slop
26+
// because objects like buffers also affect the external limit.
27+
assert(after - before < 25 << 20);
28+
});

‎test/sequential/test-async-wrap-getasyncid.js

-7
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,6 @@ function testInitialized(req, ctor_name) {
8787
}
8888

8989

90-
if (common.hasCrypto) { // eslint-disable-line crypto-check
91-
const tls = require('tls');
92-
// SecurePair
93-
testInitialized(tls.createSecurePair().ssl, 'Connection');
94-
}
95-
96-
9790
if (common.hasCrypto) { // eslint-disable-line crypto-check
9891
const crypto = require('crypto');
9992

0 commit comments

Comments
 (0)
Please sign in to comment.