52 lines
No EOL
1.7 KiB
TypeScript
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}`);
|
|
}
|
|
}
|
|
}; |