UNPKG

react-native-quick-crypto

Version:

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

144 lines (140 loc) 5.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getSortedUsages = getSortedUsages; exports.getUsagesUnion = void 0; exports.hasAnyNotIn = hasAnyNotIn; exports.isStringOrBuffer = isStringOrBuffer; exports.validateFunction = validateFunction; exports.validateJwkStructure = validateJwkStructure; exports.validateMaxBufferLength = exports.validateKeyOps = void 0; exports.validateObject = validateObject; var _safeBuffer = require("safe-buffer"); var _errors = require("./errors"); // The maximum buffer size that we'll support in the WebCrypto impl const kMaxBufferLength = 2 ** 31 - 1; function validateFunction(f) { return f !== null && typeof f === 'function'; } function isStringOrBuffer(val) { return typeof val === 'string' || ArrayBuffer.isView(val) || val instanceof ArrayBuffer; } function validateObject(value, name, options) { const useDefaultOptions = options == null; const allowArray = useDefaultOptions ? false : options.allowArray; const allowFunction = useDefaultOptions ? false : options.allowFunction; const nullable = useDefaultOptions ? false : options.nullable; if (!nullable && value === null || !allowArray && Array.isArray(value) || typeof value !== 'object' && (!allowFunction || typeof value !== 'function')) { throw new Error(`${name} is not a valid object ${value}`); } return true; } const validateMaxBufferLength = (data, name) => { const length = typeof data === 'string' || data instanceof _safeBuffer.Buffer ? data.length : data.byteLength; if (length > kMaxBufferLength) { throw (0, _errors.lazyDOMException)(`${name} must be less than ${kMaxBufferLength + 1} bits`, 'OperationError'); } }; // Returns the intersection of `usageSet` and the spread `usages`, preserving // the spread order. Dedup and canonical ordering are not performed here — // the `CryptoKey` constructor runs `getSortedUsages` on every input. exports.validateMaxBufferLength = validateMaxBufferLength; const getUsagesUnion = (usageSet, ...usages) => { const newset = []; for (let n = 0; n < usages.length; n++) { if (!usages[n] || usages[n] === undefined) continue; if (usageSet.includes(usages[n])) newset.push(usages[n]); } return newset; }; exports.getUsagesUnion = getUsagesUnion; const kCanonicalUsageOrder = ['encrypt', 'decrypt', 'sign', 'verify', 'deriveKey', 'deriveBits', 'wrapKey', 'unwrapKey', 'encapsulateKey', 'encapsulateBits', 'decapsulateKey', 'decapsulateBits']; function getSortedUsages(usages) { const set = new Set(usages); return kCanonicalUsageOrder.filter(usage => set.has(usage)); } const kKeyOps = { sign: 1, verify: 2, encrypt: 3, decrypt: 4, wrapKey: 5, unwrapKey: 6, deriveKey: 7, deriveBits: 8, encapsulateBits: 9, decapsulateBits: 10, encapsulateKey: 11, decapsulateKey: 12 }; const validateKeyOps = (keyOps, usagesSet) => { if (keyOps === undefined) return; if (!Array.isArray(keyOps)) { throw (0, _errors.lazyDOMException)('keyData.key_ops', 'InvalidArgument'); } let flags = 0; for (let n = 0; n < keyOps.length; n++) { const op = keyOps[n]; const op_flag = kKeyOps[op]; // Skipping unknown key ops if (op_flag === undefined) continue; // Have we seen it already? if so, error if (flags & 1 << op_flag) throw (0, _errors.lazyDOMException)('Duplicate key operation', 'DataError'); flags |= 1 << op_flag; // TODO(@jasnell): RFC7517 section 4.3 strong recommends validating // key usage combinations. Specifically, it says that unrelated key // ops SHOULD NOT be used together. We're not yet validating that here. } if (usagesSet !== undefined) { for (const use of usagesSet) { if (!keyOps.includes(use)) { throw (0, _errors.lazyDOMException)('Key operations and usage mismatch', 'DataError'); } } } }; // WebCrypto JWK import structural validation, mirroring Node's // `internal/crypto/webcrypto_util.validateJwk` + `validateKeyOps`: // - `ext`: if present and false, `extractable` must also be false // - `use`: if `keyUsages` is non-empty and `use` is present, must equal // the algorithm's expected use ('sig' or 'enc') // - `key_ops`: must be an array, must not contain duplicates, and every // requested usage must appear in it exports.validateKeyOps = validateKeyOps; function validateJwkStructure(jwk, extractable, keyUsages, expectedUse) { if (jwk.ext === false && extractable) { throw (0, _errors.lazyDOMException)('JWK "ext" is false but extractable was requested', 'DataError'); } if (keyUsages.length > 0 && jwk.use !== undefined) { if (jwk.use !== expectedUse) { throw (0, _errors.lazyDOMException)('Invalid JWK "use" Parameter', 'DataError'); } } if (jwk.key_ops !== undefined) { if (!Array.isArray(jwk.key_ops)) { throw (0, _errors.lazyDOMException)('JWK "key_ops" must be an array', 'DataError'); } const seen = new Set(); for (const op of jwk.key_ops) { if (seen.has(op)) { throw (0, _errors.lazyDOMException)('Duplicate key operation', 'DataError'); } seen.add(op); } for (const usage of keyUsages) { if (!jwk.key_ops.includes(usage)) { throw (0, _errors.lazyDOMException)(`JWK "key_ops" does not include requested usage "${usage}"`, 'DataError'); } } } } function hasAnyNotIn(set, checks) { for (const s of set) { if (!checks.includes(s)) { return true; } } return false; } //# sourceMappingURL=validation.js.map