Skip to content

Commit fcf7315

Browse files
committed
Update AONT Tool Functions
The function is verified.
1 parent 6241a35 commit fcf7315

4 files changed

Lines changed: 145 additions & 1 deletion

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ dist
1212
demo.zip
1313
text.html
1414
coverage
15+
temptests.test.js
1516

1617
/docs/.vitepress/cache

src/javascript/CoreHandler.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export class WenyanConfig {
5353

5454
export class FlexibleTransferConfig {
5555
/**
56-
* 魔曰 高级加密参数
56+
* 魔曰 灵活传输参数
5757
*
5858
* @param{bool}Enable 指定是否启用灵活传输功能,默认 false/不开启
5959
* @param{bool}UseAONT 指定是否启用全有或全无转换(AONT),默认 true/开启,开启后必须获得所有密文才可以解密完整内容,但是会导致密文变长,解密速度变缓慢

src/javascript/EncryptHelper.js

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
Uint8ArrayTostring,
1818
GetRandomIndex,
1919
getStep,
20+
i2osp,
2021
} from "./Misc.js";
2122
import { pbkdf2 } from "@noble/hashes/pbkdf2.js";
2223
import { sha256 } from "@noble/hashes/sha2.js";
@@ -383,3 +384,128 @@ export function Decrypt(Data, key, AdvancedEncObj = null) {
383384
return Data;
384385
}
385386
}
387+
388+
/**
389+
* 基于 SHA-256 的 MGF1 实现
390+
*
391+
* @param {Uint8Array} seed - 输入的随机种子
392+
* @param {number} maskLen - 需要生成的掩码目标长度 (字节数)
393+
* @returns {Uint8Array} - 生成的指定长度的伪随机掩码
394+
*/
395+
function mgf1_sha256(seed, maskLen) {
396+
const hLen = 32; // SHA-256 的输出长度固定为 32 字节 (256 bits)
397+
398+
// RFC 8017 规定: 掩码长度不能超过 2^32 * hLen
399+
// 在 JS 中处理超大文件时要注意内存限制,通常不会触发这个上限
400+
if (maskLen > 2 ** 32 * hLen) {
401+
throw new Error("Mask too long");
402+
}
403+
404+
// 预先分配最终输出的内存空间
405+
const mask = new Uint8Array(maskLen);
406+
407+
// 计算需要循环调用哈希函数的次数 (向上取整)
408+
const iterations = Math.ceil(maskLen / hLen);
409+
410+
let offset = 0; // 记录当前已经填充到 mask 的字节位置
411+
412+
for (let counter = 0; counter < iterations; counter++) {
413+
// 1. 将计数器转换为 4 字节的大端序字节串 C
414+
const C = i2osp(counter);
415+
416+
// 2. 拼接 seed 和 C (即 RFC 中的 seed || C)
417+
const dataToHash = new Uint8Array(seed.length + 4);
418+
dataToHash.set(seed, 0);
419+
dataToHash.set(C, seed.length);
420+
421+
// 3. 计算拼接后数据的 SHA-256 哈希值
422+
// 【注意】这里调用你的自定义/高效 SHA-256 库
423+
const hashBlock = sha256(dataToHash);
424+
425+
// 4. 将哈希块拷贝到最终的 mask 数组中
426+
// 最后一次循环可能不需要完整的 32 字节,计算剩余需要的字节数
427+
const bytesToCopy = Math.min(hLen, maskLen - offset);
428+
429+
// 使用 subarray 提取需要的字节,并通过 set 写入目标位置
430+
mask.set(hashBlock.subarray(0, bytesToCopy), offset);
431+
432+
offset += bytesToCopy;
433+
}
434+
435+
return mask; // 输出生成的掩码流
436+
}
437+
438+
/**
439+
* 基于 SHA-256 的 AONT(全有或全无转换)
440+
*
441+
* @param {Uint8Array} Data 原始数据
442+
* @returns {Uint8Array} AONT处理后数据
443+
*/
444+
export function EnAONT(Data) {
445+
let Seed = new Uint8Array(32);
446+
447+
for (let i = 0; i < 32; i++) {
448+
//获取随机种子
449+
Seed[i] = GetRandomIndex(256);
450+
}
451+
452+
//生成掩码,使用mgf1算法,配合SHA256
453+
let Mask = mgf1_sha256(Seed, Data.byteLength);
454+
455+
// 将掩码和原始数据执行异或
456+
let MaskedData = new Uint8Array(Data.byteLength);
457+
458+
for (let i = 0; i < Data.byteLength; i++) {
459+
MaskedData[i] = Data[i] ^ Mask[i];
460+
}
461+
462+
//计算异或后数据的哈希
463+
let MaskedDataHash = sha256(MaskedData);
464+
465+
//将异或后数据的哈希,和种子本身执行异或,得到控制块。
466+
let ControlBlock = new Uint8Array(32);
467+
468+
for (let i = 0; i < Seed.byteLength; i++) {
469+
ControlBlock[i] = Seed[i] ^ MaskedDataHash[i];
470+
}
471+
472+
//把控制块和异或后数据拼接在一起。
473+
let Result = new Uint8Array(MaskedData.byteLength + ControlBlock.byteLength);
474+
475+
Result.set(MaskedData, 0);
476+
Result.set(ControlBlock, MaskedData.byteLength);
477+
478+
return Result;
479+
}
480+
481+
/**
482+
* 基于 SHA-256 的 AONT(全有或全无转换) 逆处理函数
483+
*
484+
* @param {Uint8Array} Data AONT处理后数据
485+
* @returns {Uint8Array} 原始数据
486+
*/
487+
export function DeAONT(Data) {
488+
let MaskedData = Data.subarray(0, -32);
489+
let ControlBlock = Data.subarray(-32);
490+
491+
//计算异或后数据的哈希
492+
//如果有任何一丁点数据没有成功传输,则此哈希是错误的,因此,还原出的消息种子也是错误的,因此,还原出的消息也是错误的。
493+
let MaskedDataHash = sha256(MaskedData);
494+
495+
//计算消息种子
496+
let Seed = new Uint8Array(32);
497+
for (let i = 0; i < Seed.byteLength; i++) {
498+
Seed[i] = ControlBlock[i] ^ MaskedDataHash[i];
499+
}
500+
501+
//计算掩码
502+
let Mask = mgf1_sha256(Seed, MaskedData.byteLength);
503+
504+
let Result = new Uint8Array(MaskedData.byteLength);
505+
506+
for (let i = 0; i < MaskedData.byteLength; i++) {
507+
Result[i] = MaskedData[i] ^ Mask[i];
508+
}
509+
510+
return Result;
511+
}

src/javascript/Misc.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,23 @@ function unpackFlexibleTransferConfig(buffer) {
331331
return { lengthToBoundary, messageID, SerialNumber, UseAONT, iv };
332332
}
333333

334+
/**
335+
* 标准整数到字节串的转换 (I2OSP) - 32 位大端序
336+
* 将循环的计数器转换为 4 个字节的 Uint8Array
337+
*
338+
* @param {number} counter - 当前的循环计数器
339+
* @returns {Uint8Array} - 4字节的大端序字节数组
340+
*/
341+
export function i2osp(counter) {
342+
const c = new Uint8Array(4);
343+
// 使用无符号右移 (>>>) 和位与 (&) 提取各个字节
344+
c[0] = (counter >>> 24) & 0xff;
345+
c[1] = (counter >>> 16) & 0xff;
346+
c[2] = (counter >>> 8) & 0xff;
347+
c[3] = counter & 0xff;
348+
return c;
349+
}
350+
334351
/**
335352
* 工具函数
336353
*

0 commit comments

Comments
 (0)