nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
351 lines (309 loc) • 10.1 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/crypto/x509.js
import {
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,
} from "nstdlib/stub/binding/crypto";
import { PublicKeyObject, isKeyObject } from "nstdlib/lib/internal/crypto/keys";
import {
customInspectSymbol as kInspect,
kEmptyObject,
} from "nstdlib/lib/internal/util";
import {
validateBoolean,
validateObject,
validateString,
} from "nstdlib/lib/internal/validators";
import { inspect } from "nstdlib/lib/internal/util/inspect";
import { Buffer } from "nstdlib/lib/buffer";
import { isArrayBufferView } from "nstdlib/lib/internal/util/types";
import { codes as __codes__ } from "nstdlib/lib/internal/errors";
import {
markTransferMode,
kClone,
kDeserialize,
} from "nstdlib/lib/internal/worker/js_transferable";
import { kHandle } from "nstdlib/lib/internal/crypto/util";
import * as __hoisted__tls_common__ from "nstdlib/lib/_tls_common";
const { ERR_INVALID_ARG_TYPE, ERR_INVALID_ARG_VALUE } = __codes__;
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 {
[kInternalState] = new Map();
constructor(handle) {
markTransferMode(this, true, false);
this[kHandle] = handle;
}
}
class X509Certificate {
[kInternalState] = new Map();
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,
);
}
markTransferMode(this, true, false);
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 ??=
__hoisted__tls_common__.translatePeerCertificate;
return lazyTranslatePeerCertificate(this[kHandle].toLegacy());
}
}
InternalX509Certificate.prototype.constructor = X509Certificate;
Object.setPrototypeOf(
InternalX509Certificate.prototype,
X509Certificate.prototype,
);
export { X509Certificate };
export { InternalX509Certificate };
export { isX509Certificate };