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