@meeco/cryppo
Version:
In-browser encryption and decryption. Clone of Ruby Cryppo
96 lines • 4.22 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.decryptWithKeyDerivedFromString = decryptWithKeyDerivedFromString;
exports.decryptWithKey = decryptWithKey;
exports.decryptWithKeyUsingArtefacts = decryptWithKeyUsingArtefacts;
const node_forge_1 = __importDefault(require("node-forge"));
const encoding_versions_js_1 = require("../encoding-versions.js");
const derived_key_js_1 = require("../key-derivation/derived-key.js");
const strategies_js_1 = require("../strategies.js");
const util_js_1 = require("../util.js");
const { cipher, util } = node_forge_1.default;
async function decryptWithKeyDerivedFromString({ serialized, passphrase, encodingVersion = encoding_versions_js_1.EncodingVersions.latest_version, }) {
const derivedKey = await _deriveKeyWithOptions({
key: passphrase,
serializedOptions: serialized,
encodingVersion,
});
return await decryptWithKey({
serialized: serialized.split('.').slice(0, 3).join('.'),
key: derivedKey,
});
}
async function decryptWithKey({ serialized, key, }) {
const deSerialized = (0, util_js_1.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 = (0, strategies_js_1.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 &&
(0, util_js_1.encodeUtf8)((0, util_js_1.bytesToBinaryString)(key.bytes)) !== (0, util_js_1.bytesToBinaryString)(key.bytes) &&
derived_key_js_1.DerivedKeyOptions.usesDerivedKey(serialized)) {
// Decryption failed with utf-8 key style - retry with legacy utf-16 key format
legacyKey = await _deriveKeyWithOptions({
key: (0, util_js_1.bytesToBinaryString)(key.bytes),
serializedOptions: serialized,
encodingVersion: encoding_versions_js_1.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 = encoding_versions_js_1.EncodingVersions.latest_version, }) {
const derivedKeyOptions = derived_key_js_1.DerivedKeyOptions.fromSerialized(serializedOptions);
return derivedKeyOptions.deriveKey(key, encodingVersion);
}
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 (0, util_js_1.binaryStringToBytesBuffer)(decipher.output.data);
}
throw new Error('Decryption failed');
}
//# sourceMappingURL=decryption.js.map