Files
QZMusic_PC/amll-local/packages/lyric/src/formats/eqrc/custom-des.ts

364 lines
10 KiB
TypeScript
Raw Normal View History

/**
* @internal
* @module custom_des
* @description
* QRC DES
*
* <h2>
* <strong> DES </strong>
* </h2>
*
* DES的
* QRC
*/
import {
E_BOX_TABLE,
KEY_COMPRESSION,
KEY_PERM_C,
KEY_PERM_D,
KEY_RND_SHIFT,
P_BOX,
S_BOXES,
} from "./constants";
export enum Mode {
Encrypt,
Decrypt,
}
export type KeySchedule = Int32Array;
/**
* 8 BigInt
*
* C代码中的天书BITNUM宏 QQ
* 8 32
*
* 0MSB访 `key[3]`
* 31访 `key[0]`
*
* @param key 8 Uint8Array
* @param table 0-based
*/
function permuteFromKeyBytes(key: Uint8Array, table: number[]): bigint {
let output = 0n;
let currentBitMask = 1n << BigInt(table.length - 1);
for (let i = 0; i < table.length; i++) {
const pos = table[i];
const wordIndex = pos >> 5;
const bitInWord = pos & 31;
const byteInWord = bitInWord >> 3;
const bitInByte = bitInWord & 7;
const byteIndex = wordIndex * 4 + 3 - byteInWord;
const bit = (key[byteIndex] >> (7 - bitInByte)) & 1;
if (bit) {
output |= currentBitMask;
}
currentBitMask >>= 1n;
}
return output;
}
/**
* BigInt 28
* @param value 28 BigInt
* @param amount
*/
function rotateLeft28Bit(value: bigint, amount: number): bigint {
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
* 1648
*
* @param key 8DES密钥
* @param mode
*/
export function keySchedule(key: Uint8Array, mode: Mode): KeySchedule {
// 预先分配连续的内存空间
const schedule = new Int32Array(32);
// 应用 PC-1
const c0 = permuteFromKeyBytes(key, KEY_PERM_C);
const d0 = permuteFromKeyBytes(key, KEY_PERM_D);
// 将28位的结果左移4位以匹配 `rotateLeft28Bit` 对高位对齐的期望。
let c = c0 << 4n;
let d = d0 << 4n;
for (let i = 0; i < 16; i++) {
const shift = 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 < KEY_COMPRESSION.length; k++) {
const pos = 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);
}
}
// 提取高 24 位 (由第 5, 4, 3 字节组成)
const b5 = Number((subkey48bit >> 40n) & 0xffn);
const b4 = Number((subkey48bit >> 32n) & 0xffn);
const b3 = Number((subkey48bit >> 24n) & 0xffn);
const high24 = (b5 << 16) | (b4 << 8) | b3;
// 提取低 24 位 (由第 2, 1, 0 字节组成)
const b2 = Number((subkey48bit >> 16n) & 0xffn);
const b1 = Number((subkey48bit >> 8n) & 0xffn);
const b0 = Number(subkey48bit & 0xffn);
const low24 = (b2 << 16) | (b1 << 8) | b0;
// 存储到一维数组中
schedule[toGen * 2] = high24;
schedule[toGen * 2 + 1] = low24;
}
return schedule;
}
// 初始置换规则。
const IP_RULE: number[] = [
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: number[] = [
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,
];
const IP_LEFT_TABLE = new Int32Array(2048);
const IP_RIGHT_TABLE = new Int32Array(2048);
const INV_IP_LEFT_TABLE = new Int32Array(2048);
const INV_IP_RIGHT_TABLE = new Int32Array(2048);
function generatePermutationTables(): void {
const applyPermutation = (input: bigint, rule: number[]): bigint => {
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);
const idx = (bytePos << 8) | byteVal;
IP_LEFT_TABLE[idx] = Number((permuted >> 32n) & 0xffffffffn);
IP_RIGHT_TABLE[idx] = Number(permuted & 0xffffffffn);
}
}
// 生成 InvIP 结果查找表 (一维 TypedArray分为左右 32 位以避免 BigInt)
for (let blockPos = 0; blockPos < 8; blockPos++) {
for (let blockVal = 0; blockVal < 256; blockVal++) {
const input = BigInt(blockVal) << BigInt(56 - blockPos * 8);
const permuted = applyPermutation(input, INV_IP_RULE);
const idx = (blockPos << 8) | blockVal;
INV_IP_LEFT_TABLE[idx] = Number((permuted >> 32n) & 0xffffffffn);
INV_IP_RIGHT_TABLE[idx] = Number(permuted & 0xffffffffn);
}
}
}
generatePermutationTables();
/**
* DES S-
* @param a 6 u8
*/
function calculateSboxIndex(a: number): number {
return (a & 0x20) | ((a & 0x1f) >> 1) | ((a & 0x01) << 4);
}
/**
* 32 P
* @param input S- 32
*/
function applyQqPboxPermutation(input: number): number {
let output = 0;
for (let i = 0; i < 32; i++) {
const sourceBit1Based = P_BOX[i];
const destBitMask = 1 << (31 - i);
const sourceBitMask = 1 << (32 - sourceBit1Based);
if ((input & sourceBitMask) !== 0) {
output |= destBitMask;
}
}
return output;
}
const SP_TABLE = new Int32Array(512);
/**
* S-P
*/
function generateSpTables(): void {
for (let sBoxIdx = 0; sBoxIdx < 8; sBoxIdx++) {
for (let sBoxInput = 0; sBoxInput < 64; sBoxInput++) {
const sBoxIndex = calculateSboxIndex(sBoxInput);
const fourBitOutput = S_BOXES[sBoxIdx][sBoxIndex];
const prePBoxVal = fourBitOutput << (28 - sBoxIdx * 4);
SP_TABLE[(sBoxIdx << 6) | sBoxInput] = applyQqPboxPermutation(prePBoxVal);
}
}
}
generateSpTables();
const EBOX_HIGH_TABLE = new Int32Array(1024);
const EBOX_LOW_TABLE = new Int32Array(1024);
function generateEBoxTables(): void {
for (let chunkIdx = 0; chunkIdx < 4; chunkIdx++) {
const shiftIn32 = (3 - chunkIdx) * 8;
for (let byteVal = 0; byteVal < 256; byteVal++) {
let high24 = 0;
let low24 = 0;
const input = byteVal << shiftIn32;
for (let i = 0; i < 24; i++) {
const sourceBitPos = E_BOX_TABLE[i];
const bit = (input >>> (32 - sourceBitPos)) & 1;
if (bit) {
high24 |= 1 << (23 - i);
}
}
for (let i = 24; i < 48; i++) {
const sourceBitPos = E_BOX_TABLE[i];
const bit = (input >>> (32 - sourceBitPos)) & 1;
if (bit) {
low24 |= 1 << (47 - i);
}
}
const tableIdx = (chunkIdx << 8) | byteVal;
EBOX_HIGH_TABLE[tableIdx] = high24;
EBOX_LOW_TABLE[tableIdx] = low24;
}
}
}
generateEBoxTables();
/**
* DES F
*/
function fFunction(state: number, keyHigh24: number, keyLow24: number): number {
// 将 32 位状态拆分为 4 个字节,直接查表并进行按位或拼接
const b0 = (state >>> 24) & 0xff;
const b1 = (state >>> 16) & 0xff;
const b2 = (state >>> 8) & 0xff;
const b3 = state & 0xff;
const eboxHigh24 =
EBOX_HIGH_TABLE[b0] |
EBOX_HIGH_TABLE[256 | b1] |
EBOX_HIGH_TABLE[512 | b2] |
EBOX_HIGH_TABLE[768 | b3];
const eboxLow24 =
EBOX_LOW_TABLE[b0] |
EBOX_LOW_TABLE[256 | b1] |
EBOX_LOW_TABLE[512 | b2] |
EBOX_LOW_TABLE[768 | b3];
const xorHigh24 = eboxHigh24 ^ keyHigh24;
const xorLow24 = eboxLow24 ^ keyLow24;
return (
SP_TABLE[(xorHigh24 >>> 18) & 0x3f] |
SP_TABLE[64 | ((xorHigh24 >>> 12) & 0x3f)] |
SP_TABLE[128 | ((xorHigh24 >>> 6) & 0x3f)] |
SP_TABLE[192 | (xorHigh24 & 0x3f)] |
SP_TABLE[256 | ((xorLow24 >>> 18) & 0x3f)] |
SP_TABLE[320 | ((xorLow24 >>> 12) & 0x3f)] |
SP_TABLE[384 | ((xorLow24 >>> 6) & 0x3f)] |
SP_TABLE[448 | (xorLow24 & 0x3f)]
);
}
/**
* DES /64
*
* @param input 8 ()
* @param output 8 ()
* @param keySchedule 166
*/
export function desCrypt(
input: Uint8Array,
output: Uint8Array,
keySchedule: KeySchedule,
): void {
let left = 0;
let right = 0;
for (let i = 0; i < 8; i++) {
const idx = (i << 8) | input[i];
left |= IP_LEFT_TABLE[idx];
right |= IP_RIGHT_TABLE[idx];
}
for (let i = 0; i < 15; i++) {
const temp = right;
right =
(left ^ fFunction(right, keySchedule[i * 2], keySchedule[i * 2 + 1])) >>>
0;
left = temp;
}
left = (left ^ fFunction(right, keySchedule[30], keySchedule[31])) >>> 0;
let outLeft = 0;
let outRight = 0;
for (let i = 0; i < 4; i++) {
// 分别计算左侧 32 位和右侧 32 位在 INV_IP 阶段的表现
const idxL = (i << 8) | ((left >>> (24 - i * 8)) & 0xff);
outLeft |= INV_IP_LEFT_TABLE[idxL];
outRight |= INV_IP_RIGHT_TABLE[idxL];
const idxR = ((i + 4) << 8) | ((right >>> (24 - i * 8)) & 0xff);
outLeft |= INV_IP_LEFT_TABLE[idxR];
outRight |= INV_IP_RIGHT_TABLE[idxR];
}
// 使用按位移位进行输出数组写入
output[0] = (outLeft >>> 24) & 0xff;
output[1] = (outLeft >>> 16) & 0xff;
output[2] = (outLeft >>> 8) & 0xff;
output[3] = outLeft & 0xff;
output[4] = (outRight >>> 24) & 0xff;
output[5] = (outRight >>> 16) & 0xff;
output[6] = (outRight >>> 8) & 0xff;
output[7] = outRight & 0xff;
}