UNPKG

react-native-quick-crypto

Version:

A fast implementation of Node's `crypto` module written in C/C++ JSI

282 lines (276 loc) 10.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.kAesKeyLengths = exports.getAlgorithmName = exports.aesImportKey = exports.aesGenerateKey = exports.aesCipher = exports.AESKeyVariant = void 0; var _NativeQuickCrypto = require("./NativeQuickCrypto/NativeQuickCrypto"); var _Utils = require("./Utils"); var _keys = require("./keys"); var _keygen = require("./keygen"); // needs to match the values in cpp/webcrypto/crypto_aes.{h,cpp} let AESKeyVariant = exports.AESKeyVariant = /*#__PURE__*/function (AESKeyVariant) { AESKeyVariant[AESKeyVariant["AES_CTR_128"] = 0] = "AES_CTR_128"; AESKeyVariant[AESKeyVariant["AES_CTR_192"] = 1] = "AES_CTR_192"; AESKeyVariant[AESKeyVariant["AES_CTR_256"] = 2] = "AES_CTR_256"; AESKeyVariant[AESKeyVariant["AES_CBC_128"] = 3] = "AES_CBC_128"; AESKeyVariant[AESKeyVariant["AES_CBC_192"] = 4] = "AES_CBC_192"; AESKeyVariant[AESKeyVariant["AES_CBC_256"] = 5] = "AES_CBC_256"; AESKeyVariant[AESKeyVariant["AES_GCM_128"] = 6] = "AES_GCM_128"; AESKeyVariant[AESKeyVariant["AES_GCM_192"] = 7] = "AES_GCM_192"; AESKeyVariant[AESKeyVariant["AES_GCM_256"] = 8] = "AES_GCM_256"; AESKeyVariant[AESKeyVariant["AES_KW_128"] = 9] = "AES_KW_128"; AESKeyVariant[AESKeyVariant["AES_KW_192"] = 10] = "AES_KW_192"; AESKeyVariant[AESKeyVariant["AES_KW_256"] = 11] = "AES_KW_256"; return AESKeyVariant; }({}); const kMaxCounterLength = 128; const kTagLengths = [32, 64, 96, 104, 112, 120, 128]; const kAesKeyLengths = exports.kAesKeyLengths = [128, 192, 256]; const getAlgorithmName = (name, length) => { if (length === undefined) throw (0, _Utils.lazyDOMException)(`Invalid algorithm length: ${length}`, 'SyntaxError'); switch (name) { case 'AES-CBC': return `A${length}CBC`; case 'AES-CTR': return `A${length}CTR`; case 'AES-GCM': return `A${length}GCM`; case 'AES-KW': return `A${length}KW`; default: throw (0, _Utils.lazyDOMException)(`invalid algorithm name: ${name}`, 'SyntaxError'); } }; exports.getAlgorithmName = getAlgorithmName; function validateKeyLength(length) { if (length !== 128 && length !== 192 && length !== 256) throw (0, _Utils.lazyDOMException)(`Invalid key length: ${length}`, 'DataError'); } function getVariant(name, length) { switch (name) { case 'AES-CBC': switch (length) { case 128: return AESKeyVariant.AES_CBC_128; case 192: return AESKeyVariant.AES_CBC_192; case 256: return AESKeyVariant.AES_CBC_256; } // @ts-expect-error unreachable code break; case 'AES-CTR': switch (length) { case 128: return AESKeyVariant.AES_CTR_128; case 192: return AESKeyVariant.AES_CTR_192; case 256: return AESKeyVariant.AES_CTR_256; } // @ts-expect-error unreachable code break; case 'AES-GCM': switch (length) { case 128: return AESKeyVariant.AES_GCM_128; case 192: return AESKeyVariant.AES_GCM_192; case 256: return AESKeyVariant.AES_GCM_256; } // @ts-expect-error unreachable code break; case 'AES-KW': switch (length) { case 128: return AESKeyVariant.AES_KW_128; case 192: return AESKeyVariant.AES_KW_192; case 256: return AESKeyVariant.AES_KW_256; } // @ts-expect-error unreachable code break; } // @ts-expect-error unreachable code throw (0, _Utils.lazyDOMException)(`Error getting variant ${name} at length: ${length}`, 'DataError'); } function asyncAesCtrCipher(mode, key, data, { counter, length }) { (0, _Utils.validateByteLength)(counter, 'algorithm.counter', 16); // The length must specify an integer between 1 and 128. While // there is no default, this should typically be 64. if (length === 0 || length > kMaxCounterLength) { throw (0, _Utils.lazyDOMException)('AES-CTR algorithm.length must be between 1 and 128', 'OperationError'); } return _NativeQuickCrypto.NativeQuickCrypto.webcrypto.aesCipher(mode, key.keyObject.handle, data, getVariant('AES-CTR', key.algorithm.length), (0, _Utils.bufferLikeToArrayBuffer)(counter), length); } function asyncAesCbcCipher(mode, key, data, { iv }) { (0, _Utils.validateByteLength)(iv, 'algorithm.iv', 16); return _NativeQuickCrypto.NativeQuickCrypto.webcrypto.aesCipher(mode, key.keyObject.handle, data, getVariant('AES-CBC', key.algorithm.length), (0, _Utils.bufferLikeToArrayBuffer)(iv)); } // function asyncAesKwCipher( // mode: CipherOrWrapMode, // key: CryptoKey, // data: BufferLike // ): Promise<ArrayBuffer> { // return NativeQuickCrypto.webcrypto.aesCipher( // mode, // key.keyObject.handle, // data, // getVariant('AES-KW', key.algorithm.length) // ); // } function asyncAesGcmCipher(mode, key, data, { iv, additionalData, tagLength = 128 }) { if (!kTagLengths.includes(tagLength)) { throw (0, _Utils.lazyDOMException)(`${tagLength} is not a valid AES-GCM tag length`, 'OperationError'); } (0, _Utils.validateMaxBufferLength)(iv, 'algorithm.iv'); if (additionalData !== undefined) { (0, _Utils.validateMaxBufferLength)(additionalData, 'algorithm.additionalData'); } const tagByteLength = Math.floor(tagLength / 8); let length; let tag = new ArrayBuffer(0); switch (mode) { case _keys.CipherOrWrapMode.kWebCryptoCipherDecrypt: { // const slice = ArrayBuffer.isView(data) // ? DataView.prototype.buffer.slice // : ArrayBuffer.prototype.slice; tag = data.slice(-tagByteLength); // Refs: https://www.w3.org/TR/WebCryptoAPI/#aes-gcm-operations // // > If *plaintext* has a length less than *tagLength* bits, then `throw` // > an `OperationError`. if (tagByteLength > tag.byteLength) { throw (0, _Utils.lazyDOMException)('The provided data is too small.', 'OperationError'); } data = data.slice(0, -tagByteLength); break; } case _keys.CipherOrWrapMode.kWebCryptoCipherEncrypt: length = tagByteLength; break; } return _NativeQuickCrypto.NativeQuickCrypto.webcrypto.aesCipher(mode, key.keyObject.handle, data, getVariant('AES-GCM', key.algorithm.length), (0, _Utils.bufferLikeToArrayBuffer)(iv), length, (0, _Utils.bufferLikeToArrayBuffer)(tag), (0, _Utils.bufferLikeToArrayBuffer)(additionalData || new ArrayBuffer(0))); } const aesCipher = (mode, key, data, algorithm // | WrapUnwrapParams ) => { switch (algorithm.name) { case 'AES-CTR': return asyncAesCtrCipher(mode, key, data, algorithm); case 'AES-CBC': return asyncAesCbcCipher(mode, key, data, algorithm); case 'AES-GCM': return asyncAesGcmCipher(mode, key, data, algorithm); // case 'AES-KW': // return asyncAesKwCipher(mode, key, data); } throw new Error(`aesCipher: Unknown algorithm ${algorithm.name}`); }; exports.aesCipher = aesCipher; const aesGenerateKey = async (algorithm, extractable, keyUsages) => { const { name, length } = algorithm; if (!name) { throw (0, _Utils.lazyDOMException)('Algorithm name is undefined', 'SyntaxError'); } if (!kAesKeyLengths.includes(length)) { throw (0, _Utils.lazyDOMException)('AES key length must be 128, 192, or 256 bits', 'OperationError'); } const checkUsages = ['wrapKey', 'unwrapKey']; if (name !== 'AES-KW') { checkUsages.push('encrypt', 'decrypt'); } // const usagesSet = new SafeSet(keyUsages); if ((0, _Utils.hasAnyNotIn)(keyUsages, checkUsages)) { throw (0, _Utils.lazyDOMException)(`Unsupported key usage for an AES key: ${keyUsages}`, 'SyntaxError'); } const [err, key] = await (0, _keygen.generateKeyPromise)('aes', { length }); if (err) { throw (0, _Utils.lazyDOMException)(`aesGenerateKey (generateKeyPromise) failed: [${err.message}]`, { name: 'OperationError', cause: err }); } return new _keys.CryptoKey(key, { name, length }, Array.from(keyUsages), extractable); }; exports.aesGenerateKey = aesGenerateKey; const aesImportKey = async (algorithm, format, keyData, extractable, keyUsages) => { const { name } = algorithm; const checkUsages = ['wrapKey', 'unwrapKey']; if (name !== 'AES-KW') { checkUsages.push('encrypt', 'decrypt'); } // const usagesSet = new SafeSet(keyUsages); if ((0, _Utils.hasAnyNotIn)(keyUsages, checkUsages)) { throw (0, _Utils.lazyDOMException)('Unsupported key usage for an AES key', 'SyntaxError'); } let keyObject; let length; switch (format) { case 'raw': { const data = (0, _Utils.bufferLikeToArrayBuffer)(keyData); validateKeyLength(data.byteLength * 8); keyObject = (0, _keys.createSecretKey)(data); break; } case 'jwk': { const data = keyData; if (!data.kty) throw (0, _Utils.lazyDOMException)('Invalid keyData', 'DataError'); if (data.kty !== 'oct') throw (0, _Utils.lazyDOMException)('Invalid JWK "kty" Parameter', 'DataError'); if (keyUsages.length > 0 && data.use !== undefined && data.use !== 'enc') { throw (0, _Utils.lazyDOMException)('Invalid JWK "use" Parameter', 'DataError'); } (0, _Utils.validateKeyOps)(data.key_ops, keyUsages); if (data.ext !== undefined && data.ext === false && extractable === true) { throw (0, _Utils.lazyDOMException)('JWK "ext" Parameter and extractable mismatch', 'DataError'); } const handle = _NativeQuickCrypto.NativeQuickCrypto.webcrypto.createKeyObjectHandle(); handle.initJwk(data); ({ length } = handle.keyDetail()); validateKeyLength(length); if (data.alg !== undefined) { if (data.alg !== getAlgorithmName(algorithm.name, length)) throw (0, _Utils.lazyDOMException)('JWK "alg" does not match the requested algorithm', 'DataError'); } keyObject = new _keys.SecretKeyObject(handle); break; } default: throw (0, _Utils.lazyDOMException)(`Unable to import AES key with format ${format}`, 'NotSupportedError'); } if (length === undefined) { ({ length } = keyObject.handle.keyDetail()); validateKeyLength(length); } return new _keys.CryptoKey(keyObject, { name, length }, keyUsages, extractable); }; exports.aesImportKey = aesImportKey; //# sourceMappingURL=aes.js.map