encryption-rust 1.1.1

可插拔密码学组件库的 Rust 实现:在统一契约下提供对称加密、非对称加密、哈希与密钥派生(HKDF / PBKDF2),包含 AES/Sodium 与国密 SM2/SM3/SM4/ZUC 等实现
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
<!-- Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz -->

# encryption-rust

[![crates.io](https://img.shields.io/crates/v/encryption-rust.svg)](https://crates.io/crates/encryption-rust)
[![docs.rs](https://img.shields.io/docsrs/encryption-rust)](https://docs.rs/encryption-rust)
[![CI](https://github.com/erikwang2013/encryption-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/erikwang2013/encryption-rust/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)

**语言 / Languages:** **简体中文** | [English]./docs/i18n/en/README.md

<p align="center">
  <img src="./docs/pet.svg" alt="项目宠物 Locky:一只拿着金色钥匙的小锁" width="150" height="150">
</p>

可插拔密码学组件库:在统一契约下提供**对称加密**、**非对称加密**、**哈希**、**密钥派生**(HKDF / PBKDF2),并包含 AES/Sodium、国密 SM2/SM3/SM4/ZUC 等实现,通过 Cargo 安装。

**Locky** 就是上面这只小锁,也是本项目的宠物:每个算法各持一把子密钥,每次加密都用新的 IV,钥匙孔从不泄露秘密。宿主项目也可以展示它:`pet::SVG` 返回 SVG 内容(可内联进 HTML),`pet::ASCII` 返回终端横幅,`pet::NAME` 即 `Locky`。

Rust 移植自 PHP 包 [`erikwang2013/encryption`](https://github.com/erikwang2013/encryption)。

---

## 项目宠物:Locky

<img src="./docs/pet.svg" alt="Locky —— encryption-rust 项目宠物" width="340">

一只握着金钥匙的小锁。人设不是装饰,是把本库的设计画出来了:

| 形象 | 对应设计 |
|------|---------|
| 锁孔(微笑) | Key / IV 纪律:钥匙孔从不透露自己见过什么,密钥与明文从不外泄 |
| 另一只手的金色钥匙 | 主密钥:`EncryptionManagerFactory::from_master_key` 从它派生各算法子密钥 |
| 一把锁只认自己的钥匙 | 认证加密:GCM / encrypt-then-MAC 解不开就是密钥不对或密文被改,没有第三种解释 |
| 每算法一份子密钥 | 同一主密钥下 AES 与 SM4/ZUC 互不共用密钥材料,避免密钥复用 |

座右铭:**钥匙孔从不泄露秘密。**

形象以 `include_str!` 打进库里([`src/pet.rs`](./src/pet.rs),零运行时开销,不用就不链接),`pet::ASCII` 可直接打进终端或日志:

```rust
use encryption::pet;

println!("{}", pet::ASCII);

//      .-------.
//      /       \
//   .-------------.
//   |  o       o  |
//   |             |
//   |     ,-.     |
//   |    (   )    |
//   |     '-'     |
//   |      |      |
//   '-------------'
```

`pet::NAME` / `pet::TAGLINE` / `pet::ASCII` / `pet::SVG` 四个常量对外公开,README、CLI banner、下游管理界面共用同一份。

宠物的整合点不止 README:**三张设计图右上角各有一枚 Locky 小图标**(纯内联 SVG,GitHub 上直接渲染),[`docs/social-preview.svg`](./docs/social-preview.svg) / `social-preview.png` 是宠物主视觉的 GitHub 社交预览图(1280×640,上传到仓库设置即可)。

`docs/pet.svg` **不能**进 Cargo 的 `exclude` —— `include_str!` 在编译期读它,排掉就当场编译失败(`cargo package` 会直接报错,不会静默漏发)。

---

## 项目说明

### 这是什么

`encryption-rust` 是一个纯 Rust 密码学组件库,为 Rust 应用提供**类型安全、可扩展**的加解密、哈希与密钥派生能力。它不与任何框架耦合,可在 axum、actix-web 等 Web 框架,或纯库、CLI 项目中独立使用。

### 解决什么问题

Rust 生态中密码学实现长期分散:AES 用 `aes-gcm`、流密码用 `chacha20poly1305`、国密散落在 sm2 / sm3 / sm4 / zuc 等各自独立的 crate,HKDF / PBKDF2 缺少统一接口。本库在**单一契约体系**下收敛了主流对称 / 非对称 / 哈希 / KDF 算法,使得:

- **业务层只需面对接口**,切换算法不改业务代码
- **国密与 AES/Sodium 同等待遇**,注册后通过同一 Manager 调用
- **密钥管理规范化**:一键从主密钥派生各算法子密钥,避免密钥复用
- **安全默认值**:认证加密(GCM / encrypt-then-MAC)、随机 IV、常量时间比较等开箱即用

### 适用场景

- 字段级加解密(手机号、身份证号等敏感字段入库前加密)
- 多算法并存与平滑迁移(如从 AES-256-CBC 迁移到 AES-256-GCM)
- 需要国密合规的中后台系统(SM2 非对称、SM3 哈希、SM4 对称、ZUC 流密码)
- API 签名与验证(HMAC / SHA-256 / SM3)
- 基于口令或主密钥的子密钥派生(PBKDF2 / HKDF)

---

## 项目功能

- **五族能力,一套结构**:对称加密 / 非对称加密 / 哈希 / 密钥派生 / 口令派生 —— 每族都是「契约(trait)→ 注册表(Registry)→ 门面(Manager)」的同一形状,学会一族就会全部。
- **十个开箱标识**:`aes-256-gcm`、`sodium-xchacha20`、`aes-256-cbc-hmac`、`sm4-cbc`、`zuc-128`、`sm2`、`sha256`、`sm3`、`hkdf-sha256`、`pbkdf2-sha256`(与 PHP 版标识一致,迁移零改名成本)。
- **主密钥工厂**:`EncryptionManagerFactory::from_master_key` —— 一把 32 字节主密钥按用途标签派生各算法独立子密钥,一次装配全部算法,杜绝密钥复用。
- **请求守卫 Guard + 框架适配**:原生 Rust 与 axum / actix-web / rocket / poem / salvo / warp / bee-rust / e-cat 共 8 个 opt-in feature;处理器参数直接声明 `Guard` 即可加解密。
- **国密一等公民**:SM2 / SM3 / SM4 / ZUC 与 AES / SHA 走同一条注册-门面链路;SM1 / SM7 / SM9 给出明确错误而非静默。
- **安全默认值**:随机 IV / nonce、AEAD 或 encrypt-then-MAC、常量时间比对、密钥材质 Drop 清零、Debug 遮蔽、解密失败不区分原因。
- **纯 Rust、零扩展依赖**:没有 `ext-sodium` / `ext-gmp` 之类门槛;ZUC 已经 GB/T 33133.1 标准向量校验。
- **文档即设计图**:架构设计 / 功能设计 / 请求生命周期三张 SVG(中英文各一份),加上宠物 Locky。
- **零框架耦合**:核心库不依赖任何 Web 框架,框架集成全部可选、默认一个都不拉。

> 完整能力地图见下文[功能设计]#功能设计,分层结构见[架构概览]#架构概览。

## 目录

- [安装]#安装
- [快速开始]#快速开始
- [架构概览]#架构概览
- [功能设计]#功能设计
- [请求生命周期]#请求生命周期
- [内置算法与标识]#内置算法与标识
- [使用说明]#使用说明
- [框架集成(可选 feature)]#框架集成可选-feature
- [与原版的差异]#与原版的差异
- [项目目录]#项目目录
- [常见问题]#常见问题
- [安全建议]#安全建议
- [运行测试]#运行测试
- [参考原项目]#参考原项目
- [许可证]#许可证

---

## 安装

```bash
cargo add encryption-rust
```

```toml
[dependencies]
encryption-rust = "1.0"
```

## 快速开始

```rust
use encryption::factory::EncryptionManagerFactory;

// 生产环境请从环境变量 / KMS 读取随机 32 字节主密钥,并妥善备份。
let master_key: [u8; 32] = load_master_key();
let manager = EncryptionManagerFactory::from_master_key(&master_key, "aes-256-gcm")?;

// 字段级加密:密文是二进制,存 TEXT 列时自行 base64 / hex。
let stored = manager.encrypt(phone.as_bytes())?;
let phone = manager.decrypt(&stored)?;
```

完整可运行版本见 [`examples/quickstart.rs`](./examples/quickstart.rs):`cargo run --example quickstart` 会打印宠物 ASCII、加密 → 转文本 → 解密 → 篡改检测的完整往返。

## 架构概览

![架构设计:应用代码 → 门面 → 注册表 → 契约 → 实现;外加主密钥工厂与请求守卫](./docs/architecture-design.svg)

图源文件:[`docs/architecture-design.svg`](./docs/architecture-design.svg)

能力被拆成五组契约,每组一套「契约(trait)→ 注册表(Registry)→ 门面(Manager)」;`EncryptionManagerFactory` 从一把主密钥派生全部子密钥并装配对称注册表。

| 能力 | 契约(trait) | 注册表 | 门面(默认算法) |
|------|--------------|--------|------------------|
| 对称加密 | `SymmetricCipher` | `Registry<Box<dyn SymmetricCipher>>` | `EncryptionManager`(`aes-256-gcm`) |
| 非对称加密 | `AsymmetricCipher` | `Registry<Box<dyn AsymmetricCipher>>` | `AsymmetricCryptoManager`(`sm2`) |
| 哈希 | `Hasher` | `Registry<Box<dyn Hasher>>` | `HashingManager`(`sha256`) |
| 密钥派生(IKM) | `KeyDerivation` | `Registry<Box<dyn KeyDerivation>>` | `KeyDerivationManager`(`hkdf-sha256`) |
| 口令派生 | `PasswordBasedKdf` | `Registry<Box<dyn PasswordBasedKdf>>` | `PasswordBasedKdfManager`(`pbkdf2-sha256`) |

- **对称**:实例绑定固定密钥;载荷为二进制,适合字段级批量加密。
- **非对称**:每次调用传入密钥材料(十六进制,格式由实现约定,如 SM2)。
- **哈希**:单向摘要,无密钥;`digest` 返回字节,`digest_hex` 返回十六进制。
- **密钥派生**:HKDF 把高熵材料展开为子密钥;PBKDF2 拉伸人类口令(随机盐 + 高迭代数)。
- **子密钥**:`HMAC-SHA256(key = 主密钥, msg = 用途标签)`;SM4 / ZUC 取前 16 字节。
- **五个契约全部 `Send + Sync`**:实现可跨线程共享,请求守卫得以零密钥拷贝地克隆。

## 功能设计

![功能设计:六族能力 + 设计原则 + 安全默认值 + 扩展路径](./docs/functional-design.svg)

图源文件:[`docs/functional-design.svg`](./docs/functional-design.svg)

| 能力族 | 门面 | 随 crate 交付的标识 |
|--------|------|---------------------|
| 对称加密 | `EncryptionManager` | `aes-256-gcm` / `sodium-xchacha20` / `aes-256-cbc-hmac` / `sm4-cbc` / `zuc-128` |
| 非对称加密 | `AsymmetricCryptoManager` | `sm2`(另有静态门面 `Sm2Service`) |
| 哈希 | `HashingManager` | `sha256` / `sm3`(均 32 字节) |
| 密钥派生(IKM) | `KeyDerivationManager` | `hkdf-sha256`(RFC 5869) |
| 口令派生 | `PasswordBasedKdfManager` | `pbkdf2-sha256`(默认 310,000 次迭代) |
| 国密 | 同上五个门面 | `sm2` / `sm3` / `sm4-cbc` / `zuc-128`;SM1 / SM7 / SM9 → 明确错误 |

设计原则、安全默认值与「新增一个算法」的四步路径见上图右侧三块;每个算法的细节(密钥长度、载荷结构)见下文[内置算法与标识](#内置算法与标识)。

## 请求生命周期

![请求生命周期:配置 → 派生 → 注册 → 加密 → 落盘 → 解析 → 校验 → 解密;另含轮换与失败模式](./docs/lifecycle.svg)

图源文件:[`docs/lifecycle.svg`](./docs/lifecycle.svg)

写入路径(每进程引导一次,随后的 encrypt / decrypt 是每请求热路径):

1. **配置**:32 字节随机主密钥来自环境变量 / KMS,绝不写进代码;
2. **派生**:工厂按用途标签为每个算法派生子密钥,密钥永不跨算法复用;
3. **注册**:实现按自己的 `identifier()` 注册进注册表,未知标识明确报错;
4. **加密**:每次调用生成新的随机 IV / nonce,认证加密或 encrypt-then-MAC 打包为 `v1 | IV | Tag/MAC | 密文`。

读取路径:

5. **落盘**:二进制入 BLOB 列,文本存储先转 base64;
6. **解析**:记录旁的标识决定用哪个实现 —— 切换默认算法后旧数据照常解密;
7. **校验**:先查前缀与长度,再以常量时间比对 Tag / MAC,任何字节被改都在这里露馅;
8. **解密**:明文返回调用方;失败时不区分原因、不向客户端泄露任何细节。

密钥轮换、迁移与全部失败变体见图中底部的「维护与失败模式」。

## 内置算法与标识

### 对称加密(`SymmetricCipher`)

| 标识 | 类型 | 密钥长度 | 载荷 |
|------|------|----------|------|
| `aes-256-gcm` | `Aes256GcmEncryptor` | 32 字节 | `v1 \| IV(12) \| Tag(16) \| 密文`,AES-GCM 认证加密,推荐默认 |
| `sodium-xchacha20` | `SodiumXChaCha20Encryptor` | 32 字节 | `v1 \| Nonce(24) \| 密文‖Tag(16)`,XChaCha20-Poly1305,纯 Rust、恒可用 |
| `aes-256-cbc-hmac` | `Aes256CbcEncryptor` | 32 字节 | `v1 \| IV(16) \| MAC(32) \| 密文`,CBC + HMAC,兼容旧环境 |
| `sm4-cbc` | `Sm4CbcEncryptor` | 16 字节 | 同 CBC-HMAC 结构,国密 SM4 |
| `zuc-128` | `Zuc128Encryptor` | 16 字节 | 同 CBC-HMAC 结构,ZUC-128 流密码与密钥流异或 |

### 非对称加密(`AsymmetricCipher`)

| 标识 | 类型 | 说明 |
|------|------|------|
| `sm2` | `Sm2AsymmetricCipher` / `Sm2Service` | 国密 SM2;密钥与密文为十六进制,密文布局 C1C3C2 |

### 哈希(`Hasher`)

| 标识 | 类型 | 输出长度 |
|------|------|----------|
| `sha256` | `Sha256Hasher` | 32 字节 |
| `sm3` | `Sm3Hasher` | 32 字节 |

### 密钥派生

| 标识 | 类型 | 契约 | 说明 |
|------|------|------|------|
| `hkdf-sha256` | `HkdfSha256` | `KeyDerivation` | RFC 5869,基于 IKM + salt + info |
| `pbkdf2-sha256` | `Pbkdf2Sha256` | `PasswordBasedKdf` | 口令 + 盐 + 迭代次数(默认 310,000,构造参数可调) |

SM1、SM7、SM9 不提供实现:调用 `guomi::unavailable::sm1()` 等会得到明确的 `UnsupportedNationalAlgorithm` 错误,便于业务层统一捕获或接入厂商 SDK / 加密机。

## 使用说明

### 1. 对称加密:单算法与注册表

```rust
use encryption::encryptor::Aes256GcmEncryptor;
use encryption::manager::EncryptionManager;
use encryption::registry::Registry;
use encryption::contract::SymmetricCipher;

// 单算法
let key = [7u8; 32];
let encryptor = Aes256GcmEncryptor::new(key);
let blob = encryptor.encrypt(b"\xe6\x98\x8e\xe6\x96\x87")?;
let plaintext = encryptor.decrypt(&blob)?;

// 注册表 + 门面
let mut registry = Registry::new("symmetric cipher");
registry.register(Box::new(encryptor));
let manager = EncryptionManager::new(registry, "aes-256-gcm")?;
let blob = manager.encrypt(b"\xe6\x95\xb0\xe6\x8d\xae")?;
```

主密钥工厂:`EncryptionManagerFactory::from_master_key(&master_key32, "aes-256-gcm")` 一次注册 **aes-256-gcm**、**aes-256-cbc-hmac**、**sodium-xchacha20**、**sm4-cbc**、**zuc-128** 五个算法,各用按用途标签派生的独立子密钥。

### 2. 非对称加密(SM2)

```rust
use encryption::guomi::{Sm2AsymmetricCipher, Sm2Service};
use encryption::manager::AsymmetricCryptoManager;
use encryption::registry::Registry;

let pair = Sm2Service::generate_key_pair_hex()?;   // 私钥 64 位 hex,公钥 130 位 hex(04‖X‖Y)

let cipher = Sm2AsymmetricCipher::new();
let ciphertext_hex = cipher.encrypt(b"\xe6\x98\x8e\xe6\x96\x87", &pair.public_key_hex)?;
let plaintext = cipher.decrypt(&ciphertext_hex, &pair.private_key_hex)?;

let mut registry = Registry::new("asymmetric cipher");
registry.register(Box::new(cipher));
let manager = AsymmetricCryptoManager::new(registry, "sm2")?;
let ciphertext_hex = manager.encrypt(b"\xe6\x98\x8e\xe6\x96\x87", &pair.public_key_hex)?;
```

公钥接受 130 位(`04‖X‖Y`)、128 位(`X‖Y`,自动补 `04`)与 66 位(压缩点)三种十六进制写法。

### 3. 哈希

```rust
use encryption::guomi::Sm3Hasher;
use encryption::hash::Sha256Hasher;
use encryption::manager::HashingManager;
use encryption::registry::Registry;

let mut registry = Registry::new("hasher");
registry.register(Box::new(Sha256Hasher::new()));
registry.register(Box::new(Sm3Hasher::new()));

let hashing = HashingManager::new(registry, "sha256")?;
let digest = hashing.digest(b"\xe6\x95\xb0\xe6\x8d\xae")?;         // 32 字节
let hex = hashing.digest_hex_with("sm3", b"\xe6\x95\xb0\xe6\x8d\xae")?; // 十六进制
```

### 4. 密钥派生(HKDF / PBKDF2)

```rust
use encryption::kdf::{HkdfSha256, Pbkdf2Sha256};
use encryption::manager::{KeyDerivationManager, PasswordBasedKdfManager};
use encryption::registry::Registry;

// 从高熵材料派生子密钥(如信封加密中的子密钥)
let hkdf = HkdfSha256::new();
let sub_key = hkdf.derive(&ikm32, &salt, 32, b"app:v1")?;

let mut registry = Registry::new("key derivation");
registry.register(Box::new(hkdf));
let kdf = KeyDerivationManager::new(registry, "hkdf-sha256")?;

// 从用户口令派生密钥(存储密码哈希请改用 Argon2 等专用 API)
let pbkdf2 = Pbkdf2Sha256::with_default_iterations();   // 310,000 次,可 new(n) 调整
let derived = pbkdf2.derive_from_password(b"\xe5\x8f\xa3\xe4\xbb\xa4", &salt16, 32)?;

let mut registry = Registry::new("password-based kdf");
registry.register(Box::new(pbkdf2));
let pwd = PasswordBasedKdfManager::new(registry, "pbkdf2-sha256")?;
```

### 5. 国密(SM3 / SM4 / ZUC / SM2)

```rust
use encryption::guomi::{Sm2Service, Sm3Hasher, Sm4CbcEncryptor, Zuc128Encryptor};
use encryption::contract::{Hasher, SymmetricCipher};

let digest = Sm3Hasher::new().digest(b"\xe6\x95\xb0\xe6\x8d\xae")?;

let sm4 = Sm4CbcEncryptor::new([1u8; 16]);
let blob = sm4.encrypt(b"\xe6\x98\x8e\xe6\x96\x87")?;

let zuc = Zuc128Encryptor::new([2u8; 16]);
let blob = zuc.encrypt(b"\xe6\x98\x8e\xe6\x96\x87")?;

// SM2 见上文非对称示例。
```

### 6. 自定义插件

实现对应契约再注册即可,其余逻辑(门面、默认算法切换、错误语义)全部复用:

- 对称:实现 `SymmetricCipher`,注册到对称注册表;
- 非对称:实现 `AsymmetricCipher`,注册到非对称注册表;
- 哈希:实现 `Hasher`(只需实现 `digest`,`digest_hex` 有默认实现);
- KDF:实现 `KeyDerivation` 或 `PasswordBasedKdf`,注册到对应注册表。

```rust
use encryption::contract::{Identified, SymmetricCipher};
use encryption::error::Result;

struct MyCipher { /* ... */ }

impl Identified for MyCipher {
    fn identifier(&self) -> &str { "my-cipher" }
}

impl SymmetricCipher for MyCipher {
    fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> { /* ... */ Ok(plaintext.to_vec()) }
    fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> { Ok(ciphertext.to_vec()) }
}

manager.registry_mut().register(Box::new(MyCipher { /* ... */ }));
```

### 7. 错误处理

失败时返回 `encryption::error::Error`,消息与 PHP 版同指:解密失败一律是「密钥不对或密文被改」,不区分原因。业务层应捕获并记录,**不要**把错误细节透传给前端。

```rust
match manager.decrypt(&stored) {
    Ok(plaintext) => { /* ... */ }
    Err(error) => log::warn!("解密失败: {error}"),
}
```

## 框架集成(可选 feature)

[`Guard`](./src/guard.rs) 是框架无关的请求守卫:内部是一把 `Arc<EncryptionManager>`,克隆它就是引用计数递增,**不含任何密钥拷贝**。每个框架一个 opt-in feature,默认构建一个都不拉。

守卫可以直接从环境变量构造:`ENCRYPTION_MASTER_KEY` 支持 `hex:` / `base64:` 前缀,无前缀时 64 位十六进制串自动按 hex、其余按 base64(解码后必须恰为 32 字节);`ENCRYPTION_ALGORITHM` 可选,默认 `aes-256-gcm`。

```rust
use encryption::guard::Guard;

let guard = Guard::from_env()?;   // 读 ENCRYPTION_MASTER_KEY / ENCRYPTION_ALGORITHM
// 测试或自定义配置源:Guard::from_env_with(|key| my_config.get(key).cloned())
```

| feature | 集成方式 |
|---------|---------|
| (原生 Rust) | `Guard::from_master_key(&key, "aes-256-gcm")`,自行放进任意状态容器 |
| `axum` | 实现 `FromRequestParts`:处理器参数直接写 `Guard` |
| `actix-web` | 实现 `FromRequest`:注册 `web::Data::new(guard)`,处理器参数写 `Guard` |
| `rocket` | 实现 `FromRequest`(请求守卫):`manage(guard)`,处理器参数写 `Guard` |
| `poem` | 实现 `FromRequest`:`.data(guard)`,处理器参数写 `Guard` |
| `salvo` | 实现 `Extractible`:`affix_state::inject(guard)`,处理器参数写 `Guard` |
| `warp` | `with_guard(guard)` 组合子接进过滤器链 |
| `bee-rust` | bee-rust 的 `Router` 直接吃 axum handler,复用 axum 适配(模块:`integrations::bee_rust`) |
| `ecat` | `GuardLayer`(tower Layer/Service)把 Guard 注入请求扩展 |

真实应用的状态结构体不会只有一把加密器。让状态实现 `Guarded`(只需指出守卫在哪个字段),各框架的提取逻辑就都能复用:

```rust
use std::sync::Arc;
use encryption::guard::Guard;
use encryption::integrations::Guarded;

#[derive(Clone)]
struct AppState {
    pool: Arc<()>,          // 你的连接池、配置……
    encryption: Guard,      // 加密守卫
}

impl Guarded for AppState {
    fn guard(&self) -> &Guard { &self.encryption }
}

// axum 示例:处理器参数直接声明要一把守卫
async fn store_phone(guard: Guard) -> String {
    guard.encrypt("13800138000".as_bytes()).unwrap_or_default().len().to_string()
}
```

装配漏了(忘了 `manage` / `app_data` / `with_state` / `.data(...)`)时是明确的 **500**(服务端装配错误),不是 panic,也不是 4xx。加解密失败由处理器自己决定怎么回应——本库不替应用决定「解密失败该返回 500 还是 422」。

```bash
cargo add encryption-rust --features axum   # 或 actix-web / rocket / poem / salvo / warp / bee-rust / ecat
```

## 与原版的差异

本库参照 PHP 版 [`erikwang2013/encryption`](https://github.com/erikwang2013/encryption) 的架构与标识设计,但采用 **Rust 独立格式**:

1. 密文与 PHP 版**不互通**:结构沿用 `v1 | IV | Tag/MAC | 密文` 的外形,但无字节级互解承诺,也未做互解测试。
2. 只有修正后的子密钥派生(HMAC 的 key 是主密钥、message 是用途标签);原版历史遗留的 v1 反序方案不复刻。
3. 无扩展依赖:Sodium XChaCha20 为纯 Rust 实现,恒可用(原版需 `ext-sodium`);SM2 无 `ext-gmp` 门槛。
4. SM2 密文 / 密钥编码以 RustCrypto `sm2` 实现的标准为准(C1C3C2、C1 非压缩),不对齐原版 pohoc/crypto-sm 的布局。
5. 框架适配为 Rust 生态重写:原生 Rust(`Guard` 请求守卫)+ axum / actix-web / rocket / poem / salvo / warp / bee-rust / e-cat,对应原版的 Laravel / ThinkPHP / Hyperf / webman 接入方式。

## 项目目录

```text
encryption-rust/
├── src/
│   ├── lib.rs                 crate 文档与模块导出
│   ├── contract.rs            五种能力契约(trait)与 Identified
│   ├── registry.rs            泛型注册表 Registry<T>
│   ├── manager.rs             五个门面:Encryption / Asymmetric / Hashing / KeyDerivation / PasswordBasedKdf
│   ├── factory.rs             主密钥工厂:子密钥派生与注册表装配
│   ├── guard.rs               请求守卫 Guard(Arc<EncryptionManager>,框架无关)
│   ├── integrations/          8 个框架适配(各自 feature 门控)
│   │                          axum / actix / rocket / poem / salvo / warp / bee_rust / ecat
│   ├── key.rs                 定长密钥材质 Key<N>(Drop 清零、遮蔽 Debug)
│   ├── error.rs               Error / Result
│   ├── encryptor/             aes256gcm / aes256cbc / sodium_xchacha20
│   ├── hash/                  sha256
│   ├── kdf/                   hkdf_sha256 / pbkdf2_sha256
│   ├── guomi/                 sm2 / sm3 / sm4 / zuc / unavailable(SM1/SM7/SM9 占位)
│   ├── internal/              hex 工具与 etm.rs(CBC/SM4/ZUC 共用的 encrypt-then-MAC)
│   └── pet.rs                 项目宠物 Locky(NAME / TAGLINE / ASCII / SVG)
├── tests/
│   ├── crypto_roundtrip.rs    各算法与五个门面的端到端往返
│   ├── invariants.rs          跨算法不互通、篡改必败、IV 新鲜性、插件扩展
│   ├── sm2.rs                 SM2 密钥对、门面与错误路径
│   └── known_answer.rs        跨实现 KAT:OpenSSL / libsodium 生成的参考密文逐字节钉死
├── benches/crypto_bench.rs    criterion 基准(本库封装 vs 裸 aes-gcm 的开销)
├── examples/quickstart.rs     零配置快速开始(加密 → 存 → 读 → 解密 → 篡改检测)
├── docs/
│   ├── pet.svg                项目宠物形象(src/pet.rs 用 include_str! 内联)
│   ├── architecture-design.svg  架构设计(本 README 引用)
│   ├── functional-design.svg    功能设计(本 README 引用)
│   ├── lifecycle.svg            请求生命周期(本 README 引用)
│   ├── social-preview.svg/png   GitHub 社交预览(宠物 Locky 主视觉,上传仓库设置用)
│   └── i18n/en/               英文版 README 与三张英文设计图
├── Cargo.toml
├── SECURITY.md
├── CHANGELOG.md
├── .github/                   CI(fmt / clippy / test / MSRV / audit)与 dependabot
└── LICENSE
```

## 常见问题

**需要安装系统扩展吗?**
不需要。整套实现都是纯 Rust crate:没有 `ext-sodium`、`ext-gmp` 之类的门槛,`cargo build` 即可。

**密文为什么是二进制?**
与原版一致:库产出 / 接受字节。要存 JSON / TEXT 列,自行 base64 或 hex 编码(见 `examples/quickstart.rs`)。

**主密钥从哪来?**
32 字节随机数,从环境变量 / KMS / 密钥服务读取,不要写进代码、不要进版本库;**必须备份——丢了主密钥就等于丢了数据**。

**为什么解密失败时错误信息不区分原因?**
密钥不对、密文被改、算法用错都是同一种失败。区分原因等于给攻击者提供 oracle,也会把内部细节泄露给前端。

**SM2 密文的布局是什么?**
C1C3C2(现行标准默认布局),C1 为非压缩点 `04‖X‖Y`。与原版 pohoc/crypto-sm 的十六进制约定不一定逐字节一致(见「与原版的差异」)。

**设计图有英文版吗?**
有。[English README](./docs/i18n/en/README.md) 使用 `docs/i18n/en/` 下的三张英文设计图;图片源文件与本文引用的是同一套布局。

## 安全建议

- **主密钥**:32 字节 CSPRNG 随机;存环境变量 / KMS,不进代码库、不进日志;离线备份并限定访问。
- **口令**:存储用户密码用 Argon2 / bcrypt 等专用 API;PBKDF2 仅用于「从口令派生加解密密钥」,迭代次数按环境调高。
- **错误**:捕获并记录日志,不向前端透传错误细节。
- **算法选择**:新系统默认 `aes-256-gcm`;CBC / SM4-CBC 的加密-后-MAC 由库保证,勿自行拼装。
- **随机性**:IV / nonce 每次加密由库生成(系统 CSPRNG),不要复用,也不要自备。

## 运行测试

```bash
cargo test                       # 核心库(默认不拉任何框架)
cargo test --all-features        # 连同 8 个框架适配的测试一起
cargo run --example quickstart
cargo bench --bench crypto_bench # criterion:本库封装 vs 裸 crate 的开销
```

覆盖:各算法往返与篡改必败、跨算法不互通、工厂子密钥独立性与校验、注册表 / 门面行为、SM2 密钥路径、宠物常量、各框架的守卫提取(含「忘注册 → 500」路径),以及[跨实现 KAT](./tests/known_answer.rs)(OpenSSL / libsodium 生成的参考密文)。

## 参考原项目

- PHP 原版:[github.com/erikwang2013/encryption]https://github.com/erikwang2013/encryption

## 许可证

[MIT](./LICENSE) © 2026 erik — [https://erik.xyz](https://erik.xyz)

变更记录见 [CHANGELOG.md](./CHANGELOG.md)。