aiot/src/helpers/decrypt.ts

52 lines
No EOL
1.7 KiB
TypeScript

import { createDecipheriv } from "crypto";
import type { TidalManifest } from "../../Caches/PlaybackInfoTypes";
// Do not change this
const mastKey = "UIlTTEMmmLfGowo/UC60x2H45W6MdGgTRfo/umg4754=";
const mastKeyBuffer = Buffer.from(mastKey, "base64");
type DecryptedKey = {
key: Buffer;
nonce: Buffer;
};
const decryptKeyId = (keyId: string): DecryptedKey => {
// Decode the base64 strings to buffers
const keyIdBuffer = Buffer.from(keyId, "base64");
// Get the IV from the first 16 bytes of the securityToken
const iv = keyIdBuffer.subarray(0, 16);
const keyIdEnc = keyIdBuffer.subarray(16);
// Initialize decryptor
const decryptor = createDecipheriv("aes-256-cbc", Uint8Array.from(mastKeyBuffer), Uint8Array.from(iv));
// Decrypt the security token
const keyIdDec = decryptor.update(Uint8Array.from(keyIdEnc));
// Get the audio stream decryption key and nonce from the decrypted security token
const key = keyIdDec.subarray(0, 16);
const nonce = keyIdDec.subarray(16, 24);
return { key, nonce };
};
// Extend nonce to 16 bytes (nonce + counter)
const makeDecipheriv = ({ key, nonce }: DecryptedKey) => {
const iv = new Uint8Array([...nonce, ...new Uint8Array(8)]);
return createDecipheriv("aes-128-ctr", Uint8Array.from(key), iv);
};
export const makeDecipher = (manifest: { keyId: string; encryptionType: string }) => {
switch (manifest.encryptionType) {
case "OLD_AES": {
return makeDecipheriv(decryptKeyId(manifest.keyId));
}
case "NONE": {
return undefined;
}
default: {
throw new Error(`Unexpected manifest encryption type ${manifest.encryptionType}`);
}
}
};