11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ciconst common = require('../common'); 41cb0ef41Sopenharmony_ciif (!common.hasCrypto) 51cb0ef41Sopenharmony_ci common.skip('missing crypto'); 61cb0ef41Sopenharmony_ci 71cb0ef41Sopenharmony_ciconst assert = require('assert'); 81cb0ef41Sopenharmony_ciconst { X509Certificate } = require('crypto'); 91cb0ef41Sopenharmony_ciconst tls = require('tls'); 101cb0ef41Sopenharmony_ciconst fixtures = require('../common/fixtures'); 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ciconst { hasOpenSSL3 } = common; 131cb0ef41Sopenharmony_ci 141cb0ef41Sopenharmony_ci// Test that all certificate chains provided by the reporter are rejected. 151cb0ef41Sopenharmony_ci{ 161cb0ef41Sopenharmony_ci const rootPEM = fixtures.readSync('x509-escaping/google/root.pem'); 171cb0ef41Sopenharmony_ci const intermPEM = fixtures.readSync('x509-escaping/google/intermediate.pem'); 181cb0ef41Sopenharmony_ci const keyPEM = fixtures.readSync('x509-escaping/google/key.pem'); 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ci const numLeaves = 5; 211cb0ef41Sopenharmony_ci 221cb0ef41Sopenharmony_ci for (let i = 0; i < numLeaves; i++) { 231cb0ef41Sopenharmony_ci const name = `x509-escaping/google/leaf${i}.pem`; 241cb0ef41Sopenharmony_ci const leafPEM = fixtures.readSync(name, 'utf8'); 251cb0ef41Sopenharmony_ci 261cb0ef41Sopenharmony_ci const server = tls.createServer({ 271cb0ef41Sopenharmony_ci key: keyPEM, 281cb0ef41Sopenharmony_ci cert: leafPEM + intermPEM, 291cb0ef41Sopenharmony_ci }, common.mustNotCall()).listen(common.mustCall(() => { 301cb0ef41Sopenharmony_ci const { port } = server.address(); 311cb0ef41Sopenharmony_ci const socket = tls.connect(port, { 321cb0ef41Sopenharmony_ci ca: rootPEM, 331cb0ef41Sopenharmony_ci servername: 'nodejs.org', 341cb0ef41Sopenharmony_ci }, common.mustNotCall()); 351cb0ef41Sopenharmony_ci socket.on('error', common.mustCall()); 361cb0ef41Sopenharmony_ci })).unref(); 371cb0ef41Sopenharmony_ci } 381cb0ef41Sopenharmony_ci} 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci// Test escaping rules for subject alternative names. 411cb0ef41Sopenharmony_ci{ 421cb0ef41Sopenharmony_ci const expectedSANs = [ 431cb0ef41Sopenharmony_ci 'DNS:"good.example.com\\u002c DNS:evil.example.com"', 441cb0ef41Sopenharmony_ci // URIs should not require escaping. 451cb0ef41Sopenharmony_ci 'URI:http://example.com/', 461cb0ef41Sopenharmony_ci 'URI:http://example.com/?a=b&c=d', 471cb0ef41Sopenharmony_ci // Unless they contain commas. 481cb0ef41Sopenharmony_ci 'URI:"http://example.com/a\\u002cb"', 491cb0ef41Sopenharmony_ci // Percent encoding should not require escaping. 501cb0ef41Sopenharmony_ci 'URI:http://example.com/a%2Cb', 511cb0ef41Sopenharmony_ci // Malicious attempts should be escaped. 521cb0ef41Sopenharmony_ci 'URI:"http://example.com/a\\u002c DNS:good.example.com"', 531cb0ef41Sopenharmony_ci // Non-ASCII characters in DNS names should be treated as Latin-1. 541cb0ef41Sopenharmony_ci 'DNS:"ex\\u00e4mple.com"', 551cb0ef41Sopenharmony_ci // It should not be possible to cause unescaping without escaping. 561cb0ef41Sopenharmony_ci 'DNS:"\\"evil.example.com\\""', 571cb0ef41Sopenharmony_ci // IPv4 addresses should be represented as usual. 581cb0ef41Sopenharmony_ci 'IP Address:8.8.8.8', 591cb0ef41Sopenharmony_ci 'IP Address:8.8.4.4', 601cb0ef41Sopenharmony_ci // For backward-compatibility, include invalid IP address lengths. 611cb0ef41Sopenharmony_ci hasOpenSSL3 ? 'IP Address:<invalid length=5>' : 'IP Address:<invalid>', 621cb0ef41Sopenharmony_ci hasOpenSSL3 ? 'IP Address:<invalid length=6>' : 'IP Address:<invalid>', 631cb0ef41Sopenharmony_ci // IPv6 addresses are represented as OpenSSL does. 641cb0ef41Sopenharmony_ci 'IP Address:A0B:C0D:E0F:0:0:0:7A7B:7C7D', 651cb0ef41Sopenharmony_ci // Regular email addresses don't require escaping. 661cb0ef41Sopenharmony_ci 'email:foo@example.com', 671cb0ef41Sopenharmony_ci // ... but should be escaped if they contain commas. 681cb0ef41Sopenharmony_ci 'email:"foo@example.com\\u002c DNS:good.example.com"', 691cb0ef41Sopenharmony_ci // New versions of Node.js use RFC2253 to print DirName entries, which 701cb0ef41Sopenharmony_ci // almost always results in commas, which should be escaped properly. 711cb0ef41Sopenharmony_ci 'DirName:"L=Hannover\\u002cC=DE"', 721cb0ef41Sopenharmony_ci // Node.js unsets ASN1_STRFLGS_ESC_MSB to prevent unnecessarily escaping 731cb0ef41Sopenharmony_ci // Unicode characters, so Unicode characters should be preserved. 741cb0ef41Sopenharmony_ci 'DirName:"L=München\\u002cC=DE"', 751cb0ef41Sopenharmony_ci 'DirName:"L=Berlin\\\\\\u002c DNS:good.example.com\\u002cC=DE"', 761cb0ef41Sopenharmony_ci // Node.js also unsets ASN1_STRFLGS_ESC_CTRL and relies on JSON-compatible 771cb0ef41Sopenharmony_ci // escaping rules to safely escape control characters. 781cb0ef41Sopenharmony_ci 'DirName:"L=Berlin\\\\\\u002c DNS:good.example.com\\u0000' + 791cb0ef41Sopenharmony_ci 'evil.example.com\\u002cC=DE"', 801cb0ef41Sopenharmony_ci 'DirName:"L=Berlin\\\\\\u002c DNS:good.example.com\\\\\\\\\\u0000' + 811cb0ef41Sopenharmony_ci 'evil.example.com\\u002cC=DE"', 821cb0ef41Sopenharmony_ci 'DirName:"L=Berlin\\u000d\\u000a\\u002cC=DE"', 831cb0ef41Sopenharmony_ci 'DirName:"L=Berlin/CN=good.example.com\\u002cC=DE"', 841cb0ef41Sopenharmony_ci // Even OIDs that are well-known (such as the following, which is 851cb0ef41Sopenharmony_ci // sha256WithRSAEncryption) should be represented numerically only. 861cb0ef41Sopenharmony_ci 'Registered ID:1.2.840.113549.1.1.11', 871cb0ef41Sopenharmony_ci // This is an OID that will likely never be assigned to anything, thus 881cb0ef41Sopenharmony_ci // OpenSSL should not know it. 891cb0ef41Sopenharmony_ci 'Registered ID:1.3.9999.12.34', 901cb0ef41Sopenharmony_ci hasOpenSSL3 ? 911cb0ef41Sopenharmony_ci 'othername:XmppAddr:abc123' : 921cb0ef41Sopenharmony_ci 'othername:<unsupported>', 931cb0ef41Sopenharmony_ci hasOpenSSL3 ? 941cb0ef41Sopenharmony_ci 'othername:"XmppAddr:abc123\\u002c DNS:good.example.com"' : 951cb0ef41Sopenharmony_ci 'othername:<unsupported>', 961cb0ef41Sopenharmony_ci hasOpenSSL3 ? 971cb0ef41Sopenharmony_ci 'othername:"XmppAddr:good.example.com\\u0000abc123"' : 981cb0ef41Sopenharmony_ci 'othername:<unsupported>', 991cb0ef41Sopenharmony_ci // This is unsupported because the OID is not recognized. 1001cb0ef41Sopenharmony_ci 'othername:<unsupported>', 1011cb0ef41Sopenharmony_ci hasOpenSSL3 ? 'othername:SRVName:abc123' : 'othername:<unsupported>', 1021cb0ef41Sopenharmony_ci // This is unsupported because it is an SRVName with a UTF8String value, 1031cb0ef41Sopenharmony_ci // which is not allowed for SRVName. 1041cb0ef41Sopenharmony_ci 'othername:<unsupported>', 1051cb0ef41Sopenharmony_ci hasOpenSSL3 ? 1061cb0ef41Sopenharmony_ci 'othername:"SRVName:abc\\u0000def"' : 1071cb0ef41Sopenharmony_ci 'othername:<unsupported>', 1081cb0ef41Sopenharmony_ci ]; 1091cb0ef41Sopenharmony_ci 1101cb0ef41Sopenharmony_ci const serverKey = fixtures.readSync('x509-escaping/server-key.pem', 'utf8'); 1111cb0ef41Sopenharmony_ci 1121cb0ef41Sopenharmony_ci for (let i = 0; i < expectedSANs.length; i++) { 1131cb0ef41Sopenharmony_ci const pem = fixtures.readSync(`x509-escaping/alt-${i}-cert.pem`, 'utf8'); 1141cb0ef41Sopenharmony_ci 1151cb0ef41Sopenharmony_ci // Test the subjectAltName property of the X509Certificate API. 1161cb0ef41Sopenharmony_ci const cert = new X509Certificate(pem); 1171cb0ef41Sopenharmony_ci assert.strictEqual(cert.subjectAltName, expectedSANs[i]); 1181cb0ef41Sopenharmony_ci 1191cb0ef41Sopenharmony_ci // Test that the certificate obtained by checkServerIdentity has the correct 1201cb0ef41Sopenharmony_ci // subjectaltname property. 1211cb0ef41Sopenharmony_ci const server = tls.createServer({ 1221cb0ef41Sopenharmony_ci key: serverKey, 1231cb0ef41Sopenharmony_ci cert: pem, 1241cb0ef41Sopenharmony_ci }, common.mustCall((conn) => { 1251cb0ef41Sopenharmony_ci conn.destroy(); 1261cb0ef41Sopenharmony_ci server.close(); 1271cb0ef41Sopenharmony_ci })).listen(common.mustCall(() => { 1281cb0ef41Sopenharmony_ci const { port } = server.address(); 1291cb0ef41Sopenharmony_ci tls.connect(port, { 1301cb0ef41Sopenharmony_ci ca: pem, 1311cb0ef41Sopenharmony_ci servername: 'example.com', 1321cb0ef41Sopenharmony_ci checkServerIdentity: (hostname, peerCert) => { 1331cb0ef41Sopenharmony_ci assert.strictEqual(hostname, 'example.com'); 1341cb0ef41Sopenharmony_ci assert.strictEqual(peerCert.subjectaltname, expectedSANs[i]); 1351cb0ef41Sopenharmony_ci }, 1361cb0ef41Sopenharmony_ci }, common.mustCall()); 1371cb0ef41Sopenharmony_ci })); 1381cb0ef41Sopenharmony_ci } 1391cb0ef41Sopenharmony_ci} 1401cb0ef41Sopenharmony_ci 1411cb0ef41Sopenharmony_ci// Test escaping rules for authority info access. 1421cb0ef41Sopenharmony_ci{ 1431cb0ef41Sopenharmony_ci const expectedInfoAccess = [ 1441cb0ef41Sopenharmony_ci { 1451cb0ef41Sopenharmony_ci text: 'OCSP - URI:"http://good.example.com/\\u000a' + 1461cb0ef41Sopenharmony_ci 'OCSP - URI:http://evil.example.com/"', 1471cb0ef41Sopenharmony_ci legacy: { 1481cb0ef41Sopenharmony_ci 'OCSP - URI': [ 1491cb0ef41Sopenharmony_ci 'http://good.example.com/\nOCSP - URI:http://evil.example.com/', 1501cb0ef41Sopenharmony_ci ], 1511cb0ef41Sopenharmony_ci }, 1521cb0ef41Sopenharmony_ci }, 1531cb0ef41Sopenharmony_ci { 1541cb0ef41Sopenharmony_ci text: 'CA Issuers - URI:"http://ca.example.com/\\u000a' + 1551cb0ef41Sopenharmony_ci 'OCSP - URI:http://evil.example.com"\n' + 1561cb0ef41Sopenharmony_ci 'OCSP - DNS:"good.example.com\\u000a' + 1571cb0ef41Sopenharmony_ci 'OCSP - URI:http://ca.nodejs.org/ca.cert"', 1581cb0ef41Sopenharmony_ci legacy: { 1591cb0ef41Sopenharmony_ci 'CA Issuers - URI': [ 1601cb0ef41Sopenharmony_ci 'http://ca.example.com/\nOCSP - URI:http://evil.example.com', 1611cb0ef41Sopenharmony_ci ], 1621cb0ef41Sopenharmony_ci 'OCSP - DNS': [ 1631cb0ef41Sopenharmony_ci 'good.example.com\nOCSP - URI:http://ca.nodejs.org/ca.cert', 1641cb0ef41Sopenharmony_ci ], 1651cb0ef41Sopenharmony_ci }, 1661cb0ef41Sopenharmony_ci }, 1671cb0ef41Sopenharmony_ci { 1681cb0ef41Sopenharmony_ci text: '1.3.9999.12.34 - URI:http://ca.example.com/', 1691cb0ef41Sopenharmony_ci legacy: { 1701cb0ef41Sopenharmony_ci '1.3.9999.12.34 - URI': [ 1711cb0ef41Sopenharmony_ci 'http://ca.example.com/', 1721cb0ef41Sopenharmony_ci ], 1731cb0ef41Sopenharmony_ci }, 1741cb0ef41Sopenharmony_ci }, 1751cb0ef41Sopenharmony_ci hasOpenSSL3 ? { 1761cb0ef41Sopenharmony_ci text: 'OCSP - othername:XmppAddr:good.example.com\n' + 1771cb0ef41Sopenharmony_ci 'OCSP - othername:<unsupported>\n' + 1781cb0ef41Sopenharmony_ci 'OCSP - othername:SRVName:abc123', 1791cb0ef41Sopenharmony_ci legacy: { 1801cb0ef41Sopenharmony_ci 'OCSP - othername': [ 1811cb0ef41Sopenharmony_ci 'XmppAddr:good.example.com', 1821cb0ef41Sopenharmony_ci '<unsupported>', 1831cb0ef41Sopenharmony_ci 'SRVName:abc123', 1841cb0ef41Sopenharmony_ci ], 1851cb0ef41Sopenharmony_ci }, 1861cb0ef41Sopenharmony_ci } : { 1871cb0ef41Sopenharmony_ci text: 'OCSP - othername:<unsupported>\n' + 1881cb0ef41Sopenharmony_ci 'OCSP - othername:<unsupported>\n' + 1891cb0ef41Sopenharmony_ci 'OCSP - othername:<unsupported>', 1901cb0ef41Sopenharmony_ci legacy: { 1911cb0ef41Sopenharmony_ci 'OCSP - othername': [ 1921cb0ef41Sopenharmony_ci '<unsupported>', 1931cb0ef41Sopenharmony_ci '<unsupported>', 1941cb0ef41Sopenharmony_ci '<unsupported>', 1951cb0ef41Sopenharmony_ci ], 1961cb0ef41Sopenharmony_ci }, 1971cb0ef41Sopenharmony_ci }, 1981cb0ef41Sopenharmony_ci hasOpenSSL3 ? { 1991cb0ef41Sopenharmony_ci text: 'OCSP - othername:"XmppAddr:good.example.com\\u0000abc123"', 2001cb0ef41Sopenharmony_ci legacy: { 2011cb0ef41Sopenharmony_ci 'OCSP - othername': [ 2021cb0ef41Sopenharmony_ci 'XmppAddr:good.example.com\0abc123', 2031cb0ef41Sopenharmony_ci ], 2041cb0ef41Sopenharmony_ci }, 2051cb0ef41Sopenharmony_ci } : { 2061cb0ef41Sopenharmony_ci text: 'OCSP - othername:<unsupported>', 2071cb0ef41Sopenharmony_ci legacy: { 2081cb0ef41Sopenharmony_ci 'OCSP - othername': [ 2091cb0ef41Sopenharmony_ci '<unsupported>', 2101cb0ef41Sopenharmony_ci ], 2111cb0ef41Sopenharmony_ci }, 2121cb0ef41Sopenharmony_ci }, 2131cb0ef41Sopenharmony_ci ]; 2141cb0ef41Sopenharmony_ci 2151cb0ef41Sopenharmony_ci const serverKey = fixtures.readSync('x509-escaping/server-key.pem', 'utf8'); 2161cb0ef41Sopenharmony_ci 2171cb0ef41Sopenharmony_ci for (let i = 0; i < expectedInfoAccess.length; i++) { 2181cb0ef41Sopenharmony_ci const pem = fixtures.readSync(`x509-escaping/info-${i}-cert.pem`, 'utf8'); 2191cb0ef41Sopenharmony_ci const expected = expectedInfoAccess[i]; 2201cb0ef41Sopenharmony_ci 2211cb0ef41Sopenharmony_ci // Test the subjectAltName property of the X509Certificate API. 2221cb0ef41Sopenharmony_ci const cert = new X509Certificate(pem); 2231cb0ef41Sopenharmony_ci assert.strictEqual(cert.infoAccess, 2241cb0ef41Sopenharmony_ci `${expected.text}${hasOpenSSL3 ? '' : '\n'}`); 2251cb0ef41Sopenharmony_ci 2261cb0ef41Sopenharmony_ci // Test that the certificate obtained by checkServerIdentity has the correct 2271cb0ef41Sopenharmony_ci // subjectaltname property. 2281cb0ef41Sopenharmony_ci const server = tls.createServer({ 2291cb0ef41Sopenharmony_ci key: serverKey, 2301cb0ef41Sopenharmony_ci cert: pem, 2311cb0ef41Sopenharmony_ci }, common.mustCall((conn) => { 2321cb0ef41Sopenharmony_ci conn.destroy(); 2331cb0ef41Sopenharmony_ci server.close(); 2341cb0ef41Sopenharmony_ci })).listen(common.mustCall(() => { 2351cb0ef41Sopenharmony_ci const { port } = server.address(); 2361cb0ef41Sopenharmony_ci tls.connect(port, { 2371cb0ef41Sopenharmony_ci ca: pem, 2381cb0ef41Sopenharmony_ci servername: 'example.com', 2391cb0ef41Sopenharmony_ci checkServerIdentity: (hostname, peerCert) => { 2401cb0ef41Sopenharmony_ci assert.strictEqual(hostname, 'example.com'); 2411cb0ef41Sopenharmony_ci assert.deepStrictEqual(peerCert.infoAccess, 2421cb0ef41Sopenharmony_ci Object.assign(Object.create(null), 2431cb0ef41Sopenharmony_ci expected.legacy)); 2441cb0ef41Sopenharmony_ci 2451cb0ef41Sopenharmony_ci // toLegacyObject() should also produce the same properties. However, 2461cb0ef41Sopenharmony_ci // the X509Certificate is not aware of the chain, so we need to add 2471cb0ef41Sopenharmony_ci // the circular issuerCertificate reference manually for the assertion 2481cb0ef41Sopenharmony_ci // to be true. 2491cb0ef41Sopenharmony_ci const obj = cert.toLegacyObject(); 2501cb0ef41Sopenharmony_ci assert.strictEqual(obj.issuerCertificate, undefined); 2511cb0ef41Sopenharmony_ci obj.issuerCertificate = obj; 2521cb0ef41Sopenharmony_ci assert.deepStrictEqual(peerCert, obj); 2531cb0ef41Sopenharmony_ci }, 2541cb0ef41Sopenharmony_ci }, common.mustCall()); 2551cb0ef41Sopenharmony_ci })); 2561cb0ef41Sopenharmony_ci } 2571cb0ef41Sopenharmony_ci} 2581cb0ef41Sopenharmony_ci 2591cb0ef41Sopenharmony_ci// Test escaping rules for the subject field. 2601cb0ef41Sopenharmony_ci{ 2611cb0ef41Sopenharmony_ci const expectedSubjects = [ 2621cb0ef41Sopenharmony_ci { 2631cb0ef41Sopenharmony_ci text: 'L=Somewhere\nCN=evil.example.com', 2641cb0ef41Sopenharmony_ci legacy: { 2651cb0ef41Sopenharmony_ci L: 'Somewhere', 2661cb0ef41Sopenharmony_ci CN: 'evil.example.com', 2671cb0ef41Sopenharmony_ci }, 2681cb0ef41Sopenharmony_ci }, 2691cb0ef41Sopenharmony_ci { 2701cb0ef41Sopenharmony_ci text: 'L=Somewhere\\00evil.example.com', 2711cb0ef41Sopenharmony_ci legacy: { 2721cb0ef41Sopenharmony_ci L: 'Somewhere\0evil.example.com', 2731cb0ef41Sopenharmony_ci }, 2741cb0ef41Sopenharmony_ci }, 2751cb0ef41Sopenharmony_ci { 2761cb0ef41Sopenharmony_ci text: 'L=Somewhere\\0ACN=evil.example.com', 2771cb0ef41Sopenharmony_ci legacy: { 2781cb0ef41Sopenharmony_ci L: 'Somewhere\nCN=evil.example.com' 2791cb0ef41Sopenharmony_ci }, 2801cb0ef41Sopenharmony_ci }, 2811cb0ef41Sopenharmony_ci { 2821cb0ef41Sopenharmony_ci text: 'L=Somewhere\\, CN = evil.example.com', 2831cb0ef41Sopenharmony_ci legacy: { 2841cb0ef41Sopenharmony_ci L: 'Somewhere, CN = evil.example.com' 2851cb0ef41Sopenharmony_ci }, 2861cb0ef41Sopenharmony_ci }, 2871cb0ef41Sopenharmony_ci { 2881cb0ef41Sopenharmony_ci text: 'L=Somewhere/CN=evil.example.com', 2891cb0ef41Sopenharmony_ci legacy: { 2901cb0ef41Sopenharmony_ci L: 'Somewhere/CN=evil.example.com' 2911cb0ef41Sopenharmony_ci }, 2921cb0ef41Sopenharmony_ci }, 2931cb0ef41Sopenharmony_ci { 2941cb0ef41Sopenharmony_ci text: 'L=München\\\\\\0ACN=evil.example.com', 2951cb0ef41Sopenharmony_ci legacy: { 2961cb0ef41Sopenharmony_ci L: 'München\\\nCN=evil.example.com' 2971cb0ef41Sopenharmony_ci } 2981cb0ef41Sopenharmony_ci }, 2991cb0ef41Sopenharmony_ci { 3001cb0ef41Sopenharmony_ci text: 'L=Somewhere + CN=evil.example.com', 3011cb0ef41Sopenharmony_ci legacy: { 3021cb0ef41Sopenharmony_ci L: 'Somewhere', 3031cb0ef41Sopenharmony_ci CN: 'evil.example.com', 3041cb0ef41Sopenharmony_ci } 3051cb0ef41Sopenharmony_ci }, 3061cb0ef41Sopenharmony_ci { 3071cb0ef41Sopenharmony_ci text: 'L=Somewhere \\+ CN=evil.example.com', 3081cb0ef41Sopenharmony_ci legacy: { 3091cb0ef41Sopenharmony_ci L: 'Somewhere + CN=evil.example.com' 3101cb0ef41Sopenharmony_ci } 3111cb0ef41Sopenharmony_ci }, 3121cb0ef41Sopenharmony_ci // Observe that the legacy representation cannot properly distinguish 3131cb0ef41Sopenharmony_ci // between multi-value RDNs and multiple single-value RDNs. 3141cb0ef41Sopenharmony_ci { 3151cb0ef41Sopenharmony_ci text: 'L=L1 + L=L2\nL=L3', 3161cb0ef41Sopenharmony_ci legacy: { 3171cb0ef41Sopenharmony_ci L: ['L1', 'L2', 'L3'] 3181cb0ef41Sopenharmony_ci }, 3191cb0ef41Sopenharmony_ci }, 3201cb0ef41Sopenharmony_ci { 3211cb0ef41Sopenharmony_ci text: 'L=L1\nL=L2\nL=L3', 3221cb0ef41Sopenharmony_ci legacy: { 3231cb0ef41Sopenharmony_ci L: ['L1', 'L2', 'L3'] 3241cb0ef41Sopenharmony_ci }, 3251cb0ef41Sopenharmony_ci }, 3261cb0ef41Sopenharmony_ci ]; 3271cb0ef41Sopenharmony_ci 3281cb0ef41Sopenharmony_ci const serverKey = fixtures.readSync('x509-escaping/server-key.pem', 'utf8'); 3291cb0ef41Sopenharmony_ci 3301cb0ef41Sopenharmony_ci for (let i = 0; i < expectedSubjects.length; i++) { 3311cb0ef41Sopenharmony_ci const pem = fixtures.readSync(`x509-escaping/subj-${i}-cert.pem`, 'utf8'); 3321cb0ef41Sopenharmony_ci const expected = expectedSubjects[i]; 3331cb0ef41Sopenharmony_ci 3341cb0ef41Sopenharmony_ci // Test the subject property of the X509Certificate API. 3351cb0ef41Sopenharmony_ci const cert = new X509Certificate(pem); 3361cb0ef41Sopenharmony_ci assert.strictEqual(cert.subject, expected.text); 3371cb0ef41Sopenharmony_ci // The issuer MUST be the same as the subject since the cert is self-signed. 3381cb0ef41Sopenharmony_ci assert.strictEqual(cert.issuer, expected.text); 3391cb0ef41Sopenharmony_ci 3401cb0ef41Sopenharmony_ci // Test that the certificate obtained by checkServerIdentity has the correct 3411cb0ef41Sopenharmony_ci // subject property. 3421cb0ef41Sopenharmony_ci const server = tls.createServer({ 3431cb0ef41Sopenharmony_ci key: serverKey, 3441cb0ef41Sopenharmony_ci cert: pem, 3451cb0ef41Sopenharmony_ci }, common.mustCall((conn) => { 3461cb0ef41Sopenharmony_ci conn.destroy(); 3471cb0ef41Sopenharmony_ci server.close(); 3481cb0ef41Sopenharmony_ci })).listen(common.mustCall(() => { 3491cb0ef41Sopenharmony_ci const { port } = server.address(); 3501cb0ef41Sopenharmony_ci tls.connect(port, { 3511cb0ef41Sopenharmony_ci ca: pem, 3521cb0ef41Sopenharmony_ci servername: 'example.com', 3531cb0ef41Sopenharmony_ci checkServerIdentity: (hostname, peerCert) => { 3541cb0ef41Sopenharmony_ci assert.strictEqual(hostname, 'example.com'); 3551cb0ef41Sopenharmony_ci const expectedObject = Object.assign(Object.create(null), 3561cb0ef41Sopenharmony_ci expected.legacy); 3571cb0ef41Sopenharmony_ci assert.deepStrictEqual(peerCert.subject, expectedObject); 3581cb0ef41Sopenharmony_ci // The issuer MUST be the same as the subject since the cert is 3591cb0ef41Sopenharmony_ci // self-signed. Otherwise, OpenSSL would have already rejected the 3601cb0ef41Sopenharmony_ci // certificate while connecting to the TLS server. 3611cb0ef41Sopenharmony_ci assert.deepStrictEqual(peerCert.issuer, expectedObject); 3621cb0ef41Sopenharmony_ci 3631cb0ef41Sopenharmony_ci // toLegacyObject() should also produce the same properties. However, 3641cb0ef41Sopenharmony_ci // the X509Certificate is not aware of the chain, so we need to add 3651cb0ef41Sopenharmony_ci // the circular issuerCertificate reference manually for the assertion 3661cb0ef41Sopenharmony_ci // to be true. 3671cb0ef41Sopenharmony_ci const obj = cert.toLegacyObject(); 3681cb0ef41Sopenharmony_ci assert.strictEqual(obj.issuerCertificate, undefined); 3691cb0ef41Sopenharmony_ci obj.issuerCertificate = obj; 3701cb0ef41Sopenharmony_ci assert.deepStrictEqual(peerCert, obj); 3711cb0ef41Sopenharmony_ci }, 3721cb0ef41Sopenharmony_ci }, common.mustCall()); 3731cb0ef41Sopenharmony_ci })); 3741cb0ef41Sopenharmony_ci } 3751cb0ef41Sopenharmony_ci} 3761cb0ef41Sopenharmony_ci 3771cb0ef41Sopenharmony_ci// The internal parsing logic must match the JSON specification exactly. 3781cb0ef41Sopenharmony_ci{ 3791cb0ef41Sopenharmony_ci // This list is partially based on V8's own JSON tests. 3801cb0ef41Sopenharmony_ci const invalidJSON = [ 3811cb0ef41Sopenharmony_ci '"\\a invalid escape"', 3821cb0ef41Sopenharmony_ci '"\\v invalid escape"', 3831cb0ef41Sopenharmony_ci '"\\\' invalid escape"', 3841cb0ef41Sopenharmony_ci '"\\x42 invalid escape"', 3851cb0ef41Sopenharmony_ci '"\\u202 invalid escape"', 3861cb0ef41Sopenharmony_ci '"\\012 invalid escape"', 3871cb0ef41Sopenharmony_ci '"Unterminated string', 3881cb0ef41Sopenharmony_ci '"Unterminated string\\"', 3891cb0ef41Sopenharmony_ci '"Unterminated string\\\\\\"', 3901cb0ef41Sopenharmony_ci '"\u0000 control character"', 3911cb0ef41Sopenharmony_ci '"\u001e control character"', 3921cb0ef41Sopenharmony_ci '"\u001f control character"', 3931cb0ef41Sopenharmony_ci ]; 3941cb0ef41Sopenharmony_ci 3951cb0ef41Sopenharmony_ci for (const invalidStringLiteral of invalidJSON) { 3961cb0ef41Sopenharmony_ci // Usually, checkServerIdentity returns an error upon verification failure. 3971cb0ef41Sopenharmony_ci // In this case, however, it should throw an error since this is not a 3981cb0ef41Sopenharmony_ci // verification error. Node.js itself will never produce invalid JSON string 3991cb0ef41Sopenharmony_ci // literals, so this can only happen when users construct invalid subject 4001cb0ef41Sopenharmony_ci // alternative name strings (that do not follow escaping rules). 4011cb0ef41Sopenharmony_ci assert.throws(() => { 4021cb0ef41Sopenharmony_ci tls.checkServerIdentity('example.com', { 4031cb0ef41Sopenharmony_ci subjectaltname: `DNS:${invalidStringLiteral}`, 4041cb0ef41Sopenharmony_ci }); 4051cb0ef41Sopenharmony_ci }, { 4061cb0ef41Sopenharmony_ci code: 'ERR_TLS_CERT_ALTNAME_FORMAT', 4071cb0ef41Sopenharmony_ci message: 'Invalid subject alternative name string' 4081cb0ef41Sopenharmony_ci }); 4091cb0ef41Sopenharmony_ci } 4101cb0ef41Sopenharmony_ci} 4111cb0ef41Sopenharmony_ci 4121cb0ef41Sopenharmony_ci// While node does not produce commas within SAN entries, it should parse them 4131cb0ef41Sopenharmony_ci// correctly (i.e., not simply split at commas). 4141cb0ef41Sopenharmony_ci{ 4151cb0ef41Sopenharmony_ci // Regardless of the quotes, splitting this SAN string at commas would 4161cb0ef41Sopenharmony_ci // cause checkServerIdentity to see 'DNS:b.example.com' and thus to accept 4171cb0ef41Sopenharmony_ci // the certificate for b.example.com. 4181cb0ef41Sopenharmony_ci const san = 'DNS:"a.example.com, DNS:b.example.com, DNS:c.example.com"'; 4191cb0ef41Sopenharmony_ci 4201cb0ef41Sopenharmony_ci // This is what node used to do, and which is not correct! 4211cb0ef41Sopenharmony_ci const hostname = 'b.example.com'; 4221cb0ef41Sopenharmony_ci assert.strictEqual(san.split(', ')[1], `DNS:${hostname}`); 4231cb0ef41Sopenharmony_ci 4241cb0ef41Sopenharmony_ci // The new implementation should parse the string correctly. 4251cb0ef41Sopenharmony_ci const err = tls.checkServerIdentity(hostname, { subjectaltname: san }); 4261cb0ef41Sopenharmony_ci assert(err); 4271cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ERR_TLS_CERT_ALTNAME_INVALID'); 4281cb0ef41Sopenharmony_ci assert.strictEqual(err.message, 'Hostname/IP does not match certificate\'s ' + 4291cb0ef41Sopenharmony_ci 'altnames: Host: b.example.com. is not in ' + 4301cb0ef41Sopenharmony_ci 'the cert\'s altnames: DNS:"a.example.com, ' + 4311cb0ef41Sopenharmony_ci 'DNS:b.example.com, DNS:c.example.com"'); 4321cb0ef41Sopenharmony_ci} 4331cb0ef41Sopenharmony_ci 4341cb0ef41Sopenharmony_ci// The subject MUST be ignored if a dNSName subject alternative name exists. 4351cb0ef41Sopenharmony_ci{ 4361cb0ef41Sopenharmony_ci const key = fixtures.readKey('incorrect_san_correct_subject-key.pem'); 4371cb0ef41Sopenharmony_ci const cert = fixtures.readKey('incorrect_san_correct_subject-cert.pem'); 4381cb0ef41Sopenharmony_ci 4391cb0ef41Sopenharmony_ci // The hostname is the CN, but not a SAN entry. 4401cb0ef41Sopenharmony_ci const servername = 'good.example.com'; 4411cb0ef41Sopenharmony_ci const certX509 = new X509Certificate(cert); 4421cb0ef41Sopenharmony_ci assert.strictEqual(certX509.subject, `CN=${servername}`); 4431cb0ef41Sopenharmony_ci assert.strictEqual(certX509.subjectAltName, 'DNS:evil.example.com'); 4441cb0ef41Sopenharmony_ci 4451cb0ef41Sopenharmony_ci // The newer X509Certificate API allows customizing this behavior: 4461cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername), undefined); 4471cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername, { subject: 'default' }), 4481cb0ef41Sopenharmony_ci undefined); 4491cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername, { subject: 'always' }), 4501cb0ef41Sopenharmony_ci servername); 4511cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername, { subject: 'never' }), 4521cb0ef41Sopenharmony_ci undefined); 4531cb0ef41Sopenharmony_ci 4541cb0ef41Sopenharmony_ci // Try connecting to a server that uses the self-signed certificate. 4551cb0ef41Sopenharmony_ci const server = tls.createServer({ key, cert }, common.mustNotCall()); 4561cb0ef41Sopenharmony_ci server.listen(common.mustCall(() => { 4571cb0ef41Sopenharmony_ci const { port } = server.address(); 4581cb0ef41Sopenharmony_ci const socket = tls.connect(port, { 4591cb0ef41Sopenharmony_ci ca: cert, 4601cb0ef41Sopenharmony_ci servername, 4611cb0ef41Sopenharmony_ci }, common.mustNotCall()); 4621cb0ef41Sopenharmony_ci socket.on('error', common.mustCall((err) => { 4631cb0ef41Sopenharmony_ci assert.strictEqual(err.code, 'ERR_TLS_CERT_ALTNAME_INVALID'); 4641cb0ef41Sopenharmony_ci assert.strictEqual(err.message, 'Hostname/IP does not match ' + 4651cb0ef41Sopenharmony_ci "certificate's altnames: Host: " + 4661cb0ef41Sopenharmony_ci "good.example.com. is not in the cert's" + 4671cb0ef41Sopenharmony_ci ' altnames: DNS:evil.example.com'); 4681cb0ef41Sopenharmony_ci })); 4691cb0ef41Sopenharmony_ci })).unref(); 4701cb0ef41Sopenharmony_ci} 4711cb0ef41Sopenharmony_ci 4721cb0ef41Sopenharmony_ci// The subject MUST NOT be ignored if no dNSName subject alternative name 4731cb0ef41Sopenharmony_ci// exists, even if other subject alternative names exist. 4741cb0ef41Sopenharmony_ci{ 4751cb0ef41Sopenharmony_ci const key = fixtures.readKey('irrelevant_san_correct_subject-key.pem'); 4761cb0ef41Sopenharmony_ci const cert = fixtures.readKey('irrelevant_san_correct_subject-cert.pem'); 4771cb0ef41Sopenharmony_ci 4781cb0ef41Sopenharmony_ci // The hostname is the CN, but there is no dNSName SAN entry. 4791cb0ef41Sopenharmony_ci const servername = 'good.example.com'; 4801cb0ef41Sopenharmony_ci const certX509 = new X509Certificate(cert); 4811cb0ef41Sopenharmony_ci assert.strictEqual(certX509.subject, `CN=${servername}`); 4821cb0ef41Sopenharmony_ci assert.strictEqual(certX509.subjectAltName, 'IP Address:1.2.3.4'); 4831cb0ef41Sopenharmony_ci 4841cb0ef41Sopenharmony_ci // The newer X509Certificate API allows customizing this behavior: 4851cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername), servername); 4861cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername, { subject: 'default' }), 4871cb0ef41Sopenharmony_ci servername); 4881cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername, { subject: 'always' }), 4891cb0ef41Sopenharmony_ci servername); 4901cb0ef41Sopenharmony_ci assert.strictEqual(certX509.checkHost(servername, { subject: 'never' }), 4911cb0ef41Sopenharmony_ci undefined); 4921cb0ef41Sopenharmony_ci 4931cb0ef41Sopenharmony_ci // Connect to a server that uses the self-signed certificate. 4941cb0ef41Sopenharmony_ci const server = tls.createServer({ key, cert }, common.mustCall((socket) => { 4951cb0ef41Sopenharmony_ci socket.destroy(); 4961cb0ef41Sopenharmony_ci server.close(); 4971cb0ef41Sopenharmony_ci })).listen(common.mustCall(() => { 4981cb0ef41Sopenharmony_ci const { port } = server.address(); 4991cb0ef41Sopenharmony_ci tls.connect(port, { 5001cb0ef41Sopenharmony_ci ca: cert, 5011cb0ef41Sopenharmony_ci servername, 5021cb0ef41Sopenharmony_ci }, common.mustCall()); 5031cb0ef41Sopenharmony_ci })); 5041cb0ef41Sopenharmony_ci} 505