Skip to content

Commit 8cd17fa

Browse files
committed
Update FlexibleTransfer
1 parent fea08f3 commit 8cd17fa

2 files changed

Lines changed: 148 additions & 3 deletions

File tree

src/javascript/Misc.js

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
*/
1212
import { Base64 } from "js-base64";
1313
import MersenneTwister from "mersenne-twister"; //兼容性
14+
import CryptoJS from "crypto-js";
1415
import { random } from "@lukeed/csprng"; //密码学安全随机数的封装
1516

1617
const SIG_DECRYPT_JP = "桜込凪雫実沢";
@@ -143,7 +144,11 @@ export function shuffle(array) {
143144
}
144145

145146
/**
147+
* 工具函数
148+
*
146149
* 将四个 0/1(或 true/false)和一个 0..15 整数打包成一个 0..255 的字节值
150+
* 用于包装高级加密的配置位
151+
*
147152
* @param {number|boolean} b0 - 最低位(bit0)
148153
* @param {number|boolean} b1 - bit1
149154
* @param {number|boolean} b2 - bit2
@@ -168,7 +173,10 @@ export function packByte(b0, b1, b2, b3, size) {
168173
}
169174

170175
/**
171-
* 将字节解包回原始的四个 bit 与 size
176+
* 工具函数
177+
*
178+
* 用于解包装高级加密的配置位
179+
*
172180
* @param {number} byte - 0..255
173181
* @returns {object} { byte, size, bits: [b0,b1,b2,b3] (数字 0/1), flags: {b0,b1,b2,b3} (布尔值) }
174182
*/
@@ -194,6 +202,144 @@ export function unpackByte(byte) {
194202
};
195203
}
196204

