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
JavaScript
;
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