Files
WeChat-Channels-Video-File-…/api-service/lib/decrypt.js
T
Evil0ctal 8900a7c6fb fix: 修复大文件解密崩溃并将 XOR 解密移至服务端
问题修复:
- 修复解密超过 80MB 视频时页面崩溃的问题(issue #4 #6 #8)
- 根因:旧实现将整段视频经 CDP 管道传入浏览器做 XOR,base64 膨胀后超过
  Chromium DevTools 协议 100MB 单条消息上限,导致页面被关闭
- 将 XOR 解密从浏览器移至 Node.js 端,浏览器仅负责生成 128KB 密钥流,
  视频数据不再进入浏览器,文件大小不再受此限制

新功能:
- 新增 lib/decrypt.js 解密核心模块(decryptBuffer / assertMp4)
- 新增 decode_key 密钥流 LRU 缓存,命中后跳过浏览器 WASM 调用,
  可通过 KEYSTREAM_CACHE_MAX 环境变量配置上限(默认100)
- 新增 test/decrypt.test.js 单元测试,覆盖 120MB 大文件边界场景

其他:
- Dockerfile 补充拷贝 lib 目录,修复容器内模块缺失
- worker.html 的 decryptVideo 标注为已弃用(保留向后兼容)
- 更新 README 架构说明,补充大文件故障排除与「如何获取 decode_key」说明
- 版本号升级至 2.1.0
2026-06-09 22:33:15 -07:00

43 lines
1.6 KiB
JavaScript

/**
* 视频解密核心(纯函数,Node.js 端执行)
*
* 微信视频号仅加密文件前 KEYSTREAM_SIZE(128KB) 字节,其余为明文。
* 解密无需浏览器:浏览器只负责用 WASM 生成 128KB 密钥流,XOR 在此完成。
* 旧实现把整段视频经 CDP 管道传入浏览器,会触发 DevTools 协议 100MB 单条
* 消息上限,导致大文件解密时页面崩溃(见 issue #4 / #6 / #8)。
*
* @module lib/decrypt
*/
// 微信加密作用的字节数(密钥流长度)
const KEYSTREAM_SIZE = 131072;
/**
* 对加密视频执行 XOR 解密
* @param {Buffer} encrypted 原始加密视频 Buffer
* @param {Buffer} keystream 128KB 密钥流 Buffer
* @returns {Buffer} 解密后的视频 Buffer(明文部分原样保留)
*/
function decryptBuffer(encrypted, keystream) {
const decrypted = Buffer.from(encrypted); // 拷贝一份,明文部分原样保留
const decryptLen = Math.min(KEYSTREAM_SIZE, encrypted.length, keystream.length);
for (let i = 0; i < decryptLen; i++) {
decrypted[i] = encrypted[i] ^ keystream[i];
}
return decrypted;
}
/**
* 校验解密结果是否为合法 MP4(偏移 4 处应为 ftyp 签名)
* @param {Buffer} buffer 解密后的视频 Buffer
* @throws {Error} 当签名缺失时(通常是 decode_key 不匹配)
*/
function assertMp4(buffer) {
const ftyp = buffer.toString('utf8', 4, 8);
if (ftyp !== 'ftyp') {
throw new Error('解密失败:未找到 MP4 ftyp 签名,请检查 decode_key');
}
}
module.exports = { KEYSTREAM_SIZE, decryptBuffer, assertMp4 };