UNPKG

react-native-quick-crypto

Version:

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

139 lines (136 loc) 4.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.scrypt = scrypt; exports.scryptSync = scryptSync; var _reactNativeBuffer = require("@craftzdog/react-native-buffer"); var _reactNativeNitroModules = require("react-native-nitro-modules"); var _utils = require("./utils"); // Lazy load native module let native; function getNative() { if (native == null) { native = _reactNativeNitroModules.NitroModules.createHybridObject('Scrypt'); } return native; } const defaults = { N: 16384, r: 8, p: 1, maxmem: 32 * 1024 * 1024 }; // RFC 7914 § 2: scrypt parameters // N — CPU/memory cost; must be a power of 2 > 1. // r — block size; positive integer. // p — parallelization factor; positive integer. // r * p must be < 2^30 (otherwise the spec output is undefined). // The work buffer is 128 * r * N bytes, which must fit in maxmem. const SCRYPT_MAX_RP = 1 << 30; // 2^30 per RFC 7914 function isPositiveInteger(value) { return typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value) && value > 0; } function validateScryptParams(N, r, p, maxmem) { if (!isPositiveInteger(N)) { throw new RangeError(`Invalid scrypt cost (N): ${N}`); } // Power-of-two & > 1 check (RFC 7914 §6 step 1). if (N <= 1 || (N & N - 1) !== 0) { throw new RangeError(`Invalid scrypt cost (N): ${N} — must be a power of 2 greater than 1`); } if (!isPositiveInteger(r)) { throw new RangeError(`Invalid scrypt blockSize (r): ${r}`); } if (!isPositiveInteger(p)) { throw new RangeError(`Invalid scrypt parallelization (p): ${p}`); } if (r * p >= SCRYPT_MAX_RP) { throw new RangeError(`Invalid scrypt parameters: r * p (${r * p}) must be < 2^30`); } if (!isPositiveInteger(maxmem)) { throw new RangeError(`Invalid scrypt maxmem: ${maxmem}`); } // 128 * r * N is the minimum working memory. Reject early so we don't // hand a doomed parameter set to native and OOM the device. const required = 128 * r * N; if (required > maxmem) { throw new RangeError(`Invalid scrypt parameters: working memory ${required} bytes ` + `exceeds maxmem ${maxmem}`); } } function validateScryptKeylen(keylen) { if (typeof keylen !== 'number' || !Number.isFinite(keylen) || !Number.isInteger(keylen) || keylen < 0 || keylen > 0x7fff_ffff) { throw new TypeError('Bad key length'); } } function getScryptParams(options) { 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; validateScryptParams(N, r, p, maxmem); return { N, r, p, maxmem }; } function validateCallback(callback) { if (callback === undefined || typeof callback !== 'function') { throw new Error('No callback provided to scrypt'); } } function sanitizeInput(input, name) { try { return (0, _utils.binaryLikeToArrayBuffer)(input); } catch { throw new Error(`${name} must be a string, a Buffer, a typed array, or a DataView`); } } function scrypt(password, salt, keylen, options, callback) { let cb; let opts; 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'); validateScryptKeylen(keylen); const nativeMod = getNative(); nativeMod.deriveKey(sanitizedPassword, sanitizedSalt, N, r, p, maxmem, keylen).then(res => { cb(null, _reactNativeBuffer.Buffer.from(res)); }, err => { cb(err); }); } catch (err) { cb(err); } } function scryptSync(password, salt, keylen, options) { const { N, r, p, maxmem } = getScryptParams(options); const sanitizedPassword = sanitizeInput(password, 'Password'); const sanitizedSalt = sanitizeInput(salt, 'Salt'); validateScryptKeylen(keylen); const nativeMod = getNative(); const result = nativeMod.deriveKeySync(sanitizedPassword, sanitizedSalt, N, r, p, maxmem, keylen); return _reactNativeBuffer.Buffer.from(result); } //# sourceMappingURL=scrypt.js.map