Skip to content
File

Blob: src/workerd/api/node/tests/tls-nodejs-test.js

javascript1039 lines
1// Copyright (c) 2017-2022 Cloudflare, Inc.
2// Licensed under the Apache 2.0 license found in the LICENSE file or at:
3// https://opensource.org/licenses/Apache-2.0
4//
5// Copyright Joyent, Inc. and other Node contributors.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a
8// copy of this software and associated documentation files (the
9// "Software"), to deal in the Software without restriction, including
10// without limitation the rights to use, copy, modify, merge, publish,
11// distribute, sublicense, and/or sell copies of the Software, and to permit
12// persons to whom the Software is furnished to do so, subject to the
13// following conditions:
14//
15// The above copyright notice and this permission notice shall be included
16// in all copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
21// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24// USE OR OTHER DEALINGS IN THE SOFTWARE.
25import { connect } from 'cloudflare:sockets';
26import tls from 'node:tls';
27import {
28 strictEqual,
29 ok,
30 rejects,
31 throws,
32 doesNotThrow,
33 deepStrictEqual,
34} from 'node:assert';
35import { once } from 'node:events';
36import { inspect } from 'node:util';
37import net from 'node:net';
38import { translatePeerCertificate } from '_tls_common';
39import { mock } from 'node:test';
40import stream from 'node:stream';
41 
42export const checkPortsSetCorrectly = {
43 test(ctrl, env, ctx) {
44 ok(env.ECHO_SERVER_PORT);
45 ok(env.HELLO_SERVER_PORT);
46 ok(env.JS_STREAM_SERVER_PORT);
47 ok(env.STREAM_WRAP_SERVER_PORT);
48 },
49};
50 
51// Tests are taken from
52// https://github.com/nodejs/node/blob/304743655d5236c2edc39094336ee2667600b684/test/parallel/test-tls-connect-abort-controller.js
53export const tlsConnectAbortController = {
54 async test(ctrl, env, ctx) {
55 // Our tests differ from Node.js
56 // We don't check for abortSignal listener count because it's not supported.
57 const connectOptions = (signal) => ({
58 port: env.ECHO_SERVER_PORT,
59 host: 'localhost',
60 signal,
61 });
62 
63 const assertAbort = (socket, testName) => {
64 return rejects(
65 () => once(socket, 'close'),
66 {
67 name: 'AbortError',
68 },
69 `AbortError should have been thrown on ${testName}`
70 );
71 };
72 
73 async function postAbort() {
74 const ac = new AbortController();
75 const { signal } = ac;
76 const socket = tls.connect(connectOptions(signal));
77 ac.abort();
78 await assertAbort(socket, 'postAbort');
79 }
80 
81 async function preAbort() {
82 const ac = new AbortController();
83 const { signal } = ac;
84 ac.abort();
85 const socket = tls.connect(connectOptions(signal));
86 await assertAbort(socket, 'preAbort');
87 }
88 
89 async function tickAbort() {
90 const ac = new AbortController();
91 const { signal } = ac;
92 const socket = tls.connect(connectOptions(signal));
93 setImmediate(() => ac.abort());
94 await assertAbort(socket, 'tickAbort');
95 }
96 
97 async function testConstructor() {
98 const ac = new AbortController();
99 const { signal } = ac;
100 ac.abort();
101 const socket = new tls.TLSSocket(undefined, connectOptions(signal));
102 await assertAbort(socket, 'testConstructor');
103 }
104 
105 async function testConstructorPost() {
106 const ac = new AbortController();
107 const { signal } = ac;
108 const socket = new tls.TLSSocket(undefined, connectOptions(signal));
109 ac.abort();
110 await assertAbort(socket, 'testConstructorPost');
111 }
112 
113 async function testConstructorPostTick() {
114 const ac = new AbortController();
115 const { signal } = ac;
116 const socket = new tls.TLSSocket(undefined, connectOptions(signal));
117 setImmediate(() => ac.abort());
118 await assertAbort(socket, 'testConstructorPostTick');
119 }
120 
121 await postAbort();
122 await preAbort();
123 await tickAbort();
124 await testConstructor();
125 await testConstructorPost();
126 await testConstructorPostTick();
127 },
128};
129 
130// Tests are taken from
131// https://github.com/nodejs/node/blob/304743655d5236c2edc39094336ee2667600b684/test/parallel/test-tls-connect-allow-half-open-option.js
132export const connectAllowHalfOpenOption = {
133 async test(ctrl, env, ctx) {
134 {
135 const socket = tls.connect({ port: 42, lookup() {} });
136 strictEqual(socket.allowHalfOpen, false);
137 }
138 
139 {
140 const socket = tls.connect({
141 port: 42,
142 allowHalfOpen: false,
143 lookup() {},
144 });
145 strictEqual(socket.allowHalfOpen, false);
146 }
147 
148 {
149 const { promise, resolve } = Promise.withResolvers();
150 const socket = tls.connect(
151 {
152 port: env.ECHO_SERVER_PORT,
153 allowHalfOpen: true,
154 },
155 () => {
156 let message = '';
157 
158 socket.on('data', (chunk) => {
159 message += chunk;
160 });
161 
162 socket.on('end', () => {
163 strictEqual(message, 'Hello');
164 resolve();
165 });
166 
167 socket.write('Hello');
168 socket.end();
169 }
170 );
171 
172 socket.setEncoding('utf8');
173 
174 await promise;
175 }
176 },
177};
178 
179// Tests are taken from
180// https://github.com/nodejs/node/blob/755e4603fd1679de72d250514ea5096b272ae8d6/test/parallel/test-tls-connect-simple.js
181export const tlsConnectSimple = {
182 async test(ctrl, env, ctx) {
183 const promise1 = Promise.withResolvers();
184 const promise2 = Promise.withResolvers();
185 const options = { port: env.ECHO_SERVER_PORT };
186 const client1 = tls.connect(options, function () {
187 client1.end();
188 promise1.resolve();
189 });
190 const client2 = tls.connect(options);
191 client2.on('secureConnect', function () {
192 client2.end();
193 promise2.resolve();
194 });
195 await Promise.all([promise1.promise, promise2.promise]);
196 },
197};
198 
199// Tests are taken from
200// https://github.com/nodejs/node/blob/755e4603fd1679de72d250514ea5096b272ae8d6/test/parallel/test-tls-connect-timeout-option.js
201export const tlsConnectTimeoutOption = {
202 async test(ctrl, env, ctx) {
203 const socket = tls.connect({
204 port: env.ECHO_SERVER_PORT,
205 lookup: () => {},
206 timeout: 1000,
207 });
208 
209 strictEqual(socket.timeout, 1000);
210 },
211};
212 
213// Tests are taken from
214// https://github.com/nodejs/node/blob/755e4603fd1679de72d250514ea5096b272ae8d6/test/parallel/test-tls-connect-no-host.js
215export const tlsConnectNoHost = {
216 async test(ctrl, env, ctx) {
217 const { promise, resolve } = Promise.withResolvers();
218 const socket = tls.connect(
219 {
220 port: env.ECHO_SERVER_PORT,
221 // No host set here. 'localhost' is the default,
222 // but tls.checkServerIdentity() breaks before the fix with:
223 // Error: Hostname/IP doesn't match certificate's altnames:
224 // "Host: undefined. is not cert's CN: localhost"
225 },
226 function () {
227 ok(socket.authorized);
228 resolve();
229 }
230 );
231 await promise;
232 },
233};
234 
235// Tests are taken from
236// https://github.com/nodejs/node/blob/755e4603fd1679de72d250514ea5096b272ae8d6/test/parallel/test-tls-connect-given-socket.js
237export const tlsConnectGivenSocket = {
238 async test(ctrl, env, ctx) {
239 const promises = [];
240 let waiting = 2;
241 function establish(socket, calls) {
242 const { promise, resolve, reject } = Promise.withResolvers();
243 promises.push(promise);
244 const onConnectFn = mock.fn(() => {
245 if (calls === 0) {
246 reject(new Error('Should not have called onConnect callback'));
247 return;
248 }
249 let data = '';
250 let dataFn = mock.fn((chunk) => {
251 data += chunk.toString();
252 });
253 client.on('data', dataFn);
254 client.on('end', () => {
255 strictEqual(data, 'Hello');
256 if (--waiting === 0) {
257 ok(dataFn.mock.callCount());
258 resolve();
259 }
260 });
261 });
262 const client = tls.connect({ socket }, onConnectFn);
263 ok(client.readable);
264 ok(client.writable);
265 
266 if (calls === 0) {
267 queueMicrotask(resolve);
268 }
269 
270 return client;
271 }
272 
273 const port = env.HELLO_SERVER_PORT;
274 // Immediate death socket
275 const immediateDeath = net.connect(port);
276 establish(immediateDeath, 0).destroy();
277 
278 // Outliving
279 {
280 const { promise, resolve } = Promise.withResolvers();
281 promises.push(promise);
282 const outlivingTCP = net.connect(port, () => {
283 outlivingTLS.destroy();
284 next();
285 resolve();
286 });
287 const outlivingTLS = establish(outlivingTCP, 0);
288 }
289 
290 function next() {
291 // Already connected socket
292 const { promise, resolve } = Promise.withResolvers();
293 promises.push(promise);
294 const connected = net.connect(port, () => {
295 establish(connected);
296 resolve();
297 });
298 
299 // Connecting socket
300 const connecting = net.connect(port);
301 establish(connecting);
302 }
303 
304 await Promise.all(promises);
305 },
306};
307 
308export const testSecureContext = {
309 async test() {
310 throws(() => tls.connect({ port: 42, secureContext: {} }), {
311 code: 'ERR_TLS_INVALID_CONTEXT',
312 });
313 
314 doesNotThrow(() => {
315 const secureContext = tls.createSecureContext({});
316 tls.connect({ port: 42, secureContext });
317 });
318 
319 doesNotThrow(() => {
320 const secureContext = tls.SecureContext();
321 tls.connect({ port: 42, secureContext });
322 });
323 },
324};
325 
326// Tests are taken from
327// https://github.com/nodejs/node/blob/98513884684bccf944d7834f4820b061af41fb36/test/parallel/test-tls-check-server-identity.js
328export const testCheckServerIdentity = {
329 async test() {
330 const tests = [
331 // False-y values.
332 {
333 host: false,
334 cert: { subject: { CN: 'a.com' } },
335 error: "Host: false. is not cert's CN: a.com",
336 },
337 {
338 host: null,
339 cert: { subject: { CN: 'a.com' } },
340 error: "Host: null. is not cert's CN: a.com",
341 },
342 {
343 host: undefined,
344 cert: { subject: { CN: 'a.com' } },
345 error: "Host: undefined. is not cert's CN: a.com",
346 },
347 
348 // Basic CN handling
349 { host: 'a.com', cert: { subject: { CN: 'a.com' } } },
350 { host: 'a.com', cert: { subject: { CN: 'A.COM' } } },
351 {
352 host: 'a.com',
353 cert: { subject: { CN: 'b.com' } },
354 error: "Host: a.com. is not cert's CN: b.com",
355 },
356 { host: 'a.com', cert: { subject: { CN: 'a.com.' } } },
357 {
358 host: 'a.com',
359 cert: { subject: { CN: '.a.com' } },
360 error: "Host: a.com. is not cert's CN: .a.com",
361 },
362 
363 // IP address in CN. Technically allowed but so rare that we reject
364 // it anyway. If we ever do start allowing them, we should take care
365 // to only allow public (non-internal, non-reserved) IP addresses,
366 // because that's what the spec mandates.
367 {
368 host: '8.8.8.8',
369 cert: { subject: { CN: '8.8.8.8' } },
370 error: "IP: 8.8.8.8 is not in the cert's list: ",
371 },
372 
373 // The spec suggests that a "DNS:" Subject Alternative Name containing an
374 // IP address is valid but it seems so suspect that we currently reject it.
375 {
376 host: '8.8.8.8',
377 cert: { subject: { CN: '8.8.8.8' }, subjectaltname: 'DNS:8.8.8.8' },
378 error: "IP: 8.8.8.8 is not in the cert's list: ",
379 },
380 
381 // Likewise for "URI:" Subject Alternative Names.
382 // See also https://github.com/nodejs/node/issues/8108.
383 {
384 host: '8.8.8.8',
385 cert: {
386 subject: { CN: '8.8.8.8' },
387 subjectaltname: 'URI:http://8.8.8.8/',
388 },
389 error: "IP: 8.8.8.8 is not in the cert's list: ",
390 },
391 
392 // An "IP Address:" Subject Alternative Name however is acceptable.
393 {
394 host: '8.8.8.8',
395 cert: {
396 subject: { CN: '8.8.8.8' },
397 subjectaltname: 'IP Address:8.8.8.8',
398 },
399 },
400 
401 // But not when it's a CIDR.
402 {
403 host: '8.8.8.8',
404 cert: {
405 subject: { CN: '8.8.8.8' },
406 subjectaltname: 'IP Address:8.8.8.0/24',
407 },
408 error: "IP: 8.8.8.8 is not in the cert's list: ",
409 },
410 
411 // Wildcards in CN
412 { host: 'b.a.com', cert: { subject: { CN: '*.a.com' } } },
413 {
414 host: 'ba.com',
415 cert: { subject: { CN: '*.a.com' } },
416 error: "Host: ba.com. is not cert's CN: *.a.com",
417 },
418 {
419 host: '\n.b.com',
420 cert: { subject: { CN: '*n.b.com' } },
421 error: "Host: \n.b.com. is not cert's CN: *n.b.com",
422 },
423 {
424 host: 'b.a.com',
425 cert: {
426 subjectaltname: 'DNS:omg.com',
427 subject: { CN: '*.a.com' },
428 },
429 error: "Host: b.a.com. is not in the cert's altnames: " + 'DNS:omg.com',
430 },
431 {
432 host: 'b.a.com',
433 cert: { subject: { CN: 'b*b.a.com' } },
434 error: "Host: b.a.com. is not cert's CN: b*b.a.com",
435 },
436 
437 // Empty Cert
438 {
439 host: 'a.com',
440 cert: {},
441 error: 'Cert does not contain a DNS name',
442 },
443 
444 // Empty Subject w/DNS name
445 {
446 host: 'a.com',
447 cert: {
448 subjectaltname: 'DNS:a.com',
449 },
450 },
451 
452 // Empty Subject w/URI name
453 {
454 host: 'a.b.a.com',
455 cert: {
456 subjectaltname: 'URI:http://a.b.a.com/',
457 },
458 error: 'Cert does not contain a DNS name',
459 },
460 
461 // Multiple CN fields
462 {
463 host: 'foo.com',
464 cert: {
465 subject: { CN: ['foo.com', 'bar.com'] }, // CN=foo.com; CN=bar.com;
466 },
467 },
468 
469 // DNS names and CN
470 {
471 host: 'a.com',
472 cert: {
473 subjectaltname: 'DNS:*',
474 subject: { CN: 'b.com' },
475 },
476 error: "Host: a.com. is not in the cert's altnames: " + 'DNS:*',
477 },
478 {
479 host: 'a.com',
480 cert: {
481 subjectaltname: 'DNS:*.com',
482 subject: { CN: 'b.com' },
483 },
484 error: "Host: a.com. is not in the cert's altnames: " + 'DNS:*.com',
485 },
486 {
487 host: 'a.co.uk',
488 cert: {
489 subjectaltname: 'DNS:*.co.uk',
490 subject: { CN: 'b.com' },
491 },
492 },
493 {
494 host: 'a.com',
495 cert: {
496 subjectaltname: 'DNS:*.a.com',
497 subject: { CN: 'a.com' },
498 },
499 error: "Host: a.com. is not in the cert's altnames: " + 'DNS:*.a.com',
500 },
501 {
502 host: 'a.com',
503 cert: {
504 subjectaltname: 'DNS:*.a.com',
505 subject: { CN: 'b.com' },
506 },
507 error: "Host: a.com. is not in the cert's altnames: " + 'DNS:*.a.com',
508 },
509 {
510 host: 'a.com',
511 cert: {
512 subjectaltname: 'DNS:a.com',
513 subject: { CN: 'b.com' },
514 },
515 },
516 {
517 host: 'a.com',
518 cert: {
519 subjectaltname: 'DNS:A.COM',
520 subject: { CN: 'b.com' },
521 },
522 },
523 
524 // DNS names
525 {
526 host: 'a.com',
527 cert: {
528 subjectaltname: 'DNS:*.a.com',
529 subject: {},
530 },
531 error: "Host: a.com. is not in the cert's altnames: " + 'DNS:*.a.com',
532 },
533 {
534 host: 'b.a.com',
535 cert: {
536 subjectaltname: 'DNS:*.a.com',
537 subject: {},
538 },
539 },
540 {
541 host: 'c.b.a.com',
542 cert: {
543 subjectaltname: 'DNS:*.a.com',
544 subject: {},
545 },
546 error:
547 "Host: c.b.a.com. is not in the cert's altnames: " + 'DNS:*.a.com',
548 },
549 {
550 host: 'b.a.com',
551 cert: {
552 subjectaltname: 'DNS:*b.a.com',
553 subject: {},
554 },
555 },
556 {
557 host: 'a-cb.a.com',
558 cert: {
559 subjectaltname: 'DNS:*b.a.com',
560 subject: {},
561 },
562 },
563 {
564 host: 'a.b.a.com',
565 cert: {
566 subjectaltname: 'DNS:*b.a.com',
567 subject: {},
568 },
569 error:
570 "Host: a.b.a.com. is not in the cert's altnames: " + 'DNS:*b.a.com',
571 },
572 // Multiple DNS names
573 {
574 host: 'a.b.a.com',
575 cert: {
576 subjectaltname: 'DNS:*b.a.com, DNS:a.b.a.com',
577 subject: {},
578 },
579 },
580 // URI names
581 {
582 host: 'a.b.a.com',
583 cert: {
584 subjectaltname: 'URI:http://a.b.a.com/',
585 subject: {},
586 },
587 error: 'Cert does not contain a DNS name',
588 },
589 {
590 host: 'a.b.a.com',
591 cert: {
592 subjectaltname: 'URI:http://*.b.a.com/',
593 subject: {},
594 },
595 error: 'Cert does not contain a DNS name',
596 },
597 // IP addresses
598 {
599 host: 'a.b.a.com',
600 cert: {
601 subjectaltname: 'IP Address:127.0.0.1',
602 subject: {},
603 },
604 error: 'Cert does not contain a DNS name',
605 },
606 {
607 host: '127.0.0.1',
608 cert: {
609 subjectaltname: 'IP Address:127.0.0.1',
610 subject: {},
611 },
612 },
613 {
614 host: '127.0.0.2',
615 cert: {
616 subjectaltname: 'IP Address:127.0.0.1',
617 subject: {},
618 },
619 error: "IP: 127.0.0.2 is not in the cert's list: " + '127.0.0.1',
620 },
621 {
622 host: '127.0.0.1',
623 cert: {
624 subjectaltname: 'DNS:a.com',
625 subject: {},
626 },
627 error: "IP: 127.0.0.1 is not in the cert's list: ",
628 },
629 {
630 host: 'localhost',
631 cert: {
632 subjectaltname: 'DNS:a.com',
633 subject: { CN: 'localhost' },
634 },
635 error: "Host: localhost. is not in the cert's altnames: " + 'DNS:a.com',
636 },
637 // IDNA
638 {
639 host: 'xn--bcher-kva.example.com',
640 cert: { subject: { CN: '*.example.com' } },
641 },
642 // RFC 6125, section 6.4.3: "[...] the client SHOULD NOT attempt to match
643 // a presented identifier where the wildcard character is embedded within
644 // an A-label [...]"
645 {
646 host: 'xn--bcher-kva.example.com',
647 cert: { subject: { CN: 'xn--*.example.com' } },
648 error:
649 "Host: xn--bcher-kva.example.com. is not cert's CN: " +
650 'xn--*.example.com',
651 },
652 ];
653 
654 tests.forEach(function (test, i) {
655 const err = tls.checkServerIdentity(test.host, test.cert);
656 strictEqual(
657 err?.reason,
658 test.error,
659 `Test# ${i} failed: ${inspect(test)} \n` +
660 `${test.error} != ${err?.reason}`
661 );
662 });
663 },
664};
665 
666// Tests are taken from
667// https://github.com/nodejs/node/blob/1b5b019de1be9259e4374ca1d6ee7b3b28c48856/test/parallel/test-tls-translate-peer-certificate.js
668export const testTlsTranslatePeerCertificate = {
669 async test() {
670 const certString = '__proto__=42\nA=1\nB=2\nC=3';
671 
672 strictEqual(translatePeerCertificate(null), null);
673 strictEqual(translatePeerCertificate(undefined), null);
674 
675 strictEqual(translatePeerCertificate(0), null);
676 strictEqual(translatePeerCertificate(1), 1);
677 
678 deepStrictEqual(translatePeerCertificate({}), {});
679 
680 // Earlier versions of Node.js parsed the issuer property but did so
681 // incorrectly. This behavior has now reached end-of-life and user-supplied
682 // strings will not be parsed at all.
683 deepStrictEqual(translatePeerCertificate({ issuer: '' }), { issuer: '' });
684 deepStrictEqual(translatePeerCertificate({ issuer: null }), {
685 issuer: null,
686 });
687 deepStrictEqual(translatePeerCertificate({ issuer: certString }), {
688 issuer: certString,
689 });
690 
691 // Earlier versions of Node.js parsed the issuer property but did so
692 // incorrectly. This behavior has now reached end-of-life and user-supplied
693 // strings will not be parsed at all.
694 deepStrictEqual(translatePeerCertificate({ subject: '' }), { subject: '' });
695 deepStrictEqual(translatePeerCertificate({ subject: null }), {
696 subject: null,
697 });
698 deepStrictEqual(translatePeerCertificate({ subject: certString }), {
699 subject: certString,
700 });
701 
702 deepStrictEqual(translatePeerCertificate({ issuerCertificate: '' }), {
703 issuerCertificate: null,
704 });
705 deepStrictEqual(translatePeerCertificate({ issuerCertificate: null }), {
706 issuerCertificate: null,
707 });
708 deepStrictEqual(
709 translatePeerCertificate({ issuerCertificate: { subject: certString } }),
710 { issuerCertificate: { subject: certString } }
711 );
712 
713 {
714 const cert = {};
715 cert.issuerCertificate = cert;
716 deepStrictEqual(translatePeerCertificate(cert), {
717 issuerCertificate: cert,
718 });
719 }
720 
721 deepStrictEqual(translatePeerCertificate({ infoAccess: '' }), {
722 infoAccess: { __proto__: null },
723 });
724 deepStrictEqual(translatePeerCertificate({ infoAccess: null }), {
725 infoAccess: null,
726 });
727 },
728};
729 
730// Tests are taken from:
731// https://github.com/nodejs/node/blob/b1402835a512f14fa9f8dd23d3e0cee8cfe888a2/test/parallel/test-tls-basic-validations.js
732export const testConvertALPNProtocols = {
733 async test() {
734 {
735 const buffer = Buffer.from('abcd');
736 const out = {};
737 tls.convertALPNProtocols(buffer, out);
738 out.ALPNProtocols.write('efgh');
739 ok(buffer.equals(Buffer.from('abcd')));
740 ok(out.ALPNProtocols.equals(Buffer.from('efgh')));
741 }
742 
743 {
744 const protocols = [new String('a').repeat(500)];
745 const out = {};
746 throws(() => tls.convertALPNProtocols(protocols, out), {
747 code: 'ERR_OUT_OF_RANGE',
748 message:
749 'The byte length of the protocol at index 0 exceeds the ' +
750 'maximum length. It must be <= 255. Received 500',
751 });
752 }
753 },
754};
755 
756export const testStartTlsBehaviorOnUpgrade = {
757 async test(ctrl, env) {
758 const { promise, resolve, reject } = Promise.withResolvers();
759 const socket = connect(`localhost:${env.HELLO_SERVER_PORT}`, {
760 secureTransport: 'starttls',
761 });
762 strictEqual(socket.secureTransport, 'starttls');
763 strictEqual(socket.upgraded, false);
764 await socket.opened;
765 strictEqual(socket.upgraded, false);
766 socket.closed
767 .then(() => {
768 strictEqual(socket.secureTransport, 'starttls');
769 strictEqual(socket.upgraded, true);
770 resolve();
771 })
772 .catch(reject);
773 const secureSocket = socket.startTls();
774 // The newly created socket instance is not upgraded.
775 strictEqual(secureSocket.upgraded, false);
776 strictEqual(secureSocket.secureTransport, 'on');
777 await promise;
778 },
779};
780 
781// Tests are taken from:
782// https://github.com/nodejs/node/blob/52d95f53e466016120048fb43b3732ff9089ecd7/test/parallel/test-tls-destroy-whilst-write.js
783export const testTlsDestroyWhilstWrite = {
784 async test() {
785 const { promise, resolve } = Promise.withResolvers();
786 const delay = new stream.Duplex({
787 read: function read() {},
788 write: function write(data, enc, cb) {
789 queueMicrotask(cb);
790 },
791 });
792 
793 const secure = tls.connect({
794 socket: delay,
795 });
796 queueMicrotask(function () {
797 secure.destroy();
798 });
799 secure.on('close', resolve);
800 await promise;
801 },
802};
803 
804// Tests are taken from:
805// https://github.com/nodejs/node/blob/52d95f53e466016120048fb43b3732ff9089ecd7/test/parallel/test-tls-js-stream.js
806export const testTlsJsStream = {
807 async test(ctrl, env) {
808 const { promise, resolve, reject } = Promise.withResolvers();
809 const raw = net.connect(env.JS_STREAM_SERVER_PORT);
810 
811 let pending = false;
812 raw.on('readable', function () {
813 if (pending) socket._read();
814 });
815 
816 raw.on('end', function () {
817 socket.push(null);
818 });
819 
820 const socket = new stream.Duplex({
821 read: function read() {
822 pending = false;
823 
824 const chunk = raw.read();
825 if (chunk) {
826 this.push(chunk);
827 } else {
828 pending = true;
829 }
830 },
831 write: function write(data, enc, cb) {
832 raw.write(data, enc, cb);
833 },
834 });
835 
836 const onConnectFn = mock.fn(() => {
837 socket.resume();
838 socket.end('hello');
839 });
840 const conn = tls.connect({ socket }, onConnectFn);
841 conn.once('error', reject);
842 conn.once('close', resolve);
843 
844 await promise;
845 strictEqual(onConnectFn.mock.callCount(), 1);
846 },
847};
848 
849// Tests are taken from:
850// https://github.com/nodejs/node/blob/b1402835a512f14fa9f8dd23d3e0cee8cfe888a2/test/parallel/test-tls-junk-closes-server.js
851export const testTlsJunkClosesServer = {
852 async test(ctrl, env) {
853 const { promise, resolve } = Promise.withResolvers();
854 const c = net.createConnection(env.HELLO_SERVER_PORT);
855 
856 c.on('data', function () {
857 // We must consume all data sent by the server. Otherwise the
858 // end event will not be sent and the test will hang.
859 // For example, when compiled with OpenSSL32 we see the
860 // following response '15 03 03 00 02 02 16' which
861 // decodes as a fatal (0x02) TLS error alert number 22 (0x16),
862 // which corresponds to TLS1_AD_RECORD_OVERFLOW which matches
863 // the error we see if NODE_DEBUG is turned on.
864 // Some earlier OpenSSL versions did not seem to send a response
865 // but the TLS spec seems to indicate there should be one
866 // https://datatracker.ietf.org/doc/html/rfc8446#page-85
867 // and error handling seems to have been re-written/improved
868 // in OpenSSL32. Consuming the data allows the test to pass
869 // either way.
870 });
871 
872 const onConnectFn = mock.fn(() => {
873 c.write('blah\nblah\nblah\n');
874 });
875 c.on('connect', onConnectFn);
876 c.on('end', resolve);
877 await promise;
878 strictEqual(onConnectFn.mock.callCount(), 1);
879 },
880};
881 
882// Tests are taken from:
883// https://github.com/nodejs/node/blob/91d8a524ada001103a2d1c6825ca17b8393c183f/test/parallel/test-tls-on-empty-socket.js
884export const testTlsOnEmptySocket = {
885 async test(ctrl, env) {
886 const { promise, resolve, reject } = Promise.withResolvers();
887 const socket = new net.Socket();
888 let out = '';
889 
890 const s = tls.connect({ socket }, function () {
891 s.on('error', reject);
892 s.on('data', function (chunk) {
893 out += chunk;
894 });
895 s.on('end', resolve);
896 });
897 
898 const onConnectFn = mock.fn();
899 socket.connect(env.HELLO_SERVER_PORT, onConnectFn);
900 
901 await promise;
902 strictEqual(out, 'Hello');
903 strictEqual(onConnectFn.mock.callCount(), 1);
904 },
905};
906 
907// Tests are taken from:
908// https://github.com/nodejs/node/blob/91d8a524ada001103a2d1c6825ca17b8393c183f/test/parallel/test-tls-pause.js
909export const testTlsPause = {
910 async test(ctrl, env) {
911 const { promise, resolve } = Promise.withResolvers();
912 const bufSize = 1024 * 1024;
913 let sent = 0;
914 let received = 0;
915 let resumed = false;
916 const client = tls.connect(
917 {
918 port: env.ECHO_SERVER_PORT,
919 },
920 () => {
921 client.pause();
922 const send = (() => {
923 const ret = client.write(Buffer.allocUnsafe(bufSize));
924 if (ret !== false) {
925 sent += bufSize;
926 ok(sent < 100 * 1024 * 1024); // max 100MB
927 return process.nextTick(send);
928 }
929 sent += bufSize;
930 resumed = true;
931 client.resume();
932 })();
933 }
934 );
935 client.on('data', (data) => {
936 ok(resumed);
937 received += data.length;
938 if (received >= sent) {
939 client.end();
940 resolve();
941 }
942 });
943 
944 await promise;
945 },
946};
947 
948// Tests are taken from:
949// https://github.com/nodejs/node/blob/cb5f671a34da32e3c2d70d7f3e7f869cda6b806b/test/parallel/test-tls-socket-allow-half-open-option.js
950export const testTlsSocketAllowHalfOpenOption = {
951 async test() {
952 {
953 // The option is ignored when the `socket` argument is a `net.Socket`.
954 const socket = new tls.TLSSocket(new net.Socket(), {
955 allowHalfOpen: true,
956 });
957 strictEqual(socket.allowHalfOpen, false);
958 }
959 
960 {
961 // The option is ignored when the `socket` argument is a generic
962 // `stream.Duplex`.
963 const duplex = new stream.Duplex({
964 allowHalfOpen: false,
965 read() {},
966 });
967 const socket = new tls.TLSSocket(duplex, { allowHalfOpen: true });
968 strictEqual(socket.allowHalfOpen, false);
969 }
970 
971 {
972 const socket = new tls.TLSSocket();
973 strictEqual(socket.allowHalfOpen, false);
974 }
975 
976 {
977 // The option is honored when the `socket` argument is not specified.
978 const socket = new tls.TLSSocket(undefined, { allowHalfOpen: true });
979 strictEqual(socket.allowHalfOpen, true);
980 }
981 },
982};
983 
984// Tests are taken from:
985// https://github.com/nodejs/node/blob/52d95f53e466016120048fb43b3732ff9089ecd7/test/parallel/test-tls-streamwrap-buffersize.js
986export const testTlsStreamwrapBuffersize = {
987 async test(ctrl, env) {
988 // This test ensures that `bufferSize` also works for those tlsSockets
989 // created from `socket` of `Duplex`, with which, TLSSocket will wrap
990 // sockets in `StreamWrap`.
991 const iter = 10;
992 
993 function createDuplex() {
994 const [clientSide, serverSide] = stream.duplexPair();
995 const dp = Promise.withResolvers();
996 
997 const socket = net.connect(env.STREAM_WRAP_SERVER_PORT, () => {
998 clientSide.pipe(socket);
999 socket.pipe(clientSide);
1000 clientSide.on('close', () => socket.destroy());
1001 socket.on('close', () => clientSide.destroy());
1002 
1003 dp.resolve(serverSide);
1004 });
1005 
1006 return dp.promise;
1007 }
1008 
1009 const socket = await createDuplex();
1010 const { promise, resolve } = Promise.withResolvers();
1011 const onCloseFn = mock.fn(() => {
1012 // TODO(soon): This should be undefined, not 0.
1013 strictEqual(client.bufferSize, 0);
1014 resolve();
1015 });
1016 const client = tls.connect({ socket }, () => {
1017 strictEqual(client.bufferSize, 0);
1018 
1019 for (let i = 1; i < iter; i++) {
1020 client.write('a');
1021 strictEqual(client.bufferSize, i);
1022 }
1023 
1024 client.end();
1025 });
1026 
1027 client.on('close', onCloseFn);
1028 
1029 await promise;
1030 strictEqual(onCloseFn.mock.callCount(), 1);
1031 },
1032};
1033 
1034export const testEOLMethods = {
1035 async test() {
1036 strictEqual(typeof tls.createSecurePair, 'function');
1037 },
1038};