UNPKG

@meeco/cryppo

Version:

In-browser encryption and decryption. Clone of Ruby Cryppo

88 lines 3.64 kB
import forge from 'node-forge'; import { EncodingVersions } from '../encoding-versions.js'; import { DerivedKeyOptions } from '../key-derivation/derived-key.js'; import { strategyToAlgorithm } from '../strategies.js'; import { binaryStringToBytesBuffer, bytesToBinaryString, deSerialize, encodeUtf8, } from '../util.js'; const { cipher, util } = forge; export async function decryptWithKeyDerivedFromString({ serialized, passphrase, encodingVersion = EncodingVersions.latest_version, }) { const derivedKey = await _deriveKeyWithOptions({ key: passphrase, serializedOptions: serialized, encodingVersion, }); return await decryptWithKey({ serialized: serialized.split('.').slice(0, 3).join('.'), key: derivedKey, }); } export async function decryptWithKey({ serialized, key, }) { const deSerialized = deSerialize(serialized); const { encryptionStrategy } = deSerialized; const { decodedPairs } = deSerialized; if (decodedPairs[0] === '') { return null; } let output = null; let legacyKey; for (let i = 0; i < decodedPairs.length; i += 2) { const data = decodedPairs[i]; const artifacts = decodedPairs[i + 1]; const strategy = strategyToAlgorithm(encryptionStrategy); try { const decrypted = decryptWithKeyUsingArtefacts(legacyKey ? legacyKey : key, data, strategy, artifacts); // ensure correct type output = decrypted ? new Uint8Array(decrypted) : null; } catch (err) { if (!legacyKey && encodeUtf8(bytesToBinaryString(key.bytes)) !== bytesToBinaryString(key.bytes) && DerivedKeyOptions.usesDerivedKey(serialized)) { // Decryption failed with utf-8 key style - retry with legacy utf-16 key format legacyKey = await _deriveKeyWithOptions({ key: bytesToBinaryString(key.bytes), serializedOptions: serialized, encodingVersion: EncodingVersions.legacy, }); i -= 2; continue; } else { // Both utf-8 and utf-16 key formats have failed - bail throw err; } } } return output; } /** * Determine if we need to use a derived key or not based on whether or not * we have key derivation options in the serialized payload. */ function _deriveKeyWithOptions({ key, serializedOptions, encodingVersion = EncodingVersions.latest_version, }) { const derivedKeyOptions = DerivedKeyOptions.fromSerialized(serializedOptions); return derivedKeyOptions.deriveKey(key, encodingVersion); } export function decryptWithKeyUsingArtefacts(key, encryptedData, strategy, { iv, at, ad }) { if (encryptedData === '') { return null; } // @ts-expect-error node-forge createDecipher accepts Uint8Array at runtime const decipher = cipher.createDecipher(strategy, util.createBuffer(key.bytes)); const tagLength = 128; const tag = util.createBuffer(at); // authentication tag from encryption const encrypted = util.createBuffer(encryptedData); decipher.start({ iv: util.createBuffer(iv), additionalData: ad, tagLength, tag, }); decipher.update(encrypted); const pass = decipher.finish(); // pass is false if there was a failure (eg: authentication tag didn't match) if (pass) { return binaryStringToBytesBuffer(decipher.output.data); } throw new Error('Decryption failed'); } //# sourceMappingURL=decryption.js.map