mirror of
https://github.com/nodejs/node.git
synced 2025-04-30 15:41:06 +00:00

PR-URL: https://github.com/nodejs/node/pull/43159 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
369 lines
9.7 KiB
JavaScript
369 lines
9.7 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ObjectSetPrototypeOf,
|
|
SafeMap,
|
|
Symbol,
|
|
} = primordials;
|
|
|
|
const {
|
|
parseX509,
|
|
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT,
|
|
X509_CHECK_FLAG_NEVER_CHECK_SUBJECT,
|
|
X509_CHECK_FLAG_NO_WILDCARDS,
|
|
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS,
|
|
X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS,
|
|
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS,
|
|
} = internalBinding('crypto');
|
|
|
|
const {
|
|
PublicKeyObject,
|
|
isKeyObject,
|
|
} = require('internal/crypto/keys');
|
|
|
|
const {
|
|
customInspectSymbol: kInspect,
|
|
kEmptyObject,
|
|
} = require('internal/util');
|
|
|
|
const {
|
|
validateBoolean,
|
|
validateObject,
|
|
validateString,
|
|
} = require('internal/validators');
|
|
|
|
const { inspect } = require('internal/util/inspect');
|
|
|
|
const { Buffer } = require('buffer');
|
|
|
|
const {
|
|
isArrayBufferView,
|
|
} = require('internal/util/types');
|
|
|
|
const {
|
|
codes: {
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_INVALID_ARG_VALUE,
|
|
}
|
|
} = require('internal/errors');
|
|
|
|
const {
|
|
JSTransferable,
|
|
kClone,
|
|
kDeserialize,
|
|
} = require('internal/worker/js_transferable');
|
|
|
|
const {
|
|
kHandle,
|
|
} = require('internal/crypto/util');
|
|
|
|
let lazyTranslatePeerCertificate;
|
|
|
|
const kInternalState = Symbol('kInternalState');
|
|
|
|
function isX509Certificate(value) {
|
|
return value[kInternalState] !== undefined;
|
|
}
|
|
|
|
function getFlags(options = kEmptyObject) {
|
|
validateObject(options, 'options');
|
|
const {
|
|
subject = 'default', // Can be 'default', 'always', or 'never'
|
|
wildcards = true,
|
|
partialWildcards = true,
|
|
multiLabelWildcards = false,
|
|
singleLabelSubdomains = false,
|
|
} = { ...options };
|
|
let flags = 0;
|
|
validateString(subject, 'options.subject');
|
|
validateBoolean(wildcards, 'options.wildcards');
|
|
validateBoolean(partialWildcards, 'options.partialWildcards');
|
|
validateBoolean(multiLabelWildcards, 'options.multiLabelWildcards');
|
|
validateBoolean(singleLabelSubdomains, 'options.singleLabelSubdomains');
|
|
switch (subject) {
|
|
case 'default': /* Matches OpenSSL's default, no flags. */ break;
|
|
case 'always': flags |= X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT; break;
|
|
case 'never': flags |= X509_CHECK_FLAG_NEVER_CHECK_SUBJECT; break;
|
|
default:
|
|
throw new ERR_INVALID_ARG_VALUE('options.subject', subject);
|
|
}
|
|
if (!wildcards) flags |= X509_CHECK_FLAG_NO_WILDCARDS;
|
|
if (!partialWildcards) flags |= X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
|
|
if (multiLabelWildcards) flags |= X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS;
|
|
if (singleLabelSubdomains) flags |= X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS;
|
|
return flags;
|
|
}
|
|
|
|
class InternalX509Certificate extends JSTransferable {
|
|
[kInternalState] = new SafeMap();
|
|
|
|
constructor(handle) {
|
|
super();
|
|
this[kHandle] = handle;
|
|
}
|
|
}
|
|
|
|
class X509Certificate extends JSTransferable {
|
|
[kInternalState] = new SafeMap();
|
|
|
|
constructor(buffer) {
|
|
if (typeof buffer === 'string')
|
|
buffer = Buffer.from(buffer);
|
|
if (!isArrayBufferView(buffer)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'buffer',
|
|
['string', 'Buffer', 'TypedArray', 'DataView'],
|
|
buffer);
|
|
}
|
|
super();
|
|
this[kHandle] = parseX509(buffer);
|
|
}
|
|
|
|
[kInspect](depth, options) {
|
|
if (depth < 0)
|
|
return this;
|
|
|
|
const opts = {
|
|
...options,
|
|
depth: options.depth == null ? null : options.depth - 1
|
|
};
|
|
|
|
return `X509Certificate ${inspect({
|
|
subject: this.subject,
|
|
subjectAltName: this.subjectAltName,
|
|
issuer: this.issuer,
|
|
infoAccess: this.infoAccess,
|
|
validFrom: this.validFrom,
|
|
validTo: this.validTo,
|
|
fingerprint: this.fingerprint,
|
|
fingerprint256: this.fingerprint256,
|
|
fingerprint512: this.fingerprint512,
|
|
keyUsage: this.keyUsage,
|
|
serialNumber: this.serialNumber,
|
|
}, opts)}`;
|
|
}
|
|
|
|
[kClone]() {
|
|
const handle = this[kHandle];
|
|
return {
|
|
data: { handle },
|
|
deserializeInfo: 'internal/crypto/x509:InternalX509Certificate'
|
|
};
|
|
}
|
|
|
|
[kDeserialize]({ handle }) {
|
|
this[kHandle] = handle;
|
|
}
|
|
|
|
get subject() {
|
|
let value = this[kInternalState].get('subject');
|
|
if (value === undefined) {
|
|
value = this[kHandle].subject();
|
|
this[kInternalState].set('subject', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get subjectAltName() {
|
|
let value = this[kInternalState].get('subjectAltName');
|
|
if (value === undefined) {
|
|
value = this[kHandle].subjectAltName();
|
|
this[kInternalState].set('subjectAltName', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get issuer() {
|
|
let value = this[kInternalState].get('issuer');
|
|
if (value === undefined) {
|
|
value = this[kHandle].issuer();
|
|
this[kInternalState].set('issuer', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get issuerCertificate() {
|
|
let value = this[kInternalState].get('issuerCertificate');
|
|
if (value === undefined) {
|
|
const cert = this[kHandle].getIssuerCert();
|
|
if (cert)
|
|
value = new InternalX509Certificate(this[kHandle].getIssuerCert());
|
|
this[kInternalState].set('issuerCertificate', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get infoAccess() {
|
|
let value = this[kInternalState].get('infoAccess');
|
|
if (value === undefined) {
|
|
value = this[kHandle].infoAccess();
|
|
this[kInternalState].set('infoAccess', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get validFrom() {
|
|
let value = this[kInternalState].get('validFrom');
|
|
if (value === undefined) {
|
|
value = this[kHandle].validFrom();
|
|
this[kInternalState].set('validFrom', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get validTo() {
|
|
let value = this[kInternalState].get('validTo');
|
|
if (value === undefined) {
|
|
value = this[kHandle].validTo();
|
|
this[kInternalState].set('validTo', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get fingerprint() {
|
|
let value = this[kInternalState].get('fingerprint');
|
|
if (value === undefined) {
|
|
value = this[kHandle].fingerprint();
|
|
this[kInternalState].set('fingerprint', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get fingerprint256() {
|
|
let value = this[kInternalState].get('fingerprint256');
|
|
if (value === undefined) {
|
|
value = this[kHandle].fingerprint256();
|
|
this[kInternalState].set('fingerprint256', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get fingerprint512() {
|
|
let value = this[kInternalState].get('fingerprint512');
|
|
if (value === undefined) {
|
|
value = this[kHandle].fingerprint512();
|
|
this[kInternalState].set('fingerprint512', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get keyUsage() {
|
|
let value = this[kInternalState].get('keyUsage');
|
|
if (value === undefined) {
|
|
value = this[kHandle].keyUsage();
|
|
this[kInternalState].set('keyUsage', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get serialNumber() {
|
|
let value = this[kInternalState].get('serialNumber');
|
|
if (value === undefined) {
|
|
value = this[kHandle].serialNumber();
|
|
this[kInternalState].set('serialNumber', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get raw() {
|
|
let value = this[kInternalState].get('raw');
|
|
if (value === undefined) {
|
|
value = this[kHandle].raw();
|
|
this[kInternalState].set('raw', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
get publicKey() {
|
|
let value = this[kInternalState].get('publicKey');
|
|
if (value === undefined) {
|
|
value = new PublicKeyObject(this[kHandle].publicKey());
|
|
this[kInternalState].set('publicKey', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
toString() {
|
|
let value = this[kInternalState].get('pem');
|
|
if (value === undefined) {
|
|
value = this[kHandle].pem();
|
|
this[kInternalState].set('pem', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
// There's no standardized JSON encoding for X509 certs so we
|
|
// fallback to providing the PEM encoding as a string.
|
|
toJSON() { return this.toString(); }
|
|
|
|
get ca() {
|
|
let value = this[kInternalState].get('ca');
|
|
if (value === undefined) {
|
|
value = this[kHandle].checkCA();
|
|
this[kInternalState].set('ca', value);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
checkHost(name, options) {
|
|
validateString(name, 'name');
|
|
return this[kHandle].checkHost(name, getFlags(options));
|
|
}
|
|
|
|
checkEmail(email, options) {
|
|
validateString(email, 'email');
|
|
return this[kHandle].checkEmail(email, getFlags(options));
|
|
}
|
|
|
|
checkIP(ip, options) {
|
|
validateString(ip, 'ip');
|
|
// The options argument is currently undocumented since none of the options
|
|
// have any effect on the behavior of this function. However, we still parse
|
|
// the options argument in case OpenSSL adds flags in the future that do
|
|
// affect the behavior of X509_check_ip. This ensures that no invalid values
|
|
// are passed as the second argument in the meantime.
|
|
return this[kHandle].checkIP(ip, getFlags(options));
|
|
}
|
|
|
|
checkIssued(otherCert) {
|
|
if (!isX509Certificate(otherCert))
|
|
throw new ERR_INVALID_ARG_TYPE('otherCert', 'X509Certificate', otherCert);
|
|
return this[kHandle].checkIssued(otherCert[kHandle]);
|
|
}
|
|
|
|
checkPrivateKey(pkey) {
|
|
if (!isKeyObject(pkey))
|
|
throw new ERR_INVALID_ARG_TYPE('pkey', 'KeyObject', pkey);
|
|
if (pkey.type !== 'private')
|
|
throw new ERR_INVALID_ARG_VALUE('pkey', pkey);
|
|
return this[kHandle].checkPrivateKey(pkey[kHandle]);
|
|
}
|
|
|
|
verify(pkey) {
|
|
if (!isKeyObject(pkey))
|
|
throw new ERR_INVALID_ARG_TYPE('pkey', 'KeyObject', pkey);
|
|
if (pkey.type !== 'public')
|
|
throw new ERR_INVALID_ARG_VALUE('pkey', pkey);
|
|
return this[kHandle].verify(pkey[kHandle]);
|
|
}
|
|
|
|
toLegacyObject() {
|
|
// TODO(tniessen): do not depend on translatePeerCertificate here, return
|
|
// the correct legacy representation from the binding
|
|
lazyTranslatePeerCertificate ??=
|
|
require('_tls_common').translatePeerCertificate;
|
|
return lazyTranslatePeerCertificate(this[kHandle].toLegacy());
|
|
}
|
|
}
|
|
|
|
InternalX509Certificate.prototype.constructor = X509Certificate;
|
|
ObjectSetPrototypeOf(
|
|
InternalX509Certificate.prototype,
|
|
X509Certificate.prototype);
|
|
|
|
module.exports = {
|
|
X509Certificate,
|
|
InternalX509Certificate,
|
|
isX509Certificate,
|
|
};
|