UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

345 lines (339 loc) 12.8 kB
'use strict'; var crypto = require('crypto'); var constants = require('../../utils/constants.js'); var Uint8Array$1 = require('../../helpers/Uint8Array.js'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var crypto__namespace = /*#__PURE__*/_interopNamespaceDefault(crypto); /** * Random generators - Core random generation methods (bytes, ints, UUIDs) */ class RandomGenerators { /** * Generate cryptographically secure random bytes * @param length - Number of bytes to generate * @param options - Generation options * @returns Secure random bytes */ static getRandomBytes(length, options = {}) { if (length <= 0) { throw new Error("Length must be positive"); } if (length > constants.SECURITY_CONSTANTS.MAX_ENTROPY_BITS / 8) { throw new Error(`Length exceeds maximum secure length (${constants.SECURITY_CONSTANTS.MAX_ENTROPY_BITS / 8} bytes)`); } const { useEntropyPool = true, quantumSafe = false, reseedThreshold = constants.SECURITY_CONSTANTS.RESEED_THRESHOLD, validateOutput = true, } = options; let bytes; if (quantumSafe) { // Use quantum-safe generation bytes = RandomGenerators.getQuantumSafeBytes(length); } else { // Use standard secure generation bytes = RandomGenerators.getSystemRandomBytes(length); } // Validate output if requested if (validateOutput) { RandomGenerators.validateRandomOutput(bytes); } return bytes; } /** * Get system random bytes using multiple sources */ static getSystemRandomBytes(length) { const bytes = new Uint8Array(length); // Try different methods to get random bytes if (typeof crypto__namespace !== "undefined" && typeof crypto__namespace.getRandomValues === "function") { // Browser or Node.js with Web Crypto API crypto__namespace.getRandomValues(bytes); return bytes; } else if (typeof window !== "undefined" && typeof window.crypto !== "undefined" && typeof window.crypto.getRandomValues === "function") { // Browser window.crypto.getRandomValues(bytes); return bytes; } else if (typeof require === "function") { try { // Node.js const nodeRandomBytes = crypto__namespace.randomBytes(length); return new Uint8Array(nodeRandomBytes.buffer, nodeRandomBytes.byteOffset, nodeRandomBytes.byteLength); } catch (e) { // Fallback to non-secure random return RandomGenerators.getFallbackRandomBytes(length); } } else { // Ultimate fallback return RandomGenerators.getFallbackRandomBytes(length); } } /** * Get quantum-safe random bytes */ static getQuantumSafeBytes(length) { // Use multiple entropy sources and mix them const sources = []; // Primary source sources.push(RandomGenerators.getSystemRandomBytes(length)); // Additional entropy from system sources const systemEntropy = crypto__namespace.randomBytes(length); sources.push(new Uint8Array(systemEntropy)); // Combine sources using XOR const result = new Uint8Array(length); for (const source of sources) { for (let i = 0; i < length; i++) { result[i] ^= source[i % source.length]; } } // Hash the result for uniform distribution const hash = crypto__namespace.createHash("sha512").update(result).digest(); return new Uint8Array(hash.buffer, hash.byteOffset, Math.min(length, hash.byteLength)); } /** * Fallback random bytes (not cryptographically secure) */ static getFallbackRandomBytes(length) { console.warn("Using fallback random bytes - not cryptographically secure!"); const bytes = new Uint8Array(length); for (let i = 0; i < length; i++) { bytes[i] = Math.floor(Math.random() * 256); } return bytes; } /** * Validate random output quality */ static validateRandomOutput(bytes) { if (bytes.length === 0) { throw new Error("Empty random output"); } // Check for all zeros (only for larger outputs, single bytes can legitimately be 0) if (bytes.length > 1 && bytes.every((b) => b === 0)) { throw new Error("Random output is all zeros"); } // Check for all same value (only for larger outputs) if (bytes.length > 4) { const firstByte = bytes[0]; if (bytes.every((b) => b === firstByte)) { throw new Error("Random output has no entropy"); } } // Basic entropy check for larger outputs if (bytes.length >= 16) { const uniqueBytes = new Set(bytes); const entropyRatio = uniqueBytes.size / bytes.length; if (entropyRatio < 0.1) { console.warn("Low entropy detected in random output"); } } } /** * Generate cryptographically secure random integers with uniform distribution * @param min - Minimum value (inclusive) * @param max - Maximum value (inclusive) * @param options - Generation options * @returns Secure random integer */ static getSecureRandomInt(min, max, options = {}) { if (min > max) { throw new Error("Min cannot be greater than max"); } if (!Number.isInteger(min) || !Number.isInteger(max)) { throw new Error("Min and max must be integers"); } const range = max - min + 1; if (range <= 0) { throw new Error("Invalid range"); } // For small ranges, use simple method if (range <= 256) { return RandomGenerators.getSmallRangeInt(min, max, options); } // For larger ranges, use rejection sampling return RandomGenerators.getLargeRangeInt(min, max, options); } /** * Generate random integer for small ranges (≤256) */ static getSmallRangeInt(min, max, options) { const range = max - min + 1; const randomByte = RandomGenerators.getRandomBytes(1, options)[0]; // Use rejection sampling to avoid bias const threshold = Math.floor(256 / range) * range; if (randomByte < threshold) { return min + (randomByte % range); } // Retry if we hit the biased region return RandomGenerators.getSmallRangeInt(min, max, options); } /** * Generate random integer for large ranges (>256) */ static getLargeRangeInt(min, max, options) { const range = max - min + 1; const bytesNeeded = Math.ceil(Math.log2(range) / 8); let randomValue = 0; const randomBytes = RandomGenerators.getRandomBytes(bytesNeeded, options); for (let i = 0; i < bytesNeeded; i++) { randomValue = (randomValue << 8) | randomBytes[i]; } // Use rejection sampling const threshold = Math.floor(2 ** (bytesNeeded * 8) / range) * range; if (randomValue < threshold) { return min + (randomValue % range); } // Retry if we hit the biased region return RandomGenerators.getLargeRangeInt(min, max, options); } /** * Generate secure UUID v4 * @param options - Generation options * @returns Secure UUID string */ static generateSecureUUID(options = {}) { const bytes = RandomGenerators.getRandomBytes(16, options); // Set version (4) and variant bits according to RFC 4122 bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4 bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10 const hex = Array.from(bytes) .map((b) => b.toString(16).padStart(2, "0")) .join(""); return [ hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20, 32), ].join("-"); } /** * Generate multiple UUIDs efficiently * @param count - Number of UUIDs to generate * @param options - Generation options * @returns Array of secure UUID strings */ static generateSecureUUIDBatch(count, options = {}) { if (count <= 0) { throw new Error("Count must be positive"); } if (count > 1000) { throw new Error("Count too large (max 1000 per batch)"); } // Generate all random bytes at once for efficiency const allBytes = RandomGenerators.getRandomBytes(16 * count, options); const uuids = []; for (let i = 0; i < count; i++) { const offset = i * 16; const bytes = allBytes.slice(offset, offset + 16); // Set version and variant bits bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4 bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10 const hex = Array.from(bytes) .map((b) => b.toString(16).padStart(2, "0")) .join(""); const uuid = [ hex.slice(0, 8), hex.slice(8, 12), hex.slice(12, 16), hex.slice(16, 20), hex.slice(20, 32), ].join("-"); uuids.push(uuid); } return uuids; } /** * Generate random float between 0 and 1 * @param options - Generation options * @returns Secure random float */ static getSecureRandomFloat(options = {}) { // Use 8 bytes for high precision const bytes = RandomGenerators.getRandomBytes(8, options); // Convert to 64-bit integer let value = 0; for (let i = 0; i < 8; i++) { value = value * 256 + bytes[i]; } // Convert to float between 0 and 1 return value / (Math.pow(2, 64) - 1); } /** * Generate random boolean * @param options - Generation options * @returns Secure random boolean */ static getSecureRandomBoolean(options = {}) { const byte = RandomGenerators.getRandomBytes(1, options)[0]; return (byte & 1) === 1; } /** * Generate random choice from array * @param array - Array to choose from * @param options - Generation options * @returns Random element from array */ static getSecureRandomChoice(array, options = {}) { if (array.length === 0) { throw new Error("Array cannot be empty"); } const index = RandomGenerators.getSecureRandomInt(0, array.length - 1, options); return array[index]; } /** * Shuffle array using Fisher-Yates algorithm with secure randomness * @param array - Array to shuffle * @param options - Generation options * @returns Shuffled array (new array) */ static secureArrayShuffle(array, options = {}) { const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = RandomGenerators.getSecureRandomInt(0, i, options); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; } /** * Generate salt with specified length * @param length - Salt length in bytes * @param options - Generation options * @returns Salt as Buffer */ static generateSalt(length = 32, options = {}) { const bytes = RandomGenerators.getRandomBytes(length, options); return Buffer.from(bytes); } /** * Generate nonce with specified length * @param length - Nonce length in bytes * @param options - Generation options * @returns Nonce as EnhancedUint8Array */ static generateNonce(length = 12, options = {}) { const bytes = RandomGenerators.getRandomBytes(length, options); return new Uint8Array$1.EnhancedUint8Array(bytes); } } exports.RandomGenerators = RandomGenerators; //# sourceMappingURL=random-generators.js.map