react-native-quick-crypto
Version:
A fast implementation of Node's `crypto` module written in C/C++ JSI
448 lines (443 loc) • 15.3 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.subtle = exports.Subtle = void 0;
var _safeBuffer = require("safe-buffer");
var _keys = require("./keys");
var _Utils = require("./Utils");
var _ec = require("./ec");
var _pbkdf = require("./pbkdf2");
var _Hash = require("./Hash");
var _aes = require("./aes");
var _rsa = require("./rsa");
var _Algorithms = require("./Algorithms");
var _mac = require("./mac");
const exportKeySpki = async key => {
switch (key.algorithm.name) {
case 'RSASSA-PKCS1-v1_5':
// Fall through
case 'RSA-PSS':
// Fall through
case 'RSA-OAEP':
if (key.type === 'public') {
return (0, _rsa.rsaExportKey)(key, _keys.KWebCryptoKeyFormat.kWebCryptoKeyFormatSPKI);
}
break;
case 'ECDSA':
// Fall through
case 'ECDH':
if (key.type === 'public') {
return (0, _ec.ecExportKey)(key, _keys.KWebCryptoKeyFormat.kWebCryptoKeyFormatSPKI);
}
break;
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// // Fall through
// case 'X25519':
// // Fall through
// case 'X448':
// if (key.type === 'public') {
// return cfrgExportKey(key, KWebCryptoKeyFormat.kWebCryptoKeyFormatSPKI);
// }
// break;
}
throw new Error(`Unable to export a spki ${key.algorithm.name} ${key.type} key`);
};
const exportKeyPkcs8 = async key => {
switch (key.algorithm.name) {
case 'RSASSA-PKCS1-v1_5':
// Fall through
case 'RSA-PSS':
// Fall through
case 'RSA-OAEP':
if (key.type === 'private') {
return (0, _rsa.rsaExportKey)(key, _keys.KWebCryptoKeyFormat.kWebCryptoKeyFormatPKCS8);
}
break;
case 'ECDSA':
// Fall through
case 'ECDH':
if (key.type === 'private') {
return (0, _ec.ecExportKey)(key, _keys.KWebCryptoKeyFormat.kWebCryptoKeyFormatPKCS8);
}
break;
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// // Fall through
// case 'X25519':
// // Fall through
// case 'X448':
// if (key.type === 'private') {
// return cfrgExportKey(key, KWebCryptoKeyFormat.kWebCryptoKeyFormatPKCS8);
// }
// break;
}
throw new Error(`Unable to export a pkcs8 ${key.algorithm.name} ${key.type} key`);
};
const exportKeyRaw = key => {
switch (key.algorithm.name) {
case 'ECDSA':
// Fall through
case 'ECDH':
if (key.type === 'public') {
return (0, _ec.ecExportKey)(key, _keys.KWebCryptoKeyFormat.kWebCryptoKeyFormatRaw);
}
break;
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// // Fall through
// case 'X25519':
// // Fall through
// case 'X448':
// if (key.type === 'public') {
// return require('internal/crypto/cfrg')
// .cfrgExportKey(key, kWebCryptoKeyFormatRaw);
// }
// break;
case 'AES-CTR':
// Fall through
case 'AES-CBC':
// Fall through
case 'AES-GCM':
// Fall through
case 'AES-KW':
// Fall through
case 'HMAC':
return key.keyObject.export();
}
throw (0, _Utils.lazyDOMException)(`Unable to export a raw ${key.algorithm.name} ${key.type} key`, 'InvalidAccessError');
};
const exportKeyJWK = key => {
const jwk = key.keyObject.handle.exportJwk({
key_ops: key.usages,
ext: key.extractable
}, true);
switch (key.algorithm.name) {
case 'RSASSA-PKCS1-v1_5':
jwk.alg = (0, _Utils.normalizeHashName)(key.algorithm.hash, _Utils.HashContext.JwkRsa);
return jwk;
case 'RSA-PSS':
jwk.alg = (0, _Utils.normalizeHashName)(key.algorithm.hash, _Utils.HashContext.JwkRsaPss);
return jwk;
case 'RSA-OAEP':
jwk.alg = (0, _Utils.normalizeHashName)(key.algorithm.hash, _Utils.HashContext.JwkRsaOaep);
return jwk;
case 'HMAC':
jwk.alg = (0, _Utils.normalizeHashName)(key.algorithm.hash, _Utils.HashContext.JwkHmac);
return jwk;
case 'ECDSA':
// Fall through
case 'ECDH':
jwk.crv ||= key.algorithm.namedCurve;
return jwk;
// case 'X25519':
// // Fall through
// case 'X448':
// jwk.crv ||= key.algorithm.name;
// return jwk;
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// jwk.crv ||= key.algorithm.name;
// return jwk;
case 'AES-CTR':
// Fall through
case 'AES-CBC':
// Fall through
case 'AES-GCM':
// Fall through
case 'AES-KW':
jwk.alg = (0, _aes.getAlgorithmName)(key.algorithm.name, key.algorithm.length);
return jwk;
// case 'HMAC':
// jwk.alg = normalizeHashName(
// key.algorithm.hash.name,
// normalizeHashName.kContextJwkHmac);
// return jwk;
default:
// Fall through
}
throw (0, _Utils.lazyDOMException)(`JWK export not yet supported: ${key.algorithm.name}`, 'NotSupportedError');
};
const importGenericSecretKey = async ({
name,
length
}, format, keyData, extractable, keyUsages) => {
if (extractable) {
throw new Error(`${name} keys are not extractable`);
}
if ((0, _Utils.hasAnyNotIn)(keyUsages, ['deriveKey', 'deriveBits'])) {
throw new Error(`Unsupported key usage for a ${name} key`);
}
switch (format) {
case 'raw':
{
if ((0, _Utils.hasAnyNotIn)(keyUsages, ['deriveKey', 'deriveBits'])) {
throw new Error(`Unsupported key usage for a ${name} key`);
}
const checkLength = typeof keyData === 'string' || _safeBuffer.Buffer.isBuffer(keyData) ? keyData.length * 8 : keyData.byteLength * 8;
// The Web Crypto spec allows for key lengths that are not multiples of
// 8. We don't. Our check here is stricter than that defined by the spec
// in that we require that algorithm.length match keyData.length * 8 if
// algorithm.length is specified.
if (length !== undefined && length !== checkLength) {
throw new Error('Invalid key length');
}
const keyObject = (0, _keys.createSecretKey)(keyData);
return new _keys.CryptoKey(keyObject, {
name
}, keyUsages, false);
}
}
throw new Error(`Unable to import ${name} key with format ${format}`);
};
// const checkCryptoKeyUsages = (key: CryptoKey) => {
// if (
// (key.type === 'secret' || key.type === 'private') &&
// key.usages.length === 0
// ) {
// throw lazyDOMException(
// 'Usages cannot be empty when creating a key.',
// 'SyntaxError'
// );
// }
// };
const checkCryptoKeyPairUsages = pair => {
if (!(pair.privateKey instanceof Buffer) && pair.privateKey && Object.prototype.hasOwnProperty.call(pair.privateKey, 'keyUsages')) {
const priv = pair.privateKey;
if (priv.usages.length > 0) {
return;
}
}
console.log(pair.privateKey);
throw (0, _Utils.lazyDOMException)('Usages cannot be empty when creating a key.', 'SyntaxError');
};
const signVerify = (algorithm, key, data, signature) => {
const usage = signature === undefined ? 'sign' : 'verify';
algorithm = (0, _Algorithms.normalizeAlgorithm)(algorithm, usage);
if (!key.usages.includes(usage) || algorithm.name !== key.algorithm.name) {
throw (0, _Utils.lazyDOMException)(`Unable to use this key to ${usage}`, 'InvalidAccessError');
}
switch (algorithm.name) {
// case 'RSA-PSS':
// // Fall through
// case 'RSASSA-PKCS1-v1_5':
// return require('internal/crypto/rsa').rsaSignVerify(
// key,
// data,
// algorithm,
// signature
// );
case 'ECDSA':
return (0, _ec.ecdsaSignVerify)(key, data, algorithm, signature);
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// return require('internal/crypto/cfrg').eddsaSignVerify(
// key,
// data,
// algorithm,
// signature
// );
// case 'HMAC':
// return require('internal/crypto/mac').hmacSignVerify(
// key,
// data,
// algorithm,
// signature
// );
}
throw (0, _Utils.lazyDOMException)(`Unrecognized algorithm name '${algorithm}' for '${usage}'`, 'NotSupportedError');
};
const cipherOrWrap = async (mode, algorithm, key, data, op) => {
// We use a Node.js style error here instead of a DOMException because
// the WebCrypto spec is not specific what kind of error is to be thrown
// in this case. Both Firefox and Chrome throw simple TypeErrors here.
// The key algorithm and cipher algorithm must match, and the
// key must have the proper usage.
if (key.algorithm.name !== algorithm.name || !key.usages.includes(op)) {
throw (0, _Utils.lazyDOMException)('The requested operation is not valid for the provided key', 'InvalidAccessError');
}
// While WebCrypto allows for larger input buffer sizes, we limit
// those to sizes that can fit within uint32_t because of limitations
// in the OpenSSL API.
(0, _Utils.validateMaxBufferLength)(data, 'data');
switch (algorithm.name) {
case 'RSA-OAEP':
return (0, _rsa.rsaCipher)(mode, key, data, algorithm);
case 'AES-CTR':
// Fall through
case 'AES-CBC':
// Fall through
case 'AES-GCM':
return (0, _aes.aesCipher)(mode, key, data, algorithm);
// case 'AES-KW':
// if (op === 'wrapKey' || op === 'unwrapKey') {
// return aesCipher(mode, key, data, algorithm);
// }
}
// @ts-expect-error unreachable code
throw (0, _Utils.lazyDOMException)(`Unrecognized algorithm name '${algorithm}' for '${op}'`, 'NotSupportedError');
};
class Subtle {
async decrypt(algorithm, key, data) {
const normalizedAlgorithm = (0, _Algorithms.normalizeAlgorithm)(algorithm, 'decrypt');
return cipherOrWrap(_keys.CipherOrWrapMode.kWebCryptoCipherDecrypt, normalizedAlgorithm, key, (0, _Utils.bufferLikeToArrayBuffer)(data), 'decrypt');
}
async digest(algorithm, data) {
const normalizedAlgorithm = (0, _Algorithms.normalizeAlgorithm)(algorithm, 'digest');
return (0, _Hash.asyncDigest)(normalizedAlgorithm, data);
}
async deriveBits(algorithm, baseKey, length) {
if (!baseKey.keyUsages.includes('deriveBits')) {
throw new Error('baseKey does not have deriveBits usage');
}
if (baseKey.algorithm.name !== algorithm.name) throw new Error('Key algorithm mismatch');
switch (algorithm.name) {
// case 'X25519':
// // Fall through
// case 'X448':
// // Fall through
// case 'ECDH':
// return require('internal/crypto/diffiehellman')
// .ecdhDeriveBits(algorithm, baseKey, length);
// case 'HKDF':
// return require('internal/crypto/hkdf')
// .hkdfDeriveBits(algorithm, baseKey, length);
case 'PBKDF2':
return (0, _pbkdf.pbkdf2DeriveBits)(algorithm, baseKey, length);
}
throw new Error(`'subtle.deriveBits()' for ${algorithm.name} is not implemented.`);
}
async encrypt(algorithm, key, data) {
const normalizedAlgorithm = (0, _Algorithms.normalizeAlgorithm)(algorithm, 'encrypt');
return cipherOrWrap(_keys.CipherOrWrapMode.kWebCryptoCipherEncrypt, normalizedAlgorithm, key, (0, _Utils.bufferLikeToArrayBuffer)(data), 'encrypt');
}
async exportKey(format, key) {
if (!key.extractable) throw new Error('key is not extractable');
switch (format) {
case 'spki':
return await exportKeySpki(key);
case 'pkcs8':
return await exportKeyPkcs8(key);
case 'jwk':
return exportKeyJWK(key);
case 'raw':
return exportKeyRaw(key);
}
}
async generateKey(algorithm, extractable, keyUsages) {
algorithm = (0, _Algorithms.normalizeAlgorithm)(algorithm, 'generateKey');
let result;
switch (algorithm.name) {
case 'RSASSA-PKCS1-v1_5':
// Fall through
case 'RSA-PSS':
// Fall through
case 'RSA-OAEP':
result = await (0, _rsa.rsaKeyGenerate)(algorithm, extractable, keyUsages);
break;
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// // Fall through
// case 'X25519':
// // Fall through
// case 'X448':
// resultType = 'CryptoKeyPair';
// result = await cfrgGenerateKey(algorithm, extractable, keyUsages);
// break;
case 'ECDSA':
// Fall through
case 'ECDH':
result = await (0, _ec.ecGenerateKey)(algorithm, extractable, keyUsages);
checkCryptoKeyPairUsages(result);
break;
// case 'HMAC':
// result = await hmacGenerateKey(algorithm, extractable, keyUsages);
// break;
case 'AES-CTR':
// Fall through
case 'AES-CBC':
// Fall through
case 'AES-GCM':
// Fall through
case 'AES-KW':
result = await (0, _aes.aesGenerateKey)(algorithm, extractable, keyUsages);
break;
default:
throw new Error(`'subtle.generateKey()' is not implemented for ${algorithm.name}.
Unrecognized algorithm name`);
}
return result;
}
async importKey(format, data, algorithm, extractable, keyUsages) {
const normalizedAlgorithm = (0, _Algorithms.normalizeAlgorithm)(algorithm, 'importKey');
let result;
switch (normalizedAlgorithm.name) {
case 'RSASSA-PKCS1-v1_5':
// Fall through
case 'RSA-PSS':
// Fall through
case 'RSA-OAEP':
result = (0, _rsa.rsaImportKey)(format, data, normalizedAlgorithm, extractable, keyUsages);
break;
case 'ECDSA':
// Fall through
case 'ECDH':
result = (0, _ec.ecImportKey)(format, data, normalizedAlgorithm, extractable, keyUsages);
break;
// case 'Ed25519':
// // Fall through
// case 'Ed448':
// // Fall through
// case 'X25519':
// // Fall through
// case 'X448':
// result = await require('internal/crypto/cfrg').cfrgImportKey(
// format,
// keyData,
// algorithm,
// extractable,
// keyUsages
// );
// break;
case 'HMAC':
result = await (0, _mac.hmacImportKey)(normalizedAlgorithm, format, data, extractable, keyUsages);
break;
case 'AES-CTR':
// Fall through
case 'AES-CBC':
// Fall through
case 'AES-GCM':
// Fall through
case 'AES-KW':
result = await (0, _aes.aesImportKey)(normalizedAlgorithm, format, data, extractable, keyUsages);
break;
// case 'HKDF':
// // Fall through
case 'PBKDF2':
result = await importGenericSecretKey(normalizedAlgorithm, format, data, extractable, keyUsages);
break;
default:
throw new Error(`"subtle.importKey()" is not implemented for ${normalizedAlgorithm.name}`);
}
if ((result.type === 'secret' || result.type === 'private') && result.usages.length === 0) {
throw new Error(`Usages cannot be empty when importing a ${result.type} key.`);
}
return result;
}
async sign(algorithm, key, data) {
return signVerify(algorithm, key, data);
}
async verify(algorithm, key, signature, data) {
return signVerify(algorithm, key, data, signature);
}
}
exports.Subtle = Subtle;
const subtle = exports.subtle = new Subtle();
//# sourceMappingURL=subtle.js.map
;