npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cmtx/fpe-wasm

v0.1.1-alpha.2

Published

NIST SP 800-38G FF1 format-preserving encryption (WASM)

Readme

@cmtx/fpe-wasm

npm version License

NIST SP 800-38G FF1 Format-Preserving Encryption (WASM)

1. 概述

该包提供 NIST SP 800-38G 标准的 FF1 格式保留加密算法的 WebAssembly 实现,基于 Rust fpe crate。

2. 安装

pnpm add @cmtx/fpe-wasm

3. 使用

import { FF1Cipher, encrypt_string, decrypt_string } from "@cmtx/fpe-wasm";

// 创建加密器(32 字节 AES-256 密钥)
const key = new Uint8Array(32);
// ... 填充密钥

const cipher = new FF1Cipher(key, 36); // radix = 36 (0-9, A-Z)

// 加密字符串
const encrypted = encrypt_string(cipher, "ABC123");
console.log("Encrypted:", encrypted);

// 解密字符串
const decrypted = decrypt_string(cipher, encrypted);
console.log("Decrypted:", decrypted);

4. API

4.0. 懒加载机制

本包采用懒加载 + Promise 缓存机制。首次使用加密功能时会自动加载 WASM 模块,无需手动调用 loadWASM()。如需提前加载或自定义 WASM 源,可使用 loadWASM()(幂等,多次调用只加载一次)。

4.1. FF1Cipher

FF1 加密器类。

4.1.1. constructor(key: Uint8Array, radix: number)

创建新的 FF1 加密器。

  • key: 32 字节 AES-256 密钥
  • radix: 进制基数 (2-36)

4.1.2. encrypt(plaintext: Uint8Array): Uint8Array

加密字节数组。

4.1.3. decrypt(ciphertext: Uint8Array): Uint8Array

解密字节数组。

4.2. encrypt_string(cipher: FF1Cipher, plaintext: string): string

加密 radix-36 字符串。

4.3. decrypt_string(cipher: FF1Cipher, ciphertext: string): string

解密 radix-36 字符串。

4.4. 便捷函数

| 函数 | 说明 | |------|------| | encryptString(key, plaintext, radix?) | 创建临时 cipher 并加密 | | decryptString(key, ciphertext, radix?) | 创建临时 cipher 并解密 | | createFF1Cipher(key, radix?) | 工厂函数,创建 FF1Cipher 实例 | | prepareFPEKey(key) | 密钥预处理 |

4.5. WASM 管理

| 函数 | 说明 | |------|------| | loadWASM(options?) | 加载 WASM 模块(可选,懒加载自动处理) | | isWasmLoaded() | 检查 WASM 是否已加载 |

5. 构建

需要安装 Rust 和 wasm-pack:

# 安装 Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 安装 wasm-pack
cargo install wasm-pack

# 构建
cd packages/fpe-wasm
pnpm build

6. 测试

6.1. 测试概述

本包包含两层测试:

  • Rust 层测试:使用 wasm-bindgen-test 测试 WASM 核心功能(17 tests)
  • TypeScript 层测试:使用 vitest 测试 JavaScript API 封装(70 tests)

6.2. 运行测试

6.2.1. 运行所有测试

pnpm -F @cmtx/fpe-wasm test

6.2.2. 仅运行 Rust 测试

pnpm -F @cmtx/fpe-wasm run test:wasm

6.2.3. 仅运行 TypeScript 测试

pnpm -F @cmtx/fpe-wasm run test:ts

6.2.4. 运行特定测试文件

# 运行单个 TypeScript 测试文件
pnpm -F @cmtx/fpe-wasm run test:ts -- tests/ff1-cipher.test.ts

# 运行匹配模式的测试
pnpm -F @cmtx/fpe-wasm run test:ts -- --testNamePattern="encrypt"

6.3. 测试文件结构

