UNPKG

xypriss-security

Version:

Advanced High-Performance Security Framework. Military-grade encryption, post-quantum resilience, and fortified data structures.

302 lines 13.1 kB
"use strict"; /** * Key Management and Derivation * High-performance Go-backed operations */ Object.defineProperty(exports, "__esModule", { value: true }); exports.deriveKey = exports.ed25519Verify = exports.rsaDecrypt = exports.rsaEncrypt = exports.rsaVerify = exports.rsaSign = exports.generateRSAKeyPair = exports.Keys = void 0; const Password_1 = require("./Password"); const bridge_1 = require("./bridge"); const Random_1 = require("./Random"); class Keys { /** * Derives a cryptographically strong key from an input secret. * Supports complex derivation paths including Argon2id, PBKDF2, and HKDF. * * @param input - The base secret or password to derive from. * @param options - Detailed configuration for the derivation process. * @returns A Promise resolving to the derived key (hex format for PBKDF2/HKDF, signed for others). */ static async deriveKey(input, options = {}) { const algo = (options.algorithm || "argon2id").toLowerCase(); const strInput = typeof input === "string" ? input : new TextDecoder().decode(input); // Handle high-performance PBKDF2 branch if (algo === "pbkdf2") { const salt = options.salt || Random_1.Random.getRandomBytes(32).toUint8Array(); const saltBytes = typeof salt === "string" ? Buffer.from(salt) : salt; return bridge_1.Bridge.pbkdf2(strInput, saltBytes, options.iterations || 100000, options.keyLength || 32, options.digest || "sha256"); } // Handle modern HKDF branch if (algo === "hkdf") { const inputBytes = typeof input === "string" ? new TextEncoder().encode(input) : input; const salt = options.salt || new Uint8Array(0); const saltBytes = typeof salt === "string" ? Buffer.from(salt) : salt; const info = options.info || new Uint8Array(0); const infoBytes = typeof info === "string" ? Buffer.from(info) : info; return bridge_1.Bridge.hkdf(inputBytes, saltBytes, infoBytes, options.keyLength || 32); } // Default to Argon2id/Scrypt via Password module return Password_1.Password.hash(strInput, options); } /** * Generates a new RSA key pair in JSON format. * @returns A Promise resolving to an object containing publicKey and privateKey. */ static async generateRSAKeyPair() { return bridge_1.Bridge.generateRSAKeyJSON(); } /** * Signs data using RSA-PSS. * @param privateKey - The PEM-encoded RSA private key. * @param data - The data to sign. * @returns The signature in hex format. */ static async rsaSign(privateKey, data) { return bridge_1.Bridge.rsaSign(privateKey, data); } /** * Verifies an RSA-PSS signature. * @param publicKey - The PEM-encoded RSA public key. * @param data - The original data. * @param signature - The hex-encoded signature. * @returns True if the signature is valid. */ static async rsaVerify(publicKey, data, signature) { return bridge_1.Bridge.rsaVerify(publicKey, data, signature); } /** * Encrypts data using RSA-OAEP. * @param publicKey - The PEM-encoded RSA public key. * @param data - The data to encrypt. * @returns The encrypted data in hex format. */ static async rsaEncrypt(publicKey, data) { return bridge_1.Bridge.rsaEncrypt(publicKey, data); } /** * Decrypts RSA-OAEP encrypted data. * @param privateKey - The PEM-encoded RSA private key. * @param encryptedHex - The hex-encoded encrypted data. * @returns The decrypted plaintext. */ static async rsaDecrypt(privateKey, encryptedHex) { return bridge_1.Bridge.rsaDecrypt(privateKey, encryptedHex); } /** * Verifies an Ed25519 signature. * @param publicKey - The public key (hex or Uint8Array). * @param data - The data that was signed (string or Uint8Array). * @param signature - The signature (base64 or Uint8Array). * @returns True if valid. */ static ed25519Verify(publicKey, data, signature) { return bridge_1.Bridge.ed25519Verify(publicKey, data, signature); } /** * Encrypts a large file using high-performance, hardware-accelerated chunking. * * @description * Processes files via a native encryption bridge using derived keys (PBKDF2). * Implements a staging strategy to ensure data integrity: ciphertext is * finalized with a 32-byte salt header before an atomic move to the target path. * * @param {string} inputPath - Absolute path to the source file. * @param {string} outputPath - Target destination for the encrypted data. * @param {string} key - Raw passphrase for cryptographic derivation. * @param {object} [options={}] - Encryption parameters. * @param {string} [options.algorithm="aes-256-gcm"] - Selection of the cipher suite. * @param {number} [options.keyDerivationIterations=100000] - Cost factor for PBKDF2. * @param {boolean} [options.quantumSafe=false] - If true, enforces Post-Quantum resistant ciphers. * * @throws {Error | SystemError} On bridge failure, I/O exhaustion, or permission issues. * @returns {Promise<void>} * * @example * await Cipher.crypto.encryptFile('./data.zip', './data.vault', 'secret-key'); */ static async encryptFile(inputPath, outputPath, key, options = {}) { const fs = require("fs"); const os = require("os"); const path = require("path"); const { pipeline: pl } = require("stream"); const { promisify: pr } = require("util"); const pipelineAsync = pr(pl); const id = Math.random().toString(36).substring(7); const tmpDir = os.tmpdir(); const goTempPath = path.join(tmpDir, `xy-enc-${id}.go.tmp`); const finalTempPath = path.join(tmpDir, `xy-enc-${id}.out.tmp`); try { const { algorithm = "aes-256-gcm", keyDerivationIterations = 100000, quantumSafe = false, } = options; const SALT_LENGTH = 32; const salt = Random_1.Random.getRandomBytes(SALT_LENGTH); const derivedKeyHex = await bridge_1.Bridge.pbkdf2(key, salt.toUint8Array(), keyDerivationIterations, 32, "sha256"); const derivedKey = Buffer.from(derivedKeyHex, "hex"); const algoTarget = algorithm === "chacha20-poly1305" || quantumSafe ? "chacha20" : "aes"; // 1. Encrypt input into a temporary Go output file const result = bridge_1.Bridge.encryptFile(inputPath, goTempPath, derivedKey, algoTarget); if (result !== "OK") throw new Error(result); // 2. Combine Salt + Go Output into finalTempPath fs.writeFileSync(finalTempPath, Buffer.from(salt.toUint8Array())); const finalOut = fs.createWriteStream(finalTempPath, { flags: "a" }); const goTempIn = fs.createReadStream(goTempPath); await pipelineAsync(goTempIn, finalOut); // 3. Atomically move to final output await this.moveFileAtomic(finalTempPath, outputPath); } catch (error) { if (fs.existsSync(finalTempPath)) fs.unlinkSync(finalTempPath); throw error; } finally { if (fs.existsSync(goTempPath)) fs.unlinkSync(goTempPath); } } /** * Decrypts a large file using PBKDF2 key derivation and a secure bridge. * * @description * This method extracts a 32-byte salt from the input file header, derives a 256-bit key * (100,000 iterations), and performs decryption via temporary staging files to * ensure data integrity. The final output is moved atomically. * * @param {string} inputPath - Absolute path to the encrypted source file. * @param {string} outputPath - Destination path for the decrypted plaintext. * @param {string} key - The raw passphrase used for key derivation. * * @throws {Error} If the salt extraction fails or the decryption bridge returns an error. * @throws {SystemError} If disk space is insufficient for temp files or if file moves fail. * * @returns {Promise<void>} Resolves once the file is successfully decrypted and moved to `outputPath`. * * @example * await Cipher.crypto.decryptFile('./vault.enc', './vault.txt', 'super-secret-key'); */ static async decryptFile(inputPath, outputPath, key) { const fs = require("fs"); const os = require("os"); const path = require("path"); const { pipeline: pl2 } = require("stream"); const { promisify: pr2 } = require("util"); const pipelineAsync2 = pr2(pl2); const SALT_LENGTH = 32; const id = Math.random().toString(36).substring(7); const tmpDir = os.tmpdir(); const tempInPath = path.join(tmpDir, `xy-dec-${id}.in.tmp`); const tempOutPath = path.join(tmpDir, `xy-dec-${id}.out.tmp`); try { const fd = fs.openSync(inputPath, "r"); const salt = Buffer.alloc(SALT_LENGTH); fs.readSync(fd, salt, 0, SALT_LENGTH, 0); fs.closeSync(fd); const derivedKeyHex = await bridge_1.Bridge.pbkdf2(key, salt, 100000, 32, "sha256"); const derivedKey = Buffer.from(derivedKeyHex, "hex"); // 1. Prepare input for Go (remove salt) into tempInPath const finalInStream = fs.createReadStream(inputPath, { start: SALT_LENGTH, }); const tempOutInStream = fs.createWriteStream(tempInPath); await pipelineAsync2(finalInStream, tempOutInStream); // 2. Decrypt from tempInPath to tempOutPath const result = bridge_1.Bridge.decryptFile(tempInPath, tempOutPath, derivedKey); if (result !== "OK") throw new Error(result); // 3. Atomically move to final output await this.moveFileAtomic(tempOutPath, outputPath); } catch (error) { if (fs.existsSync(tempOutPath)) fs.unlinkSync(tempOutPath); throw error; } finally { if (fs.existsSync(tempInPath)) fs.unlinkSync(tempInPath); } } /** * Moves a file atomically from src to dest. * Handles EXDEV errors for cross-partition moves. */ static async moveFileAtomic(src, dest) { const fs = require("fs"); try { // Fast path: rename (on same partition) if (fs.existsSync(dest) && src !== dest) fs.unlinkSync(dest); fs.renameSync(src, dest); } catch (error) { // Fallback path: copy + unlink (cross-partition) if (error.code === "EXDEV") { fs.copyFileSync(src, dest); fs.unlinkSync(src); } else { throw error; } } } } exports.Keys = Keys; // =================================== UTILES ========================== /** * Generates a high-entropy 4096-bit RSA key pair. * * @returns A promise resolving to an object containing PEM-encoded publicKey and privateKey. */ exports.generateRSAKeyPair = Keys.generateRSAKeyPair; /** * Signs data using RSA-PSS with SHA-256. * * @param privateKey - The PEM-encoded RSA private key. * @param data - The data string to sign. * @returns A promise resolving to the hex-encoded signature. */ exports.rsaSign = Keys.rsaSign; /** * Verifies an RSA-PSS signature. * * @param publicKey - The PEM-encoded RSA public key. * @param data - The original data string that was signed. * @param signature - The hex-encoded signature to verify. * @returns A promise resolving to true if valid, false otherwise. */ exports.rsaVerify = Keys.rsaVerify; /** * Encrypts data using RSA-OAEP with SHA-256. * * @param publicKey - The PEM-encoded RSA public key. * @param data - The plaintext data string to encrypt. * @returns A promise resolving to the hex-encoded ciphertext. */ exports.rsaEncrypt = Keys.rsaEncrypt; /** * Decrypts data using RSA-OAEP with SHA-256. * * @param privateKey - The PEM-encoded RSA private key. * @param encryptedHex - The hex-encoded ciphertext to decrypt. * @returns A promise resolving to the decrypted plaintext string. */ exports.rsaDecrypt = Keys.rsaDecrypt; /** * Verifies an Ed25519 signature. * * @param publicKey - Public key (hex or Uint8Array). * @param data - Original data. * @param signature - Signature (base64 or Uint8Array). * @returns True if valid. */ exports.ed25519Verify = Keys.ed25519Verify; /** * Derives a cryptographically strong key from an input secret. * Supports multiple algorithms including Argon2id, PBKDF2, and HKDF. * * @param input - The base secret or password. * @param options - Configuration for the derivation process. * @returns A promise resolving to the derived key. */ exports.deriveKey = Keys.deriveKey; //# sourceMappingURL=keys.js.map