eq-safe-login-crypto
v0.1.0
Published
eq_safe_login_crypto 的 WASM + TypeScript 包装:SHA-512 / Argon2 / Ed25519 / ML-DSA
Readme
eq-safe-login-crypto-wasm-wrappers
将 eq_safe_login_crypto(Rust no_std 密码学原语库)编译为 WASM,并提供完整 TypeScript 包装器的工程包。
- Rust 侧(
src/lib.rs):把eq_safe_login_crypto的 SHA-512 / HMAC-SHA512 / HKDF-SHA512 / Argon2 / Ed25519 / ML-DSA 通过#[unsafe(no_mangle)] extern "C"导出为可被 JS 调用的 FFI。 - JS 侧(
wrappers/):在 FFI 之上提供两层 API —— 同步包装器与异步包装器。对外提供的产物是异步包装器,仅支持浏览器;异步包装器是对同步包装器的进一步封装。 - 构建脚本(
build.mjs):用 esbuild 产出异步包装器的ESM/IIFE两种发布形态。
算法覆盖
| 模块 | 算法 | 标准 | 关键导出 |
| ----------- | ---------------------------- | ------------- | -------------------------------------------------- |
| SHA-512 | SHA-512 | FIPS 180-4 | sha512(data) / createSha512() 增量哈希 |
| HMAC-SHA512 | HMAC-SHA512 | RFC 2104 | createHmacSha512(key) 增量 MAC |
| HKDF-SHA512 | HKDF-SHA512 Expand | RFC 5869 §2.3 | hkdfSha512Expand(prk, info, len) |
| Argon2 | Argon2d / Argon2i / Argon2id | RFC 9106 | argon2Hash(params) |
| Ed25519 | Ed25519 | RFC 8032 | ed25519PublicFromSeed / ed25519Sign / verify |
| ML-DSA | ML-DSA-44 / -65 / -87 | FIPS 204 | mldsaKeygen / mldsaSign / mldsaVerify |
API 设计
异步包装器(async/)
- 所有方法返回
Promise,计算在 Web Worker 中进行,不阻塞主线程。 - 工厂
loadAsync(options?)加载 WASM、启动 Worker、返回AsyncModule。 - 适用于浏览器主线程。
- 增量 SHA-512 通过
AsyncSha512暴露,实际状态保存在 Worker,主线程持有句柄 id。 - 增量 HMAC-SHA512 通过
AsyncHmacSha512暴露,模式与AsyncSha512一致。
import { loadAsync } from "./dist/index.min.mjs";
const client = await loadAsync();
const hash = await client.sha512(new TextEncoder().encode("abc"));
const hasher = await client.createSha512();
await hasher.update(chunk1);
await hasher.update(chunk2);
const digest = await hasher.finalize();
// HMAC-SHA512 增量
const hmac = await client.createHmacSha512(key);
await hmac.update(data);
const tag = await hmac.finalize();
// HKDF-SHA512 派生独立子密钥
const edSeed = await client.hkdfSha512Expand(
prk,
new TextEncoder().encode("ed25519-seed"),
32,
);
const mldsaSeed = await client.hkdfSha512Expand(
prk,
new TextEncoder().encode("mldsa-seed"),
32,
);
client.destroy(); // 终止 Worker,reject 所有 pending Promise资源管理约定
| 资源 | 创建 | 释放 |
| ----------------- | ---------------------------- | ------------------------------------ |
| AsyncModule | loadAsync() | destroy()(terminate Worker) |
| AsyncSha512 | client.createSha512() | 必须调用 finalize() 或 destroy() |
| AsyncHmacSha512 | client.createHmacSha512(k) | 必须调用 finalize() 或 destroy() |
调用 destroy() 后再使用相应实例会抛出异常;destroy() 幂等。
构建
前置要求
- Rust 工具链 +
wasm32-unknown-unknowntarget(项目根.cargo/config.toml已设默认 target) - Node.js ≥ 18
- npm / pnpm
构建命令
pnpm install
# 1. 仅构建 WASM(cargo build --release + 复制 .wasm 到 dist/)
pnpm build:wasm
# 2. 仅构建 TS 包装器(需要先有 dist/index.wasm)
pnpm build:wrappers
# 3. 一步构建 WASM + 包装器
pnpm build构建产物(dist/)
| 文件 | 说明 |
| ---------------------- | -------------------------------------------------- |
| index.wasm | WASM 模块 |
| worker.min.js | Worker(IIFE,经典 Worker) |
| index.min.js | 主线程入口(IIFE,<script>) |
| index-bundled.min.js | 单文件版本(IIFE,内联 WASM + Worker,零外部文件) |
| worker.min.mjs | Worker(ESM,模块 Worker) |
| index.min.mjs | 主线程入口(ESM,<script type="module">) |
| inline-wa.mjs | WASM 内联加载器(ESM,把 WASM base64 编码) |
如果使用 IIFE 方式加载模块,需要: index.wasm、 worker.min.js、 index.min.js。
如果使用 ESM 方式加载模块,需要: index.wasm、 worker.min.mjs、 index.min.mjs。
如果不方便设置 wasm 文件的 MIME 类型,有两种解决方案:
直接以 IIFE 方式加载
index-bundled.min.js,无需任何其它文件,WASM 和 Worker 都内嵌在脚本里。以 ESM 方式加载模块,但是额外准备
inline-wa.mjs,在 loadAsync 前先加载 WASM 模块:import { loadAsync } from "./dist/index.min.mjs"; import { loadInlineWASM } from "./dist/inline-wa.mjs"; const wa = await loadInlineWASM(); const client = await loadAsync({ wasmModule: wa }); // ...
浏览器中使用的三种方式
1. ESM(推荐用于现代浏览器)
<script type="module">
import { loadAsync } from "./dist/index.min.mjs";
const client = await loadAsync();
// ...
</script>需要同目录下存在 index.wasm 和 worker.min.mjs。
2. IIFE(用于传统 <script>)
<script src="./dist/index.min.js"></script>
<script>
EQSafeLoginCrypto.loadAsync().then((client) => {
// ...
});
</script>需要同目录下存在 index.wasm 和 worker.min.js。
3. 单文件部署(内联,零外部依赖)
<script src="./dist/index-bundled.min.js"></script>
<script>
EQSafeLoginCrypto.loadAsync().then((client) => {
// ...
});
</script>无需任何额外文件,WASM 和 Worker 都内联在脚本里。
开发
pnpm typecheck # tsc --noEmit
pnpm test # vitest run(需要先 pnpm build:wasm)
pnpm test:watch # vitest watch 模式
pnpm e2e # 启动 http://localhost:8765 浏览器端到端测试单元测试位于 wrappers-tests,使用 vitest 运行。
E2E 测试位于 e2e/index.html,在真实浏览器中验证两种发布形态(IIFE / ESM)以及 wasm 的两种加载方式,覆盖 SHA-512 / HMAC-SHA512 / HKDF-SHA512 / Argon2 / Ed25519 / ML-DSA 全部算法,并通过页面上的高精度时钟直观验证 Argon2 计算不阻塞主线程。
依赖关系
- Rust 侧依赖上级目录的
eq_safe_login_cryptocrate - JS 侧零运行时依赖
