import {TeaCipher} from "@/utils/tea"; const SALT_LEN = 2 const ZERO_LEN = 7 export function QmcDeriveKey(raw: Uint8Array): Uint8Array { const textDec = new TextDecoder() const rawDec = Buffer.from(textDec.decode(raw), 'base64') let n = rawDec.length; if (n < 16) { throw Error("key length is too short") } const simpleKey = simpleMakeKey(106, 8) let teaKey = new Uint8Array(16); for (let i = 0; i < 8; i++) { teaKey[i << 1] = simpleKey[i]; teaKey[(i << 1) + 1] = rawDec[i]; } const sub = decryptTencentTea(rawDec.subarray(8), teaKey) rawDec.set(sub, 8) return rawDec.subarray(0, 8 + sub.length) } // simpleMakeKey exported only for unit test export function simpleMakeKey(salt: number, length: number): number[] { const keyBuf: number[] = [] for (let i = 0; i < length; i++) { const tmp = Math.tan(salt + i * 0.1) keyBuf[i] = 0xff & (Math.abs(tmp) * 100.0) } return keyBuf } function decryptTencentTea(inBuf: Uint8Array, key: Uint8Array): Uint8Array { if (inBuf.length % 8 != 0) { throw Error("inBuf size not a multiple of the block size") } if (inBuf.length < 16) { throw Error("inBuf size too small") } const blk = new TeaCipher(key, 32) const tmpBuf = new Uint8Array(8); const tmpView = new DataView(tmpBuf.buffer); blk.decrypt(tmpView, new DataView(inBuf.buffer, inBuf.byteOffset, 8)) const nPadLen = tmpBuf[0] & 0x7;//只要最低三位 /*密文格式:PadLen(1byte)+Padding(var,0-7byte)+Salt(2byte)+Body(var byte)+Zero(7byte)*/ const outLen = inBuf.length - 1 /*PadLen*/ - nPadLen - SALT_LEN - ZERO_LEN; const outBuf = new Uint8Array(outLen) let ivPrev = new Uint8Array(8); let ivCur = inBuf.slice(0, 8); // init iv let inBufPos = 8; // 跳过 Padding Len 和 Padding let tmpIdx = 1 + nPadLen; // CBC IV 处理 const cryptBlock = () => { ivPrev = ivCur; ivCur = inBuf.slice(inBufPos, inBufPos + 8) for (let j = 0; j < 8; j++) { tmpBuf[j] ^= ivCur[j] } blk.decrypt(tmpView, tmpView) inBufPos += 8; tmpIdx = 0; } // 跳过 Salt for (let i = 1; i <= SALT_LEN;) { if (tmpIdx < 8) { tmpIdx++; i++; } else { cryptBlock() } } // 还原明文 let outBufPos = 0; while (outBufPos < outLen) { if (tmpIdx < 8) { outBuf[outBufPos] = tmpBuf[tmpIdx] ^ ivPrev[tmpIdx]; outBufPos++ tmpIdx++; } else { cryptBlock() } } // 校验Zero for (let i = 1; i <= ZERO_LEN; i++) { if (tmpBuf[tmpIdx] != ivPrev[tmpIdx]) { throw Error("zero check failed") } } return outBuf }