| 'use strict'; |
| |
| const net = require('net'); |
| const url = require('url'); |
| const util = require('util'); |
| const binding = process.binding('crypto'); |
| const Buffer = require('buffer').Buffer; |
| |
| // Allow {CLIENT_RENEG_LIMIT} client-initiated session renegotiations |
| // every {CLIENT_RENEG_WINDOW} seconds. An error event is emitted if more |
| // renegotations are seen. The settings are applied to all remote client |
| // connections. |
| exports.CLIENT_RENEG_LIMIT = 3; |
| exports.CLIENT_RENEG_WINDOW = 600; |
| |
| exports.SLAB_BUFFER_SIZE = 10 * 1024 * 1024; |
| |
| exports.DEFAULT_CIPHERS = [ |
| 'ECDHE-RSA-AES128-GCM-SHA256', |
| 'ECDHE-ECDSA-AES128-GCM-SHA256', |
| 'ECDHE-RSA-AES256-GCM-SHA384', |
| 'ECDHE-ECDSA-AES256-GCM-SHA384', |
| 'DHE-RSA-AES128-GCM-SHA256', |
| 'ECDHE-RSA-AES128-SHA256', |
| 'DHE-RSA-AES128-SHA256', |
| 'ECDHE-RSA-AES256-SHA384', |
| 'DHE-RSA-AES256-SHA384', |
| 'ECDHE-RSA-AES256-SHA256', |
| 'DHE-RSA-AES256-SHA256', |
| 'HIGH', |
| '!aNULL', |
| '!eNULL', |
| '!EXPORT', |
| '!DES', |
| '!RC4', |
| '!MD5', |
| '!PSK', |
| '!SRP', |
| '!CAMELLIA' |
| ].join(':'); |
| |
| exports.DEFAULT_ECDH_CURVE = 'prime256v1'; |
| |
| exports.getCiphers = function() { |
| const names = binding.getSSLCiphers(); |
| // Drop all-caps names in favor of their lowercase aliases, |
| var ctx = {}; |
| names.forEach(function(name) { |
| if (/^[0-9A-Z\-]+$/.test(name)) name = name.toLowerCase(); |
| ctx[name] = true; |
| }); |
| return Object.getOwnPropertyNames(ctx).sort(); |
| }; |
| |
| // Convert protocols array into valid OpenSSL protocols list |
| // ("\x06spdy/2\x08http/1.1\x08http/1.0") |
| exports.convertNPNProtocols = function convertNPNProtocols(NPNProtocols, out) { |
| // If NPNProtocols is Array - translate it into buffer |
| if (Array.isArray(NPNProtocols)) { |
| var buff = new Buffer(NPNProtocols.reduce(function(p, c) { |
| return p + 1 + Buffer.byteLength(c); |
| }, 0)); |
| |
| NPNProtocols.reduce(function(offset, c) { |
| var clen = Buffer.byteLength(c); |
| buff[offset] = clen; |
| buff.write(c, offset + 1); |
| |
| return offset + 1 + clen; |
| }, 0); |
| |
| NPNProtocols = buff; |
| } |
| |
| // If it's already a Buffer - store it |
| if (NPNProtocols instanceof Buffer) { |
| out.NPNProtocols = NPNProtocols; |
| } |
| }; |
| |
| exports.checkServerIdentity = function checkServerIdentity(host, cert) { |
| // Create regexp to much hostnames |
| function regexpify(host, wildcards) { |
| // Add trailing dot (make hostnames uniform) |
| if (!/\.$/.test(host)) host += '.'; |
| |
| // The same applies to hostname with more than one wildcard, |
| // if hostname has wildcard when wildcards are not allowed, |
| // or if there are less than two dots after wildcard (i.e. *.com or *d.com) |
| // |
| // also |
| // |
| // "The client SHOULD NOT attempt to match a presented identifier in |
| // which the wildcard character comprises a label other than the |
| // left-most label (e.g., do not match bar.*.example.net)." |
| // RFC6125 |
| if (!wildcards && /\*/.test(host) || /[\.\*].*\*/.test(host) || |
| /\*/.test(host) && !/\*.*\..+\..+/.test(host)) { |
| return /$./; |
| } |
| |
| // Replace wildcard chars with regexp's wildcard and |
| // escape all characters that have special meaning in regexps |
| // (i.e. '.', '[', '{', '*', and others) |
| var re = host.replace( |
| /\*([a-z0-9\\-_\.])|[\.,\-\\\^\$+?*\[\]\(\):!\|{}]/g, |
| function(all, sub) { |
| if (sub) return '[a-z0-9\\-_]*' + (sub === '-' ? '\\-' : sub); |
| return '\\' + all; |
| }); |
| |
| return new RegExp('^' + re + '$', 'i'); |
| } |
| |
| var dnsNames = [], |
| uriNames = [], |
| ips = [], |
| matchCN = true, |
| valid = false, |
| reason = 'Unknown reason'; |
| |
| // There're several names to perform check against: |
| // CN and altnames in certificate extension |
| // (DNS names, IP addresses, and URIs) |
| // |
| // Walk through altnames and generate lists of those names |
| if (cert.subjectaltname) { |
| cert.subjectaltname.split(/, /g).forEach(function(altname) { |
| var option = altname.match(/^(DNS|IP Address|URI):(.*)$/); |
| if (!option) |
| return; |
| if (option[1] === 'DNS') { |
| dnsNames.push(option[2]); |
| } else if (option[1] === 'IP Address') { |
| ips.push(option[2]); |
| } else if (option[1] === 'URI') { |
| var uri = url.parse(option[2]); |
| if (uri) uriNames.push(uri.hostname); |
| } |
| }); |
| } |
| |
| // If hostname is an IP address, it should be present in the list of IP |
| // addresses. |
| if (net.isIP(host)) { |
| valid = ips.some(function(ip) { |
| return ip === host; |
| }); |
| if (!valid) { |
| reason = util.format('IP: %s is not in the cert\'s list: %s', |
| host, |
| ips.join(', ')); |
| } |
| } else { |
| // Transform hostname to canonical form |
| if (!/\.$/.test(host)) host += '.'; |
| |
| // Otherwise check all DNS/URI records from certificate |
| // (with allowed wildcards) |
| dnsNames = dnsNames.map(function(name) { |
| return regexpify(name, true); |
| }); |
| |
| // Wildcards ain't allowed in URI names |
| uriNames = uriNames.map(function(name) { |
| return regexpify(name, false); |
| }); |
| |
| dnsNames = dnsNames.concat(uriNames); |
| |
| if (dnsNames.length > 0) matchCN = false; |
| |
| // Match against Common Name (CN) only if no supported identifiers are |
| // present. |
| // |
| // "As noted, a client MUST NOT seek a match for a reference identifier |
| // of CN-ID if the presented identifiers include a DNS-ID, SRV-ID, |
| // URI-ID, or any application-specific identifier types supported by the |
| // client." |
| // RFC6125 |
| if (matchCN) { |
| var commonNames = cert.subject.CN; |
| if (Array.isArray(commonNames)) { |
| for (var i = 0, k = commonNames.length; i < k; ++i) { |
| dnsNames.push(regexpify(commonNames[i], true)); |
| } |
| } else { |
| dnsNames.push(regexpify(commonNames, true)); |
| } |
| } |
| |
| valid = dnsNames.some(function(re) { |
| return re.test(host); |
| }); |
| |
| if (!valid) { |
| if (cert.subjectaltname) { |
| reason = util.format('Host: %s is not in the cert\'s altnames: %s', |
| host, |
| cert.subjectaltname); |
| } else { |
| reason = util.format('Host: %s is not cert\'s CN: %s', |
| host, |
| cert.subject.CN); |
| } |
| } |
| } |
| |
| if (!valid) { |
| var err = new Error( |
| util.format('Hostname/IP doesn\'t match certificate\'s altnames: %j', |
| reason)); |
| err.reason = reason; |
| err.host = host; |
| err.cert = cert; |
| return err; |
| } |
| }; |
| |
| // Example: |
| // C=US\nST=CA\nL=SF\nO=Joyent\nOU=Node.js\nCN=ca1\nemailAddress=ry@clouds.org |
| exports.parseCertString = function parseCertString(s) { |
| var out = {}; |
| var parts = s.split('\n'); |
| for (var i = 0, len = parts.length; i < len; i++) { |
| var sepIndex = parts[i].indexOf('='); |
| if (sepIndex > 0) { |
| var key = parts[i].slice(0, sepIndex); |
| var value = parts[i].slice(sepIndex + 1); |
| if (key in out) { |
| if (!Array.isArray(out[key])) { |
| out[key] = [out[key]]; |
| } |
| out[key].push(value); |
| } else { |
| out[key] = value; |
| } |
| } |
| } |
| return out; |
| }; |
| |
| // Public API |
| exports.createSecureContext = require('_tls_common').createSecureContext; |
| exports.SecureContext = require('_tls_common').SecureContext; |
| exports.TLSSocket = require('_tls_wrap').TLSSocket; |
| exports.Server = require('_tls_wrap').Server; |
| exports.createServer = require('_tls_wrap').createServer; |
| exports.connect = require('_tls_wrap').connect; |
| exports.createSecurePair = require('_tls_legacy').createSecurePair; |