UNPKG

k9crypt

Version:

A special encryption algorithm created for K9Crypt.

152 lines (128 loc) 5.38 kB
const crypto = require('crypto'); const { compress, decompress } = require('./utils/compression'); const { deriveKey } = require('./utils/keyDerivation'); const { encrypt, decrypt } = require('./utils/encryption'); const { hash, verifyHash } = require('./utils/hashing'); const { encryptFile, decryptFile } = require('./utils/streamCrypto'); const { encryptMany, decryptMany, encryptManyParallel, decryptManyParallel } = require('./utils/batchOperations'); const { SALT_SIZE, IV_SIZE, TAG_SIZE, ARGON2_SALT_SIZE, ARGON2_HASH_LENGTH } = require('./constants'); class K9crypt { constructor(secretKey, options = {}) { if (!secretKey) { this.secretKey = crypto.randomBytes(50); this._autoGenerated = true; } if (secretKey) { this._autoGenerated = false; this.secretKey = secretKey; } this.defaultCompressionLevel = options.compressionLevel || 3; } getGenerated() { return this._autoGenerated ? this.secretKey : null; } async encrypt(plaintext, options = {}) { try { const compressionLevel = options.compressionLevel || this.defaultCompressionLevel; const compressed = await compress(plaintext, compressionLevel); const salt = crypto.randomBytes(SALT_SIZE); const key = await deriveKey(this.secretKey, salt); const { iv1, iv2, iv3, iv4, iv5, encrypted, tag1 } = await encrypt(compressed, key); const dataToHash = Buffer.concat([salt, iv1, iv2, iv3, iv4, iv5, encrypted, tag1]); const argon2Salt = crypto.randomBytes(ARGON2_SALT_SIZE); const dataHash = await hash(dataToHash, argon2Salt); const result = Buffer.concat([salt, iv1, iv2, iv3, iv4, iv5, encrypted, tag1, argon2Salt, dataHash]); return result.toString('base64'); } catch (error) { throw new Error(`Encryption failed: ${error.message}`); } } async decrypt(ciphertext) { try { const data = Buffer.from(ciphertext, 'base64'); const salt = data.slice(0, SALT_SIZE); const iv1 = data.slice(SALT_SIZE, SALT_SIZE + IV_SIZE); const iv2 = data.slice(SALT_SIZE + IV_SIZE, SALT_SIZE + 2 * IV_SIZE); const iv3 = data.slice(SALT_SIZE + 2 * IV_SIZE, SALT_SIZE + 3 * IV_SIZE); const iv4 = data.slice(SALT_SIZE + 3 * IV_SIZE, SALT_SIZE + 4 * IV_SIZE); const iv5 = data.slice(SALT_SIZE + 4 * IV_SIZE, SALT_SIZE + 5 * IV_SIZE); const dataHash = data.slice(-ARGON2_HASH_LENGTH); const argon2Salt = data.slice(-ARGON2_HASH_LENGTH - ARGON2_SALT_SIZE, -ARGON2_HASH_LENGTH); const tag1 = data.slice(-ARGON2_HASH_LENGTH - ARGON2_SALT_SIZE - TAG_SIZE, -ARGON2_HASH_LENGTH - ARGON2_SALT_SIZE); const encrypted = data.slice(SALT_SIZE + 5 * IV_SIZE, -ARGON2_HASH_LENGTH - ARGON2_SALT_SIZE - TAG_SIZE); const dataToVerify = data.slice(0, -ARGON2_HASH_LENGTH - ARGON2_SALT_SIZE); if (!(await verifyHash(dataToVerify, dataHash, argon2Salt))) { throw new Error('Data integrity check failed'); } const key = await deriveKey(this.secretKey, salt); const decrypted = await decrypt(encrypted, key, iv1, iv2, iv3, iv4, iv5, tag1); const decompressed = await decompress(decrypted); return decompressed.toString('utf8'); } catch (error) { throw new Error(`Decryption failed: ${error.message}`); } } async encryptFile(plaintext, options = {}) { try { const compressionLevel = options.compressionLevel || this.defaultCompressionLevel; const onProgress = options.onProgress || null; return await encryptFile(plaintext, this.secretKey, { compressionLevel, onProgress }); } catch (error) { throw new Error(`File encryption failed: ${error.message}`); } } async decryptFile(ciphertext, options = {}) { try { const onProgress = options.onProgress || null; return await decryptFile(ciphertext, this.secretKey, { onProgress }); } catch (error) { throw new Error(`File decryption failed: ${error.message}`); } } async encryptMany(dataArray, options = {}) { try { const compressionLevel = options.compressionLevel || this.defaultCompressionLevel; const onProgress = options.onProgress || null; const parallel = options.parallel || false; const batchSize = options.batchSize || 10; if (parallel) { return await encryptManyParallel(dataArray, this.secretKey, { compressionLevel, batchSize }); } return await encryptMany(dataArray, this.secretKey, { compressionLevel, onProgress }); } catch (error) { throw new Error(`Multiple encryption failed: ${error.message}`); } } async decryptMany(ciphertextArray, options = {}) { try { const onProgress = options.onProgress || null; const skipInvalid = options.skipInvalid || false; const parallel = options.parallel || false; const batchSize = options.batchSize || 10; if (parallel) { return await decryptManyParallel(ciphertextArray, this.secretKey, { skipInvalid, batchSize }); } return await decryptMany(ciphertextArray, this.secretKey, { skipInvalid, onProgress }); } catch (error) { throw new Error(`Multiple decryption failed: ${error.message}`); } } } module.exports = K9crypt;