UNPKG

qrc-decoder

Version:

A TypeScript library for encrypting and decrypting QRC lyrics format.

262 lines (261 loc) 9.76 kB
"use strict"; /** * @internal * @module custom_des * @description * 本模块包含了为解密 QRC 歌词而移植的、非标准的类 DES 算法的底层实现。 * * <h2> * <strong>警告:该 DES 实现并非标准实现!</strong> * </h2> * * 它是结构类似DES的、但完全私有的分组密码算法。 * 本实现仅用于 QRC 歌词解密,不应用于实际安全目的。 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Mode = void 0; exports.keySchedule = keySchedule; exports.desCrypt = desCrypt; const constants_1 = require("./constants"); var Mode; (function (Mode) { Mode[Mode["Encrypt"] = 0] = "Encrypt"; Mode[Mode["Decrypt"] = 1] = "Decrypt"; })(Mode || (exports.Mode = Mode = {})); /** * 从8字节密钥中根据置换表提取位,生成一个 BigInt。 * * 这个函数对应原始C代码中的天书BITNUM宏,模拟 QQ 音乐特有的非标准的字节序处理方式。 * 其将 8 字节密钥视为两个独立的、小端序的32位整数拼接而成。 * * 例如,要读取第0位(MSB),它实际访问的是 `key[3]` 的最高位。 * 要读取第31位,它访问的是 `key[0]` 的最低位。 * * @param key 8字节的密钥 Uint8Array * @param table 0-based 的位索引置换表 */ function permuteFromKeyBytes(key, table) { let output = 0n; const outputLen = BigInt(table.length); for (let i = 0; i < table.length; i++) { const pos = table[i]; const wordIndex = Math.floor(pos / 32); const bitInWord = pos % 32; const byteInWord = Math.floor(bitInWord / 8); const bitInByte = bitInWord % 8; const byteIndex = wordIndex * 4 + 3 - byteInWord; const bit = (key[byteIndex] >> (7 - bitInByte)) & 1; if (bit) { output |= 1n << (outputLen - 1n - BigInt(i)); } } return output; } /** * 对一个存储在 BigInt 中的28位密钥部分进行循环左移。 * @param value 包含28位数据的高位的 BigInt * @param amount 左移的位数 */ function rotateLeft28Bit(value, amount) { const BITS_28_MASK = 0xfffffff0n; const val = value & BITS_28_MASK; const shifted = (val << BigInt(amount)) | (val >> BigInt(28 - amount)); return shifted & BITS_28_MASK; } /** * DES 密钥调度算法。 * 从一个64位的主密钥(实际使用56位,每字节的最低位是奇偶校验位,被忽略) * 生成16个48位的轮密钥。 * * @param key 8字节的DES密钥 * @param mode 加密或解密模式 */ function keySchedule(key, mode) { const schedule = Array.from({ length: 16 }, () => Array(6).fill(0)); // 应用 PC-1 const c0 = permuteFromKeyBytes(key, constants_1.KEY_PERM_C); const d0 = permuteFromKeyBytes(key, constants_1.KEY_PERM_D); // 将28位的结果左移4位,以匹配 `rotate_left_28bit_in_u32` 对高位对齐的期望。 let c = c0 << 4n; let d = d0 << 4n; for (let i = 0; i < 16; i++) { const shift = constants_1.KEY_RND_SHIFT[i]; c = rotateLeft28Bit(c, shift); d = rotateLeft28Bit(d, shift); const toGen = mode === Mode.Decrypt ? 15 - i : i; let subkey48bit = 0n; // 应用 PC-2 for (let k = 0; k < constants_1.KEY_COMPRESSION.length; k++) { const pos = constants_1.KEY_COMPRESSION[k]; const bitBigInt = pos < 28 ? (c >> BigInt(31 - pos)) & 1n : (d >> BigInt(31 - (pos - 27))) & 1n; // QQ 音乐特有的怪癖,该算法的规则就是pos - 27 if (bitBigInt === 1n) { subkey48bit |= 1n << BigInt(47 - k); } } // 将48位的 BigInt 转换为6个字节的 Uint8Array const subkeyBytes = []; for (let j = 5; j >= 0; j--) { subkeyBytes.push(Number((subkey48bit >> BigInt(j * 8)) & 0xffn)); } schedule[toGen] = subkeyBytes; } return schedule; } // 初始置换规则。 const IP_RULE = [ 34, 42, 50, 58, 2, 10, 18, 26, 36, 44, 52, 60, 4, 12, 20, 28, 38, 46, 54, 62, 6, 14, 22, 30, 40, 48, 56, 64, 8, 16, 24, 32, 33, 41, 49, 57, 1, 9, 17, 25, 35, 43, 51, 59, 3, 11, 19, 27, 37, 45, 53, 61, 5, 13, 21, 29, 39, 47, 55, 63, 7, 15, 23, 31, ]; // 逆初始置换规则。 const INV_IP_RULE = [ 37, 5, 45, 13, 53, 21, 61, 29, 38, 6, 46, 14, 54, 22, 62, 30, 39, 7, 47, 15, 55, 23, 63, 31, 40, 8, 48, 16, 56, 24, 64, 32, 33, 1, 41, 9, 49, 17, 57, 25, 34, 2, 42, 10, 50, 18, 58, 26, 35, 3, 43, 11, 51, 19, 59, 27, 36, 4, 44, 12, 52, 20, 60, 28, ]; function generatePermutationTables() { const ipTable = Array.from({ length: 8 }, () => Array(256).fill([0, 0])); const invIpTable = Array.from({ length: 8 }, () => Array(256).fill(0n)); // 对单个 64 位 BigInt 应用置换 const applyPermutation = (input, rule) => { let output = 0n; for (let i = 0; i < 64; i++) { const srcBit1Based = rule[i]; if ((input >> BigInt(64 - srcBit1Based)) & 1n) { output |= 1n << BigInt(63 - i); } } return output; }; // 生成 IP 结果查找表 for (let bytePos = 0; bytePos < 8; bytePos++) { for (let byteVal = 0; byteVal < 256; byteVal++) { const input = BigInt(byteVal) << BigInt(56 - bytePos * 8); const permuted = applyPermutation(input, IP_RULE); ipTable[bytePos][byteVal] = [ Number((permuted >> 32n) & 0xffffffffn), Number(permuted & 0xffffffffn), ]; } } // 生成 InvIP 结果查找表 for (let blockPos = 0; blockPos < 8; blockPos++) { for (let blockVal = 0; blockVal < 256; blockVal++) { const input = BigInt(blockVal) << BigInt(56 - blockPos * 8); invIpTable[blockPos][blockVal] = applyPermutation(input, INV_IP_RULE); } } return { ipTable, invIpTable }; } const { ipTable: IP_TABLE, invIpTable: INV_IP_TABLE } = generatePermutationTables(); /** * 计算 DES S-盒的查找索引。 * @param a 一个包含6位数据的 u8 */ function calculateSboxIndex(a) { return (a & 0x20) | ((a & 0x1f) >> 1) | ((a & 0x01) << 4); } /** * 对一个 32 位整数应用非标准的 P 盒置换规则。 * @param input S-盒代换后的 32 位中间结果 */ function applyQqPboxPermutation(input) { let output = 0; for (let i = 0; i < 32; i++) { const sourceBit1Based = constants_1.P_BOX[i]; const destBitMask = 1 << (31 - i); const sourceBitMask = 1 << (32 - sourceBit1Based); if ((input & sourceBitMask) !== 0) { output |= destBitMask; } } return output; } /** * 生成 S-P 盒合并查找表以提高性能。 */ function generateSpTables() { const spTables = Array.from({ length: 8 }, () => Array(64).fill(0)); for (let sBoxIdx = 0; sBoxIdx < 8; sBoxIdx++) { for (let sBoxInput = 0; sBoxInput < 64; sBoxInput++) { const sBoxIndex = calculateSboxIndex(sBoxInput); const fourBitOutput = constants_1.S_BOXES[sBoxIdx][sBoxIndex]; const prePBoxVal = fourBitOutput << (28 - sBoxIdx * 4); spTables[sBoxIdx][sBoxInput] = applyQqPboxPermutation(prePBoxVal); } } return spTables; } // 预先生成 S-P 查找表 const SP_TABLES = generateSpTables(); /** * 对一个32位整数应用 E-Box 扩展置换,生成一个48位的结果 (以BigInt表示)。 * @param input 32位的右半部分数据 (R_i-1) */ function applyEBoxPermutation(input) { let output = 0n; for (let i = 0; i < 48; i++) { const sourceBitPos = constants_1.E_BOX_TABLE[i]; const shiftAmount = 32 - sourceBitPos; const bit = (input >> shiftAmount) & 1; if (bit) { output |= 1n << BigInt(47 - i); } } return output; } /** * DES 的 F 函数。 */ function fFunction(state, key) { const keyU64 = (BigInt(key[0]) << 40n) | (BigInt(key[1]) << 32n) | (BigInt(key[2]) << 24n) | (BigInt(key[3]) << 16n) | (BigInt(key[4]) << 8n) | BigInt(key[5]); const expandedState = applyEBoxPermutation(state); const xorResult = expandedState ^ keyU64; return (SP_TABLES[0][Number((xorResult >> 42n) & 0x3fn)] | SP_TABLES[1][Number((xorResult >> 36n) & 0x3fn)] | SP_TABLES[2][Number((xorResult >> 30n) & 0x3fn)] | SP_TABLES[3][Number((xorResult >> 24n) & 0x3fn)] | SP_TABLES[4][Number((xorResult >> 18n) & 0x3fn)] | SP_TABLES[5][Number((xorResult >> 12n) & 0x3fn)] | SP_TABLES[6][Number((xorResult >> 6n) & 0x3fn)] | SP_TABLES[7][Number(xorResult & 0x3fn)]); } /** * DES 加密/解密单个64位数据块。 * * @param input 8字节的输入数据块 (明文或密文)。 * @param output 8字节的可变切片,用于存储输出数据块 (密文或明文)。 * @param keySchedule 一个包含16个轮密钥的向量的引用,每个轮密钥是6字节。 */ function desCrypt(input, output, keySchedule) { let left = 0; let right = 0; for (let i = 0; i < 8; i++) { const [l, r] = IP_TABLE[i][input[i]]; left |= l; right |= r; } for (let i = 0; i < 15; i++) { const temp = right; right = (left ^ fFunction(right, keySchedule[i])) >>> 0; left = temp; } left = (left ^ fFunction(right, keySchedule[15])) >>> 0; let result = 0n; for (let i = 0; i < 4; i++) { result |= INV_IP_TABLE[i][(left >> (24 - i * 8)) & 0xff]; result |= INV_IP_TABLE[i + 4][(right >> (24 - i * 8)) & 0xff]; } for (let i = 0; i < 8; i++) { output[i] = Number((result >> BigInt(56 - i * 8)) & 0xffn); } }