1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23 24const tls = require('tls'); 25 26const { 27 ArrayPrototypePush, 28 JSONParse, 29 ObjectCreate, 30 RegExpPrototypeSymbolReplace, 31} = primordials; 32 33const { 34 codes: { 35 ERR_TLS_INVALID_PROTOCOL_VERSION, 36 ERR_TLS_PROTOCOL_VERSION_CONFLICT, 37 }, 38} = require('internal/errors'); 39 40const { 41 crypto: { 42 SSL_OP_CIPHER_SERVER_PREFERENCE, 43 TLS1_VERSION, 44 TLS1_1_VERSION, 45 TLS1_2_VERSION, 46 TLS1_3_VERSION, 47 }, 48} = internalBinding('constants'); 49 50const { 51 kEmptyObject, 52} = require('internal/util'); 53 54const { 55 validateInteger, 56} = require('internal/validators'); 57 58const { 59 configSecureContext, 60} = require('internal/tls/secure-context'); 61 62function toV(which, v, def) { 63 if (v == null) v = def; 64 if (v === 'TLSv1') return TLS1_VERSION; 65 if (v === 'TLSv1.1') return TLS1_1_VERSION; 66 if (v === 'TLSv1.2') return TLS1_2_VERSION; 67 if (v === 'TLSv1.3') return TLS1_3_VERSION; 68 throw new ERR_TLS_INVALID_PROTOCOL_VERSION(v, which); 69} 70 71const { 72 SecureContext: NativeSecureContext, 73} = internalBinding('crypto'); 74 75function SecureContext(secureProtocol, secureOptions, minVersion, maxVersion) { 76 if (!(this instanceof SecureContext)) { 77 return new SecureContext(secureProtocol, secureOptions, minVersion, 78 maxVersion); 79 } 80 81 if (secureProtocol) { 82 if (minVersion != null) 83 throw new ERR_TLS_PROTOCOL_VERSION_CONFLICT(minVersion, secureProtocol); 84 if (maxVersion != null) 85 throw new ERR_TLS_PROTOCOL_VERSION_CONFLICT(maxVersion, secureProtocol); 86 } 87 88 this.context = new NativeSecureContext(); 89 this.context.init(secureProtocol, 90 toV('minimum', minVersion, tls.DEFAULT_MIN_VERSION), 91 toV('maximum', maxVersion, tls.DEFAULT_MAX_VERSION)); 92 93 if (secureOptions) { 94 validateInteger(secureOptions, 'secureOptions'); 95 this.context.setOptions(secureOptions); 96 } 97} 98 99function createSecureContext(options) { 100 if (!options) options = kEmptyObject; 101 102 const { 103 honorCipherOrder, 104 minVersion, 105 maxVersion, 106 secureProtocol, 107 } = options; 108 109 let { secureOptions } = options; 110 111 if (honorCipherOrder) 112 secureOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; 113 114 const c = new SecureContext(secureProtocol, secureOptions, 115 minVersion, maxVersion); 116 117 configSecureContext(c.context, options); 118 119 return c; 120} 121 122// Translate some fields from the handle's C-friendly format into more idiomatic 123// javascript object representations before passing them back to the user. Can 124// be used on any cert object, but changing the name would be semver-major. 125function translatePeerCertificate(c) { 126 if (!c) 127 return null; 128 129 if (c.issuerCertificate != null && c.issuerCertificate !== c) { 130 c.issuerCertificate = translatePeerCertificate(c.issuerCertificate); 131 } 132 if (c.infoAccess != null) { 133 const info = c.infoAccess; 134 c.infoAccess = ObjectCreate(null); 135 136 // XXX: More key validation? 137 RegExpPrototypeSymbolReplace(/([^\n:]*):([^\n]*)(?:\n|$)/g, info, 138 (all, key, val) => { 139 if (val.charCodeAt(0) === 0x22) { 140 // The translatePeerCertificate function is only 141 // used on internally created legacy certificate 142 // objects, and any value that contains a quote 143 // will always be a valid JSON string literal, 144 // so this should never throw. 145 val = JSONParse(val); 146 } 147 if (key in c.infoAccess) 148 ArrayPrototypePush(c.infoAccess[key], val); 149 else 150 c.infoAccess[key] = [val]; 151 }); 152 } 153 return c; 154} 155 156module.exports = { 157 SecureContext, 158 createSecureContext, 159 translatePeerCertificate, 160}; 161