80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import { sjcl } from "../vendor/sjcl";
|
|
import { Err, ErrorCode } from "./error";
|
|
import { PBKDF2_ITER_MAX } from "./crypto";
|
|
import { PBES2Container } from "./container";
|
|
|
|
/**
|
|
* Interface representing legacy container structure (i.e. encryption format
|
|
* used in Padlock versions 1.x and 2.x)
|
|
*/
|
|
export interface LegacyContainer {
|
|
version?: 1;
|
|
cipher: "aes";
|
|
mode: "ccm";
|
|
iv: string;
|
|
adata: string;
|
|
ts: 64 | 96 | 128;
|
|
keySize: 256;
|
|
salt: string;
|
|
iter: number;
|
|
ct: string;
|
|
}
|
|
|
|
/**
|
|
* Validates legacy container structure
|
|
*/
|
|
export function validateLegacyContainer(obj: any): boolean {
|
|
return (
|
|
typeof obj === "object" &&
|
|
(obj.version === undefined || obj.version === 1) && // has a valid version
|
|
[128, 192, 256].includes(obj.keySize) &&
|
|
typeof obj.iter == "number" && // valid PBKDF2 iteration count
|
|
obj.iter <= PBKDF2_ITER_MAX && // sane pbkdf2 iteration count
|
|
typeof obj.salt == "string" &&
|
|
["aes"].includes(obj.cipher) && // valid cipher
|
|
["ccm", "ocb2"].includes(obj.mode) && // exiting mode
|
|
typeof obj.iv == "string" && // valid initialisation vector
|
|
typeof obj.ct == "string" && // valid cipher text
|
|
typeof obj.adata == "string" && // valid authorisation data
|
|
[64, 96, 128].includes(obj.ts) // valid authorisation tag length
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Transforms a legacy container object into an instance of [[PBES2Container]]
|
|
*/
|
|
export function parseLegacyContainer(raw: LegacyContainer): PBES2Container {
|
|
if (!validateLegacyContainer(raw)) {
|
|
throw new Err(ErrorCode.ENCODING_ERROR);
|
|
}
|
|
|
|
if (raw.version === undefined) {
|
|
// Legacy versions of Padlock had a bug where the base64-encoded
|
|
// `adata` value was not converted to a BitArray before being
|
|
// passed to `sjcl.mode.ccm.encrypt/decrypt` and the raw string was
|
|
// passed instead. This went unnoticed as the functions in question
|
|
// quietly accepted the string and simply treated it as an array.
|
|
// So in order to successfully decrypt legacy containers we have to
|
|
// perfom this conversion first.
|
|
raw.adata = sjcl.codec.base64.fromBits(raw.adata);
|
|
}
|
|
|
|
return new PBES2Container().fromRaw({
|
|
encryptionParams: {
|
|
algorithm: "AES-CCM",
|
|
tagSize: raw.ts,
|
|
keySize: raw.keySize,
|
|
iv: raw.iv,
|
|
additionalData: raw.adata,
|
|
},
|
|
keyParams: {
|
|
algorithm: "PBKDF2",
|
|
hash: "SHA-256",
|
|
keySize: raw.keySize,
|
|
iterations: raw.iter,
|
|
salt: raw.salt,
|
|
},
|
|
encryptedData: raw.ct,
|
|
});
|
|
}
|