1'use strict'; 2 3const { 4 ObjectSetPrototypeOf, 5 SafeMap, 6 Symbol, 7} = primordials; 8 9const { 10 parseX509, 11 X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, 12 X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, 13 X509_CHECK_FLAG_NO_WILDCARDS, 14 X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, 15 X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS, 16 X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS, 17} = internalBinding('crypto'); 18 19const { 20 PublicKeyObject, 21 isKeyObject, 22} = require('internal/crypto/keys'); 23 24const { 25 customInspectSymbol: kInspect, 26 kEmptyObject, 27} = require('internal/util'); 28 29const { 30 validateBoolean, 31 validateObject, 32 validateString, 33} = require('internal/validators'); 34 35const { inspect } = require('internal/util/inspect'); 36 37const { Buffer } = require('buffer'); 38 39const { 40 isArrayBufferView, 41} = require('internal/util/types'); 42 43const { 44 codes: { 45 ERR_INVALID_ARG_TYPE, 46 ERR_INVALID_ARG_VALUE, 47 }, 48} = require('internal/errors'); 49 50const { 51 JSTransferable, 52 kClone, 53 kDeserialize, 54} = require('internal/worker/js_transferable'); 55 56const { 57 kHandle, 58} = require('internal/crypto/util'); 59 60let lazyTranslatePeerCertificate; 61 62const kInternalState = Symbol('kInternalState'); 63 64function isX509Certificate(value) { 65 return value[kInternalState] !== undefined; 66} 67 68function getFlags(options = kEmptyObject) { 69 validateObject(options, 'options'); 70 const { 71 subject = 'default', // Can be 'default', 'always', or 'never' 72 wildcards = true, 73 partialWildcards = true, 74 multiLabelWildcards = false, 75 singleLabelSubdomains = false, 76 } = { ...options }; 77 let flags = 0; 78 validateString(subject, 'options.subject'); 79 validateBoolean(wildcards, 'options.wildcards'); 80 validateBoolean(partialWildcards, 'options.partialWildcards'); 81 validateBoolean(multiLabelWildcards, 'options.multiLabelWildcards'); 82 validateBoolean(singleLabelSubdomains, 'options.singleLabelSubdomains'); 83 switch (subject) { 84 case 'default': /* Matches OpenSSL's default, no flags. */ break; 85 case 'always': flags |= X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT; break; 86 case 'never': flags |= X509_CHECK_FLAG_NEVER_CHECK_SUBJECT; break; 87 default: 88 throw new ERR_INVALID_ARG_VALUE('options.subject', subject); 89 } 90 if (!wildcards) flags |= X509_CHECK_FLAG_NO_WILDCARDS; 91 if (!partialWildcards) flags |= X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS; 92 if (multiLabelWildcards) flags |= X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS; 93 if (singleLabelSubdomains) flags |= X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS; 94 return flags; 95} 96 97class InternalX509Certificate extends JSTransferable { 98 [kInternalState] = new SafeMap(); 99 100 constructor(handle) { 101 super(); 102 this[kHandle] = handle; 103 } 104} 105 106class X509Certificate extends JSTransferable { 107 [kInternalState] = new SafeMap(); 108 109 constructor(buffer) { 110 if (typeof buffer === 'string') 111 buffer = Buffer.from(buffer); 112 if (!isArrayBufferView(buffer)) { 113 throw new ERR_INVALID_ARG_TYPE( 114 'buffer', 115 ['string', 'Buffer', 'TypedArray', 'DataView'], 116 buffer); 117 } 118 super(); 119 this[kHandle] = parseX509(buffer); 120 } 121 122 [kInspect](depth, options) { 123 if (depth < 0) 124 return this; 125 126 const opts = { 127 ...options, 128 depth: options.depth == null ? null : options.depth - 1, 129 }; 130 131 return `X509Certificate ${inspect({ 132 subject: this.subject, 133 subjectAltName: this.subjectAltName, 134 issuer: this.issuer, 135 infoAccess: this.infoAccess, 136 validFrom: this.validFrom, 137 validTo: this.validTo, 138 fingerprint: this.fingerprint, 139 fingerprint256: this.fingerprint256, 140 fingerprint512: this.fingerprint512, 141 keyUsage: this.keyUsage, 142 serialNumber: this.serialNumber, 143 }, opts)}`; 144 } 145 146 [kClone]() { 147 const handle = this[kHandle]; 148 return { 149 data: { handle }, 150 deserializeInfo: 'internal/crypto/x509:InternalX509Certificate', 151 }; 152 } 153 154 [kDeserialize]({ handle }) { 155 this[kHandle] = handle; 156 } 157 158 get subject() { 159 let value = this[kInternalState].get('subject'); 160 if (value === undefined) { 161 value = this[kHandle].subject(); 162 this[kInternalState].set('subject', value); 163 } 164 return value; 165 } 166 167 get subjectAltName() { 168 let value = this[kInternalState].get('subjectAltName'); 169 if (value === undefined) { 170 value = this[kHandle].subjectAltName(); 171 this[kInternalState].set('subjectAltName', value); 172 } 173 return value; 174 } 175 176 get issuer() { 177 let value = this[kInternalState].get('issuer'); 178 if (value === undefined) { 179 value = this[kHandle].issuer(); 180 this[kInternalState].set('issuer', value); 181 } 182 return value; 183 } 184 185 get issuerCertificate() { 186 let value = this[kInternalState].get('issuerCertificate'); 187 if (value === undefined) { 188 const cert = this[kHandle].getIssuerCert(); 189 if (cert) 190 value = new InternalX509Certificate(this[kHandle].getIssuerCert()); 191 this[kInternalState].set('issuerCertificate', value); 192 } 193 return value; 194 } 195 196 get infoAccess() { 197 let value = this[kInternalState].get('infoAccess'); 198 if (value === undefined) { 199 value = this[kHandle].infoAccess(); 200 this[kInternalState].set('infoAccess', value); 201 } 202 return value; 203 } 204 205 get validFrom() { 206 let value = this[kInternalState].get('validFrom'); 207 if (value === undefined) { 208 value = this[kHandle].validFrom(); 209 this[kInternalState].set('validFrom', value); 210 } 211 return value; 212 } 213 214 get validTo() { 215 let value = this[kInternalState].get('validTo'); 216 if (value === undefined) { 217 value = this[kHandle].validTo(); 218 this[kInternalState].set('validTo', value); 219 } 220 return value; 221 } 222 223 get fingerprint() { 224 let value = this[kInternalState].get('fingerprint'); 225 if (value === undefined) { 226 value = this[kHandle].fingerprint(); 227 this[kInternalState].set('fingerprint', value); 228 } 229 return value; 230 } 231 232 get fingerprint256() { 233 let value = this[kInternalState].get('fingerprint256'); 234 if (value === undefined) { 235 value = this[kHandle].fingerprint256(); 236 this[kInternalState].set('fingerprint256', value); 237 } 238 return value; 239 } 240 241 get fingerprint512() { 242 let value = this[kInternalState].get('fingerprint512'); 243 if (value === undefined) { 244 value = this[kHandle].fingerprint512(); 245 this[kInternalState].set('fingerprint512', value); 246 } 247 return value; 248 } 249 250 get keyUsage() { 251 let value = this[kInternalState].get('keyUsage'); 252 if (value === undefined) { 253 value = this[kHandle].keyUsage(); 254 this[kInternalState].set('keyUsage', value); 255 } 256 return value; 257 } 258 259 get serialNumber() { 260 let value = this[kInternalState].get('serialNumber'); 261 if (value === undefined) { 262 value = this[kHandle].serialNumber(); 263 this[kInternalState].set('serialNumber', value); 264 } 265 return value; 266 } 267 268 get raw() { 269 let value = this[kInternalState].get('raw'); 270 if (value === undefined) { 271 value = this[kHandle].raw(); 272 this[kInternalState].set('raw', value); 273 } 274 return value; 275 } 276 277 get publicKey() { 278 let value = this[kInternalState].get('publicKey'); 279 if (value === undefined) { 280 value = new PublicKeyObject(this[kHandle].publicKey()); 281 this[kInternalState].set('publicKey', value); 282 } 283 return value; 284 } 285 286 toString() { 287 let value = this[kInternalState].get('pem'); 288 if (value === undefined) { 289 value = this[kHandle].pem(); 290 this[kInternalState].set('pem', value); 291 } 292 return value; 293 } 294 295 // There's no standardized JSON encoding for X509 certs so we 296 // fallback to providing the PEM encoding as a string. 297 toJSON() { return this.toString(); } 298 299 get ca() { 300 let value = this[kInternalState].get('ca'); 301 if (value === undefined) { 302 value = this[kHandle].checkCA(); 303 this[kInternalState].set('ca', value); 304 } 305 return value; 306 } 307 308 checkHost(name, options) { 309 validateString(name, 'name'); 310 return this[kHandle].checkHost(name, getFlags(options)); 311 } 312 313 checkEmail(email, options) { 314 validateString(email, 'email'); 315 return this[kHandle].checkEmail(email, getFlags(options)); 316 } 317 318 checkIP(ip, options) { 319 validateString(ip, 'ip'); 320 // The options argument is currently undocumented since none of the options 321 // have any effect on the behavior of this function. However, we still parse 322 // the options argument in case OpenSSL adds flags in the future that do 323 // affect the behavior of X509_check_ip. This ensures that no invalid values 324 // are passed as the second argument in the meantime. 325 return this[kHandle].checkIP(ip, getFlags(options)); 326 } 327 328 checkIssued(otherCert) { 329 if (!isX509Certificate(otherCert)) 330 throw new ERR_INVALID_ARG_TYPE('otherCert', 'X509Certificate', otherCert); 331 return this[kHandle].checkIssued(otherCert[kHandle]); 332 } 333 334 checkPrivateKey(pkey) { 335 if (!isKeyObject(pkey)) 336 throw new ERR_INVALID_ARG_TYPE('pkey', 'KeyObject', pkey); 337 if (pkey.type !== 'private') 338 throw new ERR_INVALID_ARG_VALUE('pkey', pkey); 339 return this[kHandle].checkPrivateKey(pkey[kHandle]); 340 } 341 342 verify(pkey) { 343 if (!isKeyObject(pkey)) 344 throw new ERR_INVALID_ARG_TYPE('pkey', 'KeyObject', pkey); 345 if (pkey.type !== 'public') 346 throw new ERR_INVALID_ARG_VALUE('pkey', pkey); 347 return this[kHandle].verify(pkey[kHandle]); 348 } 349 350 toLegacyObject() { 351 // TODO(tniessen): do not depend on translatePeerCertificate here, return 352 // the correct legacy representation from the binding 353 lazyTranslatePeerCertificate ??= 354 require('_tls_common').translatePeerCertificate; 355 return lazyTranslatePeerCertificate(this[kHandle].toLegacy()); 356 } 357} 358 359InternalX509Certificate.prototype.constructor = X509Certificate; 360ObjectSetPrototypeOf( 361 InternalX509Certificate.prototype, 362 X509Certificate.prototype); 363 364module.exports = { 365 X509Certificate, 366 InternalX509Certificate, 367 isX509Certificate, 368}; 369