UNPKG

react-native-quick-crypto

Version:

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

248 lines (233 loc) 7.29 kB
import type { AnyAlgorithm, DeriveBitsAlgorithm, DigestAlgorithm, EncryptDecryptAlgorithm, EncryptDecryptParams, KeyPairAlgorithm, SecretKeyAlgorithm, SignVerifyAlgorithm, SubtleAlgorithm, } from './keys'; type SupportedAlgorithm<Type extends string> = { [key in Type]: string | null; }; type SupportedAlgorithms = { digest: SupportedAlgorithm<DigestAlgorithm>; generateKey: SupportedAlgorithm<KeyPairAlgorithm | SecretKeyAlgorithm>; sign: SupportedAlgorithm<SignVerifyAlgorithm>; verify: SupportedAlgorithm<SignVerifyAlgorithm>; importKey: SupportedAlgorithm< KeyPairAlgorithm | 'PBKDF2' | SecretKeyAlgorithm | 'HKDF' >; deriveBits: SupportedAlgorithm<DeriveBitsAlgorithm>; encrypt: SupportedAlgorithm<EncryptDecryptAlgorithm>; decrypt: SupportedAlgorithm<EncryptDecryptAlgorithm>; 'get key length': SupportedAlgorithm<SecretKeyAlgorithm | 'PBKDF2' | 'HKDF'>; wrapKey: SupportedAlgorithm<'AES-KW'>; unwrapKey: SupportedAlgorithm<'AES-KW'>; }; export type Operation = | 'digest' | 'generateKey' | 'sign' | 'verify' | 'importKey' | 'deriveBits' | 'encrypt' | 'decrypt' | 'get key length' | 'wrapKey' | 'unwrapKey'; const kSupportedAlgorithms: SupportedAlgorithms = { digest: { 'SHA-1': null, 'SHA-256': null, 'SHA-384': null, 'SHA-512': null, }, generateKey: { 'RSASSA-PKCS1-v1_5': 'RsaHashedKeyGenParams', 'RSA-PSS': 'RsaHashedKeyGenParams', 'RSA-OAEP': 'RsaHashedKeyGenParams', ECDSA: 'EcKeyGenParams', ECDH: 'EcKeyGenParams', 'AES-CTR': 'AesKeyGenParams', 'AES-CBC': 'AesKeyGenParams', 'AES-GCM': 'AesKeyGenParams', 'AES-KW': 'AesKeyGenParams', HMAC: 'HmacKeyGenParams', X25519: null, Ed25519: null, X448: null, Ed448: null, }, sign: { 'RSASSA-PKCS1-v1_5': null, 'RSA-PSS': 'RsaPssParams', ECDSA: 'EcdsaParams', HMAC: null, Ed25519: null, Ed448: 'Ed448Params', }, verify: { 'RSASSA-PKCS1-v1_5': null, 'RSA-PSS': 'RsaPssParams', ECDSA: 'EcdsaParams', HMAC: null, Ed25519: null, Ed448: 'Ed448Params', }, importKey: { 'RSASSA-PKCS1-v1_5': 'RsaHashedImportParams', 'RSA-PSS': 'RsaHashedImportParams', 'RSA-OAEP': 'RsaHashedImportParams', ECDSA: 'EcKeyImportParams', ECDH: 'EcKeyImportParams', HMAC: 'HmacImportParams', HKDF: null, PBKDF2: null, 'AES-CTR': null, 'AES-CBC': null, 'AES-GCM': null, 'AES-KW': null, Ed25519: null, X25519: null, Ed448: null, X448: null, }, deriveBits: { HKDF: 'HkdfParams', PBKDF2: 'Pbkdf2Params', ECDH: 'EcdhKeyDeriveParams', X25519: 'EcdhKeyDeriveParams', X448: 'EcdhKeyDeriveParams', }, encrypt: { 'RSA-OAEP': 'RsaOaepParams', 'AES-CBC': 'AesCbcParams', 'AES-GCM': 'AesGcmParams', 'AES-CTR': 'AesCtrParams', }, decrypt: { 'RSA-OAEP': 'RsaOaepParams', 'AES-CBC': 'AesCbcParams', 'AES-GCM': 'AesGcmParams', 'AES-CTR': 'AesCtrParams', }, 'get key length': { 'AES-CBC': 'AesDerivedKeyParams', 'AES-CTR': 'AesDerivedKeyParams', 'AES-GCM': 'AesDerivedKeyParams', 'AES-KW': 'AesDerivedKeyParams', HMAC: 'HmacImportParams', HKDF: null, PBKDF2: null, }, wrapKey: { 'AES-KW': null, }, unwrapKey: { 'AES-KW': null, }, }; type AlgorithmDictionaries = { [key in string]: object; }; const simpleAlgorithmDictionaries: AlgorithmDictionaries = { AesGcmParams: { iv: 'BufferSource', additionalData: 'BufferSource' }, RsaHashedKeyGenParams: { hash: 'HashAlgorithmIdentifier' }, EcKeyGenParams: {}, HmacKeyGenParams: { hash: 'HashAlgorithmIdentifier' }, RsaPssParams: {}, EcdsaParams: { hash: 'HashAlgorithmIdentifier' }, HmacImportParams: { hash: 'HashAlgorithmIdentifier' }, HkdfParams: { hash: 'HashAlgorithmIdentifier', salt: 'BufferSource', info: 'BufferSource', }, Ed448Params: { context: 'BufferSource' }, Pbkdf2Params: { hash: 'HashAlgorithmIdentifier', salt: 'BufferSource' }, RsaOaepParams: { label: 'BufferSource' }, RsaHashedImportParams: { hash: 'HashAlgorithmIdentifier' }, EcKeyImportParams: {}, }; // https://w3c.github.io/webcrypto/#algorithm-normalization-normalize-an-algorithm // adapted for Node.js from Deno's implementation // https://github.com/denoland/deno/blob/v1.29.1/ext/crypto/00_crypto.js#L195 export const normalizeAlgorithm = ( algorithm: SubtleAlgorithm | EncryptDecryptParams | AnyAlgorithm, op: Operation, ): SubtleAlgorithm | EncryptDecryptParams => { if (typeof algorithm === 'string') { return normalizeAlgorithm({ name: algorithm }, op); } // 1. const registeredAlgorithms = kSupportedAlgorithms[op]; // 2. 3. // commented, because typescript takes care of this for us 🤞👀 // const initialAlg = webidl.converters.Algorithm(algorithm, { // prefix: 'Failed to normalize algorithm', // context: 'passed algorithm', // }); // 4. let algName = algorithm.name; if (algName === undefined) return { name: 'unknown' }; // 5. let desiredType: string | null | undefined; for (const key in registeredAlgorithms) { if (!Object.prototype.hasOwnProperty.call(registeredAlgorithms, key)) { continue; } if (key.toUpperCase() === algName.toUpperCase()) { algName = key as AnyAlgorithm; desiredType = ( registeredAlgorithms as Record<string, typeof desiredType> )[algName]; } } if (desiredType === undefined) throw new Error(`Unrecognized algorithm name: ${algName}`); // Fast path everything below if the registered dictionary is null if (desiredType === null) return { name: algName }; // 6. const normalizedAlgorithm = algorithm; // TODO: implement this? Maybe via typescript? // webidl.converters[desiredType](algorithm, { // prefix: 'Failed to normalize algorithm', // context: 'passed algorithm', // }); // 7. normalizedAlgorithm.name = algName; // 9. const dict = simpleAlgorithmDictionaries[desiredType]; // 10. const dictKeys = dict ? Object.keys(dict) : []; for (let i = 0; i < dictKeys.length; i++) { const member = dictKeys[i] || ''; if (!Object.prototype.hasOwnProperty.call(dict, member)) continue; // TODO: implement this? Maybe via typescript? // const idlType = dict[member]; // const idlValue = normalizedAlgorithm[member]; // 3. // if (idlType === 'BufferSource' && idlValue) { // const isView = ArrayBufferIsView(idlValue); // normalizedAlgorithm[member] = TypedArrayPrototypeSlice( // new Uint8Array( // isView ? getDataViewOrTypedArrayBuffer(idlValue) : idlValue, // isView ? getDataViewOrTypedArrayByteOffset(idlValue) : 0, // isView // ? getDataViewOrTypedArrayByteLength(idlValue) // : ArrayBufferPrototypeGetByteLength(idlValue) // ) // ); // } else if (idlType === 'HashAlgorithmIdentifier') { // normalizedAlgorithm[member] = normalizeAlgorithm(idlValue, 'digest'); // } else if (idlType === 'AlgorithmIdentifier') { // // This extension point is not used by any supported algorithm (yet?) // throw lazyDOMException('Not implemented.', 'NotSupportedError'); // } } return normalizedAlgorithm; };