@meeco/cryppo
Version:
In-browser encryption and decryption. Clone of Ruby Cryppo
88 lines • 3.64 kB
JavaScript
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