packages/fpe-wasm/
├── src/lib.rs                 # Rust 源码 + 内联测试 (17 tests)
└── tests/                     # TypeScript 测试目录 (70 tests)
    ├── vitest.setup.ts        # WASM 加载初始化
    ├── wasm-loader.test.ts    # WASM 加载器测试 (6 tests)
    ├── ff1-cipher.test.ts     # FF1Cipher 类测试 (17 tests)
    ├── string-encryption.test.ts  # 字符串加密测试 (20 tests)
    ├── crypto-helpers.test.ts     # 辅助函数测试 (21 tests)
    └── compat.test.ts         # 兼容层测试 (6 tests)

6.4. 测试命名规范

6.4.1. Rust 测试命名

  • 函数命名格式:test_<功能描述>
  • 使用 #[wasm_bindgen_test] 宏标记
  • 示例:test_encrypt_decrypt_string, test_invalid_key_length

6.4.2. TypeScript 测试命名

  • 文件命名格式:<模块名>.test.ts
  • 使用 describe 分组,it 定义测试用例
  • 示例:
describe("FF1Cipher", () => {
    describe("constructor", () => {
        it("should create cipher with 32-byte key and radix 36", () => {
            // ...
        });
    });
});

6.5. 手动测试示例

6.5.1. 测试加密解密功能

import { loadWASM, FF1Cipher, prepareFPEKey, encrypt_string, decrypt_string } from "@cmtx/fpe-wasm";

// 1. 加载 WASM(必须先执行)
await loadWASM();

// 2. 准备密钥(字符串自动填充至 32 字节)
const key = prepareFPEKey("my-secret-key-32-bytes!!");

// 3. 创建加密器
const cipher = new FF1Cipher(key, 36); // radix = 36 (0-9, A-Z)

// 4. 测试字符串加密
const plaintext = "ABC123";
const encrypted = encrypt_string(cipher, plaintext);
const decrypted = decrypt_string(cipher, encrypted);

console.log("Plaintext:", plaintext);
console.log("Encrypted:", encrypted);
console.log("Decrypted:", decrypted);
console.log("Match:", plaintext === decrypted); // true
console.log("Length preserved:", encrypted.length === plaintext.length); // true

6.5.2. 测试字节数组加密

import { loadWASM, FF1Cipher } from "@cmtx/fpe-wasm";

await loadWASM();

const key = new Uint8Array(32).fill(42);
const cipher = new FF1Cipher(key, 36);

const plaintext = new Uint8Array([1, 2, 3, 4, 5, 6]);
const encrypted = cipher.encrypt(plaintext);
const decrypted = cipher.decrypt(encrypted);

console.log(
    "Match:",
    decrypted.every((v, i) => v === plaintext[i]),
); // true

6.5.3. 测试不同进制

import { loadWASM, FF1Cipher, encrypt_string, decrypt_string } from "@cmtx/fpe-wasm";

await loadWASM();

const key = new Uint8Array(32).fill(0);

// radix = 10 (仅数字 0-9)
const cipher10 = new FF1Cipher(key, 10);
const digits = "123456";
const enc10 = encrypt_string(cipher10, digits);
console.log("radix-10:", enc10); // 仅包含数字

// radix = 16 (十六进制 0-9, A-F)
const cipher16 = new FF1Cipher(key, 16);
const hex = "ABCDEF";
const enc16 = encrypt_string(cipher16, hex);
console.log("radix-16:", enc16); // 仅包含 0-9, A-F

6.6. 集成测试

本包被 @cmtx/rule-engine 包集成使用,相关集成测试位于:

# 运行集成测试
pnpm -F @cmtx/rule-engine run test

主要集成测试文件:

  • packages/rule-engine/tests/encrypted-id.test.ts - FF1 加密 ID 生成器测试(45 tests)
  • packages/rule-engine/tests/id-validator.test.ts - ID 验证测试(8 tests)

6.7. 测试覆盖率

| 测试类型 | 数量 | 覆盖范围 | | ---------------------- | ---- | ------------------------------------- | | Rust wasm-bindgen-test | 17 | FF1Cipher 核心、加密解密、错误处理 | | TypeScript vitest | 70 | WASM 加载、API 封装、辅助函数、兼容层 | | 集成测试 | 115 | 实际使用场景、ID 生成、验证 |


7. 安全性

本实现严格遵循 NIST SP 800-38G 标准,使用 AES-256 作为底层分组密码。

8. 许可证

Apache-2.0