UNPKG

lockbyte

Version:

Enterprise-grade password hashing and user authentication library with Argon2-inspired algorithm, memory-hard functions, and comprehensive security features

262 lines • 10.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SecureCrypto = void 0; const crypto_1 = __importDefault(require("crypto")); class SecureCrypto { constructor(config = {}) { this.config = { ...SecureCrypto.DEFAULT_CONFIG, ...config }; this.validateConfig(); this.warnAboutPepper(); } validateConfig() { if (this.config.memoryCost < 4096) { throw new Error("Memory cost must be at least 4096 KB"); } if (this.config.timeCost < 1) { throw new Error("Time cost must be at least 1"); } if (this.config.parallelism < 1) { throw new Error("Parallelism must be at least 1"); } if (this.config.saltLength < 16) { throw new Error("Salt length must be at least 16 bytes"); } } warnAboutPepper() { const noPepper = !this.config.pepper || this.config.pepper.length === 0; if (this.config.requirePepper && noPepper) { throw new Error("Pepper is required but not provided. Set pepper in config or disable requirePepper."); } if (noPepper) { console.warn(` āš ļø SECURITY WARNING: No pepper configured! - Auth tags will use derived keys instead of pepper - For maximum security, set a strong pepper: new SecureCrypto({ pepper: 'your-secret-pepper-here' }) - Pepper should be kept secret and stored securely - Different peppers will generate different hashes - Set requirePepper: true to make pepper mandatory `.trim()); } } generateSalt() { return crypto_1.default.randomBytes(this.config.saltLength); } memoryHardFunction(password, salt, memoryCost) { const blocks = []; const blockSize = 1024; const effectiveMemoryCost = memoryCost || this.config.memoryCost; const numBlocks = Math.floor(effectiveMemoryCost / blockSize); let state = Buffer.concat([password, salt]); for (let i = 0; i < numBlocks; i++) { state = crypto_1.default.createHash('blake2b512').update(state).update(Buffer.from([i])).digest(); blocks.push(Buffer.from(state)); } for (let round = 0; round < this.config.timeCost; round++) { for (let i = 0; i < numBlocks; i++) { const prevIndex = i === 0 ? numBlocks - 1 : i - 1; const randomIndex = state.readUInt32BE(0) % numBlocks; const combined = Buffer.concat([ blocks[prevIndex], blocks[randomIndex], Buffer.from([round, i]) ]); blocks[i] = crypto_1.default.createHash('blake2b512').update(combined).digest(); state = blocks[i]; } } let finalHash = Buffer.alloc(0); for (const block of blocks) { finalHash = crypto_1.default.createHash('blake2b512').update(Buffer.concat([finalHash, block])).digest(); } return finalHash.subarray(0, this.config.hashLength); } parallelProcess(password, salt) { const lanes = []; const laneSize = Math.ceil(this.config.memoryCost / this.config.parallelism); if (this.config.debugMode) { console.log(`šŸ”§ DEBUG: Processing ${this.config.parallelism} lanes with ${laneSize}KB each`); } for (let lane = 0; lane < this.config.parallelism; lane++) { const laneSalt = crypto_1.default.createHash('blake2b512') .update(salt) .update(Buffer.from([lane])) .update(Buffer.from(this.config.authKeyContext || 'SecureCrypto-v2.0')) .digest().subarray(0, 32); lanes.push(this.memoryHardFunction(password, laneSalt, laneSize)); } let result = Buffer.alloc(this.config.hashLength); for (let laneIndex = 0; laneIndex < lanes.length; laneIndex++) { const lane = lanes[laneIndex]; for (let i = 0; i < result.length; i++) { result[i] ^= lane[i % lane.length] ^ laneIndex; } } const finalMix = crypto_1.default.createHash('blake2b512') .update(result) .update(salt) .update(Buffer.from(SecureCrypto.VERSION)) .digest(); return finalMix.subarray(0, this.config.hashLength); } deriveAuthKey(salt, hash) { if (this.config.pepper) { return crypto_1.default.createHash('sha256') .update(this.config.pepper) .update(this.config.authKeyContext || 'SecureCrypto-v2.0') .digest(); } if (this.config.requirePepper) { throw new Error("Pepper required but not provided - cannot derive auth key"); } if (this.config.debugMode) { console.warn('šŸ”§ DEBUG: Using PBKDF2 fallback for auth key derivation'); } const derivedKey = crypto_1.default.pbkdf2Sync(hash.subarray(0, 32), Buffer.concat([ salt, Buffer.from(this.config.authKeyContext || 'SecureCrypto-v2.0'), Buffer.from(SecureCrypto.VERSION) ]), 100000, 32, 'sha512'); return derivedKey; } createAuthTag(hash, salt) { const key = this.deriveAuthKey(salt, hash); return crypto_1.default.createHmac('sha512', key) .update(hash) .update(salt) .update(SecureCrypto.VERSION) .digest(); } constantTimeCompare(a, b) { if (a.length !== b.length) { return false; } return crypto_1.default.timingSafeEqual(a, b); } hash(password) { if (!password || password.length === 0) { throw new Error("Password cannot be empty"); } const passwordBuffer = Buffer.from(password, 'utf8'); const salt = this.generateSalt(); const hash = this.parallelProcess(passwordBuffer, salt); const authTag = this.createAuthTag(hash, salt); const paramBuffer = Buffer.alloc(12); paramBuffer.writeUInt32BE(this.config.memoryCost, 0); paramBuffer.writeUInt32BE(this.config.timeCost, 4); paramBuffer.writeUInt32BE(this.config.parallelism, 8); const combined = Buffer.concat([ paramBuffer, salt, hash, authTag ]); return { hash: combined.toString('base64'), version: SecureCrypto.VERSION, algorithm: SecureCrypto.ALGORITHM }; } verify(password, hashResult) { if (!password) { return false; } try { const hashData = typeof hashResult === 'string' ? { hash: hashResult, version: SecureCrypto.VERSION, algorithm: SecureCrypto.ALGORITHM } : hashResult; const combined = Buffer.from(hashData.hash, 'base64'); if (combined.length < 12 + this.config.saltLength) { return false; } const memoryCost = combined.readUInt32BE(0); const timeCost = combined.readUInt32BE(4); const parallelism = combined.readUInt32BE(8); const salt = combined.subarray(12, 12 + this.config.saltLength); const hash = combined.subarray(12 + this.config.saltLength, 12 + this.config.saltLength + this.config.hashLength); const authTag = combined.subarray(12 + this.config.saltLength + this.config.hashLength); const tempConfig = { ...this.config, memoryCost, timeCost, parallelism }; const tempCrypto = new SecureCrypto(tempConfig); const passwordBuffer = Buffer.from(password, 'utf8'); const expectedHash = tempCrypto.parallelProcess(passwordBuffer, salt); const expectedAuthTag = tempCrypto.createAuthTag(expectedHash, salt); return this.constantTimeCompare(hash, expectedHash) && this.constantTimeCompare(authTag, expectedAuthTag); } catch (error) { return false; } } getConfig() { return { ...this.config }; } static benchmark(targetTime = 1000, pepper) { const testPassword = "benchmark_password_123"; let memoryCost = 4096; let timeCost = 1; const baseConfig = { memoryCost, timeCost, requirePepper: false, debugMode: false }; if (pepper) { baseConfig.pepper = pepper; baseConfig.requirePepper = true; } console.log(`šŸš€ Benchmarking for ${targetTime}ms target...`); while (true) { const config = { ...baseConfig, memoryCost, timeCost }; const start = Date.now(); const crypto = new SecureCrypto(config); crypto.hash(testPassword); const elapsed = Date.now() - start; console.log(`ā±ļø Memory: ${memoryCost}KB, Time: ${timeCost}, Elapsed: ${elapsed}ms`); if (elapsed >= targetTime) { break; } if (elapsed < targetTime / 4) { memoryCost *= 2; } else { timeCost++; } } const finalConfig = { memoryCost, timeCost, parallelism: 4, hashLength: 64, saltLength: 32, pepper: pepper || "", requirePepper: !!pepper, debugMode: false, authKeyContext: 'SecureCrypto-v2.0' }; console.log(`āœ… Optimal config found:`, finalConfig); return finalConfig; } } exports.SecureCrypto = SecureCrypto; SecureCrypto.VERSION = "2.0.0"; SecureCrypto.ALGORITHM = "SECURE-ARGON2"; SecureCrypto.DEFAULT_CONFIG = { memoryCost: 65536, timeCost: 3, parallelism: 4, hashLength: 64, saltLength: 32, pepper: process.env.SECURE_CRYPTO_PEPPER || "", requirePepper: true, debugMode: process.env.NODE_ENV === 'development', authKeyContext: process.env.SECURE_CRYPTO_CONTEXT || 'SecureCrypto-v2.0' }; //# sourceMappingURL=crypto.js.map