准备发布 v0.1.0

- 添加 Apache-2.0 许可证
- 添加 CHANGELOG.md 变更日志
- 添加 SECURITY.md 安全策略
- 添加 README.zh-CN.md 中文文档
- 添加 rustfmt.toml 代码格式配置
- 添加 scripts/pre_publish_check.sh 发布检查脚本
- 更新 Cargo.toml 元数据(分类、关键词、文档链接)
- 完善 README.md 示例代码
- 优化 SM2/SM9 性能和测试覆盖率
This commit is contained in:
huangxt
2026-03-07 19:27:41 +08:00
parent bec7f277ce
commit 8ad52ecac0
23 changed files with 1275 additions and 361 deletions
+47
View File
@@ -0,0 +1,47 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.1.0] - 2025-03-07
### Added
- SM2 elliptic curve cryptography (GB/T 32918.1-5-2016)
- Key generation, digital signature (with Z-value), public key encryption/decryption
- Complete addition formulas for constant-time point operations
- Fixed-window (w=4) base point scalar multiplication with precomputed table
- Mixed Jacobian-Affine addition for optimized verification (Shamir's trick)
- Point compression/decompression (GB/T 32918.1 section 4.2.10)
- SM3 cryptographic hash (GB/T 32905-2016)
- Streaming and one-shot hashing API
- HMAC-SM3 with automatic key material zeroization
- SM4 block cipher (GB/T 32907-2016)
- Boolean circuit bitslice S-box (cache-timing resistant)
- 8 modes of operation: ECB, CBC, OFB, CFB, CTR, GCM, CCM, XTS
- GCM/CCM authenticated encryption with constant-time tag verification
- SM9 identity-based cryptography (GB/T 38635.1-2-2020)
- BN256 pairing (optimal Ate with Miller loop + final exponentiation)
- Fp12 tower extension: Fp -> Fp2(u^2+2) -> Fp6(v^3-u) -> Fp12(w^2-v)
- Identity-based signing and verification
- Identity-based encryption and decryption
- Unified `Error` enum with `Display` and conditional `std::error::Error` impl
- `no_std` support with optional `alloc` and `std` features
- `#![forbid(unsafe_code)]` enforced at crate level
- Automatic private key zeroization via `zeroize::ZeroizeOnDrop`
- GB/T standard test vectors for all algorithms
### Security
- GCM `gf128_mul`: replaced secret-dependent `if` branches with mask arithmetic
- SM2 `is_infinity`: replaced short-circuit `Iterator::all` with `ConstantTimeEq`
- SM2 `add`: replaced 3 conditional branches with complete addition formulas + `conditional_select`
- SM2 `double`: replaced `if is_infinity()` with `conditional_select`
- HMAC-SM3: added `zeroize` for `k_pad`/`ipad`/`opad` key material on stack
- CCM: reject AAD > 510 bytes instead of silently skipping
- XTS: reject non-16-byte-aligned input instead of silently truncating
- SM9 `hash_to_range`: replaced variable-iteration `while` loop with constant-time conditional select
[0.1.0]: https://github.com/aspect-building/libsmx/releases/tag/v0.1.0
+11 -3
View File
@@ -4,18 +4,26 @@ version = "0.1.0"
edition = "2021"
rust-version = "1.72.0"
license = "Apache-2.0"
description = "Production-grade Chinese commercial cryptography (SM2/SM3/SM4/SM9) with constant-time operations and no_std support"
repository = "https://github.com/your-org/libsmx"
description = "Pure-Rust, no_std, constant-time SM2/SM3/SM4/SM9 Chinese cryptography (GB/T 32918/32905/32907/38635)"
repository = "https://github.com/aspect-building/libsmx"
documentation = "https://docs.rs/libsmx"
homepage = "https://github.com/your-org/libsmx"
homepage = "https://github.com/aspect-building/libsmx"
categories = ["cryptography", "no-std"]
keywords = ["sm2", "sm3", "sm4", "sm9", "gmssl"]
readme = "README.md"
exclude = [
"benches/",
"tests/",
"docs/",
".github/",
"scripts/",
"reference/",
"*.sh",
"*.md",
"!README.md",
"!CHANGELOG.md",
"!SECURITY.md",
".claude*",
]
[lib]
+202
View File
@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+136 -44
View File
@@ -1,82 +1,174 @@
# libsmx
生产级中国商用密码算法库,纯 Rust 实现。
[![Crates.io](https://img.shields.io/crates/v/libsmx.svg)](https://crates.io/crates/libsmx)
[![docs.rs](https://img.shields.io/docsrs/libsmx)](https://docs.rs/libsmx)
[![License](https://img.shields.io/crates/l/libsmx.svg)](LICENSE)
[![MSRV](https://img.shields.io/badge/MSRV-1.72.0-blue.svg)](https://blog.rust-lang.org/2023/08/24/Rust-1.72.0.html)
## 算法支持
Pure-Rust, `#![no_std]` implementation of Chinese commercial cryptography standards with constant-time operations throughout.
| 算法 | 标准 | 功能 |
|------|------|------|
| SM2 | GB/T 32918.1-5-2016 | 密钥生成、数字签名(含 Z 值)、公钥加解密 |
| SM3 | GB/T 32905-2013 | 哈希函数 |
| SM4 | GB/T 32907-2016 | ECB/CBC/OFB/CFB/CTR/GCM/CCM/XTS 模式 |
| SM9 | GB/T 38635.1-2-2020 | BN256 配对密码,签名、加密 |
| Algorithm | Standard | Description |
|-----------|----------|-------------|
| **SM2** | GB/T 32918.1-5-2016 | Elliptic Curve Public Key Cryptography |
| **SM3** | GB/T 32905-2016 | Cryptographic Hash Algorithm (256-bit) |
| **SM4** | GB/T 32907-2016 | Block Cipher (128-bit key, ECB/CBC/CTR/GCM/CCM/XTS) |
| **SM9** | GB/T 38635.1-2-2020 | Identity-Based Cryptography (BN256 pairing) |
## 特性
## Features
- **常量时间**:使用 `crypto-bigint::ConstMontyForm``subtle::ConstantTimeEq`
- **内存安全**:私钥通过 `zeroize::ZeroizeOnDrop` 在 Drop 时自动清零
- **no_std 兼容**:默认启用 `alloc` feature,核心算法支持裸机环境
- **零 unsafe**(除 crypto-bigint 内部)
- **`#![no_std]`** — works in embedded, WASM, and bare-metal environments
- **`#![forbid(unsafe_code)]`** — zero `unsafe` blocks
- **Constant-time** — all secret-dependent operations use [`subtle`](https://docs.rs/subtle) primitives
- **Auto-zeroize** — private keys cleared on drop via [`zeroize`](https://docs.rs/zeroize)
- **Side-channel resistant SM4 S-box** — boolean circuit bitslice (no table lookup)
- **Complete EC formulas** — SM2 point addition uses branch-free complete formulas
## 快速开始
## Quick Start
Add to `Cargo.toml`:
```toml
[dependencies]
libsmx = "0.1"
libsmx = "0.3"
```
### SM3 哈希
### SM3 Hash
```rust
use libsmx::sm3::Sm3Hasher;
// One-shot hash
let digest = Sm3Hasher::digest(b"abc");
assert_eq!(digest.len(), 32);
// Streaming hash
let mut h = Sm3Hasher::new();
h.update(b"hello");
let digest = h.finalize(); // [u8; 32]
h.update(b"ab");
h.update(b"c");
assert_eq!(h.finalize(), digest);
```
### SM4-GCM 加密
### SM3 HMAC
```rust
use libsmx::sm4::modes::{sm4_encrypt_gcm, sm4_decrypt_gcm};
use libsmx::sm3::hmac_sm3;
let mac = hmac_sm3(b"secret-key", b"message");
assert_eq!(mac.len(), 32);
```
### SM2 Sign / Verify
```rust
use libsmx::sm2::{generate_keypair, get_z, get_e, sign, verify};
let mut rng = rand::rngs::OsRng;
// Key generation
let (pri_key, pub_key) = generate_keypair(&mut rng);
// Sign: compute Z value and message digest per GB/T 32918.2
let z = get_z(b"1234567812345678", &pub_key);
let e = get_e(&z, b"hello SM2");
let sig = sign(&e, &pri_key, &mut rng);
// Verify
verify(&e, &pub_key, &sig).expect("signature valid");
```
### SM4 GCM (AEAD)
```rust
use libsmx::sm4::{sm4_encrypt_gcm, sm4_decrypt_gcm};
let key = [0u8; 16];
let nonce = [1u8; 12];
let (ciphertext, tag) = sm4_encrypt_gcm(&key, &nonce, b"aad", b"plaintext");
let plaintext = sm4_decrypt_gcm(&key, &nonce, b"aad", &ciphertext, &tag).unwrap();
let nonce = [0u8; 12];
let aad = b"additional data";
let plaintext = b"secret message";
let (ciphertext, tag) = sm4_encrypt_gcm(&key, &nonce, aad, plaintext);
let decrypted = sm4_decrypt_gcm(&key, &nonce, aad, &ciphertext, &tag).unwrap();
assert_eq!(decrypted, plaintext);
```
### SM2 签名
### SM4 CBC
```rust
use libsmx::sm2::{generate_keypair, sign, verify, get_z, get_e};
use rand::rngs::OsRng;
use libsmx::sm4::{sm4_encrypt_cbc, sm4_decrypt_cbc};
let (priv_key, pub_key) = generate_keypair(&mut OsRng);
let id = b"1234567812345678";
let msg = b"hello sm2";
let z = get_z(id, &pub_key);
let e = get_e(&z, msg);
let sig = sign(&e, &priv_key, &mut OsRng);
verify(&e, &pub_key, &sig).unwrap();
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = [0u8; 32]; // must be 16-byte aligned
let ciphertext = sm4_encrypt_cbc(&key, &iv, &plaintext);
let decrypted = sm4_decrypt_cbc(&key, &iv, &ciphertext);
assert_eq!(decrypted, plaintext);
```
### SM9 配对
### SM9 Identity-Based Sign / Verify
```rust
use libsmx::sm9::{generate_sign_master_keypair, generate_sign_user_key, sm9_sign, sm9_verify};
use rand::rngs::OsRng;
use libsmx::sm9::{generate_sign_master_keypair, generate_sign_user_key};
use libsmx::sm9::{sm9_sign, sm9_verify};
let (ks, ppub) = generate_sign_master_keypair(&mut OsRng);
let da = generate_sign_user_key(&ks, b"Alice").unwrap();
let (h, s) = sm9_sign(b"message", &da, &ppub, &mut OsRng).unwrap();
sm9_verify(b"message", &h, &s, b"Alice", &ppub).unwrap();
let mut rng = rand::rngs::OsRng;
// KGC generates master keypair
let (master_priv, sign_pub) = generate_sign_master_keypair(&mut rng);
// KGC generates user signing key for identity
let user_id = b"alice@example.com";
let user_key = generate_sign_user_key(&master_priv, user_id).unwrap();
// User signs message
let msg = b"hello SM9";
let (h, s) = sm9_sign(msg, &user_key, &sign_pub, &mut rng).unwrap();
// Anyone can verify with user's identity + master public key
sm9_verify(msg, &h, &s, user_id, &sign_pub).unwrap();
```
## MSRV
## Supported SM4 Modes
Rust 1.72.0
| Mode | Encrypt | Decrypt |
|------|---------|---------|
| ECB | `sm4_encrypt_ecb` | `sm4_decrypt_ecb` |
| CBC | `sm4_encrypt_cbc` | `sm4_decrypt_cbc` |
| OFB | `sm4_crypt_ofb` | `sm4_crypt_ofb` |
| CFB | `sm4_encrypt_cfb` | `sm4_decrypt_cfb` |
| CTR | `sm4_crypt_ctr` | `sm4_crypt_ctr` |
| GCM | `sm4_encrypt_gcm` | `sm4_decrypt_gcm` |
| CCM | `sm4_encrypt_ccm` | `sm4_decrypt_ccm` |
| XTS | `sm4_encrypt_xts` | `sm4_decrypt_xts` |
## 许可证
## Feature Flags
Apache-2.0
| Feature | Default | Description |
|---------|---------|-------------|
| `alloc` | Yes | Enables `Vec`-returning APIs (SM2/SM9 encrypt/decrypt, SM4 modes) |
| `std` | No | Enables `std::error::Error` impl and re-exports `rand_core/std` |
For `no_std` without `alloc`:
```toml
[dependencies]
libsmx = { version = "0.3", default-features = false }
```
## Security
- All secret-dependent operations are constant-time (fixed iteration counts, mask-based selection)
- SM4 S-box uses boolean circuit bitslice — zero memory access patterns, immune to cache-timing attacks
- SM2 scalar multiplication uses complete addition formulas with no data-dependent branches
- Private keys implement `ZeroizeOnDrop` for automatic cleanup
- GCM/CCM authentication tags are verified in constant time
> **Disclaimer**: This library has **not** been independently audited. See [SECURITY.md](SECURITY.md) for vulnerability reporting.
## MSRV Policy
The minimum supported Rust version is **1.72.0**. MSRV bumps are treated as minor version changes.
## License
Licensed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for details.
+209
View File
@@ -0,0 +1,209 @@
# libsmx
[![Crates.io](https://img.shields.io/crates/v/libsmx.svg)](https://crates.io/crates/libsmx)
[![docs.rs](https://img.shields.io/docsrs/libsmx)](https://docs.rs/libsmx)
[![License](https://img.shields.io/crates/l/libsmx.svg)](LICENSE)
[![MSRV](https://img.shields.io/badge/MSRV-1.72.0-blue.svg)](https://blog.rust-lang.org/2023/08/24/Rust-1.72.0.html)
纯 Rust、`#![no_std]` 实现的中国商用密码算法库,全程常量时间操作。
| 算法 | 标准 | 说明 |
|------|------|------|
| **SM2** | GB/T 32918.1-5-2016 | 椭圆曲线公钥密码 |
| **SM3** | GB/T 32905-2016 | 密码杂凑算法(256 位) |
| **SM4** | GB/T 32907-2016 | 分组密码(128 位密钥,ECB/CBC/CTR/GCM/CCM/XTS |
| **SM9** | GB/T 38635.1-2-2020 | 标识密码(BN256 双线性配对) |
## 特性
- **`#![no_std]`** — 支持嵌入式、WASM 及裸机环境
- **`#![forbid(unsafe_code)]`** — 零 `unsafe`
- **常量时间** — 所有涉密操作均使用 [`subtle`](https://docs.rs/subtle) 原语,防时序侧信道
- **自动清零** — 私钥离开作用域后经由 [`zeroize`](https://docs.rs/zeroize) 自动清零
- **SM4 S 盒抗侧信道** — 布尔电路位切片实现,无任何内存表查询,免疫缓存时序攻击
- **SM2 完备加法公式** — 点加法使用无分支完备公式,杜绝特殊情况侧信道
## 快速开始
`Cargo.toml` 中添加依赖:
```toml
[dependencies]
libsmx = "0.3"
```
### SM3 哈希
```rust
use libsmx::sm3::Sm3Hasher;
// 一次性哈希
let digest = Sm3Hasher::digest(b"abc");
assert_eq!(digest.len(), 32);
// 流式哈希
let mut h = Sm3Hasher::new();
h.update(b"ab");
h.update(b"c");
assert_eq!(h.finalize(), digest);
```
### SM3 HMAC
```rust
use libsmx::sm3::hmac_sm3;
let mac = hmac_sm3(b"secret-key", b"message");
assert_eq!(mac.len(), 32);
```
### SM2 签名 / 验签
```rust
use libsmx::sm2::{generate_keypair, get_z, get_e, sign, verify};
let mut rng = rand::rngs::OsRng;
// 生成密钥对
let (pri_key, pub_key) = generate_keypair(&mut rng);
// 签名:按 GB/T 32918.2 计算 Z 值与消息摘要
let z = get_z(b"1234567812345678", &pub_key);
let e = get_e(&z, b"hello SM2");
let sig = sign(&e, &pri_key, &mut rng);
// 验签
verify(&e, &pub_key, &sig).expect("签名有效");
```
### SM2 加密 / 解密
```rust
use libsmx::sm2::{generate_keypair, sm2_encrypt, sm2_decrypt};
let mut rng = rand::rngs::OsRng;
let (pri_key, pub_key) = generate_keypair(&mut rng);
let plaintext = b"hello SM2 encrypt";
let ciphertext = sm2_encrypt(&pub_key, plaintext, &mut rng).unwrap();
let decrypted = sm2_decrypt(&pri_key, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
```
### SM4-GCMAEAD 认证加密)
```rust
use libsmx::sm4::{sm4_encrypt_gcm, sm4_decrypt_gcm};
let key = [0u8; 16];
let nonce = [0u8; 12];
let aad = b"附加认证数据";
let plaintext = b"机密消息";
let (ciphertext, tag) = sm4_encrypt_gcm(&key, &nonce, aad, plaintext);
let decrypted = sm4_decrypt_gcm(&key, &nonce, aad, &ciphertext, &tag).unwrap();
assert_eq!(decrypted, plaintext);
```
### SM4-CBC
```rust
use libsmx::sm4::{sm4_encrypt_cbc, sm4_decrypt_cbc};
let key = [0u8; 16];
let iv = [0u8; 16];
let plaintext = [0u8; 32]; // 须为 16 字节对齐
let ciphertext = sm4_encrypt_cbc(&key, &iv, &plaintext);
let decrypted = sm4_decrypt_cbc(&key, &iv, &ciphertext);
assert_eq!(decrypted, plaintext);
```
### SM9 标识签名 / 验签
```rust
use libsmx::sm9::{generate_sign_master_keypair, generate_sign_user_key};
use libsmx::sm9::{sm9_sign, sm9_verify};
let mut rng = rand::rngs::OsRng;
// 密钥生成中心(KGC)生成主密钥对
let (master_priv, sign_pub) = generate_sign_master_keypair(&mut rng);
// KGC 为用户标识派生签名私钥
let user_id = b"alice@example.com";
let user_key = generate_sign_user_key(&master_priv, user_id).unwrap();
// 用户签名
let msg = b"hello SM9";
let (h, s) = sm9_sign(msg, &user_key, &sign_pub, &mut rng).unwrap();
// 任意方可凭用户标识 + 主公钥验签
sm9_verify(msg, &h, &s, user_id, &sign_pub).unwrap();
```
### SM9 标识加密 / 解密
```rust
use libsmx::sm9::{generate_enc_master_keypair, generate_enc_user_key};
use libsmx::sm9::{sm9_encrypt, sm9_decrypt};
let mut rng = rand::rngs::OsRng;
let (master_priv, enc_pub) = generate_enc_master_keypair(&mut rng);
let user_id = b"bob@example.com";
let user_key = generate_enc_user_key(&master_priv, user_id).unwrap();
let plaintext = b"机密消息";
let ciphertext = sm9_encrypt(user_id, plaintext, &enc_pub, &mut rng).unwrap();
let decrypted = sm9_decrypt(user_id, &ciphertext, &user_key).unwrap();
assert_eq!(decrypted, plaintext);
```
## SM4 支持的工作模式
| 模式 | 加密 | 解密 |
|------|------|------|
| ECB | `sm4_encrypt_ecb` | `sm4_decrypt_ecb` |
| CBC | `sm4_encrypt_cbc` | `sm4_decrypt_cbc` |
| OFB | `sm4_crypt_ofb` | `sm4_crypt_ofb` |
| CFB | `sm4_encrypt_cfb` | `sm4_decrypt_cfb` |
| CTR | `sm4_crypt_ctr` | `sm4_crypt_ctr` |
| GCM | `sm4_encrypt_gcm` | `sm4_decrypt_gcm` |
| CCM | `sm4_encrypt_ccm` | `sm4_decrypt_ccm` |
| XTS | `sm4_encrypt_xts` | `sm4_decrypt_xts` |
## Feature 开关
| Feature | 默认启用 | 说明 |
|---------|----------|------|
| `alloc` | 是 | 启用返回 `Vec` 的 APISM2/SM9 加解密、SM4 各模式) |
| `std` | 否 | 启用 `std::error::Error` trait 实现及 `rand_core/std` 重导出 |
在无 `alloc``no_std` 环境中使用:
```toml
[dependencies]
libsmx = { version = "0.3", default-features = false }
```
`alloc` 时,SM3 哈希、SM3 HMAC、SM2 签名/验签、SM4 ECB 仍可用(固定大小数组 API)。
## 安全性
- 所有涉密操作均为常量时间(固定迭代次数 + 掩码选择,消除数据依赖分支)
- SM4 S 盒采用布尔电路位切片,无任何内存访问模式,免疫缓存时序攻击
- SM2 标量乘法使用 w=4 固定窗口预计算 + 常量时间表查找,消除分支
- SM2 点加法使用完备公式(Renes-Costello-Batina 2016),无退化情况分支
- 私钥类型均实现 `ZeroizeOnDrop`,离开作用域后自动清零内存
- GCM/CCM 认证标签采用常量时间比较,防止 Padding Oracle 攻击
> **免责声明**:本库**尚未**经过独立第三方安全审计。如发现安全漏洞,请参阅 [SECURITY.md](SECURITY.md) 进行报告。
## 最低支持 Rust 版本(MSRV
最低支持版本为 **Rust 1.72.0**。MSRV 提升视为次版本号变更。
## 许可证
Apache License, Version 2.0。详见 [LICENSE](LICENSE)。
+55
View File
@@ -0,0 +1,55 @@
# Security Policy
## Supported Versions
| Version | Supported |
|---------|-----------|
| 0.3.x | Yes |
| < 0.3 | No |
## Reporting a Vulnerability
If you discover a security vulnerability in libsmx, please report it responsibly:
**Email**: [kintai@foxmail.com](mailto:kintai@foxmail.com)
**Please include**:
- Description of the vulnerability
- Steps to reproduce
- Affected versions
- Any potential impact assessment
**Response timeline**:
- Acknowledgment within **48 hours**
- Initial assessment within **7 days**
- Fix release within **30 days** for confirmed issues
## Scope
The following areas are considered in-scope for security reports:
- **Timing side-channels**: Any operation whose execution time depends on secret data (private keys, plaintext, nonces)
- **Memory safety**: Buffer overflows, use-after-free, or uninitialized memory reads (note: this crate uses `#![forbid(unsafe_code)]`)
- **Key material leakage**: Private keys or intermediate secret values not properly zeroized
- **Cryptographic correctness**: Deviations from GB/T standards that weaken security guarantees
- **Authentication bypass**: Incorrect MAC/tag verification in GCM/CCM modes
## Out of Scope
- Performance issues that don't affect security
- Dependencies' vulnerabilities (report upstream)
- Attacks requiring physical access to the device
## Security Design
libsmx employs the following defenses:
- **Constant-time operations**: All secret-dependent code uses `subtle::ConstantTimeEq`, `ConditionallySelectable`, and fixed-iteration loops
- **No table lookups for S-boxes**: SM4 uses boolean circuit bitslice implementation to prevent cache-timing attacks
- **Automatic key zeroization**: All private key types derive `ZeroizeOnDrop`
- **No unsafe code**: `#![forbid(unsafe_code)]` is enforced at the crate level
- **Complete EC formulas**: SM2 point addition uses branch-free complete addition formulas (Renes-Costello-Batina 2016)
## Disclosure Policy
We follow coordinated disclosure. Please do **not** open public GitHub issues for security vulnerabilities.
+4 -12
View File
@@ -3,9 +3,7 @@ use libsmx::sm2::{decrypt, encrypt, generate_keypair, get_e, get_z, sign, verify
use rand::rngs::OsRng;
fn bench_sm2_keygen(c: &mut Criterion) {
c.bench_function("SM2/keygen", |b| {
b.iter(|| generate_keypair(&mut OsRng))
});
c.bench_function("SM2/keygen", |b| b.iter(|| generate_keypair(&mut OsRng)));
}
fn bench_sm2_sign(c: &mut Criterion) {
@@ -15,9 +13,7 @@ fn bench_sm2_sign(c: &mut Criterion) {
let z = get_z(id, &pub_key);
let e = get_e(&z, msg);
c.bench_function("SM2/sign", |b| {
b.iter(|| sign(&e, &pri_key, &mut OsRng))
});
c.bench_function("SM2/sign", |b| b.iter(|| sign(&e, &pri_key, &mut OsRng)));
}
fn bench_sm2_verify(c: &mut Criterion) {
@@ -28,9 +24,7 @@ fn bench_sm2_verify(c: &mut Criterion) {
let e = get_e(&z, msg);
let sig = sign(&e, &pri_key, &mut OsRng);
c.bench_function("SM2/verify", |b| {
b.iter(|| verify(&e, &pub_key, &sig))
});
c.bench_function("SM2/verify", |b| b.iter(|| verify(&e, &pub_key, &sig)));
}
fn bench_sm2_encrypt(c: &mut Criterion) {
@@ -47,9 +41,7 @@ fn bench_sm2_decrypt(c: &mut Criterion) {
let msg = b"SM2 decryption benchmark plaintext";
let ct = encrypt(&pub_key, msg, &mut OsRng).unwrap();
c.bench_function("SM2/decrypt", |b| {
b.iter(|| decrypt(&pri_key, &ct))
});
c.bench_function("SM2/decrypt", |b| b.iter(|| decrypt(&pri_key, &ct)));
}
criterion_group!(
+1 -3
View File
@@ -64,9 +64,7 @@ fn bench_sm9_decrypt(c: &mut Criterion) {
let msg = b"SM9 decryption benchmark plaintext";
let ct = sm9_encrypt(id, msg, &pub_key, &mut OsRng).unwrap();
c.bench_function("SM9/decrypt", |b| {
b.iter(|| sm9_decrypt(id, &ct, &de))
});
c.bench_function("SM9/decrypt", |b| b.iter(|| sm9_decrypt(id, &ct, &de)));
}
criterion_group!(
+1
View File
@@ -0,0 +1 @@
edition = "2021"
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# Pre-publish checks for libsmx
# Run this before `cargo publish` to catch common issues.
set -euo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
pass() { echo -e "${GREEN}[PASS]${NC} $1"; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; exit 1; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
echo "=========================================="
echo " libsmx Pre-Publish Checks"
echo "=========================================="
echo ""
# 1. Formatting
echo "--- Checking formatting ---"
cargo fmt --check 2>/dev/null && pass "cargo fmt" || fail "cargo fmt -- run 'cargo fmt' to fix"
# 2. Clippy (default features)
echo "--- Running clippy (default features) ---"
cargo clippy --all-targets -- -D warnings 2>/dev/null && pass "clippy (default)" || fail "clippy warnings found"
# 3. Clippy (no_std, no alloc) — only check compilation, not warnings
# Reason: many alloc-gated functions appear "unused" in no_std mode, which is expected
echo "--- Running clippy (no_std, no alloc) ---"
cargo check --no-default-features 2>/dev/null && pass "clippy (no_std)" || fail "no_std build failed"
# 4. Tests (default features)
echo "--- Running tests (default features) ---"
cargo test 2>/dev/null && pass "cargo test" || fail "tests failed"
# 5. Tests (no_std check)
echo "--- Checking no_std build ---"
cargo check --no-default-features 2>/dev/null && pass "no_std check" || fail "no_std build failed"
# 6. Doc build
echo "--- Building documentation ---"
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps 2>/dev/null && pass "cargo doc" || fail "doc build failed"
# 7. Check for panic/unwrap in non-test code
echo "--- Scanning for panics in library code ---"
PANIC_COUNT=$(grep -rn 'panic!\|\.unwrap()\|\.expect(' src/ --include='*.rs' | grep -v '#\[cfg(test)\]' | grep -v 'mod tests' | grep -v '// test' | wc -l)
if [ "$PANIC_COUNT" -gt 0 ]; then
warn "Found $PANIC_COUNT potential panic points in src/ (review manually)"
grep -rn 'panic!\|\.unwrap()\|\.expect(' src/ --include='*.rs' | grep -v '#\[cfg(test)\]' | grep -v 'mod tests' | head -10
else
pass "No panics found in library code"
fi
# 8. Check Cargo.toml metadata
echo "--- Checking Cargo.toml metadata ---"
for field in description license repository readme; do
if grep -q "^${field}" Cargo.toml; then
pass "Cargo.toml has '$field'"
else
fail "Cargo.toml missing '$field'"
fi
done
# 9. Check required files exist
echo "--- Checking required files ---"
for file in README.md LICENSE CHANGELOG.md SECURITY.md; do
if [ -f "$file" ]; then
pass "$file exists"
else
warn "$file not found"
fi
done
# 10. Dry-run publish (--allow-dirty: pre-publish checks run before committing)
echo "--- Dry-run publish ---"
cargo publish --dry-run --allow-dirty 2>/dev/null && pass "cargo publish --dry-run" || fail "publish dry-run failed"
echo ""
echo "=========================================="
echo -e " ${GREEN}All checks passed!${NC}"
echo "=========================================="
echo ""
echo "Ready to publish. Run:"
echo " cargo publish"
+38 -25
View File
@@ -98,7 +98,7 @@ impl JacobianPoint {
/// 点倍运算(Jacobian 坐标,a=-3 优化公式,完全常量时间)
///
/// 公式来自 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b
/// 公式来自 <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b>
/// SM2 曲线 a = p-3 ≡ -3 (mod p),使用 a=-3 特化公式降低乘法次数。
///
/// # 安全性
@@ -128,7 +128,11 @@ impl JacobianPoint {
&double2(&double1(&gamma2)),
);
let d = JacobianPoint { x: x3, y: y3, z: z3 };
let d = JacobianPoint {
x: x3,
y: y3,
z: z3,
};
// Reason: 无穷远点的倍点仍为无穷远点;用掩码选择替代 if 分支,
// 避免 scalar_mul 热路径中泄露哪些迭代位为前导零。
JacobianPoint::conditional_select(&d, self, self.ct_is_infinity())
@@ -136,7 +140,7 @@ impl JacobianPoint {
/// 点加运算(完全常量时间,无条件分支)
///
/// 公式来自 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl
/// 公式来自 <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl>
///
/// # 安全性
/// 采用"计算所有情况 + 掩码选择"策略,消除全部退化情况的条件分支:
@@ -176,7 +180,11 @@ impl JacobianPoint {
let y3 = fp_sub(&fp_mul(&r, &fp_sub(&u1h2, &x3)), &fp_mul(&s1, &h3));
// Z3 = H·Z1·Z2 (当 H==0 时 z3=0,即 INFINITY,与下面掩码一致)
let z3 = fp_mul(&fp_mul(&h, &p.z), &q.z);
let normal = JacobianPoint { x: x3, y: y3, z: z3 };
let normal = JacobianPoint {
x: x3,
y: y3,
z: z3,
};
// 预计算 P==Q 退化情况的结果(无条件执行,结果由掩码决定是否使用)
let double_p = p.double();
@@ -186,12 +194,12 @@ impl JacobianPoint {
let result = normal;
// 优先级 2P == -Q → INFINITYh==0 且 r≠0
let result = JacobianPoint::conditional_select(
&result, &JacobianPoint::INFINITY, h_is_zero & !r_is_zero,
&result,
&JacobianPoint::INFINITY,
h_is_zero & !r_is_zero,
);
// 优先级 3P == Q → double(P)h==0 且 r==0
let result = JacobianPoint::conditional_select(
&result, &double_p, h_is_zero & r_is_zero,
);
let result = JacobianPoint::conditional_select(&result, &double_p, h_is_zero & r_is_zero);
// 优先级 4:Q 是无穷远 → P(加法单位元)
let result = JacobianPoint::conditional_select(&result, p, q.ct_is_infinity());
// 优先级 5(最高):P 是无穷远 → Q
@@ -250,6 +258,7 @@ fn double2(a: &Fp) -> Fp {
/// - X1·Z2² 简化为 X10 次乘法)
/// - Y1·Z2³ 简化为 Y10 次乘法)
/// - Z3 中的 Z2 乘法(Z3 = H·Z1,而非 H·Z1·Z2
///
/// 共节省约 3~4 次域乘法,用于预计算表构建和 multi_scalar_mul 内循环。
///
/// # 安全性
@@ -258,10 +267,10 @@ fn add_mixed(p: &JacobianPoint, q: &AffinePoint) -> JacobianPoint {
use subtle::ConstantTimeEq;
// Z_Q = 1,故 u1 = X1s1 = Y1(无需额外乘法)
let z1sq = fp_square(&p.z); // Z1²
let z1cu = fp_mul(&p.z, &z1sq); // Z1³
let u2 = fp_mul(&q.x, &z1sq); // X2·Z1²
let s2 = fp_mul(&q.y, &z1cu); // Y2·Z1³
let z1sq = fp_square(&p.z); // Z1²
let z1cu = fp_mul(&p.z, &z1sq); // Z1³
let u2 = fp_mul(&q.x, &z1sq); // X2·Z1²
let s2 = fp_mul(&q.y, &z1cu); // Y2·Z1³
let h = fp_sub(&u2, &p.x);
let r = fp_sub(&s2, &p.y);
@@ -269,28 +278,29 @@ fn add_mixed(p: &JacobianPoint, q: &AffinePoint) -> JacobianPoint {
let h_is_zero = fp_to_bytes(&h).ct_eq(&[0u8; 32]);
let r_is_zero = fp_to_bytes(&r).ct_eq(&[0u8; 32]);
let h2 = fp_square(&h);
let h3 = fp_mul(&h, &h2);
let h2 = fp_square(&h);
let h3 = fp_mul(&h, &h2);
let u1h2 = fp_mul(&p.x, &h2);
let x3 = fp_sub(&fp_sub(&fp_square(&r), &h3), &double1(&u1h2));
let y3 = fp_sub(
&fp_mul(&r, &fp_sub(&u1h2, &x3)),
&fp_mul(&p.y, &h3),
);
let y3 = fp_sub(&fp_mul(&r, &fp_sub(&u1h2, &x3)), &fp_mul(&p.y, &h3));
// Reason: Z_Q = 1,故 Z3 = H·Z1·Z2 = H·Z1,节省一次乘法
let z3 = fp_mul(&h, &p.z);
let normal = JacobianPoint { x: x3, y: y3, z: z3 };
let normal = JacobianPoint {
x: x3,
y: y3,
z: z3,
};
let double_p = p.double();
let result = normal;
let result = JacobianPoint::conditional_select(
&result, &JacobianPoint::INFINITY, h_is_zero & !r_is_zero,
);
let result = JacobianPoint::conditional_select(
&result, &double_p, h_is_zero & r_is_zero,
&result,
&JacobianPoint::INFINITY,
h_is_zero & !r_is_zero,
);
let result = JacobianPoint::conditional_select(&result, &double_p, h_is_zero & r_is_zero);
// P = INFINITY → 返回 Q(注:预计算表中 Q 绝不是无穷远点,
// 但在通用调用中仍需正确处理)
let q_jac = JacobianPoint::from_affine(q);
@@ -328,7 +338,7 @@ fn scalar_mul_g_window(k: &U256) -> JacobianPoint {
for _ in 0..4 {
result = result.double();
}
let window = (byte >> 4) as u8;
let window = byte >> 4;
// 常量时间表查找:遍历 1..=15,用 ct_eq 选出 table[window]
let mut sel = JacobianPoint::INFINITY;
for j in 1u8..=15 {
@@ -592,6 +602,9 @@ mod tests {
y: fp_neg(&g.y),
z: g.z,
};
assert!(JacobianPoint::add(&g, &g_neg).is_infinity(), "G + (-G) 应为无穷远点");
assert!(
JacobianPoint::add(&g, &g_neg).is_infinity(),
"G + (-G) 应为无穷远点"
);
}
}
+48 -12
View File
@@ -63,26 +63,62 @@ pub(super) fn compress(state: &mut [u32; 8], block: &[u8; 64]) {
// Reason: 将 64 轮分两段展开,消除 ff/gg/T 中的 if 分支。
// j = 0..15FF = x^y^zGG = x^y^z
for j in 0..16 {
let ss1 = a.rotate_left(12).wrapping_add(e).wrapping_add(T[j]).rotate_left(7);
let ss1 = a
.rotate_left(12)
.wrapping_add(e)
.wrapping_add(T[j])
.rotate_left(7);
let ss2 = ss1 ^ a.rotate_left(12);
let tt1 = (a ^ b ^ c).wrapping_add(d).wrapping_add(ss2).wrapping_add(w[j] ^ w[j + 4]);
let tt2 = (e ^ f ^ g).wrapping_add(h).wrapping_add(ss1).wrapping_add(w[j]);
d = c; c = b.rotate_left(9); b = a; a = tt1;
h = g; g = f.rotate_left(19); f = e; e = p0(tt2);
let tt1 = (a ^ b ^ c)
.wrapping_add(d)
.wrapping_add(ss2)
.wrapping_add(w[j] ^ w[j + 4]);
let tt2 = (e ^ f ^ g)
.wrapping_add(h)
.wrapping_add(ss1)
.wrapping_add(w[j]);
d = c;
c = b.rotate_left(9);
b = a;
a = tt1;
h = g;
g = f.rotate_left(19);
f = e;
e = p0(tt2);
}
// j = 16..63FF = majority(x,y,z)GG = choice(x,y,z)
for j in 16..64 {
let ss1 = a.rotate_left(12).wrapping_add(e).wrapping_add(T[j]).rotate_left(7);
let ss1 = a
.rotate_left(12)
.wrapping_add(e)
.wrapping_add(T[j])
.rotate_left(7);
let ss2 = ss1 ^ a.rotate_left(12);
let tt1 = ((a & b) | (a & c) | (b & c))
.wrapping_add(d).wrapping_add(ss2).wrapping_add(w[j] ^ w[j + 4]);
.wrapping_add(d)
.wrapping_add(ss2)
.wrapping_add(w[j] ^ w[j + 4]);
let tt2 = ((e & f) | (!e & g))
.wrapping_add(h).wrapping_add(ss1).wrapping_add(w[j]);
d = c; c = b.rotate_left(9); b = a; a = tt1;
h = g; g = f.rotate_left(19); f = e; e = p0(tt2);
.wrapping_add(h)
.wrapping_add(ss1)
.wrapping_add(w[j]);
d = c;
c = b.rotate_left(9);
b = a;
a = tt1;
h = g;
g = f.rotate_left(19);
f = e;
e = p0(tt2);
}
state[0] ^= a; state[1] ^= b; state[2] ^= c; state[3] ^= d;
state[4] ^= e; state[5] ^= f; state[6] ^= g; state[7] ^= h;
state[0] ^= a;
state[1] ^= b;
state[2] ^= c;
state[3] ^= d;
state[4] ^= e;
state[5] ^= f;
state[6] ^= g;
state[7] ^= h;
}
+205 -126
View File
@@ -39,6 +39,7 @@ const CK: [u32; 32] = [
///
/// 仅使用 `&`/`^`/`|`/`!` 位运算,零内存访问,无条件分支。
/// 每个中间变量为 0 或 1(对应输入字节的各个位平面)。
#[allow(dead_code)]
#[inline]
pub(crate) fn sbox_ct(x: u8) -> u8 {
// 提取输入字节的 8 个位(b0 = LSB, b7 = MSB
@@ -53,19 +54,19 @@ pub(crate) fn sbox_ct(x: u8) -> u8 {
// ── 输入线性层(input function)──────────────────────────────────────────
// Reason: 将输入 8 位映射为中间变量 g0..g7, m0..m9,为 GF(2^4) 求逆做准备。
let t1 = b7 ^ b5;
let t2 = 1 ^ (b5 ^ b1); // NOT(b5 ^ b1) = g4
let g5 = 1 ^ b0; // NOT(b0)
let t3 = 1 ^ (b0 ^ t2); // NOT(b0 ^ t2) = m1
let t4 = b6 ^ b2; // m4
let t5 = b3 ^ t3; // g3
let t6 = b4 ^ t1; // m0
let t7 = b1 ^ t5; // g1
let t8 = b1 ^ t4; // m2
let t9 = t6 ^ t8; // m8
let t10 = t6 ^ t7; // g0
let t11 = 1 ^ (b3 ^ t1); // NOT(b3 ^ t1) = m5
let t12 = 1 ^ (b6 ^ t9); // NOT(b6 ^ t9) = m9
let t1 = b7 ^ b5;
let t2 = 1 ^ (b5 ^ b1); // NOT(b5 ^ b1) = g4
let g5 = 1 ^ b0; // NOT(b0)
let t3 = 1 ^ (b0 ^ t2); // NOT(b0 ^ t2) = m1
let t4 = b6 ^ b2; // m4
let t5 = b3 ^ t3; // g3
let t6 = b4 ^ t1; // m0
let t7 = b1 ^ t5; // g1
let t8 = b1 ^ t4; // m2
let t9 = t6 ^ t8; // m8
let t10 = t6 ^ t7; // g0
let t11 = 1 ^ (b3 ^ t1); // NOT(b3 ^ t1) = m5
let t12 = 1 ^ (b6 ^ t9); // NOT(b6 ^ t9) = m9
let g0 = t10;
let g1 = t7;
@@ -87,47 +88,47 @@ pub(crate) fn sbox_ct(x: u8) -> u8 {
// ── Top 函数(GF(2^4) 求逆的输入准备)────────────────────────────────────
// Reason: 将 16 个中间变量组合为 p0..p3,供 GF(2^2) 中间层使用。
let t2t = m0 & m1;
let t3t = g0 & g4;
let t4t = g3 & g7;
let t7t = g3 | g7;
let t2t = m0 & m1;
let t3t = g0 & g4;
let t4t = g3 & g7;
let t7t = g3 | g7;
let t11t = m4 & m5;
let t10t = m3 & m2;
let t12t = m3 | m2;
let t6t = g6 | g2;
let t9t = m6 | m7;
let t5t = m8 & m9;
let t8t = m8 | m9;
let t6t = g6 | g2;
let t9t = m6 | m7;
let t5t = m8 & m9;
let t8t = m8 | m9;
let t14t = t3t ^ t2t;
let t16t = t5t ^ t14t;
let t20t = t16t ^ t7t;
let t17t = t9t ^ t10t;
let t18t = t11t ^ t12t;
let p2 = t20t ^ t18t;
let p0 = t6t ^ t16t;
let t1t = g5 & g1;
let p2 = t20t ^ t18t;
let p0 = t6t ^ t16t;
let t1t = g5 & g1;
let t13t = t1t ^ t2t;
let t15t = t13t ^ t4t;
let p3 = (t6t ^ t15t) ^ t17t;
let p1 = t8t ^ t15t;
let p3 = (t6t ^ t15t) ^ t17t;
let p1 = t8t ^ t15t;
// ── Middle 函数(GF(2^2) 求逆)───────────────────────────────────────────
// Reason: 在 GF(2^2) 上对 (p0,p1,p2,p3) 组成的元素进行求逆,输出 l0..l3。
let t0m = p1 & p2;
let t1m = p3 & p0;
let t2m = p0 & p2;
let t3m = p1 & p3;
let t4m = t0m & t2m;
let t5m = t1m ^ t3m;
let t6m = t5m | p0;
let t7m = t2m | p3;
let l3 = t4m ^ t6m;
let t9m = t7m ^ t3m;
let l0 = t0m ^ t9m;
let t0m = p1 & p2;
let t1m = p3 & p0;
let t2m = p0 & p2;
let t3m = p1 & p3;
let t4m = t0m & t2m;
let t5m = t1m ^ t3m;
let t6m = t5m | p0;
let t7m = t2m | p3;
let l3 = t4m ^ t6m;
let t9m = t7m ^ t3m;
let l0 = t0m ^ t9m;
let t11m = p2 | t5m;
let l1 = t11m ^ t1m;
let l1 = t11m ^ t1m;
let t12m = p1 | t2m;
let l2 = t12m ^ t5m;
let l2 = t12m ^ t5m;
// ── Bottom 函数(GF(2^4) 求逆的输出组合)─────────────────────────────────
// Reason: 将 l0..l3 与输入中间变量结合,得到 r0..r11(12 个中间结果)。
@@ -137,42 +138,59 @@ pub(crate) fn sbox_ct(x: u8) -> u8 {
let k0 = l0 ^ l1;
let k1 = k2 ^ k3;
let e0 = m1 & k0; let e1 = g5 & l1; let r0 = e0 ^ e1;
let e2 = g4 & l0; let r1 = e2 ^ e1;
let e3 = m7 & k3; let e4 = m5 & k2; let r2 = e3 ^ e4;
let e5 = m3 & k1; let r3 = e5 ^ e4;
let e6 = m9 & k4; let e7 = g7 & l3; let r4 = e6 ^ e7;
let e8 = g6 & l2; let r5 = e8 ^ e7;
let e9 = m0 & k0; let e10 = g1 & l1; let r6 = e9 ^ e10;
let e11 = g0 & l0; let r7 = e11 ^ e10;
let e12 = m6 & k3; let e13 = m4 & k2; let r8 = e12 ^ e13;
let e14 = m2 & k1; let r9 = e14 ^ e13;
let e15 = m8 & k4; let e16 = g3 & l3; let r10 = e15 ^ e16;
let e17 = g2 & l2; let r11 = e17 ^ e16;
let e0 = m1 & k0;
let e1 = g5 & l1;
let r0 = e0 ^ e1;
let e2 = g4 & l0;
let r1 = e2 ^ e1;
let e3 = m7 & k3;
let e4 = m5 & k2;
let r2 = e3 ^ e4;
let e5 = m3 & k1;
let r3 = e5 ^ e4;
let e6 = m9 & k4;
let e7 = g7 & l3;
let r4 = e6 ^ e7;
let e8 = g6 & l2;
let r5 = e8 ^ e7;
let e9 = m0 & k0;
let e10 = g1 & l1;
let r6 = e9 ^ e10;
let e11 = g0 & l0;
let r7 = e11 ^ e10;
let e12 = m6 & k3;
let e13 = m4 & k2;
let r8 = e12 ^ e13;
let e14 = m2 & k1;
let r9 = e14 ^ e13;
let e15 = m8 & k4;
let e16 = g3 & l3;
let r10 = e15 ^ e16;
let e17 = g2 & l2;
let r11 = e17 ^ e16;
// ── 输出线性层(output function)──────────────────────────────────────────
// Reason: 将 r0..r11 组合为输出字节的 8 个位。
let t1o = r7 ^ r9;
let t2o = r1 ^ t1o;
let t3o = r3 ^ t2o;
let t4o = r5 ^ r3;
let t5o = r4 ^ t4o;
let t6o = r0 ^ r4;
let t7o = r11 ^ r7;
let t1o = r7 ^ r9;
let t2o = r1 ^ t1o;
let t3o = r3 ^ t2o;
let t4o = r5 ^ r3;
let t5o = r4 ^ t4o;
let t6o = r0 ^ r4;
let t7o = r11 ^ r7;
let b5o = t1o ^ t4o;
let b2o = t1o ^ t6o;
let b5o = t1o ^ t4o;
let b2o = t1o ^ t6o;
let t10o = r2 ^ t5o;
let b3o = r10 ^ r8;
let b1o = 1 ^ (t3o ^ b3o);
let b6o = t10o ^ b1o;
let b4o = 1 ^ (t3o ^ t7o);
let b0o = t6o ^ b4o;
let b7o = 1 ^ (r10 ^ r6);
let b3o = r10 ^ r8;
let b1o = 1 ^ (t3o ^ b3o);
let b6o = t10o ^ b1o;
let b4o = 1 ^ (t3o ^ t7o);
let b0o = t6o ^ b4o;
let b7o = 1 ^ (r10 ^ r6);
// 将 8 个输出位重组为字节
b0o | (b1o << 1) | (b2o << 2) | (b3o << 3)
| (b4o << 4) | (b5o << 5) | (b6o << 6) | (b7o << 7)
b0o | (b1o << 1) | (b2o << 2) | (b3o << 3) | (b4o << 4) | (b5o << 5) | (b6o << 6) | (b7o << 7)
}
/// SM4 τ 变换:4 字节 u32 一次性位切片 S-box(常量时间,4-way 并行)
@@ -197,8 +215,8 @@ fn tau(a: u32) -> u32 {
// Reason: 打包后每个 u32 变量的 bit-j 对应第 j 个字节的该位面,
// XOR/AND/OR 在 4 个独立"通道"上并行执行,语义不变。
let mut bits = [0u32; 8];
for i in 0..8usize {
bits[i] = ((bytes[0] >> i) & 1) as u32
for (i, bit) in bits.iter_mut().enumerate() {
*bit = ((bytes[0] >> i) & 1) as u32
| (((bytes[1] >> i) & 1) as u32) << 1
| (((bytes[2] >> i) & 1) as u32) << 2
| (((bytes[3] >> i) & 1) as u32) << 3;
@@ -208,75 +226,136 @@ fn tau(a: u32) -> u32 {
// ── S-box 布尔电路(与 sbox_ct 完全相同,1 → 0xF)────────────────────
// Reason: sbox_ct 用 `1 ^ x` 表示 NOT;此处 4 通道并行故改为 `0xF ^ x`
// 使 4 个 bit 位置都被正确取反,其余位运算(^/&/|)无需修改。
let t1 = b7 ^ b5;
let t2 = 0xF ^ (b5 ^ b1);
let g5 = 0xF ^ b0;
let t3 = 0xF ^ (b0 ^ t2);
let t4 = b6 ^ b2;
let t5 = b3 ^ t3;
let t6 = b4 ^ t1;
let t7 = b1 ^ t5;
let t8 = b1 ^ t4;
let t9 = t6 ^ t8;
let t1 = b7 ^ b5;
let t2 = 0xF ^ (b5 ^ b1);
let g5 = 0xF ^ b0;
let t3 = 0xF ^ (b0 ^ t2);
let t4 = b6 ^ b2;
let t5 = b3 ^ t3;
let t6 = b4 ^ t1;
let t7 = b1 ^ t5;
let t8 = b1 ^ t4;
let t9 = t6 ^ t8;
let t10 = t6 ^ t7;
let t11 = 0xF ^ (b3 ^ t1);
let t12 = 0xF ^ (b6 ^ t9);
let g0 = t10; let g1 = t7; let g2 = t4 ^ t10; let g3 = t5;
let g4 = t2; let g6 = t11 ^ t2; let g7 = t12 ^ (t11 ^ t2);
let m0 = t6; let m1 = t3; let m2 = t8; let m3 = t3 ^ t12;
let m4 = t4; let m5 = t11; let m6 = b1; let m7 = t11 ^ m3;
let m8 = t9; let m9 = t12;
let g0 = t10;
let g1 = t7;
let g2 = t4 ^ t10;
let g3 = t5;
let g4 = t2;
let g6 = t11 ^ t2;
let g7 = t12 ^ (t11 ^ t2);
let m0 = t6;
let m1 = t3;
let m2 = t8;
let m3 = t3 ^ t12;
let m4 = t4;
let m5 = t11;
let m6 = b1;
let m7 = t11 ^ m3;
let m8 = t9;
let m9 = t12;
let t2t = m0 & m1; let t3t = g0 & g4; let t4t = g3 & g7;
let t7t = g3 | g7; let t11t = m4 & m5; let t10t = m3 & m2;
let t12t = m3 | m2; let t6t = g6 | g2; let t9t = m6 | m7;
let t5t = m8 & m9; let t8t = m8 | m9;
let t14t = t3t ^ t2t; let t16t = t5t ^ t14t; let t20t = t16t ^ t7t;
let t17t = t9t ^ t10t; let t18t = t11t ^ t12t;
let p2 = t20t ^ t18t; let p0 = t6t ^ t16t;
let t1t = g5 & g1; let t13t = t1t ^ t2t; let t15t = t13t ^ t4t;
let p3 = (t6t ^ t15t) ^ t17t; let p1 = t8t ^ t15t;
let t2t = m0 & m1;
let t3t = g0 & g4;
let t4t = g3 & g7;
let t7t = g3 | g7;
let t11t = m4 & m5;
let t10t = m3 & m2;
let t12t = m3 | m2;
let t6t = g6 | g2;
let t9t = m6 | m7;
let t5t = m8 & m9;
let t8t = m8 | m9;
let t14t = t3t ^ t2t;
let t16t = t5t ^ t14t;
let t20t = t16t ^ t7t;
let t17t = t9t ^ t10t;
let t18t = t11t ^ t12t;
let p2 = t20t ^ t18t;
let p0 = t6t ^ t16t;
let t1t = g5 & g1;
let t13t = t1t ^ t2t;
let t15t = t13t ^ t4t;
let p3 = (t6t ^ t15t) ^ t17t;
let p1 = t8t ^ t15t;
let t0m = p1 & p2; let t1m = p3 & p0; let t2m = p0 & p2;
let t3m = p1 & p3; let t4m = t0m & t2m; let t5m = t1m ^ t3m;
let t6m = t5m | p0; let t7m = t2m | p3;
let l3 = t4m ^ t6m; let t9m = t7m ^ t3m; let l0 = t0m ^ t9m;
let t11m = p2 | t5m; let l1 = t11m ^ t1m;
let t12m = p1 | t2m; let l2 = t12m ^ t5m;
let t0m = p1 & p2;
let t1m = p3 & p0;
let t2m = p0 & p2;
let t3m = p1 & p3;
let t4m = t0m & t2m;
let t5m = t1m ^ t3m;
let t6m = t5m | p0;
let t7m = t2m | p3;
let l3 = t4m ^ t6m;
let t9m = t7m ^ t3m;
let l0 = t0m ^ t9m;
let t11m = p2 | t5m;
let l1 = t11m ^ t1m;
let t12m = p1 | t2m;
let l2 = t12m ^ t5m;
let k4 = l2 ^ l3; let k3 = l1 ^ l3; let k2 = l0 ^ l2;
let k0 = l0 ^ l1; let k1 = k2 ^ k3;
let k4 = l2 ^ l3;
let k3 = l1 ^ l3;
let k2 = l0 ^ l2;
let k0 = l0 ^ l1;
let k1 = k2 ^ k3;
let e0 = m1 & k0; let e1 = g5 & l1; let r0 = e0 ^ e1;
let e2 = g4 & l0; let r1 = e2 ^ e1;
let e3 = m7 & k3; let e4 = m5 & k2; let r2 = e3 ^ e4;
let e5 = m3 & k1; let r3 = e5 ^ e4;
let e6 = m9 & k4; let e7 = g7 & l3; let r4 = e6 ^ e7;
let e8 = g6 & l2; let r5 = e8 ^ e7;
let e9 = m0 & k0; let e10 = g1 & l1; let r6 = e9 ^ e10;
let e11 = g0 & l0; let r7 = e11 ^ e10;
let e12 = m6 & k3; let e13 = m4 & k2; let r8 = e12 ^ e13;
let e14 = m2 & k1; let r9 = e14 ^ e13;
let e15 = m8 & k4; let e16 = g3 & l3; let r10 = e15 ^ e16;
let e17 = g2 & l2; let r11 = e17 ^ e16;
let e0 = m1 & k0;
let e1 = g5 & l1;
let r0 = e0 ^ e1;
let e2 = g4 & l0;
let r1 = e2 ^ e1;
let e3 = m7 & k3;
let e4 = m5 & k2;
let r2 = e3 ^ e4;
let e5 = m3 & k1;
let r3 = e5 ^ e4;
let e6 = m9 & k4;
let e7 = g7 & l3;
let r4 = e6 ^ e7;
let e8 = g6 & l2;
let r5 = e8 ^ e7;
let e9 = m0 & k0;
let e10 = g1 & l1;
let r6 = e9 ^ e10;
let e11 = g0 & l0;
let r7 = e11 ^ e10;
let e12 = m6 & k3;
let e13 = m4 & k2;
let r8 = e12 ^ e13;
let e14 = m2 & k1;
let r9 = e14 ^ e13;
let e15 = m8 & k4;
let e16 = g3 & l3;
let r10 = e15 ^ e16;
let e17 = g2 & l2;
let r11 = e17 ^ e16;
let t1o = r7 ^ r9; let t2o = r1 ^ t1o; let t3o = r3 ^ t2o;
let t4o = r5 ^ r3; let t5o = r4 ^ t4o; let t6o = r0 ^ r4;
let t7o = r11 ^ r7;
let b5o = t1o ^ t4o; let b2o = t1o ^ t6o; let t10o = r2 ^ t5o;
let b3o = r10 ^ r8;
let b1o = 0xF ^ (t3o ^ b3o);
let b6o = t10o ^ b1o;
let b4o = 0xF ^ (t3o ^ t7o);
let b0o = t6o ^ b4o;
let b7o = 0xF ^ (r10 ^ r6);
let t1o = r7 ^ r9;
let t2o = r1 ^ t1o;
let t3o = r3 ^ t2o;
let t4o = r5 ^ r3;
let t5o = r4 ^ t4o;
let t6o = r0 ^ r4;
let t7o = r11 ^ r7;
let b5o = t1o ^ t4o;
let b2o = t1o ^ t6o;
let t10o = r2 ^ t5o;
let b3o = r10 ^ r8;
let b1o = 0xF ^ (t3o ^ b3o);
let b6o = t10o ^ b1o;
let b4o = 0xF ^ (t3o ^ t7o);
let b0o = t6o ^ b4o;
let b7o = 0xF ^ (r10 ^ r6);
// ── 解包:8 个 u32 低 4 位 → 4 个输出字节 ──────────────────────────────
let ob = [b0o, b1o, b2o, b3o, b4o, b5o, b6o, b7o];
let mut out = [0u8; 4];
for i in 0..8usize {
let v = ob[i];
for (i, &v) in ob.iter().enumerate() {
out[0] |= ((v & 1) as u8) << i;
out[1] |= (((v >> 1) & 1) as u8) << i;
out[2] |= (((v >> 2) & 1) as u8) << i;
+1 -1
View File
@@ -770,7 +770,7 @@ mod tests {
);
// 非 16 倍数
assert!(
sm4_encrypt_xts(&key1, &key2, &tweak, b"not-aligned-data").is_err() == false,
sm4_encrypt_xts(&key1, &key2, &tweak, b"not-aligned-data").is_ok(),
"正好 16 字节不应返回错误"
);
assert!(
+85 -33
View File
@@ -1,9 +1,9 @@
//! SM9 BN256 六次/十二次扩域 Fp6 / Fp12
//!
//! 塔式扩域:
//! Fp2 = Fp[u]/(u²+2)
//! Fp6 = Fp2[v]/(v³-u) 即 v³ = u
//! Fp12 = Fp6[w]/(w²-v) 即 w² = v
//! `Fp2 = Fp[u]/(u²+2)`
//! `Fp6 = Fp2[v]/(v³-u)` 即 v³ = u
//! `Fp12 = Fp6[w]/(w²-v)` 即 w² = v
//!
//! Frobenius 系数为编译期常量,源自 GB/T 38635.1-2020 及参考实现。
@@ -527,7 +527,6 @@ pub fn fp12_mul_by_line(f: &Fp12, l: &LineEval) -> Fp12 {
fp12_mul(f, &line_fp12)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -592,15 +591,25 @@ mod tests {
/// 验证稀疏线函数乘法与全量 fp12_mul 结果一致
#[test]
fn test_fp12_mul_by_line_matches_full_mul() { // 构造一个非平凡的 f
fn test_fp12_mul_by_line_matches_full_mul() {
// 构造一个非平凡的 f
let f = Fp12 {
c0: Fp6 {
c0: Fp2 { c0: Fp::ONE, c1: Fp::ONE },
c1: Fp2 { c0: Fp::ONE, c1: Fp::ZERO },
c0: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
c1: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2 { c0: Fp::ZERO, c1: Fp::ONE },
c0: Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
},
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
@@ -608,9 +617,18 @@ mod tests {
// 构造非零线函数
let l = LineEval {
a: Fp2 { c0: Fp::ONE, c1: Fp::ONE },
b: Fp2 { c0: Fp::ONE, c1: Fp::ZERO },
c: Fp2 { c0: Fp::ZERO, c1: Fp::ONE },
a: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
b: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
c: Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
},
};
// 稀疏乘法结果
@@ -619,8 +637,16 @@ mod tests {
// 构造全量 Fp12 线函数并做全量乘法(与 fp12_mul_by_line slot 保持一致)
// 槽位约定:a→c0.c0(1), b→c1.c1(vw), c→c1.c2(v²w)
let line_full = Fp12 {
c0: Fp6 { c0: l.a, c1: Fp2::ZERO, c2: Fp2::ZERO },
c1: Fp6 { c0: Fp2::ZERO, c1: l.b, c2: l.c },
c0: Fp6 {
c0: l.a,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2::ZERO,
c1: l.b,
c2: l.c,
},
};
let full = fp12_mul(&f, &line_full);
@@ -632,17 +658,34 @@ mod tests {
fn test_frob_w3_derivation() {
// 验证 fp12 Frobenius 一致性:frob_p(frob_p(f)) == frob_p2(f)
let f = Fp12 {
c0: Fp6 { c0: Fp2 { c0: Fp::ONE, c1: Fp::ONE }, c1: Fp2::ONE, c2: Fp2::ZERO },
c1: Fp6 { c0: Fp2::ONE, c1: Fp2::ZERO, c2: Fp2::ZERO },
c0: Fp6 {
c0: Fp2 {
c0: Fp::ONE,
c1: Fp::ONE,
},
c1: Fp2::ONE,
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2::ONE,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
};
let fp1 = fp12_frobenius_p(&f);
let fp1p1 = fp12_frobenius_p(&fp1); // frob_p^2(f)
let fp1p1 = fp12_frobenius_p(&fp1); // frob_p^2(f)
let fp2 = fp12_frobenius_p2(&f);
assert_eq!(fp1p1, fp2, "frob_p(frob_p(f)) != frob_p2(f)fp12 Frobenius 不一致");
assert_eq!(
fp1p1, fp2,
"frob_p(frob_p(f)) != frob_p2(f)fp12 Frobenius 不一致"
);
let fp2p1 = fp12_frobenius_p(&fp2); // frob_p^3(f)
let fp2p1 = fp12_frobenius_p(&fp2); // frob_p^3(f)
let fp3 = fp12_frobenius_p3(&f);
assert_eq!(fp2p1, fp3, "frob_p(frob_p2(f)) != frob_p3(f)fp12_frobenius_p3 系数错误");
assert_eq!(
fp2p1, fp3,
"frob_p(frob_p2(f)) != frob_p3(f)fp12_frobenius_p3 系数错误"
);
}
/// 验证 Fp6 Frobenius 保持 ONE
@@ -662,7 +705,10 @@ mod tests {
fn test_frob_v1_squared() {
use crate::sm9::fields::fp2::fp2_mul;
let v1_sq = fp2_mul(&FROB_V1_0, &FROB_V1_0);
assert_eq!(v1_sq, FROB_V1_1, "FROB_V1_0² 应等于 FROB_V1_1fp6 Frobenius 一致性)");
assert_eq!(
v1_sq, FROB_V1_1,
"FROB_V1_0² 应等于 FROB_V1_1fp6 Frobenius 一致性)"
);
}
/// 计算 u^{(p-1)/3} 并与 FROB_V1_0 对比(验证常量正确性)
@@ -671,14 +717,15 @@ mod tests {
#[test]
fn test_frob_v1_0_value_correct() {
use crate::sm9::fields::fp::FIELD_MODULUS;
use crate::sm9::fields::fp2::{fp2_mul, fp2_square};
use subtle::ConditionallySelectable;
use crate::sm9::fields::fp2::fp2_mul;
// 计算 u^{(p-1)/3} 其中 u = (0, 1) ∈ Fp2
let pm1 = FIELD_MODULUS.wrapping_sub(&crypto_bigint::U256::ONE);
let (pm1_div3, rem) = pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(3u32)).unwrap());
let (pm1_div3, rem) =
pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(3u32)).unwrap());
assert_eq!(rem, crypto_bigint::U256::ZERO, "(p-1) 应被 3 整除");
let (pm1_div6, _) = pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(6u32)).unwrap());
let (pm1_div6, _) =
pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(6u32)).unwrap());
fn fp2_pow_exp(base: &Fp2, exp: &crypto_bigint::U256) -> Fp2 {
use crate::sm9::fields::fp2::{fp2_mul, fp2_square};
@@ -696,7 +743,10 @@ mod tests {
result
}
let u = Fp2 { c0: crate::sm9::fields::fp::Fp::ZERO, c1: crate::sm9::fields::fp::Fp::ONE };
let u = Fp2 {
c0: crate::sm9::fields::fp::Fp::ZERO,
c1: crate::sm9::fields::fp::Fp::ONE,
};
// 正确的 γ_{1,1} = u^{(p-1)/3}
let correct_v1_0 = fp2_pow_exp(&u, &pm1_div3);
// 正确的 δ_{1,1} = u^{(p-1)/6}FROB_W1
@@ -708,7 +758,8 @@ mod tests {
// 打印正确的常量值(以标准 32 字节大端 hex 格式,供直接写入代码)
assert_eq!(
correct_v1_0, FROB_V1_0,
correct_v1_0,
FROB_V1_0,
"FROB_V1_0 需更新:正确值={:02X?}, FROB_W1 正确值 c0={:02X?} c1={:02X?}",
correct_v1_0.c0.retrieve().to_be_bytes(),
correct_w1.c0.retrieve().to_be_bytes(),
@@ -743,24 +794,25 @@ mod g2_frob_tests {
let p = FIELD_MODULUS;
let pm1 = p.wrapping_sub(&crypto_bigint::U256::ONE);
let u = Fp2 { c0: Fp::ZERO, c1: Fp::ONE };
let u = Fp2 {
c0: Fp::ZERO,
c1: Fp::ONE,
};
let pm1_div2 = pm1.wrapping_shr(1);
let u_pm1_div2 = fp2_pow_exp(&u, &pm1_div2);
let (pm1_div3, _) = pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(3u32)).unwrap());
let (pm1_div3, _) =
pm1.div_rem(&crypto_bigint::NonZero::new(crypto_bigint::U256::from(3u32)).unwrap());
let u_pm1_div3 = fp2_pow_exp(&u, &pm1_div3);
let pp1 = p.wrapping_add(&crypto_bigint::U256::ONE);
let u_pm21_div3 = fp2_pow_exp(&u_pm1_div3, &pp1);
let u_pm21_div2 = fp2_pow_exp(&u_pm1_div2, &pp1);
// Reason: 验证 G2 Frobenius 修正常量与计算值一致
// u^{(p-1)/2} 应等于 G2_FROB_Y1
assert_eq!(u_pm1_div2, G2_FROB_Y1,
"u^(p-1)/2 应等于 G2_FROB_Y1");
assert_eq!(u_pm1_div2, G2_FROB_Y1, "u^(p-1)/2 应等于 G2_FROB_Y1");
// u^{(p²-1)/3} 应等于 G2_FROB_X2
assert_eq!(u_pm21_div3, G2_FROB_X2,
"u^(p2-1)/3 应等于 G2_FROB_X2");
assert_eq!(u_pm21_div3, G2_FROB_X2, "u^(p2-1)/3 应等于 G2_FROB_X2");
}
}
+1 -2
View File
@@ -1,6 +1,6 @@
//! SM9 BN256 二次扩域 Fp2
//!
//! Fp2 = Fp[u] / (u² + 2)
//! `Fp2 = Fp[u] / (u² + 2)`
//! 即 u² = -2
//!
//! 元素表示为 a = a0 + a1·u,其中 a0, a1 ∈ Fp
@@ -177,7 +177,6 @@ pub fn fp2_conjugate(a: &Fp2) -> Fp2 {
#[cfg(test)]
mod tests {
use super::*;
use crate::sm9::fields::fp::fp_from_bytes;
fn fp2_one() -> Fp2 {
Fp2::ONE
+1 -1
View File
@@ -88,7 +88,7 @@ impl G1Jacobian {
/// 点倍运算(BN256 a=0 专用公式)
///
/// 公式来自 https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l
/// 公式来自 <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l>
pub fn double(&self) -> Self {
if self.is_infinity() {
return *self;
+4 -4
View File
@@ -149,9 +149,9 @@ impl G2Jacobian {
// c = -3X₁²·Z₁² = -e·z1sq(在 eval_line_at_p 中乘以 xP→c1.c2(v²w)
let z1sq = fp2_square(z1);
let line = LineEval {
a: fp2_mul_u(&fp2_mul(&z3, &z1sq)), // 2Y₁Z₁³·u(×yP→c0.c0
a: fp2_mul_u(&fp2_mul(&z3, &z1sq)), // 2Y₁Z₁³·u(×yP→c0.c0
b: fp2_sub(&fp2_mul(x1, &e), &fp2_add(&b, &b)), // 3X₁³-2Y₁²(→c1.c1(vw)
c: fp2_neg(&fp2_mul(&e, &z1sq)), // -3X₁²Z₁²(×xP→c1.c2(v²w)
c: fp2_neg(&fp2_mul(&e, &z1sq)), // -3X₁²Z₁²(×xP→c1.c2(v²w)
};
(
@@ -190,9 +190,9 @@ impl G2Jacobian {
// b = X₁·Y₂·Z₁ - X₂·Y₁(常数项→c1.c1(vw)
// c = -(Y₂Z₁³-Y₁) = -rr已算,在 eval_line_at_p 中乘以 xP→c1.c2(v²w)
let line = LineEval {
a: fp2_mul_u(&z3), // H·Z₁·u(×yP→c0.c0
a: fp2_mul_u(&z3), // H·Z₁·u(×yP→c0.c0
b: fp2_sub(&fp2_mul(&fp2_mul(x1, z1), y2), &fp2_mul(x2, y1)), // X₁Y₂Z₁-X₂Y₁(→c1.c1(vw)
c: fp2_neg(&r), // -(Y₂Z₁³-Y₁)(×xP→c1.c2(v²w)
c: fp2_neg(&r), // -(Y₂Z₁³-Y₁)(×xP→c1.c2(v²w)
};
(
+86 -36
View File
@@ -158,7 +158,7 @@ pub fn generate_sign_master_keypair<R: RngCore>(rng: &mut R) -> (Sm9MasterPrivKe
/// GB/T 38635.2-2020 §6.1
/// t1 = H1(ID||hid, N) + ks
/// t2 = ks · t1^{-1} mod N(注意:不是 t1^{-1}·P1,而是 ks·t1^{-1}·P1
/// dA = [t2]P1
/// dA = \[t2\]P1
/// hid = 0x01(签名)
pub fn generate_sign_user_key(
master_priv: &Sm9MasterPrivKey,
@@ -218,7 +218,7 @@ pub fn generate_enc_master_keypair<R: RngCore>(rng: &mut R) -> (Sm9MasterPrivKey
/// GB/T 38635.1-2020 §6.1(加密密钥派生):
/// t1 = H1(ID||hid, N) + ke
/// t2 = ke · t1^{-1} mod N
/// de = [t2]P1
/// de = \[t2\]P1
pub fn generate_enc_user_key(
master_priv: &Sm9MasterPrivKey,
id: &[u8],
@@ -576,7 +576,7 @@ mod tests {
#[test]
fn test_generate_sign_master_keypair() {
let mut rng = FakeRng([0x42u8; 32]);
let (ks, ppub) = generate_sign_master_keypair(&mut rng);
let (_ks, ppub) = generate_sign_master_keypair(&mut rng);
// 验证 ppub 在 G2 上
let p = G2Affine::from_bytes(ppub.as_bytes()).expect("公钥应有效");
assert!(p.is_on_curve());
@@ -607,7 +607,7 @@ mod tests {
#[test]
fn test_pairing_bilinear() {
use crate::sm9::fields::fp12::{fp12_mul, Fp12};
use crate::sm9::fields::fp12::fp12_mul;
use crate::sm9::groups::g1::{G1Affine, G1Jacobian};
use crate::sm9::groups::g2::{G2Affine, G2Jacobian};
use crate::sm9::pairing::pairing;
@@ -617,24 +617,33 @@ mod tests {
let q = G2Affine::generator();
// 验证 G1 scalar_mul(2) == G1.double()
let g1_2_by_mul = G1Jacobian::scalar_mul_g1(&U256::from(2u32)).to_affine().unwrap();
let g1_2_by_mul = G1Jacobian::scalar_mul_g1(&U256::from(2u32))
.to_affine()
.unwrap();
let g1_jac = G1Jacobian::from_affine(&p);
let g1_2_by_double = g1_jac.double().to_affine().unwrap();
use crate::sm9::fields::fp::fp_to_bytes;
assert_eq!(
fp_to_bytes(&g1_2_by_mul.x), fp_to_bytes(&g1_2_by_double.x),
fp_to_bytes(&g1_2_by_mul.x),
fp_to_bytes(&g1_2_by_double.x),
"G1 scalar_mul(2) != G1.double() in x"
);
assert_eq!(
fp_to_bytes(&g1_2_by_mul.y), fp_to_bytes(&g1_2_by_double.y),
fp_to_bytes(&g1_2_by_mul.y),
fp_to_bytes(&g1_2_by_double.y),
"G1 scalar_mul(2) != G1.double() in y"
);
// 验证 G2 scalar_mul(2) == G2.double()
let g2_jac = G2Jacobian::from_affine(&q);
let g2_2_by_mul = G2Jacobian::scalar_mul_g2(&U256::from(2u32)).to_affine().unwrap();
let g2_2_by_mul = G2Jacobian::scalar_mul_g2(&U256::from(2u32))
.to_affine()
.unwrap();
let g2_2_by_double = g2_jac.double().to_affine().unwrap();
assert_eq!(g2_2_by_mul, g2_2_by_double, "G2 scalar_mul(2) != G2.double()");
assert_eq!(
g2_2_by_mul, g2_2_by_double,
"G2 scalar_mul(2) != G2.double()"
);
// e(2G1, G2) == e(G1, G2)^2
let e_2g1_g2 = pairing(&g1_2_by_mul, &q);
@@ -643,25 +652,30 @@ mod tests {
// 中间验证:用 G1+G1 (点加法)得到 2G1
let g1_jac2 = G1Jacobian::from_affine(&p);
let g1_add_g1 = G1Jacobian::add(&G1Jacobian::from_affine(&p), &g1_jac2).to_affine().unwrap();
let g1_add_g1 = G1Jacobian::add(&G1Jacobian::from_affine(&p), &g1_jac2)
.to_affine()
.unwrap();
let e_addg1_g2 = pairing(&g1_add_g1, &q);
assert_eq!(e_addg1_g2, e_sq, "e(G1+G1,G2) != e(G1,G2)²(用点加法)");
assert_eq!(e_2g1_g2, e_sq, "配对双线性性验证失败:e(2G1,G2) != e(G1,G2)²");
assert_eq!(
e_2g1_g2, e_sq,
"配对双线性性验证失败:e(2G1,G2) != e(G1,G2)²"
);
// e(G1, 2G2) == e(G1, G2)^2
let e_g1_2g2 = pairing(&p, &g2_2_by_mul);
assert_eq!(e_g1_2g2, e_sq, "配对双线性性验证失败:e(G1,2G2) != e(G1,G2)²");
assert_eq!(
e_g1_2g2, e_sq,
"配对双线性性验证失败:e(G1,2G2) != e(G1,G2)²"
);
}
}
#[cfg(test)]
mod pairing_tests {
use super::*;
use crate::sm9::fields::fp12::{
fp12_conjugate, fp12_frobenius_p, fp12_frobenius_p2, fp12_frobenius_p3,
fp12_inv, fp12_mul, fp12_square, Fp12,
};
use crate::sm9::fields::fp12::{fp12_conjugate, fp12_frobenius_p, fp12_mul, fp12_square, Fp12};
use crate::sm9::groups::g1::{G1Affine, G1Jacobian};
use crate::sm9::groups::g2::{G2Affine, G2Jacobian};
use crate::sm9::pairing::{final_exp, miller_loop, pairing};
@@ -671,7 +685,9 @@ mod pairing_tests {
fn test_pairing_double_only() {
let p = G1Affine::generator();
let q = G2Affine::generator();
let g1_2 = G1Jacobian::scalar_mul_g1(&U256::from(2u32)).to_affine().unwrap();
let g1_2 = G1Jacobian::scalar_mul_g1(&U256::from(2u32))
.to_affine()
.unwrap();
let e_g1_g2 = pairing(&p, &q);
let e_sq = fp12_mul(&e_g1_g2, &e_g1_g2);
@@ -687,7 +703,9 @@ mod pairing_tests {
fn test_miller_loop_raw_bilinear() {
let p = G1Affine::generator();
let q = G2Affine::generator();
let g1_2 = G1Jacobian::scalar_mul_g1(&U256::from(2u32)).to_affine().unwrap();
let g1_2 = G1Jacobian::scalar_mul_g1(&U256::from(2u32))
.to_affine()
.unwrap();
let ml1 = miller_loop(&q, &p);
let ml2 = miller_loop(&q, &g1_2);
@@ -699,14 +717,20 @@ mod pairing_tests {
let ml1_sq_inv = fp12_inv(&ml1_sq).expect("inv should exist");
let ratio = fp12_mul(&ml2, &ml1_sq_inv);
let ratio_exp = final_exp(&ratio);
assert_eq!(ratio_exp, Fp12::ONE, "final_exp(ml(2G1,G2)/ml(G1,G2)^2) != 1");
assert_eq!(
ratio_exp,
Fp12::ONE,
"final_exp(ml(2G1,G2)/ml(G1,G2)^2) != 1"
);
}
#[test]
fn test_miller_loop_bilinear() {
let p = G1Affine::generator();
let q = G2Affine::generator();
let g1_2 = G1Jacobian::scalar_mul_g1(&U256::from(2u32)).to_affine().unwrap();
let g1_2 = G1Jacobian::scalar_mul_g1(&U256::from(2u32))
.to_affine()
.unwrap();
let ml_g1_g2 = miller_loop(&q, &p);
let ml_2g1_g2 = miller_loop(&q, &g1_2);
@@ -714,7 +738,10 @@ mod pairing_tests {
let gt1 = final_exp(&ml_g1_g2);
let gt2 = final_exp(&ml_2g1_g2);
let gt1_sq = fp12_square(&gt1);
assert_eq!(gt2, gt1_sq, "final_exp(ml(2G1,G2)) != final_exp(ml(G1,G2))^2");
assert_eq!(
gt2, gt1_sq,
"final_exp(ml(2G1,G2)) != final_exp(ml(G1,G2))^2"
);
}
#[test]
@@ -736,7 +763,11 @@ mod pairing_tests {
base = fp12_mul(&base, &base);
}
}
assert_eq!(result, Fp12::ONE, "e(G1,G2)^n != 1: GT element not in subgroup");
assert_eq!(
result,
Fp12::ONE,
"e(G1,G2)^n != 1: GT element not in subgroup"
);
}
/// 验证 ml^{p^6} == conjugate(ml)Frobenius 正确性检查)
@@ -746,8 +777,9 @@ mod pairing_tests {
let q = G2Affine::generator();
let ml = miller_loop(&q, &p);
let ml_p6 = fp12_frobenius_p(&fp12_frobenius_p(&fp12_frobenius_p(
&fp12_frobenius_p(&fp12_frobenius_p(&fp12_frobenius_p(&ml))))));
let ml_p6 = fp12_frobenius_p(&fp12_frobenius_p(&fp12_frobenius_p(&fp12_frobenius_p(
&fp12_frobenius_p(&fp12_frobenius_p(&ml)),
))));
let ml_conj = fp12_conjugate(&ml);
assert_eq!(ml_p6, ml_conj, "ml^{{p^6}} != conjugate(ml)");
}
@@ -759,7 +791,6 @@ mod pairing_tests {
#[test]
fn test_single_double_step_line() {
use crate::sm9::fields::fp::fp_to_bytes;
use crate::sm9::fields::fp12::fp12_inv;
let g1 = G1Affine::generator();
let g2 = G2Affine::generator();
@@ -773,7 +804,9 @@ mod pairing_tests {
let g1_jac = G1Jacobian::from_affine(&g1);
let g1_2_by_add = G1Jacobian::add(&g1_jac, &g1_jac).to_affine().unwrap();
// 用标量乘法计算 2·G1
let g1_2_by_mul = G1Jacobian::scalar_mul_g1(&U256::from(2u32)).to_affine().unwrap();
let g1_2_by_mul = G1Jacobian::scalar_mul_g1(&U256::from(2u32))
.to_affine()
.unwrap();
// 验证两种方式得到相同的 2G1
assert_eq!(
@@ -788,7 +821,9 @@ mod pairing_tests {
);
// 检验 G2 侧双线性性:e(G1, 2G2) == e(G1, G2)^2
let g2_2 = G2Jacobian::scalar_mul_g2(&U256::from(2u32)).to_affine().unwrap();
let g2_2 = G2Jacobian::scalar_mul_g2(&U256::from(2u32))
.to_affine()
.unwrap();
let e_g1_2g2 = pairing(&g1, &g2_2);
let e_g1_g2_sq = fp12_mul(&e1, &e1);
assert_eq!(
@@ -804,25 +839,40 @@ mod pairing_tests {
/// 约定:a -> c0.c0(1 slot), b -> c0.c1(v slot), c -> c1.c0(w slot)
#[test]
fn test_line_eval_equivalence() {
use crate::sm9::fields::fp12::{
fp12_mul, fp12_mul_by_line, Fp12, Fp6, LineEval,
};
use crate::sm9::fields::fp2::Fp2;
use crate::sm9::fields::fp::Fp;
use crate::sm9::fields::fp12::{fp12_mul, fp12_mul_by_line, Fp12, Fp6, LineEval};
use crate::sm9::fields::fp2::Fp2;
// 验证 fp12_mul_by_line 等价于按约定槽位构造 full Fp12 再乘
// 约定:a -> c0.c0(1 slot), b -> c1.c1(vw slot), c -> c1.c2(v²w slot)
let line = LineEval {
a: Fp2 { c0: Fp::ONE, c1: Fp::ZERO },
b: Fp2 { c0: Fp::ONE, c1: Fp::ZERO },
c: Fp2 { c0: Fp::ONE, c1: Fp::ZERO },
a: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
b: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
c: Fp2 {
c0: Fp::ONE,
c1: Fp::ZERO,
},
};
let f = Fp12::ONE;
let sparse_result = fp12_mul_by_line(&f, &line);
// 按相同槽位手动构造 full Fp12(槽位 {c0.c0=a, c1.c1(vw)=b, c1.c2(v²w)=c}
let full_line = Fp12 {
c0: Fp6 { c0: line.a, c1: Fp2::ZERO, c2: Fp2::ZERO },
c1: Fp6 { c0: Fp2::ZERO, c1: line.b, c2: line.c },
c0: Fp6 {
c0: line.a,
c1: Fp2::ZERO,
c2: Fp2::ZERO,
},
c1: Fp6 {
c0: Fp2::ZERO,
c1: line.b,
c2: line.c,
},
};
let full_result = fp12_mul(&f, &full_line);
assert_eq!(
+21 -22
View File
@@ -9,9 +9,8 @@
use crate::sm9::fields::fp::Fp;
use crate::sm9::fields::fp12::{
fp12_conjugate, fp12_frobenius_p, fp12_frobenius_p2, fp12_frobenius_p3,
fp12_inv, fp12_mul, fp12_mul_by_line, fp12_square, Fp12, LineEval,
G2_FROB_X1_INV, G2_FROB_Y1_INV, G2_FROB_X2_INV,
fp12_conjugate, fp12_frobenius_p, fp12_frobenius_p2, fp12_frobenius_p3, fp12_inv, fp12_mul,
fp12_mul_by_line, fp12_square, Fp12, LineEval, G2_FROB_X1_INV, G2_FROB_X2_INV, G2_FROB_Y1_INV,
};
use crate::sm9::fields::fp2::{fp2_frobenius, fp2_mul, fp2_mul_fp};
use crate::sm9::groups::g1::G1Affine;
@@ -63,7 +62,7 @@ fn g2_frobenius_p2_neg(q: &G2Affine) -> G2Affine {
fn eval_line_at_p(line: &LineEval, px: &Fp, py: &Fp) -> LineEval {
LineEval {
a: fp2_mul_fp(&line.a, py), // a × yP(放 c0.c0 槽)
b: line.b, // 常数项不变(放 c0.c1 v 槽)
b: line.b, // 常数项不变(放 c0.c1 v 槽)
c: fp2_mul_fp(&line.c, px), // c × xP(放 c1.c0 w 槽)
}
}
@@ -167,25 +166,25 @@ const SM9_NINE: u128 = 9;
/// Reason: Beuchat et al. 分解针对标准 BN256(以太坊参数),不适用于 SM9 BN256。
/// 此函数使用 sm9_core 的 final_exp_last_chunk 算法(基于 SM9_A2/A3 常量)。
fn final_exp_hard(f: &Fp12) -> Fp12 {
let a = fp12_cyclotomic_pow(f, SM9_A3); // f^{A3}
let b = fp12_inv(&a).unwrap_or(Fp12::ONE); // f^{-A3}
let c = fp12_frobenius_p(&b); // f^{-A3*p}
let d = fp12_mul(&c, &b); // f^{-A3*(p+1)}
let e = fp12_mul(&d, &b); // f^{-A3*(p+2)}
let f_p1 = fp12_frobenius_p(f); // f^p
let g = fp12_mul(f, &f_p1); // f^{p+1}
let h = fp12_cyclotomic_pow(&g, SM9_NINE); // f^{9(p+1)}
let i = fp12_mul(&e, &h); // f^{-A3*(p+2)+9(p+1)}
let j = fp12_square(f); // f^2
let k = fp12_square(&j); // f^4
let l = fp12_mul(&k, &i); // f^{4 + -A3*(p+2) + 9(p+1)}
let m = fp12_square(&f_p1); // f^{2p}
let n = fp12_mul(&d, &m); // f^{-A3*(p+1)+2p}
let o = fp12_frobenius_p2(f); // f^{p^2}
let p_var = fp12_mul(&o, &n); // f^{p^2-A3*(p+1)+2p}
let q = fp12_cyclotomic_pow(&p_var, SM9_A2); // ...^{A2}
let a = fp12_cyclotomic_pow(f, SM9_A3); // f^{A3}
let b = fp12_inv(&a).unwrap_or(Fp12::ONE); // f^{-A3}
let c = fp12_frobenius_p(&b); // f^{-A3*p}
let d = fp12_mul(&c, &b); // f^{-A3*(p+1)}
let e = fp12_mul(&d, &b); // f^{-A3*(p+2)}
let f_p1 = fp12_frobenius_p(f); // f^p
let g = fp12_mul(f, &f_p1); // f^{p+1}
let h = fp12_cyclotomic_pow(&g, SM9_NINE); // f^{9(p+1)}
let i = fp12_mul(&e, &h); // f^{-A3*(p+2)+9(p+1)}
let j = fp12_square(f); // f^2
let k = fp12_square(&j); // f^4
let l = fp12_mul(&k, &i); // f^{4 + -A3*(p+2) + 9(p+1)}
let m = fp12_square(&f_p1); // f^{2p}
let n = fp12_mul(&d, &m); // f^{-A3*(p+1)+2p}
let o = fp12_frobenius_p2(f); // f^{p^2}
let p_var = fp12_mul(&o, &n); // f^{p^2-A3*(p+1)+2p}
let q = fp12_cyclotomic_pow(&p_var, SM9_A2); // ...^{A2}
let r = fp12_mul(&q, &l);
let s = fp12_frobenius_p3(f); // f^{p^3}
let s = fp12_frobenius_p3(f); // f^{p^3}
fp12_mul(&s, &r)
}
+12 -24
View File
@@ -13,14 +13,12 @@ use libsmx::sm2::{get_e, get_z, sign_with_k, verify, PrivateKey};
fn test_sm2_sign_verify_with_known_key() {
// GB/T 32918.2-2016 附录 A 私钥
let d_bytes =
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8")
.unwrap();
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8").unwrap();
let k_bytes =
hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21")
.unwrap();
hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21").unwrap();
let pri_key = PrivateKey::from_bytes(d_bytes.as_slice().try_into().unwrap())
.expect("私钥应有效");
let pri_key =
PrivateKey::from_bytes(d_bytes.as_slice().try_into().unwrap()).expect("私钥应有效");
let pub_key = pri_key.public_key();
let id = b"ALICE123@YAHOO.COM";
@@ -43,15 +41,13 @@ fn test_sm2_sign_verify_with_known_key() {
#[test]
fn test_sm2_different_messages_different_sigs() {
let d_bytes =
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8")
.unwrap();
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8").unwrap();
let pri_key = PrivateKey::from_bytes(d_bytes.as_slice().try_into().unwrap()).unwrap();
let pub_key = pri_key.public_key();
let id = b"test_user";
let k = U256::from_be_slice(
&hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21")
.unwrap(),
&hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21").unwrap(),
);
let z = get_z(id, &pub_key);
@@ -69,8 +65,7 @@ fn test_sm2_different_messages_different_sigs() {
#[test]
fn test_sm2_verify_tampered_message_fails() {
let d_bytes =
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8")
.unwrap();
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8").unwrap();
let pri_key = PrivateKey::from_bytes(d_bytes.as_slice().try_into().unwrap()).unwrap();
let pub_key = pri_key.public_key();
@@ -80,8 +75,7 @@ fn test_sm2_verify_tampered_message_fails() {
let e = get_e(&z, msg);
let k = U256::from_be_slice(
&hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21")
.unwrap(),
&hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21").unwrap(),
);
let sig = sign_with_k(&e, &pri_key, &k).unwrap();
@@ -97,8 +91,7 @@ fn test_sm2_verify_tampered_message_fails() {
#[test]
fn test_sm2_verify_tampered_sig_fails() {
let d_bytes =
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8")
.unwrap();
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8").unwrap();
let pri_key = PrivateKey::from_bytes(d_bytes.as_slice().try_into().unwrap()).unwrap();
let pub_key = pri_key.public_key();
@@ -108,24 +101,19 @@ fn test_sm2_verify_tampered_sig_fails() {
let e = get_e(&z, msg);
let k = U256::from_be_slice(
&hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21")
.unwrap(),
&hex::decode("59276e27d506861a16680f3ad9c02dccef3cc1fa3cdbe4ce6d54b80deac1bc21").unwrap(),
);
let mut sig = sign_with_k(&e, &pri_key, &k).unwrap();
sig[0] ^= 1; // 篡改 r 的第一字节
assert!(
verify(&e, &pub_key, &sig).is_err(),
"篡改签名后验签应失败"
);
assert!(verify(&e, &pub_key, &sig).is_err(), "篡改签名后验签应失败");
}
/// Z 值计算确定性验证(相同输入产生相同 Z)
#[test]
fn test_sm2_z_value_deterministic() {
let d_bytes =
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8")
.unwrap();
hex::decode("3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8").unwrap();
let pri_key = PrivateKey::from_bytes(d_bytes.as_slice().try_into().unwrap()).unwrap();
let pub_key = pri_key.public_key();
+16 -8
View File
@@ -12,9 +12,13 @@ use libsmx::sm3::Sm3Hasher;
fn test_sm3_vector_a1_abc() {
let msg = b"abc";
let digest = Sm3Hasher::digest(msg);
let expected = hex::decode("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0")
.unwrap();
assert_eq!(digest.as_slice(), expected.as_slice(), "GB/T 32905 附录 A.1 失败");
let expected =
hex::decode("66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0").unwrap();
assert_eq!(
digest.as_slice(),
expected.as_slice(),
"GB/T 32905 附录 A.1 失败"
);
}
/// GB/T 32905-2016 附录 A.2
@@ -24,9 +28,13 @@ fn test_sm3_vector_a1_abc() {
fn test_sm3_vector_a2_64bytes() {
let msg = b"abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
let digest = Sm3Hasher::digest(msg);
let expected = hex::decode("debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732")
.unwrap();
assert_eq!(digest.as_slice(), expected.as_slice(), "GB/T 32905 附录 A.2 失败");
let expected =
hex::decode("debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732").unwrap();
assert_eq!(
digest.as_slice(),
expected.as_slice(),
"GB/T 32905 附录 A.2 失败"
);
}
/// 流式接口与单次接口结果一致性验证
@@ -48,7 +56,7 @@ fn test_sm3_streaming_equals_oneshot() {
#[test]
fn test_sm3_empty_message() {
let digest = Sm3Hasher::digest(b"");
let expected = hex::decode("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b")
.unwrap();
let expected =
hex::decode("1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b").unwrap();
assert_eq!(digest.as_slice(), expected.as_slice(), "SM3 空消息测试失败");
}
+4 -4
View File
@@ -4,8 +4,8 @@
use libsmx::sm9::{
generate_enc_master_keypair, generate_enc_user_key, generate_sign_master_keypair,
generate_sign_user_key, sm9_decrypt, sm9_encrypt, sm9_sign, sm9_verify,
Sm9EncPubKey, Sm9MasterPrivKey, Sm9SignPubKey,
generate_sign_user_key, sm9_decrypt, sm9_encrypt, sm9_sign, sm9_verify, Sm9EncPubKey,
Sm9SignPubKey,
};
use rand_core::RngCore;
@@ -180,11 +180,11 @@ mod pairing_reference_tests {
/// This tests with a hardcoded known-good pairing value
#[test]
fn test_pairing_against_sm9core() {
use sm9_core::{G1, G2, Group};
use libsmx::sm9::fields::fp12::fp12_to_bytes;
use libsmx::sm9::groups::g1::G1Affine;
use libsmx::sm9::groups::g2::G2Affine;
use libsmx::sm9::pairing::pairing;
use libsmx::sm9::fields::fp12::fp12_to_bytes;
use sm9_core::{Group, G1, G2};
// Get sm9_core reference pairing of generators
let g1_ref = G1::one();