react-native-quick-crypto
Version:
A fast implementation of Node's `crypto` module written in C/C++ JSI
497 lines (473 loc) • 18.2 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createPrivateKey = exports.WebCryptoKeyExportStatus = exports.SecretKeyObject = exports.PublicKeyObject = exports.PrivateKeyObject = exports.KeyType = exports.KeyEncoding = exports.KWebCryptoKeyFormat = exports.KFormatType = exports.CryptoKey = exports.CipherOrWrapMode = void 0;
exports.createPublicKey = createPublicKey;
exports.createSecretKey = createSecretKey;
exports.kNamedCurveAliases = exports.isCryptoKey = void 0;
exports.parsePrivateKeyEncoding = parsePrivateKeyEncoding;
exports.parsePublicKeyEncoding = parsePublicKeyEncoding;
exports.preparePrivateKey = preparePrivateKey;
exports.preparePublicOrPrivateKey = preparePublicOrPrivateKey;
var _Utils = require("./Utils");
var _NativeQuickCrypto = require("./NativeQuickCrypto/NativeQuickCrypto");
const kNamedCurveAliases = exports.kNamedCurveAliases = {
'P-256': 'prime256v1',
'P-384': 'secp384r1',
'P-521': 'secp521r1'
};
// On node this value is defined on the native side, for now I'm just creating it here in JS
// TODO(osp) move this into native side to make sure they always match
let KFormatType = exports.KFormatType = /*#__PURE__*/function (KFormatType) {
KFormatType[KFormatType["kKeyFormatDER"] = 0] = "kKeyFormatDER";
KFormatType[KFormatType["kKeyFormatPEM"] = 1] = "kKeyFormatPEM";
KFormatType[KFormatType["kKeyFormatJWK"] = 2] = "kKeyFormatJWK";
return KFormatType;
}({});
// Same as KFormatType, this enum needs to be defined on the native side
let KeyType = exports.KeyType = /*#__PURE__*/function (KeyType) {
KeyType[KeyType["Secret"] = 0] = "Secret";
KeyType[KeyType["Public"] = 1] = "Public";
KeyType[KeyType["Private"] = 2] = "Private";
return KeyType;
}({});
// Same as KFormatType, this enum needs to be defined on the native side
let KWebCryptoKeyFormat = exports.KWebCryptoKeyFormat = /*#__PURE__*/function (KWebCryptoKeyFormat) {
KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatRaw"] = 0] = "kWebCryptoKeyFormatRaw";
KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatPKCS8"] = 1] = "kWebCryptoKeyFormatPKCS8";
KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatSPKI"] = 2] = "kWebCryptoKeyFormatSPKI";
KWebCryptoKeyFormat[KWebCryptoKeyFormat["kWebCryptoKeyFormatJWK"] = 3] = "kWebCryptoKeyFormatJWK";
return KWebCryptoKeyFormat;
}({});
let WebCryptoKeyExportStatus = exports.WebCryptoKeyExportStatus = /*#__PURE__*/function (WebCryptoKeyExportStatus) {
WebCryptoKeyExportStatus[WebCryptoKeyExportStatus["OK"] = 0] = "OK";
WebCryptoKeyExportStatus[WebCryptoKeyExportStatus["INVALID_KEY_TYPE"] = 1] = "INVALID_KEY_TYPE";
WebCryptoKeyExportStatus[WebCryptoKeyExportStatus["FAILED"] = 2] = "FAILED";
return WebCryptoKeyExportStatus;
}({});
var KeyInputContext = /*#__PURE__*/function (KeyInputContext) {
KeyInputContext[KeyInputContext["kConsumePublic"] = 0] = "kConsumePublic";
KeyInputContext[KeyInputContext["kConsumePrivate"] = 1] = "kConsumePrivate";
KeyInputContext[KeyInputContext["kCreatePublic"] = 2] = "kCreatePublic";
KeyInputContext[KeyInputContext["kCreatePrivate"] = 3] = "kCreatePrivate";
return KeyInputContext;
}(KeyInputContext || {});
let KeyEncoding = exports.KeyEncoding = /*#__PURE__*/function (KeyEncoding) {
KeyEncoding[KeyEncoding["kKeyEncodingPKCS1"] = 0] = "kKeyEncodingPKCS1";
KeyEncoding[KeyEncoding["kKeyEncodingPKCS8"] = 1] = "kKeyEncodingPKCS8";
KeyEncoding[KeyEncoding["kKeyEncodingSPKI"] = 2] = "kKeyEncodingSPKI";
KeyEncoding[KeyEncoding["kKeyEncodingSEC1"] = 3] = "kKeyEncodingSEC1";
return KeyEncoding;
}({});
const encodingNames = {
[KeyEncoding.kKeyEncodingPKCS1]: 'pkcs1',
[KeyEncoding.kKeyEncodingPKCS8]: 'pkcs8',
[KeyEncoding.kKeyEncodingSPKI]: 'spki',
[KeyEncoding.kKeyEncodingSEC1]: 'sec1'
};
let CipherOrWrapMode = exports.CipherOrWrapMode = /*#__PURE__*/function (CipherOrWrapMode) {
CipherOrWrapMode[CipherOrWrapMode["kWebCryptoCipherEncrypt"] = 0] = "kWebCryptoCipherEncrypt";
CipherOrWrapMode[CipherOrWrapMode["kWebCryptoCipherDecrypt"] = 1] = "kWebCryptoCipherDecrypt";
return CipherOrWrapMode;
}({}); // kWebCryptoWrapKey,
// kWebCryptoUnwrapKey,
function option(name, objName) {
return objName === undefined ? `options.${name}` : `options.${objName}.${name}`;
}
function parseKeyFormat(formatStr, defaultFormat, optionName) {
if (formatStr === undefined && defaultFormat !== undefined) return defaultFormat;else if (formatStr === 'pem') return KFormatType.kKeyFormatPEM;else if (formatStr === 'der') return KFormatType.kKeyFormatDER;else if (formatStr === 'jwk') return KFormatType.kKeyFormatJWK;
throw new Error(`Invalid key format str: ${optionName}`);
// 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 Error(`Crypto incompatible key options: ${typeStr} can only be used for RSA keys`);
}
return KeyEncoding.kKeyEncodingPKCS1;
} else if (typeStr === 'spki' && isPublic !== false) {
return KeyEncoding.kKeyEncodingSPKI;
} else if (typeStr === 'pkcs8' && isPublic !== true) {
return KeyEncoding.kKeyEncodingPKCS8;
} else if (typeStr === 'sec1' && isPublic !== true) {
if (keyType !== undefined && keyType !== 'ec') {
throw new Error(`Incompatible key options ${typeStr} can only be used for EC keys`);
}
return KeyEncoding.kKeyEncodingSEC1;
}
throw new Error(`Invalid option ${optionName} - ${typeStr}`);
}
function parseKeyFormatAndType(enc, keyType, isPublic, objName) {
const {
format: formatStr,
type: typeStr
} = enc;
const isInput = keyType === undefined;
const format = parseKeyFormat(formatStr, isInput ? KFormatType.kKeyFormatPEM : undefined, option('format', objName));
const isRequired = (!isInput || format === KFormatType.kKeyFormatDER) && format !== KFormatType.kKeyFormatJWK;
const type = parseKeyType(typeStr, isRequired, keyType, isPublic, option('type', objName));
return {
format,
type
};
}
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 Error(`Invalid argument ${option('cipher', objName)}: ${cipher}`);
if (format === KFormatType.kKeyFormatDER && (type === KeyEncoding.kKeyEncodingPKCS1 || type === KeyEncoding.kKeyEncodingSEC1)) {
throw new Error(`Incompatible key options ${encodingNames[type]} does not support encryption`);
}
} else if (passphrase !== undefined) {
throw new Error(`invalid argument ${option('cipher', objName)}: ${cipher}`);
}
}
if (isInput && passphrase !== undefined && !(0, _Utils.isStringOrBuffer)(passphrase) || !isInput && cipher != null && !(0, _Utils.isStringOrBuffer)(passphrase)) {
throw new Error(`Invalid argument value ${option('passphrase', objName)}: ${passphrase}`);
}
}
if (passphrase !== undefined) passphrase = (0, _Utils.binaryLikeToArrayBuffer)(passphrase, encoding);
return {
format,
type,
cipher,
passphrase
};
}
function prepareAsymmetricKey(key, ctx) {
// TODO(osp) check, KeyObject some node object
// 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 ((0, _Utils.isStringOrBuffer)(key)) {
// Expect PEM by default, mostly for backward compatibility.
return {
format: KFormatType.kKeyFormatPEM,
data: (0, _Utils.binaryLikeToArrayBuffer)(key)
};
} else if (typeof key === 'object') {
const {
key: data,
encoding
} = 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 (isJwk(data) && format === 'jwk')
// return { data: getKeyObjectHandleFromJwk(data, ctx), format: 'jwk' };
// Either PEM or DER using PKCS#1 or SPKI.
if (!(0, _Utils.isStringOrBuffer)(data)) {
throw new Error('prepareAsymmetricKey: key is not a string or ArrayBuffer');
}
const isPublic = ctx === KeyInputContext.kConsumePrivate || ctx === KeyInputContext.kCreatePrivate ? false : undefined;
return {
data: (0, _Utils.binaryLikeToArrayBuffer)(data, encoding),
...parseKeyEncoding(key, undefined, isPublic)
};
}
throw new Error('[prepareAsymetricKey] Invalid argument key: ${key}');
}
// TODO(osp) any here is a node KeyObject
function preparePrivateKey(key) {
return prepareAsymmetricKey(key, KeyInputContext.kConsumePrivate);
}
// TODO(osp) any here is a node KeyObject
function preparePublicOrPrivateKey(key) {
return prepareAsymmetricKey(key, KeyInputContext.kConsumePublic);
}
// 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: any, ctx: KeyInputContext) {
// if (ctx === KeyInputContext.kConsumePublic) {
// throw new Error(
// 'Invalid argument type for "key". Need ArrayBuffer, TypeArray, KeyObject, CryptoKey, string'
// );
// }
// if (key.type !== 'private') {
// if (
// ctx === KeyInputContext.kConsumePrivate ||
// ctx === KeyInputContext.kCreatePublic
// )
// throw new Error(`Invalid KeyObject type: ${key.type}, expected 'public'`);
// if (key.type !== 'public') {
// throw new Error(
// `Invalid KeyObject type: ${key.type}, expected 'private' or 'public'`
// );
// }
// }
// return key.handle;
// }
function prepareSecretKey(key, encoding, bufferOnly = false) {
try {
if (!bufferOnly) {
// TODO: maybe use `key.constructor.name === 'KeyObject'` ?
if (key instanceof KeyObject) {
if (key.type !== 'secret') throw new Error(`invalid KeyObject type: ${key.type}, expected 'secret'`);
return key.handle.export();
}
// TODO: maybe use `key.constructor.name === 'CryptoKey'` ?
else if (key instanceof CryptoKey) {
if (key.type !== 'secret') throw new Error(`invalid CryptoKey type: ${key.type}, expected 'secret'`);
return key.keyObject.handle.export();
}
}
if (key instanceof ArrayBuffer) {
return key;
}
return (0, _Utils.binaryLikeToArrayBuffer)(key, encoding);
} catch (error) {
throw new Error('Invalid argument type for "key". Need ArrayBuffer, TypedArray, KeyObject, CryptoKey, string', {
cause: error
});
}
}
function createSecretKey(key, encoding) {
const k = prepareSecretKey(key, encoding, true);
const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle();
handle.init(KeyType.Secret, k);
return new SecretKeyObject(handle);
}
function createPublicKey(key) {
const {
format,
type,
data,
passphrase
} = prepareAsymmetricKey(key, KeyInputContext.kCreatePublic);
const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle();
if (format === KFormatType.kKeyFormatJWK) {
handle.init(KeyType.Public, data);
} else {
handle.init(KeyType.Public, data, format, type, passphrase);
}
return new PublicKeyObject(handle);
}
const createPrivateKey = key => {
const {
format,
type,
data,
passphrase
} = prepareAsymmetricKey(key, KeyInputContext.kCreatePrivate);
const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle();
if (format === KFormatType.kKeyFormatJWK) {
handle.init(KeyType.Private, data);
} else {
handle.init(KeyType.Private, data, format, type, passphrase);
}
return new PrivateKeyObject(handle);
};
// const isKeyObject = (obj: any): obj is KeyObject => {
// return obj != null && obj.keyType !== undefined;
// };
exports.createPrivateKey = createPrivateKey;
class CryptoKey {
constructor(keyObject, keyAlgorithm, keyUsages, keyExtractable) {
this.keyObject = keyObject;
this.keyAlgorithm = keyAlgorithm;
this.keyUsages = keyUsages;
this.keyExtractable = keyExtractable;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
inspect(_depth, _options) {
throw new Error('CryptoKey.inspect is not implemented');
// 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() {
// if (!(this instanceof CryptoKey)) throw new Error('Invalid CryptoKey');
return this.keyObject.type;
}
get extractable() {
return this.keyExtractable;
}
get algorithm() {
return this.keyAlgorithm;
}
get usages() {
return this.keyUsages;
}
}
exports.CryptoKey = CryptoKey;
class KeyObject {
type = 'unknown';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export(_options) {
return new ArrayBuffer(0);
}
constructor(type, handle) {
if (type !== 'secret' && type !== 'public' && type !== 'private') throw new Error(`invalid KeyObject type: ${type}`);
this.handle = handle;
this.type = type;
}
// get type(): string {
// return this.type;
// }
// 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) {
if (options.format === 'jwk') {
throw new Error('SecretKey export for jwk is not implemented');
// return this.handle.exportJwk({}, false);
}
}
return this.handle.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;
// }
exports.SecretKeyObject = SecretKeyObject;
class AsymmetricKeyObject extends KeyObject {
constructor(type, handle) {
super(type, handle);
}
get asymmetricKeyType() {
if (!this._asymmetricKeyType) {
this._asymmetricKeyType = this.handle.getAsymmetricKeyType();
}
return this._asymmetricKeyType;
}
// 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?.format === 'jwk') {
throw new Error('PublicKey export for jwk is not implemented');
// return this.handle.exportJwk({}, false);
}
const {
format,
type
} = parsePublicKeyEncoding(options, this.asymmetricKeyType);
return this.handle.export(format, type);
}
}
exports.PublicKeyObject = PublicKeyObject;
class PrivateKeyObject extends AsymmetricKeyObject {
constructor(handle) {
super('private', handle);
}
export(options) {
if (options?.format === 'jwk') {
if (options.passphrase !== undefined) {
throw new Error('jwk does not support encryption');
}
throw new Error('PrivateKey export for jwk is not implemented');
// return this.handle.exportJwk({}, false);
}
const {
format,
type,
cipher,
passphrase
} = parsePrivateKeyEncoding(options, this.asymmetricKeyType);
return this.handle.export(format, type, cipher, passphrase);
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
exports.PrivateKeyObject = PrivateKeyObject;
const isCryptoKey = obj => {
return obj !== null && obj?.keyObject !== undefined;
};
exports.isCryptoKey = isCryptoKey;
//# sourceMappingURL=keys.js.map
;