UNPKG

crypto-key-composer

Version:

A library to decompose and compose crypto keys of different types and formats

388 lines (290 loc) 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.encryptWithPassword = exports.decryptWithPassword = void 0; var _util = require("node-forge/lib/util"); var _sha = _interopRequireDefault(require("node-forge/lib/sha1")); var _sha2 = _interopRequireDefault(require("node-forge/lib/sha256")); var _sha3 = _interopRequireDefault(require("node-forge/lib/sha512")); var _md = _interopRequireDefault(require("node-forge/lib/md5")); var _pbkdf = _interopRequireDefault(require("node-forge/lib/pbkdf2")); var _aes = _interopRequireDefault(require("node-forge/lib/aes")); var _des = _interopRequireDefault(require("node-forge/lib/des")); var _rc = _interopRequireDefault(require("node-forge/lib/rc2")); var _random = _interopRequireDefault(require("./random")); var _binary = require("./binary"); var _errors = require("./errors"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const deriveKeyWithPbkdf2 = (password, params) => { const { salt, iterationCount, keyLength, prf } = params; const saltStr = (0, _binary.uint8ArrayToBinaryString)(salt); let prfMd; switch (prf) { case 'hmac-with-sha1': prfMd = _sha.default.create(); break; // TODO: node-forge doesn't have sha224 support, see: https://github.com/digitalbazaar/forge/issues/669 // case 'hmacWithSHA224': // prfMd = sha256.sha224.create(); // break; case 'hmac-with-sha256': prfMd = _sha2.default.create(); break; case 'hmac-with-sha384': prfMd = _sha3.default.sha384.create(); break; case 'hmac-with-sha512': prfMd = _sha3.default.create(); break; default: throw new _errors.UnsupportedAlgorithmError(`Unsupported PBKDF2 prf id '${prf}'`); } const keyStr = (0, _pbkdf.default)(password, saltStr, iterationCount, keyLength, prfMd); return (0, _binary.binaryStringToUint8Array)(keyStr); }; const deriveKeyWithOpensslDeriveBytes = (password, params) => { const { salt, keyLength } = params; const saltStr = (0, _binary.uint8ArrayToBinaryString)(salt); const md = _md.default.create(); const hash = bytes => md.start().update(bytes).digest().getBytes(); const digests = [hash(password + saltStr)]; for (let length = 16, i = 1; length < keyLength; i += 1, length += 16) { digests.push(hash(digests[i - 1] + password + saltStr)); } const digestStr = digests.join('').substr(0, keyLength); return (0, _binary.binaryStringToUint8Array)(digestStr); }; const decryptWithAes = (key, encryptedData, params) => { const { iv, mode } = params; const ivStr = (0, _binary.uint8ArrayToBinaryString)(iv); const keyStr = (0, _binary.uint8ArrayToBinaryString)(key); const cipher = _aes.default.createDecryptionCipher(keyStr, mode); cipher.start(ivStr); cipher.update((0, _util.createBuffer)((0, _binary.uint8ArrayToBinaryString)(encryptedData))); if (!cipher.finish()) { throw new _errors.DecryptionFailedError('Decryption failed, mostly likely the password is wrong'); } return (0, _binary.binaryStringToUint8Array)(cipher.output.getBytes()); }; const encryptWithAes = (key, data, params) => { const { iv, mode } = params; const ivStr = (0, _binary.uint8ArrayToBinaryString)(iv); const keyStr = (0, _binary.uint8ArrayToBinaryString)(key); const cipher = _aes.default.createEncryptionCipher(keyStr, mode); cipher.start(ivStr); cipher.update((0, _util.createBuffer)((0, _binary.uint8ArrayToBinaryString)(data))); cipher.finish(); return (0, _binary.binaryStringToUint8Array)(cipher.output.getBytes()); }; const decryptWithDes = (key, encryptedData, params) => { const { iv, mode } = params; const ivStr = (0, _binary.uint8ArrayToBinaryString)(iv); const keyStr = (0, _binary.uint8ArrayToBinaryString)(key); const cipher = _des.default.createDecryptionCipher(keyStr, mode); cipher.start(ivStr); cipher.update((0, _util.createBuffer)((0, _binary.uint8ArrayToBinaryString)(encryptedData))); if (!cipher.finish()) { throw new _errors.DecryptionFailedError('Decryption failed, mostly likely the password is wrong'); } return (0, _binary.binaryStringToUint8Array)(cipher.output.getBytes()); }; const encryptWithDes = (key, data, params) => { const { iv, mode } = params; const ivStr = (0, _binary.uint8ArrayToBinaryString)(iv); const keyStr = (0, _binary.uint8ArrayToBinaryString)(key); const cipher = _des.default.createEncryptionCipher(keyStr, mode); cipher.start(ivStr); cipher.update((0, _util.createBuffer)((0, _binary.uint8ArrayToBinaryString)(data))); cipher.finish(); return (0, _binary.binaryStringToUint8Array)(cipher.output.getBytes()); }; const decryptWithRc2 = (key, encryptedData, params) => { const { iv, bits } = params; const ivStr = (0, _binary.uint8ArrayToBinaryString)(iv); const keyStr = (0, _binary.uint8ArrayToBinaryString)(key); const cipher = _rc.default.createDecryptionCipher(keyStr, bits); cipher.start(ivStr); cipher.update((0, _util.createBuffer)((0, _binary.uint8ArrayToBinaryString)(encryptedData))); if (!cipher.finish()) { throw new _errors.DecryptionFailedError('Decryption failed, mostly likely the password is wrong'); } return (0, _binary.binaryStringToUint8Array)(cipher.output.getBytes()); }; const encryptWithRc2 = (key, data, params) => { const { iv, bits } = params; const ivStr = (0, _binary.uint8ArrayToBinaryString)(iv); const keyStr = (0, _binary.uint8ArrayToBinaryString)(key); const cipher = _rc.default.createEncryptionCipher(keyStr, bits); cipher.start(ivStr); cipher.update((0, _util.createBuffer)((0, _binary.uint8ArrayToBinaryString)(data))); cipher.finish(); return (0, _binary.binaryStringToUint8Array)(cipher.output.getBytes()); }; const getRc2KeyLength = bits => { // RC2-CBCParameter encoding of the "effective key bits" as defined in: // https://tools.ietf.org/html/rfc2898#appendix-B.2.3 switch (bits) { case 40: return 5; case 64: return 8; case 128: return 16; default: throw new _errors.UnsupportedAlgorithmError(`Unsupported RC2 bits parameter with value '${bits}'`); } }; const decryptWithPassword = (encryptedData, encryptionAlgorithm, password) => { const { keyDerivationFunc, encryptionScheme } = encryptionAlgorithm; let deriveKeyFn; let derivedKeyLength; let decryptFn; // Process encryption scheme switch (encryptionScheme.id) { case 'aes128-cbc': case 'aes192-cbc': case 'aes256-cbc': decryptFn = key => decryptWithAes(key, encryptedData, { ...encryptionScheme, mode: 'CBC' }); derivedKeyLength = Number(encryptionScheme.id.match(/^aes(\d+)-/)[1]) / 8; break; case 'rc2-cbc': decryptFn = key => decryptWithRc2(key, encryptedData, encryptionScheme); derivedKeyLength = getRc2KeyLength(encryptionScheme.bits); break; case 'des-ede3-cbc': decryptFn = key => decryptWithDes(key, encryptedData, { ...encryptionScheme, mode: 'CBC' }); derivedKeyLength = 24; break; case 'des-cbc': decryptFn = key => decryptWithDes(key, encryptedData, { ...encryptionScheme, mode: 'CBC' }); derivedKeyLength = 8; break; default: throw new _errors.UnsupportedAlgorithmError(`Unsupported encryption scheme id '${encryptionScheme.id}'`); } // Process key derivation func switch (keyDerivationFunc.id) { case 'pbkdf2': deriveKeyFn = () => deriveKeyWithPbkdf2(password, { ...keyDerivationFunc, keyLength: keyDerivationFunc.keyLength || derivedKeyLength }); break; case 'openssl-derive-bytes': deriveKeyFn = () => deriveKeyWithOpensslDeriveBytes(password, { keyLength: derivedKeyLength, salt: encryptionScheme.iv.slice(0, 8) }); break; default: throw new _errors.UnsupportedAlgorithmError(`Unsupported key derivation function id '${keyDerivationFunc.id}'`); } const derivedKey = deriveKeyFn(); const decryptedData = decryptFn(derivedKey); return decryptedData; }; exports.decryptWithPassword = decryptWithPassword; const encryptWithPassword = (data, encryptionAlgorithm, password) => { const keyDerivationFunc = { ...encryptionAlgorithm.keyDerivationFunc }; const encryptionScheme = { ...encryptionAlgorithm.encryptionScheme }; let deriveKeyFn; let derivedKeyLength; let encryptFn; // Process encryption scheme switch (encryptionScheme.id) { case 'aes128-cbc': case 'aes192-cbc': case 'aes256-cbc': encryptionScheme.iv = encryptionScheme.iv || (0, _random.default)(16); encryptFn = key => encryptWithAes(key, data, { ...encryptionScheme, mode: 'CBC' }); derivedKeyLength = Number(encryptionScheme.id.match(/^aes(\d+)-/)[1]) / 8; break; case 'rc2-cbc': encryptionScheme.bits = encryptionScheme.bits || 128; encryptionScheme.iv = encryptionScheme.iv || (0, _random.default)(16); encryptFn = key => encryptWithRc2(key, data, encryptionScheme); derivedKeyLength = getRc2KeyLength(encryptionScheme.bits); break; case 'des-ede3-cbc': encryptionScheme.iv = encryptionScheme.iv || (0, _random.default)(8); encryptFn = key => encryptWithDes(key, data, { ...encryptionScheme, mode: 'CBC' }); derivedKeyLength = 24; break; case 'des-cbc': encryptionScheme.iv = encryptionScheme.iv || (0, _random.default)(8); encryptFn = key => encryptWithDes(key, data, { ...encryptionScheme, mode: 'CBC' }); derivedKeyLength = 8; break; default: throw new _errors.UnsupportedAlgorithmError(`Unsupported encryption scheme id '${encryptionScheme.id}'`); } // Process key derivation name switch (keyDerivationFunc.id) { case 'pbkdf2': if (keyDerivationFunc.keyLength != null && derivedKeyLength !== keyDerivationFunc.keyLength) { throw new _errors.UnsupportedAlgorithmError(`The specified key length must be equal to ${derivedKeyLength} (or omitted)`); } keyDerivationFunc.salt = keyDerivationFunc.salt || (0, _random.default)(16); keyDerivationFunc.iterationCount = keyDerivationFunc.iterationCount || 10000; keyDerivationFunc.keyLength = keyDerivationFunc.keyLength || derivedKeyLength; keyDerivationFunc.prf = keyDerivationFunc.prf || 'hmac-with-sha512'; deriveKeyFn = () => deriveKeyWithPbkdf2(password, keyDerivationFunc); break; case 'openssl-derive-bytes': keyDerivationFunc.keyLength = derivedKeyLength; keyDerivationFunc.salt = encryptionScheme.iv.slice(0, 8); deriveKeyFn = () => deriveKeyWithOpensslDeriveBytes(password, keyDerivationFunc); break; default: throw new _errors.UnsupportedAlgorithmError(`Unsupported key derivation function id '${keyDerivationFunc.id}'`); } const derivedKey = deriveKeyFn(); const encryptedData = encryptFn(derivedKey); return { effectiveEncryptionAlgorithm: { keyDerivationFunc, encryptionScheme }, encryptedData }; }; exports.encryptWithPassword = encryptWithPassword;