UNPKG

react-native-quick-crypto

Version:

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

135 lines (115 loc) 3.12 kB
import { Buffer } from '@craftzdog/react-native-buffer'; import { NitroModules } from 'react-native-nitro-modules'; import type { Scrypt as NativeScrypt } from './specs/scrypt.nitro'; import { binaryLikeToArrayBuffer } from './utils'; import type { BinaryLike } from './utils'; type Password = BinaryLike; type Salt = BinaryLike; export interface ScryptOptions { N?: number; r?: number; p?: number; cost?: number; blockSize?: number; parallelization?: number; maxmem?: number; } type ScryptCallback = (err: Error | null, derivedKey?: Buffer) => void; // Lazy load native module let native: NativeScrypt; function getNative(): NativeScrypt { if (native == null) { native = NitroModules.createHybridObject<NativeScrypt>('Scrypt'); } return native; } const defaults = { N: 16384, r: 8, p: 1, maxmem: 32 * 1024 * 1024, }; function getScryptParams(options?: ScryptOptions) { const N = options?.N ?? options?.cost ?? defaults.N; const r = options?.r ?? options?.blockSize ?? defaults.r; const p = options?.p ?? options?.parallelization ?? defaults.p; const maxmem = options?.maxmem ?? defaults.maxmem; return { N, r, p, maxmem }; } function validateCallback(callback: ScryptCallback) { if (callback === undefined || typeof callback !== 'function') { throw new Error('No callback provided to scrypt'); } } function sanitizeInput(input: BinaryLike, name: string): ArrayBuffer { try { return binaryLikeToArrayBuffer(input); } catch { throw new Error( `${name} must be a string, a Buffer, a typed array, or a DataView`, ); } } export function scrypt( password: Password, salt: Salt, keylen: number, options?: ScryptOptions | ScryptCallback, callback?: ScryptCallback, ): void { let cb: ScryptCallback; let opts: ScryptOptions | undefined; if (typeof options === 'function') { cb = options; opts = undefined; } else { cb = callback!; opts = options; } validateCallback(cb); try { const { N, r, p, maxmem } = getScryptParams(opts); const sanitizedPassword = sanitizeInput(password, 'Password'); const sanitizedSalt = sanitizeInput(salt, 'Salt'); if (keylen < 0) { throw new TypeError('Bad key length'); } const nativeMod = getNative(); nativeMod .deriveKey(sanitizedPassword, sanitizedSalt, N, r, p, maxmem, keylen) .then( res => { cb(null, Buffer.from(res)); }, err => { cb(err); }, ); } catch (err) { cb(err as Error); } } export function scryptSync( password: Password, salt: Salt, keylen: number, options?: ScryptOptions, ): Buffer { const { N, r, p, maxmem } = getScryptParams(options); const sanitizedPassword = sanitizeInput(password, 'Password'); const sanitizedSalt = sanitizeInput(salt, 'Salt'); if (keylen < 0) { throw new TypeError('Bad key length'); } const nativeMod = getNative(); const result = nativeMod.deriveKeySync( sanitizedPassword, sanitizedSalt, N, r, p, maxmem, keylen, ); return Buffer.from(result); }