11'use strict' ;
22
33const common = require ( '../common' ) ;
4- const { it, describe, before } = require ( 'node:test' ) ;
4+ const { it, describe } = require ( 'node:test' ) ;
55const assert = require ( 'node:assert' ) ;
6- const fs = require ( 'node:fs' ) ;
76const tls = require ( 'node:tls' ) ;
87const https = require ( 'node:https' ) ;
98const dns = require ( 'node:dns' ) ;
@@ -13,93 +12,83 @@ const fixtures = require('../common/fixtures');
1312const crypto = require ( 'node:crypto' ) ;
1413
1514const root = {
16- cert : fixtures . readKey ( 'ca5-cert.pem' ) ,
17- key : fixtures . readKey ( 'ca5-key.pem' )
15+ cert : fixtures . readKey ( 'ca5-cert.pem' ) ,
16+ key : fixtures . readKey ( 'ca5-key.pem' )
1817} ;
1918
2019const agent1 = {
21- cert : fixtures . readKey ( 'agent1-cert.pem' ) ,
22- key : fixtures . readKey ( 'agent1-key.pem' )
20+ cert : fixtures . readKey ( 'agent1-cert.pem' ) ,
21+ key : fixtures . readKey ( 'agent1-key.pem' )
2322} ;
2423
2524const PORT = 8443 ;
2625
2726const sni = {
28- 'ca5.com' : {
29- cert : new crypto . X509Certificate ( root . cert ) ,
30- secureContext : tls . createSecureContext ( root )
31- } ,
32- 'agent1.com' : {
33- cert : new crypto . X509Certificate ( agent1 . cert ) ,
34- context : tls . createSecureContext ( agent1 ) ,
35- } ,
27+ 'ca5.com' : {
28+ cert : new crypto . X509Certificate ( root . cert ) ,
29+ secureContext : tls . createSecureContext ( root )
30+ } ,
31+ 'agent1.com' : {
32+ cert : new crypto . X509Certificate ( agent1 . cert ) ,
33+ context : tls . createSecureContext ( agent1 ) ,
34+ } ,
3635} ;
3736
37+ const agent = new https . Agent ( {
38+ lookup : ( hostname , options , cb ) => {
39+ if ( Object . keys ( sni ) . includes ( hostname ) )
40+ hostname = 'localhost' ;
41+ return dns . lookup ( hostname , options , cb ) ;
42+ }
43+ } ) ;
44+
45+ function makeRequest ( url , expectedCert ) {
46+ return new Promise ( ( resolve , reject ) => {
47+ https . get ( url , { rejectUnauthorized : false , agent } , common . mustCall ( ( response ) => {
48+ const actualCert = response . socket . getPeerX509Certificate ( ) ;
49+
50+ assert . deepStrictEqual ( actualCert . subject , expectedCert . subject ) ;
51+
52+ response . on ( 'data' , common . mustCall ( ( chunk ) => {
53+ assert . strictEqual ( chunk . toString ( ) , 'Hello, World!' ) ;
54+ resolve ( ) ;
55+ } ) ) ;
56+
57+ response . on ( 'error' , reject ) ;
58+ } ) ) . on ( 'error' , reject ) ;
59+ } ) ;
60+ }
61+
3862describe ( 'Regression test for SNICallback / Certification prioritization issue' , ( ) => {
39- it ( 'should use certificates from SNICallback' , async ( t ) => {
40- let snicbCount = 0 ;
41- const server = https . createServer ( {
42- cert : root . cert ,
43- key : root . key ,
44- SNICallback : ( servername , cb ) => {
45- snicbCount ++ ;
46- // This returns the secure context generated from the respective certificate
47- cb ( null , sni [ servername ] . context )
48- }
49- } , ( req , res ) => {
50- res . writeHead ( 200 ) ;
51- res . end ( 'Hello, World!' ) ;
52- } ) . on ( 'error' , common . mustNotCall ) ;
53-
54- const agent = new https . Agent ( {
55- lookup : ( hostname , options , cb ) => {
56- if ( Object . keys ( sni ) . includes ( hostname ) )
57- hostname = 'localhost' ;
58- return dns . lookup ( hostname , options , cb ) ;
59- }
60- } ) ;
61-
62- server . listen ( PORT ) ;
63- await events . once ( server , 'listening' ) ;
64-
65- await assert . doesNotReject ( ( ) => new Promise ( ( resolve , reject ) => {
66- https . get ( `https://127.0.0.1:${ PORT } ` , { rejectUnauthorized : false , agent } , ( response ) => {
67- const actualCert = response . socket . getPeerX509Certificate ( ) ;
68-
69- // Assert that raw IP address gets the root cert
70- assert . deepStrictEqual ( actualCert . subject , sni [ 'ca5.com' ] . cert . subject ) ;
71-
72- response . on ( 'data' , ( chunk ) => {
73- assert . strictEqual ( chunk . toString ( ) , 'Hello, World!' ) ;
74- resolve ( ) ;
75- } ) ;
76-
77- response . on ( 'error' , reject ) ;
78- } ) . on ( 'error' , reject ) ;
79- } ) ) ;
80-
81- for ( const [ hostname , { cert : expectedCert } ] of Object . entries ( sni ) ) {
82- await assert . doesNotReject ( ( ) => new Promise ( ( resolve , reject ) => {
83- https . get ( `https://${ hostname } :${ PORT } ` , { rejectUnauthorized : false , agent } , ( response ) => {
84- const actualCert = response . socket . getPeerX509Certificate ( ) ;
85-
86- // This assertion will fail if the certificate on the response does not match the one that is meant to be associated with the hostname
87- // Currently, the agent1 request will fail as it receives the root cert (ca5) instead.
88- assert . deepStrictEqual ( actualCert . subject , expectedCert . subject ) ;
89-
90- response . on ( 'data' , ( chunk ) => {
91- assert . strictEqual ( chunk . toString ( ) , 'Hello, World!' ) ;
92- resolve ( ) ;
93- } ) ;
94-
95- response . on ( 'error' , reject ) ;
96- } ) . on ( 'error' , reject ) ;
97- } ) ) ;
98- }
99-
100- // SNICallback should only be called for the hostname requests, not the IP one
101- assert . strictEqual ( snicbCount , 2 ) ;
102-
103- server . close ( ) ;
104- } ) ;
105- } )
63+ it ( 'should use certificates from SNICallback' , async ( t ) => {
64+ let snicbCount = 0 ;
65+ const server = https . createServer ( {
66+ cert : root . cert ,
67+ key : root . key ,
68+ SNICallback : ( servername , cb ) => {
69+ snicbCount ++ ;
70+ // This returns the secure context generated from the respective certificate
71+ cb ( null , sni [ servername ] . context ) ;
72+ }
73+ } , ( req , res ) => {
74+ res . writeHead ( 200 ) ;
75+ res . end ( 'Hello, World!' ) ;
76+ } ) . on ( 'error' , common . mustNotCall ) ;
77+
78+ server . listen ( PORT ) ;
79+ await events . once ( server , 'listening' ) ;
80+
81+ // Assert that raw IP address gets the root cert
82+ await makeRequest ( `https://127.0.0.1:${ PORT } ` , sni [ 'ca5.com' ] . cert ) ;
83+
84+ for ( const [ hostname , { cert : expectedCert } ] of Object . entries ( sni ) ) {
85+ // Currently, the agent1 request will fail as it receives the root cert (ca5) instead.
86+ await makeRequest ( `https://${ hostname } :${ PORT } ` , expectedCert ) ;
87+ }
88+
89+ // SNICallback should only be called for the hostname requests, not the IP one
90+ assert . strictEqual ( snicbCount , 2 ) ;
91+
92+ server . close ( ) ;
93+ } ) ;
94+ } ) ;
0 commit comments