UNPKG

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
"use strict"; 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