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.
223 lines (220 loc) • 8.07 kB
JavaScript
import * as crypto from 'crypto';
import { ALGORITHM_REGISTRY } from '../../../algorithms/registry.js';
import { CryptoAlgorithmUtils } from '../../../utils/CryptoAlgorithmUtils.js';
/**
* Cryptographic Operations Module
* Handles hashing, HMAC, and key derivation for SecureString
*/
/**
* Handles cryptographic operations for SecureString
*/
class CryptoOperations {
/**
* Creates a hash of the string content
*/
static async hash(content, algorithm = "SHA-256", format = "hex") {
// Validate algorithm
const validatedAlgorithm = CryptoAlgorithmUtils.validateAlgorithm(algorithm);
const encoder = new TextEncoder();
const data = encoder.encode(content);
const hashBuffer = await crypto.subtle.digest(validatedAlgorithm, data);
const hashArray = new Uint8Array(hashBuffer);
return this.formatHash(hashArray, format);
}
/**
* Creates an HMAC of the string content
*/
static async hmac(content, options, format = "hex") {
const { key, algorithm } = options;
// Validate algorithm
if (!CryptoAlgorithmUtils.isSupported(algorithm)) {
throw new Error(`Unsupported HMAC algorithm: ${algorithm}`);
}
// Prepare key
let keyData;
if (typeof key === "string") {
keyData = new TextEncoder().encode(key);
}
else if (this.isSecureString(key)) {
keyData = new TextEncoder().encode(key.toString());
}
else {
keyData = key;
}
// Extract hash algorithm from HMAC algorithm
const hashAlgorithm = algorithm.replace("HMAC-", "");
// Import key
const cryptoKey = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: hashAlgorithm }, false, ["sign"]);
// Sign data
const data = new TextEncoder().encode(content);
const signature = await crypto.subtle.sign("HMAC", cryptoKey, data);
const signatureArray = new Uint8Array(signature);
return this.formatHash(signatureArray, format);
}
/**
* Derives a key using PBKDF2
*/
static async deriveKeyPBKDF2(content, options, format = "hex") {
const { salt, iterations, keyLength, hash } = options;
// Validate parameters
if (iterations < 1000) {
console.warn("Warning: PBKDF2 iterations should be at least 1000 for security");
}
// Prepare salt
const saltData = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
// Import password
const passwordKey = await crypto.subtle.importKey("raw", new TextEncoder().encode(content), "PBKDF2", false, ["deriveBits"]);
// Derive key
const derivedBits = await crypto.subtle.deriveBits({
name: "PBKDF2",
salt: saltData,
iterations: iterations,
hash: hash,
}, passwordKey, keyLength * 8 // Convert bytes to bits
);
const derivedArray = new Uint8Array(derivedBits);
return this.formatHash(derivedArray, format);
}
/**
* Derives a key using scrypt (Node.js only)
*/
static async deriveKeyScrypt(content, salt, keyLength = 32, options = {}, format = "hex") {
const { N = 16384, r = 8, p = 1 } = options;
return new Promise((resolve, reject) => {
const saltData = typeof salt === "string"
? Buffer.from(salt, "utf8")
: Buffer.from(salt);
crypto.scrypt(content, saltData, keyLength, { N, r, p }, (err, derivedKey) => {
if (err) {
reject(err);
return;
}
const derivedArray = new Uint8Array(derivedKey);
resolve(this.formatHash(derivedArray, format));
});
});
}
/**
* Derives a key using Argon2 (requires external library)
*/
static async deriveKeyArgon2(_content, _salt, _keyLength = 32, _options = {}, _format = "hex") {
// Note: This would require an external Argon2 library
// For now, we'll throw an error indicating it's not implemented
throw new Error("Argon2 key derivation requires an external library. Use PBKDF2 or scrypt instead.");
}
/**
* Generates a cryptographically secure salt
*/
static generateSalt(length = 32) {
return crypto.getRandomValues(new Uint8Array(length));
}
/**
* Generates a cryptographically secure salt as hex string
*/
static generateSaltHex(length = 32) {
const salt = this.generateSalt(length);
return this.formatHash(salt, "hex");
}
/**
* Generates a cryptographically secure salt as base64 string
*/
static generateSaltBase64(length = 32) {
const salt = this.generateSalt(length);
return this.formatHash(salt, "base64");
}
/**
* Formats hash output according to specified format
*/
static formatHash(hashArray, format) {
switch (format) {
case "hex":
return Array.from(hashArray)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
case "base64":
return btoa(String.fromCharCode(...hashArray));
case "base64url":
return btoa(String.fromCharCode(...hashArray))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
case "uint8array":
return hashArray;
default:
throw new Error(`Unsupported hash format: ${format}`);
}
}
/**
* Checks if a value is a SecureString instance
*/
static isSecureString(value) {
return (value &&
typeof value === "object" &&
typeof value.toString === "function");
}
/**
* Gets information about available algorithms
*/
static getAlgorithmInfo() {
return { ...ALGORITHM_REGISTRY };
}
/**
* Lists all supported hash algorithms
*/
static getSupportedHashAlgorithms() {
return ["SHA-1", "SHA-256", "SHA-384", "SHA-512"];
}
/**
* Lists all supported HMAC algorithms
*/
static getSupportedHMACAlgorithms() {
return ["HMAC-SHA-1", "HMAC-SHA-256", "HMAC-SHA-384", "HMAC-SHA-512"];
}
/**
* Validates if an algorithm is supported
*/
static isAlgorithmSupported(algorithm) {
return CryptoAlgorithmUtils.isSupported(algorithm);
}
/**
* Gets algorithm information for a specific algorithm
*/
static getAlgorithmDetails(algorithm) {
return ALGORITHM_REGISTRY[algorithm];
}
/**
* Compares two hashes in constant time
*/
static constantTimeHashCompare(hash1, hash2) {
if (hash1.length !== hash2.length) {
return false;
}
let result = 0;
for (let i = 0; i < hash1.length; i++) {
result |= hash1.charCodeAt(i) ^ hash2.charCodeAt(i);
}
return result === 0;
}
/**
* Verifies a hash against content
*/
static async verifyHash(content, expectedHash, algorithm = "SHA-256", format = "hex") {
const actualHash = await this.hash(content, algorithm, format);
if (typeof actualHash !== "string") {
throw new Error("Hash verification requires string format");
}
return this.constantTimeHashCompare(actualHash, expectedHash);
}
/**
* Verifies an HMAC against content
*/
static async verifyHMAC(content, expectedHMAC, options, format = "hex") {
const actualHMAC = await this.hmac(content, options, format);
if (typeof actualHMAC !== "string") {
throw new Error("HMAC verification requires string format");
}
return this.constantTimeHashCompare(actualHMAC, expectedHMAC);
}
}
export { CryptoOperations };
//# sourceMappingURL=crypto-operations.js.map