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.
400 lines (394 loc) • 15 kB
JavaScript
;
var hashCore = require('../../../core/hash/hash-core.js');
require('../../../core/hash/hash-types.js');
var crypto = require('crypto');
require('../../../core/hash/hash-security.js');
require('../../../core/hash/hash-advanced.js');
require('../../../algorithms/hash-algorithms.js');
var randomCore = require('../../../core/random/random-core.js');
require('../../../core/random/random-types.js');
require('../../../core/random/random-sources.js');
require('nehonix-uri-processor');
require('../../../utils/memory/index.js');
require('../../../types.js');
var randomCrypto = require('../../../core/random/random-crypto.js');
var validators = require('../../../core/validators.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);
/**
* FortifyJS Express Encryption Service
* encryption/decryption service using FortifyJS cryptographic utilities
*
* Features:
* - AES-256-GCM encryption with authentication
* - ChaCha20-Poly1305 fallback for quantum-safe encryption
* - Proper key derivation using PBKDF2
* - Secure IV/nonce generation
* - Constant-time operations
* - Memory-safe operations with secure wiping
*/
/**
* encryption service using FortifyJS utilities
*/
class EncryptionService {
/**
* Encrypt data using production-grade encryption
*/
static async encrypt(data, key, options = {}) {
try {
// Validate inputs
this.validateInputs(data, key);
const { algorithm = "aes-256-gcm", keyDerivationIterations = this.DEFAULT_ITERATIONS, additionalData, quantumSafe = false, } = options;
// Serialize data
const jsonData = JSON.stringify(data);
const dataBuffer = new TextEncoder().encode(jsonData);
// Generate cryptographically secure salt
const salt = randomCore.SecureRandom.getRandomBytes(this.SALT_LENGTH);
// Derive encryption key using PBKDF2
const derivedKey = await this.deriveKey(key, salt, keyDerivationIterations);
// Generate secure IV/nonce
const iv = this.generateIV(algorithm);
// Encrypt based on algorithm
let encrypted;
let authTag;
if (algorithm === "chacha20-poly1305" || quantumSafe) {
({ encrypted, authTag } = this.encryptChaCha20Poly1305(dataBuffer, derivedKey, iv, additionalData));
}
else {
({ encrypted, authTag } = this.encryptAES256GCM(dataBuffer, derivedKey, iv, additionalData));
}
// Create encrypted package
const package_ = {
algorithm: quantumSafe ? "chacha20-poly1305" : algorithm,
iv: this.bufferToHex(iv),
data: this.bufferToHex(encrypted),
authTag: this.bufferToHex(authTag),
salt: this.bufferToHex(salt.toUint8Array()),
timestamp: Date.now(),
version: this.VERSION,
};
// Secure memory cleanup
this.secureWipe(derivedKey);
this.secureWipe(dataBuffer);
return JSON.stringify(package_);
}
catch (error) {
throw new Error(`Encryption failed: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
/**
* Decrypt data using production-grade decryption
*/
static async decrypt(encryptedData, key) {
try {
// Parse encrypted package
const package_ = JSON.parse(encryptedData);
this.validatePackage(package_);
// Convert hex strings back to buffers
const iv = this.hexToBuffer(package_.iv);
const encrypted = this.hexToBuffer(package_.data);
const authTag = this.hexToBuffer(package_.authTag);
const salt = this.hexToBuffer(package_.salt);
// Derive the same key
const derivedKey = await this.deriveKey(key, salt, this.DEFAULT_ITERATIONS);
// Decrypt based on algorithm
let decrypted;
if (package_.algorithm === "chacha20-poly1305") {
decrypted = this.decryptChaCha20Poly1305(encrypted, derivedKey, iv, authTag);
}
else {
decrypted = this.decryptAES256GCM(encrypted, derivedKey, iv, authTag);
}
// Convert back to string and parse JSON
const jsonString = new TextDecoder().decode(decrypted);
const result = JSON.parse(jsonString);
// Secure memory cleanup
this.secureWipe(derivedKey);
this.secureWipe(decrypted);
return result;
}
catch (error) {
throw new Error(`Decryption failed: ${error instanceof Error
? error.message
: "Invalid encrypted data"}`);
}
}
/**
* Derive encryption key using PBKDF2
*/
static async deriveKey(password, salt, iterations) {
const passwordBuffer = new TextEncoder().encode(password);
const saltBuffer = salt instanceof Uint8Array ? salt : salt.toUint8Array();
// Use FortifyJS Hash for key derivation
const hashResult = hashCore.Hash.createSecureHash(passwordBuffer, saltBuffer, {
algorithm: "sha256",
iterations,
outputFormat: "buffer",
});
// Handle both sync and async results
const derivedKey = hashResult instanceof Promise
? Buffer.from((await hashResult))
: Buffer.from(hashResult);
return new Uint8Array(derivedKey);
}
/**
* Generate secure IV/nonce for encryption
*/
static generateIV(algorithm) {
return randomCrypto.RandomCrypto.generateNonce(algorithm === "chacha20-poly1305" ? "chacha20-poly1305" : "aes-gcm", { quantumSafe: true });
}
/**
* Encrypt using AES-256-GCM
*/
static encryptAES256GCM(data, key, iv, additionalData) {
try {
// Use Node.js crypto for AES-GCM
const cipher = crypto__namespace.createCipheriv("aes-256-gcm", key.slice(0, 32), iv);
if (additionalData) {
cipher.setAAD(new TextEncoder().encode(additionalData));
}
const encrypted = Buffer.concat([
cipher.update(Buffer.from(data)),
cipher.final(),
]);
const authTag = cipher.getAuthTag();
return {
encrypted: new Uint8Array(encrypted),
authTag: new Uint8Array(authTag),
};
}
catch (error) {
throw new Error(`AES-GCM encryption failed: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
/**
* Decrypt using AES-256-GCM
*/
static decryptAES256GCM(encrypted, key, iv, authTag) {
try {
const decipher = crypto__namespace.createDecipheriv("aes-256-gcm", key.slice(0, 32), iv);
decipher.setAuthTag(Buffer.from(authTag));
const decrypted = Buffer.concat([
decipher.update(Buffer.from(encrypted)),
decipher.final(),
]);
return new Uint8Array(decrypted);
}
catch (error) {
throw new Error(`AES-GCM decryption failed: ${error instanceof Error
? error.message
: "Authentication failed"}`);
}
}
/**
* Encrypt using ChaCha20-Poly1305 (quantum-safe fallback)
*/
static encryptChaCha20Poly1305(data, key, iv, additionalData) {
// Try to use libsodium if available
try {
const sodium = require("libsodium-wrappers");
if (sodium &&
typeof sodium.crypto_aead_chacha20poly1305_ietf_encrypt ===
"function") {
const aad = additionalData
? new TextEncoder().encode(additionalData)
: null;
const result = sodium.crypto_aead_chacha20poly1305_ietf_encrypt(data, aad, null, iv, key.slice(0, 32));
// Split result into encrypted data and auth tag
const encrypted = result.slice(0, -16);
const authTag = result.slice(-16);
return {
encrypted: new Uint8Array(encrypted),
authTag: new Uint8Array(authTag),
};
}
}
catch (error) {
// Fall back to AES-GCM if ChaCha20-Poly1305 is not available
console.warn("ChaCha20-Poly1305 not available, falling back to AES-GCM");
}
// Fallback to AES-GCM
return this.encryptAES256GCM(data, key, iv, additionalData);
}
/**
* Decrypt using ChaCha20-Poly1305
*/
static decryptChaCha20Poly1305(encrypted, key, iv, authTag) {
// Try to use libsodium if available
try {
const sodium = require("libsodium-wrappers");
if (sodium &&
typeof sodium.crypto_aead_chacha20poly1305_ietf_decrypt ===
"function") {
const ciphertext = new Uint8Array(encrypted.length + authTag.length);
ciphertext.set(encrypted);
ciphertext.set(authTag, encrypted.length);
const result = sodium.crypto_aead_chacha20poly1305_ietf_decrypt(null, ciphertext, null, iv, key.slice(0, 32));
return new Uint8Array(result);
}
}
catch (error) {
// Fall back to AES-GCM if ChaCha20-Poly1305 is not available
console.warn("ChaCha20-Poly1305 not available, falling back to AES-GCM");
}
// Fallback to AES-GCM
return this.decryptAES256GCM(encrypted, key, iv, authTag);
}
/**
* Validate inputs for encryption
*/
static validateInputs(data, key) {
if (data === undefined || data === null) {
throw new Error("Data cannot be null or undefined");
}
if (!key || typeof key !== "string") {
throw new Error("Key must be a non-empty string");
}
if (key.length < 8) {
throw new Error("Key must be at least 8 characters long");
}
// Use FortifyJS validators for additional validation
try {
validators.Validators.validateLength(key.length, 8, 1024);
}
catch (error) {
throw new Error(`Invalid key: ${error instanceof Error ? error.message : "Unknown error"}`);
}
}
/**
* Validate encrypted package structure
*/
static validatePackage(package_) {
const requiredFields = [
"algorithm",
"iv",
"data",
"authTag",
"salt",
"timestamp",
"version",
];
for (const field of requiredFields) {
if (!package_[field]) {
throw new Error(`Invalid encrypted package: missing ${field}`);
}
}
if (!["aes-256-gcm", "chacha20-poly1305"].includes(package_.algorithm)) {
throw new Error(`Unsupported encryption algorithm: ${package_.algorithm}`);
}
// Validate timestamp (not too old, not in future)
const now = Date.now();
const maxAge = 24 * 60 * 60 * 1000; // 24 hours
if (package_.timestamp > now + 60000) {
// 1 minute future tolerance
throw new Error("Invalid encrypted package: timestamp in future");
}
if (now - package_.timestamp > maxAge) {
throw new Error("Invalid encrypted package: timestamp too old");
}
}
/**
* Convert buffer to hex string
*/
static bufferToHex(buffer) {
return Array.from(buffer)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
/**
* Convert hex string to buffer
*/
static hexToBuffer(hex) {
if (hex.length % 2 !== 0) {
throw new Error("Invalid hex string length");
}
const buffer = new Uint8Array(hex.length / 2);
for (let i = 0; i < hex.length; i += 2) {
buffer[i / 2] = parseInt(hex.substring(i, i + 2), 16);
}
return buffer;
}
/**
* Secure memory wipe using FortifyJS utilities
*/
static secureWipe(buffer) {
try {
// Use FortifyJS secure memory wiping
if (buffer && buffer.length > 0) {
// Overwrite with random data first
const randomData = randomCore.SecureRandom.getRandomBytes(buffer.length);
buffer.set(randomData.toUint8Array());
// Then overwrite with zeros
buffer.fill(0);
// Finally overwrite with 0xFF
buffer.fill(0xff);
buffer.fill(0);
}
}
catch (error) {
// Fallback to simple zero fill
if (buffer && buffer.length > 0) {
buffer.fill(0);
}
}
}
/**
* Generate a secure session key for temporary use
*/
static generateSessionKey() {
const keyBytes = randomCore.SecureRandom.getRandomBytes(32);
return this.bufferToHex(keyBytes.toUint8Array());
}
/**
* Verify the integrity of encrypted data without decrypting
*/
static verifyIntegrity(encryptedData) {
try {
const package_ = JSON.parse(encryptedData);
this.validatePackage(package_);
return true;
}
catch (error) {
return false;
}
}
/**
* Get encryption metadata without decrypting
*/
static getMetadata(encryptedData) {
try {
const package_ = JSON.parse(encryptedData);
return {
algorithm: package_.algorithm,
timestamp: package_.timestamp,
version: package_.version,
};
}
catch (error) {
throw new Error("Invalid encrypted data format");
}
}
}
EncryptionService.VERSION = "1.0.0";
EncryptionService.DEFAULT_ITERATIONS = 100000;
EncryptionService.KEY_LENGTH = 32; // 256 bits
EncryptionService.IV_LENGTH = 12; // 96 bits for GCM
EncryptionService.SALT_LENGTH = 32; // 256 bits
EncryptionService.AUTH_TAG_LENGTH = 16; // 128 bits
exports.EncryptionService = EncryptionService;
//# sourceMappingURL=EncryptionService.js.map