205+
/**
206+
* 工具函数
207+
*
208+
* 将四个分段传输参数(合共48位,6字节)打包成字节值
209+
* 用于包装高级加密的配置位
210+
*
211+
* @param{number} lengthToBoundary - 检测到标头的位置距离边界还有多少个载荷字(0~511)
212+
* @param{number} messageID - 消息辨识ID(0~4095)
213+
* @param{number} SerialNumber - 消息序号(0~4095)
214+
* @param{boolean} UseAONT - 本段消息是否使用了AONT(全有或全无转换)
215+
* @param{string} key - 明文密钥,若提供则自动执行 AES-CTR 局部加密
216+
* @param{number} iv - 14bit的初始向量 (0~16383)
217+
* @returns{Uint8Array} 一个字节数组,长度为6。
218+
*/
219+
export function packFlexibleTransferConfig(
220+
lengthToBoundary,
221+
messageID,
222+
SerialNumber,
223+
UseAONT,
224+
key = null,
225+
iv = 0
226+
) {
227+
// 1. 安全掩码截断:防止外部传入的数值过大导致位溢出
228+
const len = lengthToBoundary & 0x1ff; // 9 bits (最大 511)
229+
const msg = messageID & 0xfff; // 12 bits (最大 4095)
230+
const ser = SerialNumber & 0xfff; // 12 bits (最大 4095)
231+
const aont = UseAONT ? 1 : 0; // 1 bit
232+
233+
const safeIv = iv & 0x3fff; // 14 bits (最大 16383)
234+
235+
// 2. 避免 JS 32位位移溢出,将 48 位切割为高 24 位与低 24 位
236+
const high24 = (len << 15) | (msg << 3) | (ser >> 9);
237+
const low24 = ((ser & 0x1ff) << 15) | (aont << 14) | safeIv;
238+
239+
// 3. 将 24 位区块映射为 Uint8Array (大端序 Big-Endian)
240+
const buffer = new Uint8Array(6);
241+
buffer[0] = (high24 >> 16) & 0xff;
242+
buffer[1] = (high24 >> 8) & 0xff;
243+
buffer[2] = high24 & 0xff;
244+
buffer[3] = (low24 >> 16) & 0xff;
245+
buffer[4] = (low24 >> 8) & 0xff;
246+
buffer[5] = low24 & 0xff;
247+
248+
// 4. CryptoJS 局部加密逻辑 (若传入了 KEY)
249+
if (key) {
250+
// 【步骤 A】:派生 AES 的实际加密 Key -> SHA256(KEY)
251+
const hash1 = CryptoJS.SHA256(key);
252+
253+
// 【步骤 B】:派生实际的 IV (CTR 计数器块)
254+
// 14bit 扩充填充到 16bit (2字节)
255+
const ivByte0 = (safeIv >> 8) & 0xff;
256+
const ivByte1 = safeIv & 0xff;
257+
258+
// 手动将 2 字节转换为 CryptoJS 的 WordArray 格式,避免版本兼容问题
259+
const ivWord = (ivByte0 << 24) | (ivByte1 << 16);
260+
const ivWordArr = CryptoJS.lib.WordArray.create([ivWord], 2);
261+
262+
// actualIV = SHA256( Hash1 + 2字节的IV )
263+
const concatData = hash1.clone().concat(ivWordArr);
264+
const actualIV = CryptoJS.SHA256(concatData);
265+
266+
// 【步骤 C】:生成 CTR 密钥流 (Keystream)
267+
// 截取 actualIV 的前 16 字节(128 bit)作为 AES 的计数器块
268+
const counterBlock = CryptoJS.lib.WordArray.create(
269+
actualIV.words.slice(0, 4),
270+
16
271+
);
272+
273+
// 用 hash1 (第一层哈希) 作密钥,以 ECB 模式加密 counterBlock,
274+
// 这在密码学上等价于输出了 CTR 模式的第一块密钥流。
275+
const encryptedBlock = CryptoJS.AES.encrypt(counterBlock, hash1, {
276+
mode: CryptoJS.mode.ECB,
277+
padding: CryptoJS.pad.NoPadding,
278+
});
279+
280+
// 将 CryptoJS 的 WordArray 输出无损转换为标准的 Uint8Array
281+
const keystream = new Uint8Array(16);
282+
const words = encryptedBlock.ciphertext.words;
283+
for (let i = 0; i < 16; i++) {
284+
keystream[i] = (words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff;
285+
}
286+
287+
// 【步骤 D】:掩码异或,精准加密前 34 bits
288+
buffer[0] ^= keystream[0];
289+
buffer[1] ^= keystream[1];
290+
buffer[2] ^= keystream[2];
291+
buffer[3] ^= keystream[3];
292+
// 0xC0 = 11000000,精准异或高 2 位 (载荷),保留低 6 位和第 5 字节的 8 位(合计 14bit IV) 绝对明文
293+
buffer[4] ^= keystream[4] & 0xc0;
294+
}
295+
296+
return buffer;
297+
}
298+
299+
/**
300+
* 工具函数
301+
*
302+
* 将四个分段传输参数(合共48位,6字节)逆向拆包
303+
* 用于将 6 字节的 Uint8Array 还原为具体的配置参数
304+
*
305+
* @param {Uint8Array} buffer - 长度为 6 的字节数组 (需为解密后的数据)
306+
*/
307+
function unpackFlexibleTransferConfig(buffer) {
308+
if (buffer.length !== 6) throw new Error("Buffer must be exactly 6 bytes");
309+
310+
// 1. 避免 JS 32位带符号整数溢出,将 6 字节分为高 24 位与低 24 位分别合并
311+
const high24 = (buffer[0] << 16) | (buffer[1] << 8) | buffer[2];
312+
const low24 = (buffer[3] << 16) | (buffer[4] << 8) | buffer[5];
313+
314+
// 2. 按约定的位宽与偏移量逐个提取字段
315+
// [Length 9位]:提取 high24 的前 9 位 (右移 15 位,掩码 0x1FF)
316+
const lengthToBoundary = (high24 >> 15) & 0x1ff;
317+
318+
// [MessageID 12位]:提取 high24 的中间 12 位 (右移 3 位,掩码 0xFFF)
319+
const messageID = (high24 >> 3) & 0xfff;
320+
321+
// [SerialNumber 12位]:跨越了高低 24 位的分界线,需拆解并重新缝合
322+
const serHigh3 = high24 & 0x7; // 截取 high24 剩余的低 3 位
323+
const serLow9 = (low24 >> 15) & 0x1ff; // 截取 low24 开头的高 9 位
324+
const SerialNumber = (serHigh3 << 9) | serLow9; // 重新拼装为 12 位的完整序号
325+
326+
// [UseAONT 1位]:提取 low24 中紧接其后的 1 位
327+
const UseAONT = ((low24 >> 14) & 0x1) === 1;
328+
329+
// [IV 14位]:提取 low24 最后剩余的 14 位 (掩码 0x3FFF)
330+
const iv = low24 & 0x3fff;
331+
332+
return { lengthToBoundary, messageID, SerialNumber, UseAONT, iv };
333+
}
334+
335+
/**
336+
* 工具函数
337+
*
338+
* 获取TOTP加密时候,十六个步长选项对应的实际步长(秒数)
339+
*
340+
* @param{number} key 整数(0~15)
341+
*
342+
*/
197343
export function getStep(key) {
198344
let second = 0;
199345
/* v8 ignore next 50 */
@@ -258,7 +404,6 @@ export class ValueNoise1D {
258404
* 在一些不适合纯随机分布的情况下适用。
259405
*
260406
* **/
261-
262407
constructor(seed = Math.random()) {
263408
this.seed = seed;
264409
}

src/javascript/main.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export interface AdvancedEncConfig {
4444
//分段加密和分段传输的配置。
4545
//每段密文单独执行高级加密,但是,AONT在高级加密之后单独执行。
4646
export interface FlexibleTransferConfig {
47-
/** 指定是否启用灵活传输功能,默认 false/不开启,想要启用此项,必须启用高级加密; */
47+
/** 指定是否启用灵活传输功能,默认 false/不开启 */
4848
Enable?: boolean;
4949
/** 指定是否启用全有或全无转换(AONT),默认 true/开启,开启后必须获得所有密文才可以解密完整内容,但是会导致密文变长,解密速度变缓慢*/
5050
UseAONT?: boolean;

0 commit comments

Comments
 (0)