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

Fixes #44471 PR-URL: https://github.com/nodejs/node/pull/44475 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
749 lines
20 KiB
JavaScript
749 lines
20 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
ArrayFrom,
|
|
ArrayPrototypeSlice,
|
|
ObjectDefineProperty,
|
|
ObjectSetPrototypeOf,
|
|
Symbol,
|
|
Uint8Array,
|
|
} = primordials;
|
|
|
|
const {
|
|
KeyObjectHandle,
|
|
createNativeKeyObjectClass,
|
|
kKeyTypeSecret,
|
|
kKeyTypePublic,
|
|
kKeyTypePrivate,
|
|
kKeyFormatPEM,
|
|
kKeyFormatDER,
|
|
kKeyFormatJWK,
|
|
kKeyEncodingPKCS1,
|
|
kKeyEncodingPKCS8,
|
|
kKeyEncodingSPKI,
|
|
kKeyEncodingSEC1,
|
|
} = internalBinding('crypto');
|
|
|
|
const {
|
|
validateObject,
|
|
validateOneOf,
|
|
validateString,
|
|
} = require('internal/validators');
|
|
|
|
const {
|
|
codes: {
|
|
ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS,
|
|
ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE,
|
|
ERR_CRYPTO_INVALID_JWK,
|
|
ERR_ILLEGAL_CONSTRUCTOR,
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_INVALID_ARG_VALUE,
|
|
}
|
|
} = require('internal/errors');
|
|
|
|
const {
|
|
kHandle,
|
|
kKeyObject,
|
|
getArrayBufferOrView,
|
|
bigIntArrayToUnsignedBigInt,
|
|
} = require('internal/crypto/util');
|
|
|
|
const {
|
|
isAnyArrayBuffer,
|
|
isArrayBufferView,
|
|
} = require('internal/util/types');
|
|
|
|
const {
|
|
JSTransferable,
|
|
kClone,
|
|
kDeserialize,
|
|
} = require('internal/worker/js_transferable');
|
|
|
|
const {
|
|
customInspectSymbol: kInspect,
|
|
} = require('internal/util');
|
|
|
|
const { inspect } = require('internal/util/inspect');
|
|
|
|
const { Buffer } = require('buffer');
|
|
|
|
const kAlgorithm = Symbol('kAlgorithm');
|
|
const kExtractable = Symbol('kExtractable');
|
|
const kKeyType = Symbol('kKeyType');
|
|
const kKeyUsages = Symbol('kKeyUsages');
|
|
|
|
// Key input contexts.
|
|
const kConsumePublic = 0;
|
|
const kConsumePrivate = 1;
|
|
const kCreatePublic = 2;
|
|
const kCreatePrivate = 3;
|
|
|
|
const encodingNames = [];
|
|
for (const m of [[kKeyEncodingPKCS1, 'pkcs1'], [kKeyEncodingPKCS8, 'pkcs8'],
|
|
[kKeyEncodingSPKI, 'spki'], [kKeyEncodingSEC1, 'sec1']])
|
|
encodingNames[m[0]] = m[1];
|
|
|
|
// Creating the KeyObject class is a little complicated due to inheritance
|
|
// and the fact that KeyObjects should be transferrable between threads,
|
|
// which requires the KeyObject base class to be implemented in C++.
|
|
// The creation requires a callback to make sure that the NativeKeyObject
|
|
// base class cannot exist without the other KeyObject implementations.
|
|
const {
|
|
0: KeyObject,
|
|
1: SecretKeyObject,
|
|
2: PublicKeyObject,
|
|
3: PrivateKeyObject,
|
|
} = createNativeKeyObjectClass((NativeKeyObject) => {
|
|
// Publicly visible KeyObject class.
|
|
class KeyObject extends NativeKeyObject {
|
|
constructor(type, handle) {
|
|
if (type !== 'secret' && type !== 'public' && type !== 'private')
|
|
throw new ERR_INVALID_ARG_VALUE('type', type);
|
|
if (typeof handle !== 'object' || !(handle instanceof KeyObjectHandle))
|
|
throw new ERR_INVALID_ARG_TYPE('handle', 'object', handle);
|
|
|
|
super(handle);
|
|
|
|
this[kKeyType] = type;
|
|
|
|
ObjectDefineProperty(this, kHandle, {
|
|
__proto__: null,
|
|
value: handle,
|
|
enumerable: false,
|
|
configurable: false,
|
|
writable: false
|
|
});
|
|
}
|
|
|
|
get type() {
|
|
return this[kKeyType];
|
|
}
|
|
|
|
static from(key) {
|
|
if (!isCryptoKey(key))
|
|
throw new ERR_INVALID_ARG_TYPE('key', 'CryptoKey', key);
|
|
return key[kKeyObject];
|
|
}
|
|
|
|
equals(otherKeyObject) {
|
|
if (!isKeyObject(otherKeyObject)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'otherKeyObject', 'KeyObject', otherKeyObject);
|
|
}
|
|
|
|
return otherKeyObject.type === this.type &&
|
|
this[kHandle].equals(otherKeyObject[kHandle]);
|
|
}
|
|
}
|
|
|
|
class SecretKeyObject extends KeyObject {
|
|
constructor(handle) {
|
|
super('secret', handle);
|
|
}
|
|
|
|
get symmetricKeySize() {
|
|
return this[kHandle].getSymmetricKeySize();
|
|
}
|
|
|
|
export(options) {
|
|
if (options !== undefined) {
|
|
validateObject(options, 'options');
|
|
validateOneOf(
|
|
options.format, 'options.format', [undefined, 'buffer', 'jwk']);
|
|
if (options.format === 'jwk') {
|
|
return this[kHandle].exportJwk({}, false);
|
|
}
|
|
}
|
|
return this[kHandle].export();
|
|
}
|
|
}
|
|
|
|
const kAsymmetricKeyType = Symbol('kAsymmetricKeyType');
|
|
const kAsymmetricKeyDetails = Symbol('kAsymmetricKeyDetails');
|
|
|
|
function normalizeKeyDetails(details = {}) {
|
|
if (details.publicExponent !== undefined) {
|
|
return {
|
|
...details,
|
|
publicExponent:
|
|
bigIntArrayToUnsignedBigInt(new Uint8Array(details.publicExponent))
|
|
};
|
|
}
|
|
return details;
|
|
}
|
|
|
|
class AsymmetricKeyObject extends KeyObject {
|
|
// eslint-disable-next-line no-useless-constructor
|
|
constructor(type, handle) {
|
|
super(type, handle);
|
|
}
|
|
|
|
get asymmetricKeyType() {
|
|
return this[kAsymmetricKeyType] ||
|
|
(this[kAsymmetricKeyType] = this[kHandle].getAsymmetricKeyType());
|
|
}
|
|
|
|
get asymmetricKeyDetails() {
|
|
switch (this.asymmetricKeyType) {
|
|
case 'rsa':
|
|
case 'rsa-pss':
|
|
case 'dsa':
|
|
case 'ec':
|
|
return this[kAsymmetricKeyDetails] ||
|
|
(this[kAsymmetricKeyDetails] = normalizeKeyDetails(
|
|
this[kHandle].keyDetail({})
|
|
));
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
|
|
class PublicKeyObject extends AsymmetricKeyObject {
|
|
constructor(handle) {
|
|
super('public', handle);
|
|
}
|
|
|
|
export(options) {
|
|
if (options && options.format === 'jwk') {
|
|
return this[kHandle].exportJwk({}, false);
|
|
}
|
|
const {
|
|
format,
|
|
type
|
|
} = parsePublicKeyEncoding(options, this.asymmetricKeyType);
|
|
return this[kHandle].export(format, type);
|
|
}
|
|
}
|
|
|
|
class PrivateKeyObject extends AsymmetricKeyObject {
|
|
constructor(handle) {
|
|
super('private', handle);
|
|
}
|
|
|
|
export(options) {
|
|
if (options && options.format === 'jwk') {
|
|
if (options.passphrase !== undefined) {
|
|
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
|
|
'jwk', 'does not support encryption');
|
|
}
|
|
return this[kHandle].exportJwk({}, false);
|
|
}
|
|
const {
|
|
format,
|
|
type,
|
|
cipher,
|
|
passphrase
|
|
} = parsePrivateKeyEncoding(options, this.asymmetricKeyType);
|
|
return this[kHandle].export(format, type, cipher, passphrase);
|
|
}
|
|
}
|
|
|
|
return [KeyObject, SecretKeyObject, PublicKeyObject, PrivateKeyObject];
|
|
});
|
|
|
|
function parseKeyFormat(formatStr, defaultFormat, optionName) {
|
|
if (formatStr === undefined && defaultFormat !== undefined)
|
|
return defaultFormat;
|
|
else if (formatStr === 'pem')
|
|
return kKeyFormatPEM;
|
|
else if (formatStr === 'der')
|
|
return kKeyFormatDER;
|
|
else if (formatStr === 'jwk')
|
|
return kKeyFormatJWK;
|
|
throw new ERR_INVALID_ARG_VALUE(optionName, formatStr);
|
|
}
|
|
|
|
function parseKeyType(typeStr, required, keyType, isPublic, optionName) {
|
|
if (typeStr === undefined && !required) {
|
|
return undefined;
|
|
} else if (typeStr === 'pkcs1') {
|
|
if (keyType !== undefined && keyType !== 'rsa') {
|
|
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
|
|
typeStr, 'can only be used for RSA keys');
|
|
}
|
|
return kKeyEncodingPKCS1;
|
|
} else if (typeStr === 'spki' && isPublic !== false) {
|
|
return kKeyEncodingSPKI;
|
|
} else if (typeStr === 'pkcs8' && isPublic !== true) {
|
|
return kKeyEncodingPKCS8;
|
|
} else if (typeStr === 'sec1' && isPublic !== true) {
|
|
if (keyType !== undefined && keyType !== 'ec') {
|
|
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
|
|
typeStr, 'can only be used for EC keys');
|
|
}
|
|
return kKeyEncodingSEC1;
|
|
}
|
|
|
|
throw new ERR_INVALID_ARG_VALUE(optionName, typeStr);
|
|
}
|
|
|
|
function option(name, objName) {
|
|
return objName === undefined ?
|
|
`options.${name}` : `options.${objName}.${name}`;
|
|
}
|
|
|
|
function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
|
|
const { format: formatStr, type: typeStr } = enc;
|
|
|
|
const isInput = keyType === undefined;
|
|
const format = parseKeyFormat(formatStr,
|
|
isInput ? kKeyFormatPEM : undefined,
|
|
option('format', objName));
|
|
|
|
const isRequired = (!isInput ||
|
|
format === kKeyFormatDER) &&
|
|
format !== kKeyFormatJWK;
|
|
const type = parseKeyType(typeStr,
|
|
isRequired,
|
|
keyType,
|
|
isPublic,
|
|
option('type', objName));
|
|
return { format, type };
|
|
}
|
|
|
|
function isStringOrBuffer(val) {
|
|
return typeof val === 'string' ||
|
|
isArrayBufferView(val) ||
|
|
isAnyArrayBuffer(val);
|
|
}
|
|
|
|
function parseKeyEncoding(enc, keyType, isPublic, objName) {
|
|
validateObject(enc, 'options');
|
|
|
|
const isInput = keyType === undefined;
|
|
|
|
const {
|
|
format,
|
|
type
|
|
} = parseKeyFormatAndType(enc, keyType, isPublic, objName);
|
|
|
|
let cipher, passphrase, encoding;
|
|
if (isPublic !== true) {
|
|
({ cipher, passphrase, encoding } = enc);
|
|
|
|
if (!isInput) {
|
|
if (cipher != null) {
|
|
if (typeof cipher !== 'string')
|
|
throw new ERR_INVALID_ARG_VALUE(option('cipher', objName), cipher);
|
|
if (format === kKeyFormatDER &&
|
|
(type === kKeyEncodingPKCS1 ||
|
|
type === kKeyEncodingSEC1)) {
|
|
throw new ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS(
|
|
encodingNames[type], 'does not support encryption');
|
|
}
|
|
} else if (passphrase !== undefined) {
|
|
throw new ERR_INVALID_ARG_VALUE(option('cipher', objName), cipher);
|
|
}
|
|
}
|
|
|
|
if ((isInput && passphrase !== undefined &&
|
|
!isStringOrBuffer(passphrase)) ||
|
|
(!isInput && cipher != null && !isStringOrBuffer(passphrase))) {
|
|
throw new ERR_INVALID_ARG_VALUE(option('passphrase', objName),
|
|
passphrase);
|
|
}
|
|
}
|
|
|
|
if (passphrase !== undefined)
|
|
passphrase = getArrayBufferOrView(passphrase, 'key.passphrase', encoding);
|
|
|
|
return { format, type, cipher, passphrase };
|
|
}
|
|
|
|
// Parses the public key encoding based on an object. keyType must be undefined
|
|
// when this is used to parse an input encoding and must be a valid key type if
|
|
// used to parse an output encoding.
|
|
function parsePublicKeyEncoding(enc, keyType, objName) {
|
|
return parseKeyEncoding(enc, keyType, keyType ? true : undefined, objName);
|
|
}
|
|
|
|
// Parses the private key encoding based on an object. keyType must be undefined
|
|
// when this is used to parse an input encoding and must be a valid key type if
|
|
// used to parse an output encoding.
|
|
function parsePrivateKeyEncoding(enc, keyType, objName) {
|
|
return parseKeyEncoding(enc, keyType, false, objName);
|
|
}
|
|
|
|
function getKeyObjectHandle(key, ctx) {
|
|
if (ctx === kCreatePrivate) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'key',
|
|
['string', 'ArrayBuffer', 'Buffer', 'TypedArray', 'DataView'],
|
|
key
|
|
);
|
|
}
|
|
|
|
if (key.type !== 'private') {
|
|
if (ctx === kConsumePrivate || ctx === kCreatePublic)
|
|
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'private');
|
|
if (key.type !== 'public') {
|
|
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type,
|
|
'private or public');
|
|
}
|
|
}
|
|
|
|
return key[kHandle];
|
|
}
|
|
|
|
function getKeyTypes(allowKeyObject, bufferOnly = false) {
|
|
const types = [
|
|
'ArrayBuffer',
|
|
'Buffer',
|
|
'TypedArray',
|
|
'DataView',
|
|
'string', // Only if bufferOnly == false
|
|
'KeyObject', // Only if allowKeyObject == true && bufferOnly == false
|
|
'CryptoKey', // Only if allowKeyObject == true && bufferOnly == false
|
|
];
|
|
if (bufferOnly) {
|
|
return ArrayPrototypeSlice(types, 0, 4);
|
|
} else if (!allowKeyObject) {
|
|
return ArrayPrototypeSlice(types, 0, 5);
|
|
}
|
|
return types;
|
|
}
|
|
|
|
function getKeyObjectHandleFromJwk(key, ctx) {
|
|
validateObject(key, 'key');
|
|
validateOneOf(
|
|
key.kty, 'key.kty', ['RSA', 'EC', 'OKP']);
|
|
const isPublic = ctx === kConsumePublic || ctx === kCreatePublic;
|
|
|
|
if (key.kty === 'OKP') {
|
|
validateString(key.crv, 'key.crv');
|
|
validateOneOf(
|
|
key.crv, 'key.crv', ['Ed25519', 'Ed448', 'X25519', 'X448']);
|
|
validateString(key.x, 'key.x');
|
|
|
|
if (!isPublic)
|
|
validateString(key.d, 'key.d');
|
|
|
|
let keyData;
|
|
if (isPublic)
|
|
keyData = Buffer.from(key.x, 'base64');
|
|
else
|
|
keyData = Buffer.from(key.d, 'base64');
|
|
|
|
switch (key.crv) {
|
|
case 'Ed25519':
|
|
case 'X25519':
|
|
if (keyData.byteLength !== 32) {
|
|
throw new ERR_CRYPTO_INVALID_JWK();
|
|
}
|
|
break;
|
|
case 'Ed448':
|
|
if (keyData.byteLength !== 57) {
|
|
throw new ERR_CRYPTO_INVALID_JWK();
|
|
}
|
|
break;
|
|
case 'X448':
|
|
if (keyData.byteLength !== 56) {
|
|
throw new ERR_CRYPTO_INVALID_JWK();
|
|
}
|
|
break;
|
|
}
|
|
|
|
const handle = new KeyObjectHandle();
|
|
|
|
const keyType = isPublic ? kKeyTypePublic : kKeyTypePrivate;
|
|
if (!handle.initEDRaw(key.crv, keyData, keyType)) {
|
|
throw new ERR_CRYPTO_INVALID_JWK();
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
if (key.kty === 'EC') {
|
|
validateString(key.crv, 'key.crv');
|
|
validateOneOf(
|
|
key.crv, 'key.crv', ['P-256', 'secp256k1', 'P-384', 'P-521']);
|
|
validateString(key.x, 'key.x');
|
|
validateString(key.y, 'key.y');
|
|
|
|
const jwk = {
|
|
kty: key.kty,
|
|
crv: key.crv,
|
|
x: key.x,
|
|
y: key.y
|
|
};
|
|
|
|
if (!isPublic) {
|
|
validateString(key.d, 'key.d');
|
|
jwk.d = key.d;
|
|
}
|
|
|
|
const handle = new KeyObjectHandle();
|
|
const type = handle.initJwk(jwk, jwk.crv);
|
|
if (type === undefined)
|
|
throw new ERR_CRYPTO_INVALID_JWK();
|
|
|
|
return handle;
|
|
}
|
|
|
|
// RSA
|
|
validateString(key.n, 'key.n');
|
|
validateString(key.e, 'key.e');
|
|
|
|
const jwk = {
|
|
kty: key.kty,
|
|
n: key.n,
|
|
e: key.e
|
|
};
|
|
|
|
if (!isPublic) {
|
|
validateString(key.d, 'key.d');
|
|
validateString(key.p, 'key.p');
|
|
validateString(key.q, 'key.q');
|
|
validateString(key.dp, 'key.dp');
|
|
validateString(key.dq, 'key.dq');
|
|
validateString(key.qi, 'key.qi');
|
|
jwk.d = key.d;
|
|
jwk.p = key.p;
|
|
jwk.q = key.q;
|
|
jwk.dp = key.dp;
|
|
jwk.dq = key.dq;
|
|
jwk.qi = key.qi;
|
|
}
|
|
|
|
const handle = new KeyObjectHandle();
|
|
const type = handle.initJwk(jwk);
|
|
if (type === undefined)
|
|
throw new ERR_CRYPTO_INVALID_JWK();
|
|
|
|
return handle;
|
|
}
|
|
|
|
function prepareAsymmetricKey(key, ctx) {
|
|
if (isKeyObject(key)) {
|
|
// Best case: A key object, as simple as that.
|
|
return { data: getKeyObjectHandle(key, ctx) };
|
|
} else if (isCryptoKey(key)) {
|
|
return { data: getKeyObjectHandle(key[kKeyObject], ctx) };
|
|
} else if (isStringOrBuffer(key)) {
|
|
// Expect PEM by default, mostly for backward compatibility.
|
|
return { format: kKeyFormatPEM, data: getArrayBufferOrView(key, 'key') };
|
|
} else if (typeof key === 'object') {
|
|
const { key: data, encoding, format } = key;
|
|
|
|
// The 'key' property can be a KeyObject as well to allow specifying
|
|
// additional options such as padding along with the key.
|
|
if (isKeyObject(data))
|
|
return { data: getKeyObjectHandle(data, ctx) };
|
|
else if (isCryptoKey(data))
|
|
return { data: getKeyObjectHandle(data[kKeyObject], ctx) };
|
|
else if (format === 'jwk') {
|
|
validateObject(data, 'key.key');
|
|
return { data: getKeyObjectHandleFromJwk(data, ctx), format: 'jwk' };
|
|
}
|
|
|
|
// Either PEM or DER using PKCS#1 or SPKI.
|
|
if (!isStringOrBuffer(data)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'key.key',
|
|
getKeyTypes(ctx !== kCreatePrivate),
|
|
data);
|
|
}
|
|
|
|
const isPublic =
|
|
(ctx === kConsumePrivate || ctx === kCreatePrivate) ? false : undefined;
|
|
return {
|
|
data: getArrayBufferOrView(data, 'key', encoding),
|
|
...parseKeyEncoding(key, undefined, isPublic)
|
|
};
|
|
}
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'key',
|
|
getKeyTypes(ctx !== kCreatePrivate),
|
|
key);
|
|
}
|
|
|
|
function preparePrivateKey(key) {
|
|
return prepareAsymmetricKey(key, kConsumePrivate);
|
|
}
|
|
|
|
function preparePublicOrPrivateKey(key) {
|
|
return prepareAsymmetricKey(key, kConsumePublic);
|
|
}
|
|
|
|
function prepareSecretKey(key, encoding, bufferOnly = false) {
|
|
if (!bufferOnly) {
|
|
if (isKeyObject(key)) {
|
|
if (key.type !== 'secret')
|
|
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret');
|
|
return key[kHandle];
|
|
} else if (isCryptoKey(key)) {
|
|
if (key.type !== 'secret')
|
|
throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, 'secret');
|
|
return key[kKeyObject][kHandle];
|
|
}
|
|
}
|
|
if (typeof key !== 'string' &&
|
|
!isArrayBufferView(key) &&
|
|
!isAnyArrayBuffer(key)) {
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
'key',
|
|
getKeyTypes(!bufferOnly, bufferOnly),
|
|
key);
|
|
}
|
|
return getArrayBufferOrView(key, 'key', encoding);
|
|
}
|
|
|
|
function createSecretKey(key, encoding) {
|
|
key = prepareSecretKey(key, encoding, true);
|
|
const handle = new KeyObjectHandle();
|
|
handle.init(kKeyTypeSecret, key);
|
|
return new SecretKeyObject(handle);
|
|
}
|
|
|
|
function createPublicKey(key) {
|
|
const { format, type, data, passphrase } =
|
|
prepareAsymmetricKey(key, kCreatePublic);
|
|
let handle;
|
|
if (format === 'jwk') {
|
|
handle = data;
|
|
} else {
|
|
handle = new KeyObjectHandle();
|
|
handle.init(kKeyTypePublic, data, format, type, passphrase);
|
|
}
|
|
return new PublicKeyObject(handle);
|
|
}
|
|
|
|
function createPrivateKey(key) {
|
|
const { format, type, data, passphrase } =
|
|
prepareAsymmetricKey(key, kCreatePrivate);
|
|
let handle;
|
|
if (format === 'jwk') {
|
|
handle = data;
|
|
} else {
|
|
handle = new KeyObjectHandle();
|
|
handle.init(kKeyTypePrivate, data, format, type, passphrase);
|
|
}
|
|
return new PrivateKeyObject(handle);
|
|
}
|
|
|
|
function isKeyObject(obj) {
|
|
return obj != null && obj[kKeyType] !== undefined;
|
|
}
|
|
|
|
// Our implementation of CryptoKey is a simple wrapper around a KeyObject
|
|
// that adapts it to the standard interface. This implementation also
|
|
// extends the JSTransferable class, allowing the CryptoKey to be cloned
|
|
// to Workers.
|
|
// TODO(@jasnell): Embedder environments like electron may have issues
|
|
// here similar to other things like URL. A chromium provided CryptoKey
|
|
// will not be recognized as a Node.js CryptoKey, and vice versa. It
|
|
// would be fantastic if we could find a way of making those interop.
|
|
class CryptoKey extends JSTransferable {
|
|
constructor() {
|
|
throw new ERR_ILLEGAL_CONSTRUCTOR();
|
|
}
|
|
|
|
[kInspect](depth, options) {
|
|
if (depth < 0)
|
|
return this;
|
|
|
|
const opts = {
|
|
...options,
|
|
depth: options.depth == null ? null : options.depth - 1
|
|
};
|
|
|
|
return `CryptoKey ${inspect({
|
|
type: this.type,
|
|
extractable: this.extractable,
|
|
algorithm: this.algorithm,
|
|
usages: this.usages
|
|
}, opts)}`;
|
|
}
|
|
|
|
get type() {
|
|
return this[kKeyObject].type;
|
|
}
|
|
|
|
get extractable() {
|
|
return this[kExtractable];
|
|
}
|
|
|
|
get algorithm() {
|
|
return this[kAlgorithm];
|
|
}
|
|
|
|
get usages() {
|
|
return ArrayFrom(this[kKeyUsages]);
|
|
}
|
|
|
|
[kClone]() {
|
|
const keyObject = this[kKeyObject];
|
|
const algorithm = this.algorithm;
|
|
const extractable = this.extractable;
|
|
const usages = this.usages;
|
|
|
|
return {
|
|
data: {
|
|
keyObject,
|
|
algorithm,
|
|
usages,
|
|
extractable,
|
|
},
|
|
deserializeInfo: 'internal/crypto/keys:InternalCryptoKey'
|
|
};
|
|
}
|
|
|
|
[kDeserialize]({ keyObject, algorithm, usages, extractable }) {
|
|
this[kKeyObject] = keyObject;
|
|
this[kAlgorithm] = algorithm;
|
|
this[kKeyUsages] = usages;
|
|
this[kExtractable] = extractable;
|
|
}
|
|
}
|
|
|
|
// All internal code must use new InternalCryptoKey to create
|
|
// CryptoKey instances. The CryptoKey class is exposed to end
|
|
// user code but is not permitted to be constructed directly.
|
|
class InternalCryptoKey extends JSTransferable {
|
|
constructor(
|
|
keyObject,
|
|
algorithm,
|
|
keyUsages,
|
|
extractable) {
|
|
super();
|
|
// Using symbol properties here currently instead of private
|
|
// properties because (for now) the performance penalty of
|
|
// private fields is still too high.
|
|
this[kKeyObject] = keyObject;
|
|
this[kAlgorithm] = algorithm;
|
|
this[kExtractable] = extractable;
|
|
this[kKeyUsages] = keyUsages;
|
|
}
|
|
}
|
|
|
|
InternalCryptoKey.prototype.constructor = CryptoKey;
|
|
ObjectSetPrototypeOf(InternalCryptoKey.prototype, CryptoKey.prototype);
|
|
|
|
function isCryptoKey(obj) {
|
|
return obj != null && obj[kKeyObject] !== undefined;
|
|
}
|
|
|
|
module.exports = {
|
|
// Public API.
|
|
createSecretKey,
|
|
createPublicKey,
|
|
createPrivateKey,
|
|
KeyObject,
|
|
CryptoKey,
|
|
InternalCryptoKey,
|
|
|
|
// These are designed for internal use only and should not be exposed.
|
|
parsePublicKeyEncoding,
|
|
parsePrivateKeyEncoding,
|
|
parseKeyEncoding,
|
|
preparePrivateKey,
|
|
preparePublicOrPrivateKey,
|
|
prepareSecretKey,
|
|
SecretKeyObject,
|
|
PublicKeyObject,
|
|
PrivateKeyObject,
|
|
isKeyObject,
|
|
isCryptoKey,
|
|
};
|