重构:RustCrypto 兼容接口

主要变更:
- 新增 sm2/、sm3/、sm4/ 独立 crate,实现 RustCrypto traits
  - sm2:实现 signature、elliptic-curve traits
  - sm3:实现 digest、crypto-common traits
  - sm4:实现 cipher、aead traits
- 新增 fuzz/ 模糊测试目标
- 新增 tests/sm2_proptest.rs 属性测试
- 重构 src/sm4/ 使用 RustCrypto AEAD traits
- 更新 CI 工作流支持多 crate 测试
- 更新 .gitignore 忽略 fuzz/target/
This commit is contained in:
huangxt
2026-03-11 18:32:21 +08:00
parent 21e7e65b32
commit a4ca734d0d
36 changed files with 5184 additions and 961 deletions
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "sm2"
version = "0.1.0"
edition = "2021"
rust-version = "1.83.0"
license = "Apache-2.0"
description = "SM2 (ShangMi 2) elliptic curve cryptography — signature, encryption, key exchange (GB/T 32918-2016). Pure-Rust, no_std."
repository = "https://github.com/kintaiW/libsmx"
documentation = "https://docs.rs/sm2"
readme = "README.md"
categories = ["cryptography", "no-std"]
keywords = ["crypto", "ecc", "sm2", "shangmi", "signature"]
[dependencies]
# SM3 is used by SM2 for Z-value and message digest computation
sm3 = { path = "../sm3" }
# digest::Digest + Update traits (re-exported by sm3, but listed explicitly for clarity)
digest = { workspace = true }
crypto-bigint = { version = "0.6", default-features = false }
subtle = { workspace = true }
zeroize = { workspace = true }
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
# signature trait integration
signature = { workspace = true }
[dev-dependencies]
hex-literal = { workspace = true }
rand = { version = "0.8", default-features = false, features = ["std_rng"] }
[features]
default = ["alloc"]
# alloc: required for encrypt/decrypt (Vec-based ciphertext) and DER encoding
alloc = []
# hazmat: exposes sign_with_k (dangerous raw-k API, for testing only)
hazmat = []
+223
View File
@@ -0,0 +1,223 @@
# sm2
**SM2 椭圆曲线公钥密码算法** — 纯 Rust、`no_std`、常量时间实现,符合 GB/T 32918-2016。
**SM2 Elliptic Curve Public-Key Cryptography** — Pure-Rust, `no_std`, constant-time implementation conforming to GB/T 32918-2016.
[![Crates.io](https://img.shields.io/crates/v/sm2.svg)](https://crates.io/crates/sm2)
[![Docs.rs](https://docs.rs/sm2/badge.svg)](https://docs.rs/sm2)
[![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](../LICENSE)
---
## 目录 / Table of Contents
- [功能 / Features](#功能--features)
- [快速开始 / Quick Start](#快速开始--quick-start)
- [API 概览 / API Overview](#api-概览--api-overview)
- [安全性 / Security](#安全性--security)
- [Feature 标志 / Feature Flags](#feature-标志--feature-flags)
- [依赖 / Dependencies](#依赖--dependencies)
---
## 功能 / Features
**中文:** 本 crate 实现以下 GB/T 32918-2016 标准算法:
**English:** This crate implements the following GB/T 32918-2016 standard algorithms:
| 功能 / Feature | 函数/类型 / Function / Type | 标准 / Standard |
|---|---|---|
| 密钥生成 / Key Generation | `generate_keypair` | GB/T 32918.1 §6.1 |
| 数字签名 / Digital Signature | `sign`, `sign_message`, `SigningKey` | GB/T 32918.2 §6.2 |
| 签名验证 / Verification | `verify`, `verify_message`, `VerifyingKey` | GB/T 32918.2 §6.3 |
| 公钥加密 / Encryption | `encrypt` | GB/T 32918.4 §7.1 |
| 公钥解密 / Decryption | `decrypt` | GB/T 32918.4 §7.2 |
| 密钥交换 / Key Exchange | `key_exchange::ecdh`, `exchange_a/b` | GB/T 32918.3 |
| DER 编解码 / DER Encoding | `der::sig_to_der`, `private_key_from_pkcs8_der` | RFC 5915/5480 |
---
## 快速开始 / Quick Start
`Cargo.toml` 中添加 / Add to your `Cargo.toml`:
```toml
[dependencies]
sm2 = { path = "path/to/sm2" }
# 或 crates.io 发布后 / or after crates.io release:
# sm2 = "0.1"
```
### 签名与验签 / Sign and Verify
```rust
use sm2::{SigningKey, VerifyingKey, DEFAULT_ID, generate_keypair};
use sm2::signature::{Signer, Verifier};
use rand_core::OsRng;
// 生成密钥对 / Generate key pair
let (private_key, public_key_bytes) = generate_keypair(&mut OsRng);
// 创建签名/验证密钥 / Create signing and verifying keys
let signing_key = SigningKey::new(private_key, DEFAULT_ID);
let verifying_key = VerifyingKey::new(public_key_bytes, DEFAULT_ID);
// 签名 / Sign
let message = b"Hello, SM2!";
let signature = signing_key.sign(message);
// 验签 / Verify
verifying_key.verify(message, &signature).expect("验签应通过 / verification should pass");
```
### 底层签名 API / Low-level Signing API
```rust
use sm2::{PrivateKey, generate_keypair, get_z, get_e, sign, verify, DEFAULT_ID};
use rand_core::OsRng;
let (pri_key, pub_key) = generate_keypair(&mut OsRng);
// 计算 Z 值和摘要 / Compute Z-value and digest
let z = get_z(DEFAULT_ID, &pub_key);
let e = get_e(&z, b"my message");
// 签名 / Sign
let sig = sign(&e, &pri_key, &mut OsRng);
// 验签 / Verify
verify(&e, &pub_key, &sig).expect("ok");
```
### 公钥加解密 / Public-key Encrypt / Decrypt
```rust
# #[cfg(feature = "alloc")]
use sm2::{encrypt, decrypt, generate_keypair};
use rand_core::OsRng;
let (pri_key, pub_key) = generate_keypair(&mut OsRng);
let plaintext = b"secret message";
let ciphertext = encrypt(&pub_key, plaintext, &mut OsRng).unwrap();
let recovered = decrypt(&pri_key, &ciphertext).unwrap();
assert_eq!(recovered, plaintext);
```
密文格式为 `C1 || C3 || C2`(65 + 32 + n 字节),符合 GB/T 32918.4 §6.1。
Ciphertext format: `C1 || C3 || C2` (65 + 32 + n bytes), per GB/T 32918.4 §6.1.
### SM2-ECDH 密钥交换 / Key Exchange
```rust
use sm2::{generate_keypair, key_exchange::ecdh};
use rand_core::OsRng;
let (pri_a, pub_a) = generate_keypair(&mut OsRng);
let (pri_b, pub_b) = generate_keypair(&mut OsRng);
// 双方各自计算,结果一致 / Both parties compute the same shared secret
let shared_a = ecdh(&pri_a, &pub_b).unwrap();
let shared_b = ecdh(&pri_b, &pub_a).unwrap();
assert_eq!(shared_a, shared_b);
```
---
## API 概览 / API Overview
### 类型 / Types
| 类型 / Type | 说明 / Description |
|---|---|
| `PrivateKey` | SM2 私钥(32 字节,离开作用域自动清零)/ SM2 private key (auto-zeroized on drop) |
| `SigningKey<'id>` | 签名密钥(私钥 + 用户 ID/ Signing key (private key + user ID) |
| `VerifyingKey<'id>` | 验证密钥(公钥 + 用户 ID/ Verifying key (public key + user ID) |
| `Sm2Signature` | 签名结果(r\|\|s64 字节)/ Signature (r\|\|s, 64 bytes) |
| `key_exchange::EphemeralKey` | 密钥交换临时密钥对 / Ephemeral key pair for key exchange |
| `Error` | 统一错误类型 / Unified error type |
### 常量 / Constants
| 常量 / Constant | 值 / Value | 说明 / Description |
|---|---|---|
| `DEFAULT_ID` | `b"1234567812345678"` | GB/T 32918.2 §A.2 示例用户 ID / Example user ID from spec |
### 关键函数 / Key Functions
```
generate_keypair(rng) → (PrivateKey, [u8; 65])
get_z(id, pub_key) → [u8; 32]
get_e(z, msg) → [u8; 32]
sign(e, pri_key, rng) → [u8; 64]
sign_message(msg, id, pri, rng) → [u8; 64]
verify(e, pub_key, sig) → Result<(), Error>
verify_message(msg, id, pub, sig) → Result<(), Error>
encrypt(pub_key, msg, rng) → Result<Vec<u8>, Error> // alloc
decrypt(pri_key, ciphertext) → Result<Vec<u8>, Error> // alloc
```
---
## 安全性 / Security
**中文:**
- **常量时间**:所有私钥相关运算均为常量时间(Montgomery 域算术 + `subtle::ConditionallySelectable`
- **标量乘法**:固定迭代 256 位,不跳过前导零,防止时序侧信道
- **自动清零**`PrivateKey` 离开作用域后自动清零([`ZeroizeOnDrop`]
- **无 unsafe**:全 crate 使用 `#![forbid(unsafe_code)]`
- **SM4 S-box**:(通过 `sm4` 依赖)使用布尔电路位切片实现,无查表
**English:**
- **Constant-time**: All secret-dependent operations use Montgomery-domain arithmetic + `subtle::ConditionallySelectable`
- **Scalar multiplication**: Iterates all 256 bits regardless of leading zeros — no timing leakage
- **Auto-zeroize**: `PrivateKey` is automatically cleared on drop via [`ZeroizeOnDrop`]
- **No unsafe code**: The entire crate is `#![forbid(unsafe_code)]`
- **Bitslice S-box**: (via `sm4` dependency) uses boolean-circuit implementation, no table lookups
> **危险 API / Dangerous API**: `sign_with_k` 仅在启用 `hazmat` feature 时可用,用于测试向量验证。误用相同 k 值会泄露私钥。
>
> `sign_with_k` is only available with the `hazmat` feature, intended for test-vector validation only. Reusing k across signatures leaks the private key.
---
## Feature 标志 / Feature Flags
| Feature | 默认启用 / Default | 说明 / Description |
|---|---|---|
| `alloc` | ✅ | 启用 `encrypt`/`decrypt` 和 DER 编码(需要 `Vec`/ Enables `encrypt`/`decrypt` and DER encoding (requires `Vec`) |
| `hazmat` | ❌ | 暴露 `sign_with_k`(危险的固定 k 签名,仅用于测试)/ Exposes `sign_with_k` (dangerous fixed-k signing, test only) |
---
## 依赖 / Dependencies
| Crate | 版本 / Version | 用途 / Purpose |
|---|---|---|
| `sm3` | workspace | SM3 哈希(Z 值、消息摘要、KDF/ SM3 hash (Z-value, digest, KDF) |
| `crypto-bigint` | 0.6 | 常量时间大整数(Montgomery 域)/ Constant-time big integers |
| `subtle` | 2.6 | 常量时间比较 / Constant-time comparisons |
| `zeroize` | 1.8 | 密钥安全清零 / Secure key zeroization |
| `rand_core` | 0.6 | RNG trait(含 OsRng/ RNG traits (including OsRng) |
| `signature` | 2.2 | `Signer`/`Verifier` trait 集成 / `Signer`/`Verifier` trait integration |
---
## 许可证 / License
Apache-2.0 — 见 / see [`LICENSE`](../LICENSE)
---
## 参考标准 / Reference Standards
- GB/T 32918.1-2016SM2 公钥密码算法 第1部分:总则
- GB/T 32918.2-2016SM2 公钥密码算法 第2部分:数字签名算法
- GB/T 32918.3-2016SM2 公钥密码算法 第3部分:密钥交换协议
- GB/T 32918.4-2016SM2 公钥密码算法 第4部分:公钥加密算法
- GB/T 32918.5-2017SM2 公钥密码算法 第5部分:参数定义
+533
View File
@@ -0,0 +1,533 @@
//! SM2 签名与密钥 DER 编解码
//!
//! ## 签名格式
//! TLS 使用 ASN.1 DER 格式表示签名:
//! ```text
//! SEQUENCE {
//! INTEGER r,
//! INTEGER s
//! }
//! ```
//! 而 libsmx 内部使用原始 `r||s`(64 字节)。本模块提供两者互转。
//!
//! ## 私钥格式
//! - **SEC1**RFC 5915):`ECPrivateKey SEQUENCE { version INTEGER(1), privateKey OCTET STRING, ... }`
//! - **PKCS#8**RFC 5958):`PrivateKeyInfo SEQUENCE { version INTEGER(0), algorithm, privateKey OCTET STRING(SEC1) }`
//!
//! ## 公钥 SPKI 格式
//! rustls `SigningKey::public_key()` 需要 `SubjectPublicKeyInfoDer`
//! ```text
//! SEQUENCE {
//! SEQUENCE {
//! OID id-ecPublicKey (1.2.840.10045.2.1)
//! OID SM2 (1.2.156.10197.1.301)
//! }
//! BIT STRING (04 || x(32B) || y(32B))
//! }
//! ```
//!
//! ## DER INTEGER 编码规则
//! - 去除前导零(但若最高位为 1,需在前补 0x00 防止被解析为负数)
//! - tag = 0x02length 占 1 字节(r/s < 256 位时长度 ≤ 33
//! - SEQUENCE tag = 0x30
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::error::Error;
use crate::PrivateKey;
/// 将原始签名 `r||s`64 字节)编码为 DER SEQUENCE
///
/// 输出格式:`30 <len> 02 <rlen> <r> 02 <slen> <s>`
#[cfg(feature = "alloc")]
pub fn sig_to_der(raw: &[u8; 64]) -> Vec<u8> {
let r = &raw[..32];
let s = &raw[32..];
let r_enc = encode_integer(r);
let s_enc = encode_integer(s);
let inner_len = r_enc.len() + s_enc.len();
let mut der = Vec::with_capacity(2 + inner_len);
der.push(0x30); // SEQUENCE tag
der.push(inner_len as u8); // SEQUENCE lengthinner < 256 字节)
der.extend_from_slice(&r_enc);
der.extend_from_slice(&s_enc);
der
}
/// 将 DER 编码签名解码为原始 `r||s`(64 字节)
///
/// # 错误
/// 格式不合法时返回 `Error::InvalidSignature`
pub fn sig_from_der(der: &[u8]) -> Result<[u8; 64], Error> {
let err = || Error::InvalidSignature;
// SEQUENCE tag
let (tag, rest) = split_first(der).ok_or_else(err)?;
if *tag != 0x30 {
return Err(err());
}
// SEQUENCE length
let (seq_len, rest) = split_first(rest).ok_or_else(err)?;
let seq_len = *seq_len as usize;
if rest.len() < seq_len {
return Err(err());
}
let body = &rest[..seq_len];
// 解析 r
let (r_bytes, body) = decode_integer(body).ok_or_else(err)?;
// 解析 s
let (s_bytes, body) = decode_integer(body).ok_or_else(err)?;
// 不应有多余数据
if !body.is_empty() {
return Err(err());
}
// r 和 s 都必须是正整数,不超过 32 字节
if r_bytes.is_empty() || r_bytes.len() > 33 || s_bytes.is_empty() || s_bytes.len() > 33 {
return Err(err());
}
let mut raw = [0u8; 64];
// Reason: DER INTEGER 可能有前缀 0x00(最高位保护),去除后左对齐写入 32 字节槽
let r_stripped = strip_leading_zero(r_bytes);
let s_stripped = strip_leading_zero(s_bytes);
if r_stripped.len() > 32 || s_stripped.len() > 32 {
return Err(err());
}
let r_off = 32 - r_stripped.len();
let s_off = 32 - s_stripped.len();
raw[r_off..32].copy_from_slice(r_stripped);
raw[32 + s_off..64].copy_from_slice(s_stripped);
Ok(raw)
}
// ── 内部辅助 ──────────────────────────────────────────────────────────────────
/// 将 32 字节大端整数编码为 DER INTEGER(带 tag 0x02 和 length
#[cfg(feature = "alloc")]
fn encode_integer(bytes: &[u8]) -> Vec<u8> {
// 去除前导零(至少保留 1 字节)
let start = bytes
.iter()
.position(|&b| b != 0)
.unwrap_or(bytes.len() - 1);
let val = &bytes[start..];
// 最高位为 1 时需补 0x00,防止被解析为负数
let needs_pad = val[0] & 0x80 != 0;
let val_len = val.len() + if needs_pad { 1 } else { 0 };
let mut enc = Vec::with_capacity(2 + val_len);
enc.push(0x02); // INTEGER tag
enc.push(val_len as u8); // length
if needs_pad {
enc.push(0x00);
}
enc.extend_from_slice(val);
enc
}
/// 从字节流中解析一个 DER INTEGER,返回 (value_bytes, 剩余字节)
fn decode_integer(data: &[u8]) -> Option<(&[u8], &[u8])> {
let (tag, rest) = split_first(data)?;
if *tag != 0x02 {
return None;
}
let (len, rest) = split_first(rest)?;
let len = *len as usize;
if rest.len() < len {
return None;
}
Some((&rest[..len], &rest[len..]))
}
/// 去除前导 0x00 字节
fn strip_leading_zero(bytes: &[u8]) -> &[u8] {
match bytes.iter().position(|&b| b != 0) {
Some(i) => &bytes[i..],
None => &bytes[bytes.len().saturating_sub(1)..], // 全零时保留末字节
}
}
fn split_first(data: &[u8]) -> Option<(&u8, &[u8])> {
data.split_first()
}
// ── DER 长度解码 ──────────────────────────────────────────────────────────────
/// 解析 DER 长度字段,返回 (length, 剩余字节)
///
/// 支持:单字节(< 0x80)、两字节(0x81 nn)、三字节(0x82 nn nn
fn parse_length(data: &[u8]) -> Option<(usize, &[u8])> {
let (first, rest) = data.split_first()?;
if *first < 0x80 {
// Reason: 最高位为 0 时,本字节直接表示长度
Some((*first as usize, rest))
} else if *first == 0x81 {
let (len, rest) = rest.split_first()?;
Some((*len as usize, rest))
} else if *first == 0x82 {
if rest.len() < 2 {
return None;
}
let len = (rest[0] as usize) << 8 | rest[1] as usize;
Some((len, &rest[2..]))
} else {
// 不支持更长或不定长编码
None
}
}
/// 解析一个 TLVtag-length-value),返回 (value_bytes, 剩余字节)
fn parse_tlv(data: &[u8], expected_tag: u8) -> Option<(&[u8], &[u8])> {
let (tag, rest) = data.split_first()?;
if *tag != expected_tag {
return None;
}
let (len, rest) = parse_length(rest)?;
if rest.len() < len {
return None;
}
Some((&rest[..len], &rest[len..]))
}
// ── 私钥 DER 解析 ─────────────────────────────────────────────────────────────
/// 从 SEC1 DER 解析 SM2 私钥(RFC 5915
///
/// 格式:
/// ```text
/// ECPrivateKey ::= SEQUENCE {
/// version INTEGER { ecPrivkeyVer1(1) },
/// privateKey OCTET STRING, -- 32 字节原始私钥
/// [0] ECParameters OPTIONAL,
/// [1] BIT STRING OPTIONAL
/// }
/// ```
///
/// # 错误
/// DER 格式不合法或私钥范围不合法时返回 `Error::InvalidPrivateKey`
pub fn private_key_from_sec1_der(der: &[u8]) -> Result<PrivateKey, Error> {
let err = || Error::InvalidPrivateKey;
// 解析外层 SEQUENCE
let (seq_body, _) = parse_tlv(der, 0x30).ok_or_else(err)?;
// version INTEGER,值应为 1ecPrivkeyVer1
let (ver_bytes, rest) = parse_tlv(seq_body, 0x02).ok_or_else(err)?;
if ver_bytes != [0x01] {
return Err(err());
}
// privateKey OCTET STRING32 字节)
let (key_bytes, _rest) = parse_tlv(rest, 0x04).ok_or_else(err)?;
if key_bytes.len() != 32 {
return Err(err());
}
let key_arr: &[u8; 32] = key_bytes.try_into().map_err(|_| err())?;
PrivateKey::from_bytes(key_arr)
}
/// 从 PKCS#8 DER 解析 SM2 私钥(RFC 5958
///
/// 格式:
/// ```text
/// PrivateKeyInfo ::= SEQUENCE {
/// version INTEGER (0),
/// algorithm AlgorithmIdentifier SEQUENCE { ... },
/// privateKey OCTET STRING (SEC1 DER)
/// }
/// ```
///
/// # 错误
/// DER 格式不合法或私钥范围不合法时返回 `Error::InvalidPrivateKey`
pub fn private_key_from_pkcs8_der(der: &[u8]) -> Result<PrivateKey, Error> {
let err = || Error::InvalidPrivateKey;
// 解析外层 SEQUENCEPrivateKeyInfo
let (seq_body, _) = parse_tlv(der, 0x30).ok_or_else(err)?;
// version INTEGER,值应为 0
let (ver_bytes, rest) = parse_tlv(seq_body, 0x02).ok_or_else(err)?;
if ver_bytes != [0x00] {
return Err(err());
}
// AlgorithmIdentifier SEQUENCE(跳过,不验证 OID
let (_, rest) = parse_tlv(rest, 0x30).ok_or_else(err)?;
// privateKey OCTET STRING(内含 SEC1 DER
let (sec1_der, _) = parse_tlv(rest, 0x04).ok_or_else(err)?;
private_key_from_sec1_der(sec1_der)
}
// ── SM2 公钥 SPKI DER 编码 ────────────────────────────────────────────────────
/// 将 SM2 公钥(65 字节,04||x||y)编码为 SubjectPublicKeyInfo DER
///
/// 格式(RFC 5480):
/// ```text
/// SEQUENCE {
/// SEQUENCE {
/// OID 1.2.840.10045.2.1 (id-ecPublicKey, 7 字节)
/// OID 1.2.156.10197.1.301 (SM2, 8 字节)
/// }
/// BIT STRING 0x00 || pub_key (65 字节 + 1 字节前缀)
/// }
/// ```
///
/// 此格式是 rustls `SigningKey::public_key()` 所需的 `SubjectPublicKeyInfoDer`。
#[cfg(feature = "alloc")]
pub fn public_key_to_spki_der(pub_key: &[u8; 65]) -> Vec<u8> {
// OID 1.2.840.10045.2.1 (id-ecPublicKey): 06 07 2a 86 48 ce 3d 02 01
let oid_ec: &[u8] = &[0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
// OID 1.2.156.10197.1.301 (SM2): 06 08 2a 81 1c cf 55 01 82 2d
let oid_sm2: &[u8] = &[0x06, 0x08, 0x2a, 0x81, 0x1c, 0xcf, 0x55, 0x01, 0x82, 0x2d];
// AlgorithmIdentifier SEQUENCE
let alg_inner_len = oid_ec.len() + oid_sm2.len();
let mut alg = Vec::with_capacity(2 + alg_inner_len);
alg.push(0x30);
alg.push(alg_inner_len as u8);
alg.extend_from_slice(oid_ec);
alg.extend_from_slice(oid_sm2);
// BIT STRING: 0x03 <len> 0x00 <pub_key>
// Reason: 0x00 是 unused bits 字段,表示最后一字节无填充位
let bit_str_len = 1 + pub_key.len(); // 0x00 前缀 + 65 字节公钥
let mut bit_str = Vec::with_capacity(2 + bit_str_len);
bit_str.push(0x03);
bit_str.push(bit_str_len as u8);
bit_str.push(0x00); // unused bits = 0
bit_str.extend_from_slice(pub_key);
// 外层 SEQUENCE
let outer_len = alg.len() + bit_str.len();
let mut der = Vec::with_capacity(2 + outer_len);
der.push(0x30);
der.push(outer_len as u8);
der.extend_from_slice(&alg);
der.extend_from_slice(&bit_str);
der
}
#[cfg(test)]
mod tests {
use super::*;
fn make_raw(r: [u8; 32], s: [u8; 32]) -> [u8; 64] {
let mut raw = [0u8; 64];
raw[..32].copy_from_slice(&r);
raw[32..].copy_from_slice(&s);
raw
}
#[cfg(feature = "alloc")]
#[test]
fn test_der_roundtrip_basic() {
let r = [0x01u8; 32];
let s = [0x02u8; 32];
let raw = make_raw(r, s);
let der = sig_to_der(&raw);
let recovered = sig_from_der(&der).unwrap();
assert_eq!(recovered, raw);
}
#[cfg(feature = "alloc")]
#[test]
fn test_der_roundtrip_high_bit_set() {
// r/s 最高位为 1,需要 DER 填充 0x00
let mut r = [0u8; 32];
r[0] = 0x80; // 最高位为 1
let mut s = [0u8; 32];
s[0] = 0xFF;
let raw = make_raw(r, s);
let der = sig_to_der(&raw);
// 验证 DER 中有 0x00 填充
let recovered = sig_from_der(&der).unwrap();
assert_eq!(recovered, raw);
}
#[cfg(feature = "alloc")]
#[test]
fn test_der_roundtrip_leading_zeros() {
// r 前有大量前导零
let mut r = [0u8; 32];
r[31] = 0x42; // 只有最后一字节非零
let s = [0x01u8; 32];
let raw = make_raw(r, s);
let der = sig_to_der(&raw);
let recovered = sig_from_der(&der).unwrap();
assert_eq!(recovered, raw);
}
#[test]
fn test_der_invalid_tag() {
// 非 SEQUENCE tag
let bad = [0x10, 0x08, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00];
assert!(sig_from_der(&bad).is_err());
}
#[test]
fn test_der_truncated() {
let bad = [0x30, 0x10]; // length 声明 16 字节但无内容
assert!(sig_from_der(&bad).is_err());
}
#[cfg(feature = "alloc")]
#[test]
fn test_der_structure() {
// 验证 DER 字节结构符合 ASN.1 规范
let r = [0x01u8; 32];
let s = [0x01u8; 32];
let raw = make_raw(r, s);
let der = sig_to_der(&raw);
assert_eq!(der[0], 0x30); // SEQUENCE
assert_eq!(der[2], 0x02); // INTEGER tag for r
// 长度字段合理(r/s 各最多 33 字节 + 2 字节头 = 35,×2 + 2 = 72
assert!(der.len() <= 72);
assert!(der.len() >= 8);
}
// ── 私钥 DER 解析测试 ──────────────────────────────────────────────────────
// 已知 SM2 私钥原始字节(与其他测试共用)
const RAW_KEY: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3, 0x9f,
0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef, 0x4d, 0xf7,
0xc5, 0xb8,
];
/// 构造最小 SEC1 DER(只有 version + privateKey 字段)
#[cfg(feature = "alloc")]
fn make_sec1_der(key: &[u8; 32]) -> alloc::vec::Vec<u8> {
// version INTEGER = 102 01 01
// privateKey OCTET STRING04 20 <32 bytes>
// inner = 3 + 2 + 32 = 37 bytes → SEQUENCE 30 25 ...
let mut der = alloc::vec![0x30u8, 0x25, 0x02, 0x01, 0x01, 0x04, 0x20];
der.extend_from_slice(key);
der
}
/// 构造最小 PKCS#8 DER(包含虚拟 AlgorithmIdentifier OID
#[cfg(feature = "alloc")]
fn make_pkcs8_der(key: &[u8; 32]) -> alloc::vec::Vec<u8> {
let sec1 = make_sec1_der(key);
// AlgorithmIdentifier 最小化:30 06 06 01 00 06 01 00(两个 OID,各 1 字节占位)
let alg_id: &[u8] = &[0x30, 0x06, 0x06, 0x01, 0x00, 0x06, 0x01, 0x00];
// version INTEGER = 002 01 00
let version: &[u8] = &[0x02, 0x01, 0x00];
// privateKey OCTET STRING 包装 sec1
let mut priv_oct = alloc::vec![0x04u8, sec1.len() as u8];
priv_oct.extend_from_slice(&sec1);
// inner = version + alg_id + priv_oct
let inner_len = version.len() + alg_id.len() + priv_oct.len();
let mut der = alloc::vec![0x30u8, inner_len as u8];
der.extend_from_slice(version);
der.extend_from_slice(alg_id);
der.extend_from_slice(&priv_oct);
der
}
#[cfg(feature = "alloc")]
#[test]
fn test_sec1_der_roundtrip() {
let der = make_sec1_der(&RAW_KEY);
let key = private_key_from_sec1_der(&der).expect("SEC1 解析应成功");
assert_eq!(key.as_bytes(), &RAW_KEY);
}
#[cfg(feature = "alloc")]
#[test]
fn test_pkcs8_der_roundtrip() {
let der = make_pkcs8_der(&RAW_KEY);
let key = private_key_from_pkcs8_der(&der).expect("PKCS#8 解析应成功");
assert_eq!(key.as_bytes(), &RAW_KEY);
}
#[test]
fn test_sec1_der_invalid_tag() {
// 首字节不是 SEQUENCE tag
let bad = [0x02u8, 0x25, 0x02, 0x01, 0x01, 0x04, 0x20, 0x00];
assert!(private_key_from_sec1_der(&bad).is_err());
}
#[test]
fn test_sec1_der_wrong_version() {
// version 应为 1,此处给 0;最后 32 字节填充为 RAW_KEY
let mut der = [0u8; 39];
der[0] = 0x30;
der[1] = 0x25; // SEQUENCE length 37
der[2] = 0x02;
der[3] = 0x01;
der[4] = 0x00; // version = 0(错误,应为 1
der[5] = 0x04;
der[6] = 0x20; // OCTET STRING 32 字节
der[7..39].copy_from_slice(&RAW_KEY);
assert!(private_key_from_sec1_der(&der).is_err());
}
#[test]
fn test_sec1_der_key_too_short() {
// privateKey 只有 16 字节(不足 32
let der = [
0x30, 0x15, // SEQUENCE 21 字节
0x02, 0x01, 0x01, // version = 1
0x04, 0x10, // OCTET STRING 16 字节
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
assert!(private_key_from_sec1_der(&der).is_err());
}
#[cfg(feature = "alloc")]
#[test]
fn test_pkcs8_der_invalid_outer_tag() {
let mut der = make_pkcs8_der(&RAW_KEY);
der[0] = 0x04; // 破坏外层 SEQUENCE tag
assert!(private_key_from_pkcs8_der(&der).is_err());
}
// ── SPKI DER 测试 ──────────────────────────────────────────────────────────
#[cfg(feature = "alloc")]
#[test]
fn test_spki_der_structure() {
use crate::PrivateKey;
let pri = PrivateKey::from_bytes(&RAW_KEY).unwrap();
let pub_key = pri.public_key();
let spki = public_key_to_spki_der(&pub_key);
// 外层 SEQUENCE
assert_eq!(spki[0], 0x30, "外层 tag 应为 SEQUENCE");
// BIT STRING 内包含 04||x||y65字节)
// 确认公钥原始字节出现在 SPKI 中
let pos = spki.windows(65).position(|w| w == pub_key);
assert!(pos.is_some(), "SPKI 应包含原始公钥字节");
}
#[cfg(feature = "alloc")]
#[test]
fn test_spki_der_oid_ec() {
use crate::PrivateKey;
let pri = PrivateKey::from_bytes(&RAW_KEY).unwrap();
let pub_key = pri.public_key();
let spki = public_key_to_spki_der(&pub_key);
// id-ecPublicKey OID bytes
let oid_ec: &[u8] = &[0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
assert!(
spki.windows(oid_ec.len()).any(|w| w == oid_ec),
"SPKI 应包含 id-ecPublicKey OID"
);
}
}
+610
View File
@@ -0,0 +1,610 @@
//! SM2 椭圆曲线点运算(GB/T 32918.1-2016 §4.2
//!
//! 使用 Jacobian 射影坐标(X:Y:Z),仿射坐标满足 x = X/Z², y = Y/Z³。
//! 避免热路径中的 Fp 求逆运算,性能优于仿射坐标加法。
use crypto_bigint::U256;
use subtle::{Choice, ConditionallySelectable};
use crate::error::Error;
use crate::field::{
fp_add, fp_from_bytes, fp_inv, fp_mul, fp_neg, fp_square, fp_sub, fp_to_bytes, Fp, CURVE_A,
CURVE_B, FIELD_MODULUS, GX, GY,
};
// ── 仿射坐标点 ────────────────────────────────────────────────────────────────
/// SM2 曲线上的仿射坐标点(公开类型,用于序列化/反序列化)
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AffinePoint {
/// x 坐标
pub x: Fp,
/// y 坐标
pub y: Fp,
}
// ── Jacobian 射影坐标点(内部运算专用)────────────────────────────────────────
/// SM2 曲线上的 Jacobian 射影坐标点(内部使用)
///
/// 仿射点 (x, y) 对应射影点 (X:Y:Z) 满足 x = X/Z², y = Y/Z³
#[derive(Clone, Copy, Debug)]
pub struct JacobianPoint {
pub(crate) x: Fp,
pub(crate) y: Fp,
pub(crate) z: Fp,
}
// ── Jacobian 常量时间选择 ──────────────────────────────────────────────────────
/// 为 JacobianPoint 实现常量时间条件选择
///
/// Reason: 标量乘中用掩码选择替代 if/else,消除基于标量位的条件分支。
impl ConditionallySelectable for JacobianPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
JacobianPoint {
x: Fp::conditional_select(&a.x, &b.x, choice),
y: Fp::conditional_select(&a.y, &b.y, choice),
z: Fp::conditional_select(&a.z, &b.z, choice),
}
}
}
impl JacobianPoint {
/// 无穷远点(群的单位元),用 Z=0 表示
pub const INFINITY: Self = JacobianPoint {
x: Fp::ONE,
y: Fp::ONE,
z: Fp::ZERO,
};
/// 从仿射坐标构造(Z=1
pub fn from_affine(p: &AffinePoint) -> Self {
JacobianPoint {
x: p.x,
y: p.y,
z: Fp::ONE,
}
}
/// 转换为仿射坐标(需要一次 Fp 求逆,仅在最终输出时使用)
pub fn to_affine(&self) -> Result<AffinePoint, Error> {
if self.is_infinity() {
return Err(Error::PointAtInfinity);
}
let z_inv = fp_inv(&self.z).ok_or(Error::PointAtInfinity)?;
let z_inv2 = fp_square(&z_inv);
let z_inv3 = fp_mul(&z_inv2, &z_inv);
Ok(AffinePoint {
x: fp_mul(&self.x, &z_inv2),
y: fp_mul(&self.y, &z_inv3),
})
}
/// 判断是否为无穷远点(常量时间,公开接口)
pub fn is_infinity(&self) -> bool {
bool::from(self.ct_is_infinity())
}
/// 常量时间无穷远判断(内部辅助,返回 Choice)
///
/// Reason: 返回 Choice 供 conditional_select 直接使用,避免 bool 转换后再转回 Choice
fn ct_is_infinity(&self) -> Choice {
// Reason: 用 ConstantTimeEq 比较所有 32 字节,执行时间与 Z 值无关,
// 替代 Iterator::all 的短路求值(后者泄露 Z 坐标前缀信息)。
use subtle::ConstantTimeEq;
fp_to_bytes(&self.z).ct_eq(&[0u8; 32])
}
/// 点倍运算(Jacobian 坐标,a=-3 优化公式,完全常量时间)
///
/// 公式来自 <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#doubling-dbl-2001-b>
/// SM2 曲线 a = p-3 ≡ -3 (mod p),使用 a=-3 特化公式降低乘法次数。
///
/// # 安全性
/// 无条件执行完整运算,用 `conditional_select` 处理无穷远退化情况,
/// 消除 `if is_infinity()` 分支对标量前导零位的泄露。
pub fn double(&self) -> Self {
let (x1, y1, z1) = (&self.x, &self.y, &self.z);
let delta = fp_square(z1); // Z1²
let gamma = fp_square(y1); // Y1²
let beta = fp_mul(x1, &gamma); // X1·Y1²
// alpha = 3·(X1-delta)·(X1+delta) [a=-3 优化]
let alpha = fp_mul(&fp_sub(x1, &delta), &fp_add(x1, &delta));
let alpha = fp_add(&fp_add(&alpha, &alpha), &alpha); // 3·alpha
// X3 = alpha² - 8·beta
let x3 = fp_sub(&fp_square(&alpha), &double2(&double1(&beta)));
// Z3 = (Y1+Z1)² - gamma - delta
let z3 = fp_sub(&fp_sub(&fp_square(&fp_add(y1, z1)), &gamma), &delta);
// Y3 = alpha·(4·beta - X3) - 8·gamma²
let gamma2 = fp_square(&gamma);
let y3 = fp_sub(
&fp_mul(&alpha, &fp_sub(&double2(&beta), &x3)),
&double2(&double1(&gamma2)),
);
let d = JacobianPoint {
x: x3,
y: y3,
z: z3,
};
// Reason: 无穷远点的倍点仍为无穷远点;用掩码选择替代 if 分支,
// 避免 scalar_mul 热路径中泄露哪些迭代位为前导零。
JacobianPoint::conditional_select(&d, self, self.ct_is_infinity())
}
/// 点加运算(完全常量时间,无条件分支)
///
/// 公式来自 <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-3.html#addition-add-2007-bl>
///
/// # 安全性
/// 采用"计算所有情况 + 掩码选择"策略,消除全部退化情况的条件分支:
/// - P = ∞ → Q(无穷远加法单位元)
/// - Q = ∞ → P
/// - P = Q → double(P)(相同点,用 ct_eq 检测 H==0 且 R==0
/// - P = -Q → ∞(互反点,用 ct_eq 检测 H==0 且 R≠0
/// - 正常情况 → 标准 Jacobian 加法
///
/// Reason: 原实现的 3 处 `if` 分支(is_infinity、H==0、R==0
/// 在 scalar_mul 热路径中泄露标量的汉明重量及位分布。
pub fn add(p: &JacobianPoint, q: &JacobianPoint) -> JacobianPoint {
use subtle::ConstantTimeEq;
let z1sq = fp_square(&p.z);
let z2sq = fp_square(&q.z);
let u1 = fp_mul(&p.x, &z2sq); // X1·Z2²
let u2 = fp_mul(&q.x, &z1sq); // X2·Z1²
let s1 = fp_mul(&p.y, &fp_mul(&q.z, &z2sq)); // Y1·Z2³
let s2 = fp_mul(&q.y, &fp_mul(&p.z, &z1sq)); // Y2·Z1³
let h = fp_sub(&u2, &u1);
let r = fp_sub(&s2, &s1);
// 常量时间零判断(替代 Iterator::all 短路)
let h_is_zero = fp_to_bytes(&h).ct_eq(&[0u8; 32]);
let r_is_zero = fp_to_bytes(&r).ct_eq(&[0u8; 32]);
// 无条件执行标准 Jacobian 加法(当 h==0 时结果为垃圾值,后续掩码覆盖)
let h2 = fp_square(&h);
let h3 = fp_mul(&h, &h2);
let u1h2 = fp_mul(&u1, &h2);
// X3 = R² - H³ - 2·U1·H²
let x3 = fp_sub(&fp_sub(&fp_square(&r), &h3), &double1(&u1h2));
// Y3 = R·(U1·H² - X3) - S1·H³
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,
};
// 预计算 P==Q 退化情况的结果(无条件执行,结果由掩码决定是否使用)
let double_p = p.double();
// 按优先级从低到高用 conditional_select 叠加(后面覆盖前面):
// 优先级 1(最低):正常 Jacobian 加法
let result = normal;
// 优先级 2P == -Q → INFINITYh==0 且 r≠0
let result = JacobianPoint::conditional_select(
&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);
// 优先级 4:Q 是无穷远 → P(加法单位元)
let result = JacobianPoint::conditional_select(&result, p, q.ct_is_infinity());
// 优先级 5(最高):P 是无穷远 → Q
JacobianPoint::conditional_select(&result, q, p.ct_is_infinity())
}
/// 标量乘 k·P(常量时间,固定 256 位迭代)
///
/// Reason: 固定迭代次数 + `conditional_select` 掩码选择,消除基于标量位的条件分支,
/// 防止时序侧信道攻击。执行路径与标量 k 的值完全无关。
pub fn scalar_mul(k: &U256, p: &JacobianPoint) -> JacobianPoint {
let mut result = JacobianPoint::INFINITY;
// 固定 256 次迭代,不跳过前导零
for byte in &k.to_be_bytes() {
for b in (0..8).rev() {
// 始终执行倍点(与标量位无关)
result = result.double();
// 始终计算加法(与标量位无关)
let sum = JacobianPoint::add(&result, p);
// Reason: 用掩码选择结果,无条件分支:bit=1 取 sumbit=0 取 result
let bit = Choice::from((byte >> b) & 1);
result = JacobianPoint::conditional_select(&result, &sum, bit);
}
}
result
}
/// 基点标量乘 k·G(密钥生成和签名专用,使用 w=4 固定窗口加速)
pub fn scalar_mul_g(k: &U256) -> JacobianPoint {
scalar_mul_g_window(k)
}
}
// ── 辅助倍增函数(用于 Jacobian 公式中的常数倍计算)────────────────────────
#[inline]
fn double1(a: &Fp) -> Fp {
fp_add(a, a)
}
#[inline]
fn double2(a: &Fp) -> Fp {
let t = double1(a);
double1(&t)
}
// ── 混合 Jacobian-仿射加法(q.Z = 1 优化)────────────────────────────────────
/// 混合点加 PJacobian+ QAffineZ=1
///
/// 相比标准 Jacobian+Jacobian 加法,利用 Z_Q=1 省去:
/// - Z2² 计算(1 次 fp_square
/// - X1·Z2² 简化为 X10 次乘法)
/// - Y1·Z2³ 简化为 Y10 次乘法)
/// - Z3 中的 Z2 乘法(Z3 = H·Z1,而非 H·Z1·Z2
///
/// 共节省约 3~4 次域乘法,用于预计算表构建和 multi_scalar_mul 内循环。
///
/// # 安全性
/// 完全常量时间,退化情况处理与 `JacobianPoint::add` 相同。
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 h = fp_sub(&u2, &p.x);
let r = fp_sub(&s2, &p.y);
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 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));
// 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 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);
// P = INFINITY → 返回 Q(注:预计算表中 Q 绝不是无穷远点,
// 但在通用调用中仍需正确处理)
let q_jac = JacobianPoint::from_affine(q);
JacobianPoint::conditional_select(&result, &q_jac, p.ct_is_infinity())
}
// ── SM2 基点固定窗口标量乘(w=4)─────────────────────────────────────────────
/// 基点固定窗口标量乘 k·G(w=4,预计算 15 个点,常量时间)
///
/// 原理:将 256-bit 标量按 4-bit 切分为 64 个窗口。
/// 每个窗口先执行 4 次倍点,再常量时间查表做一次加法。
/// 共需 256 次 double + 64 次 add,相比双倍-加法的 256 次 add 节省约 75%。
///
/// Reason: 预计算表仅含 G 的已知倍数(公开常量基点),不依赖秘密输入;
/// 窗口值为秘密标量位,但表查找通过 15 次 `conditional_select` 实现,
/// 不含任何数据依赖分支,保持常量时间性质。
fn scalar_mul_g_window(k: &U256) -> JacobianPoint {
use subtle::ConstantTimeEq;
let g_aff = AffinePoint { x: GX, y: GY };
let g_jac = JacobianPoint::from_affine(&g_aff);
// 预计算表:table[i] = i·Gi = 0..=15table[0] = INFINITY,占位不用)
// Reason: 使用 add_mixed 构建表,g_aff 始终 Z=1,节省约 3 次域乘/步
let mut table = [JacobianPoint::INFINITY; 16];
table[1] = g_jac;
for i in 2..=15usize {
table[i] = add_mixed(&table[i - 1], &g_aff);
}
let mut result = JacobianPoint::INFINITY;
for byte in &k.to_be_bytes() {
// ── 高 4 位窗口 ─────────────────────────────────────────────────────
for _ in 0..4 {
result = result.double();
}
let window = byte >> 4;
// 常量时间表查找:遍历 1..=15,用 ct_eq 选出 table[window]
let mut sel = JacobianPoint::INFINITY;
for j in 1u8..=15 {
let eq = window.ct_eq(&j);
sel = JacobianPoint::conditional_select(&sel, &table[j as usize], eq);
}
// window=0 时 sel 仍为 INFINITYadd(result, INFINITY) = result
result = JacobianPoint::add(&result, &sel);
// ── 低 4 位窗口 ─────────────────────────────────────────────────────
for _ in 0..4 {
result = result.double();
}
let window = byte & 0xF;
let mut sel = JacobianPoint::INFINITY;
for j in 1u8..=15 {
let eq = window.ct_eq(&j);
sel = JacobianPoint::conditional_select(&sel, &table[j as usize], eq);
}
result = JacobianPoint::add(&result, &sel);
}
result
}
// ── AffinePoint 公开接口 ──────────────────────────────────────────────────────
impl AffinePoint {
/// SM2 基点 G
pub fn generator() -> Self {
AffinePoint { x: GX, y: GY }
}
/// 验证点是否在 SM2 曲线上:y² ≡ x³ + ax + b (mod p)
pub fn is_on_curve(&self) -> bool {
let x2 = fp_square(&self.x);
let x3 = fp_mul(&x2, &self.x);
let ax = fp_mul(&CURVE_A, &self.x);
let rhs = fp_add(&fp_add(&x3, &ax), &CURVE_B);
fp_square(&self.y) == rhs
}
/// 从未压缩格式 04||x||y65 字节)解析点
///
/// 符合 GB/T 32918.1-2016 §4.2.9
pub fn from_bytes(bytes: &[u8; 65]) -> Result<Self, Error> {
if bytes[0] != 0x04 {
return Err(Error::InvalidPublicKey);
}
let x_bytes: [u8; 32] = bytes[1..33].try_into().unwrap();
let y_bytes: [u8; 32] = bytes[33..65].try_into().unwrap();
// 检查坐标在 [0, p-1] 范围内
use crypto_bigint::subtle::ConstantTimeGreater;
let x_val = U256::from_be_slice(&x_bytes);
let y_val = U256::from_be_slice(&y_bytes);
if bool::from(x_val.ct_gt(&FIELD_MODULUS))
|| x_val == FIELD_MODULUS
|| bool::from(y_val.ct_gt(&FIELD_MODULUS))
|| y_val == FIELD_MODULUS
{
return Err(Error::InvalidPublicKey);
}
let p = AffinePoint {
x: fp_from_bytes(&x_bytes),
y: fp_from_bytes(&y_bytes),
};
if !p.is_on_curve() {
return Err(Error::InvalidPublicKey);
}
Ok(p)
}
/// 序列化为未压缩格式 04||x||y(65 字节)
pub fn to_bytes(&self) -> [u8; 65] {
let mut out = [0u8; 65];
out[0] = 0x04;
out[1..33].copy_from_slice(&fp_to_bytes(&self.x));
out[33..65].copy_from_slice(&fp_to_bytes(&self.y));
out
}
/// 从压缩格式 02/03||x33 字节)解压缩点
///
/// 符合 GB/T 32918.1-2016 §4.2.10
pub fn decompress(bytes: &[u8; 33]) -> Result<Self, Error> {
let prefix = bytes[0];
if prefix != 0x02 && prefix != 0x03 {
return Err(Error::InvalidPublicKey);
}
let x_bytes: [u8; 32] = bytes[1..33].try_into().unwrap();
use crypto_bigint::subtle::ConstantTimeGreater;
let x_val = U256::from_be_slice(&x_bytes);
if bool::from(x_val.ct_gt(&FIELD_MODULUS)) || x_val == FIELD_MODULUS {
return Err(Error::InvalidPublicKey);
}
let x = fp_from_bytes(&x_bytes);
// 计算 y² = x³ + ax + b
let x2 = fp_square(&x);
let x3 = fp_mul(&x2, &x);
let ax = fp_mul(&CURVE_A, &x);
let y2 = fp_add(&fp_add(&x3, &ax), &CURVE_B);
let y = crate::field::fp_sqrt(&y2).ok_or(Error::InvalidPublicKey)?;
// 按前缀奇偶性选择正确的 y
// prefix 02 → 偶数(LSB=0),prefix 03 → 奇数(LSB=1
let y_lsb = fp_to_bytes(&y)[31] & 1;
let want_odd = prefix & 1;
let y_final = if y_lsb == want_odd { y } else { fp_neg(&y) };
Ok(AffinePoint { x, y: y_final })
}
}
// ── 双标量乘:u·G + v·Q(用于签名验证)─────────────────────────────────────
/// 计算 u·G + v·Q(顺序双标量乘,用于 SM2 验签第 3 步)
/// 双标量乘 u·G + v·QShamir's trick 交错法,用于验签)
///
/// Reason: 验签时 u、v 均为公开值(非秘密),无需常量时间。
/// Shamir's trick 预计算 {P, Q, P+Q},每位只需 1 次 double + 最多 1 次 add
/// 比两次独立标量乘(各 256 次 double + 平均 128 add)快约 25%。
pub fn multi_scalar_mul(u: &U256, v: &U256, q: &AffinePoint) -> Result<AffinePoint, Error> {
// Reason: u、v 均为验签公开值,non-CT 的 match 分支不泄露秘密;
// 使用 add_mixed(Jacobian, Affine) 替代全量 Jacobian add
// 节省约 3 次域乘/步,g 和 q 已是仿射坐标直接传入。
let g = AffinePoint::generator();
let q_jac = JacobianPoint::from_affine(q);
let g_jac = JacobianPoint::from_affine(&g);
// 预计算 G+QJacobian,含退化处理)
let gq_jac = JacobianPoint::add(&g_jac, &q_jac);
let u_bytes = u.to_be_bytes();
let v_bytes = v.to_be_bytes();
let mut result = JacobianPoint::INFINITY;
for i in 0..32 {
let ub = u_bytes[i];
let vb = v_bytes[i];
for b in (0..8).rev() {
result = result.double();
let ui = (ub >> b) & 1;
let vi = (vb >> b) & 1;
// Reason: u、v 公开,match 分支安全;add_mixed 对仿射 g/q 节省域乘,
// gq 为 Jacobian 仍用全量 add(无额外求逆开销)
match (ui, vi) {
(1, 0) => result = add_mixed(&result, &g),
(0, 1) => result = add_mixed(&result, q),
(1, 1) => result = JacobianPoint::add(&result, &gq_jac),
_ => {}
}
}
}
result.to_affine()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::{fp_to_bytes, GX, GY};
#[test]
fn test_generator_on_curve() {
assert!(AffinePoint::generator().is_on_curve());
}
#[test]
fn test_double_stays_on_curve() {
let g = JacobianPoint::from_affine(&AffinePoint::generator());
let g2 = g.double().to_affine().unwrap();
assert!(g2.is_on_curve());
}
#[test]
fn test_add_commutativity() {
let g = JacobianPoint::from_affine(&AffinePoint::generator());
let g2 = g.double();
let p1 = JacobianPoint::add(&g2, &g).to_affine().unwrap();
let p2 = JacobianPoint::add(&g, &g2).to_affine().unwrap();
assert_eq!(fp_to_bytes(&p1.x), fp_to_bytes(&p2.x));
assert_eq!(fp_to_bytes(&p1.y), fp_to_bytes(&p2.y));
assert!(p1.is_on_curve());
}
#[test]
fn test_scalar_mul_one_is_g() {
let g1 = JacobianPoint::scalar_mul_g(&U256::ONE).to_affine().unwrap();
assert_eq!(fp_to_bytes(&g1.x), fp_to_bytes(&GX));
assert_eq!(fp_to_bytes(&g1.y), fp_to_bytes(&GY));
}
#[test]
fn test_serialization_roundtrip() {
let g = AffinePoint::generator();
let bytes = g.to_bytes();
assert_eq!(bytes[0], 0x04);
let g2 = AffinePoint::from_bytes(&bytes).unwrap();
assert_eq!(fp_to_bytes(&g.x), fp_to_bytes(&g2.x));
assert_eq!(fp_to_bytes(&g.y), fp_to_bytes(&g2.y));
}
#[test]
fn test_keypair_on_curve() {
// 测试私钥 → 公钥在曲线上
let k_hex = "f927525e176ae5607c628bc508ec0465ef285b74415bf876130a8a5d004c789e";
let k_bytes: [u8; 32] = {
let mut b = [0u8; 32];
for (i, chunk) in k_hex.as_bytes().chunks(2).enumerate() {
b[i] = u8::from_str_radix(core::str::from_utf8(chunk).unwrap(), 16).unwrap();
}
b
};
let k = U256::from_be_slice(&k_bytes);
let pub_aff = JacobianPoint::scalar_mul_g(&k).to_affine().unwrap();
assert!(pub_aff.is_on_curve());
// 验证 y² = x³ + ax + b
let x2 = fp_square(&pub_aff.x);
let x3 = fp_mul(&x2, &pub_aff.x);
let ax = fp_mul(&CURVE_A, &pub_aff.x);
let rhs = fp_add(&fp_add(&x3, &ax), &CURVE_B);
assert_eq!(rhs, fp_square(&pub_aff.y));
}
/// 验证完备加法公式的退化情况(常量时间 add 的正确性)
#[test]
fn test_add_degenerate_cases() {
let g = JacobianPoint::from_affine(&AffinePoint::generator());
let inf = JacobianPoint::INFINITY;
// ∞ + G = G
let r = JacobianPoint::add(&inf, &g).to_affine().unwrap();
assert_eq!(fp_to_bytes(&r.x), fp_to_bytes(&GX), "∞ + G 的 x 坐标错误");
assert_eq!(fp_to_bytes(&r.y), fp_to_bytes(&GY), "∞ + G 的 y 坐标错误");
// G + ∞ = G
let r = JacobianPoint::add(&g, &inf).to_affine().unwrap();
assert_eq!(fp_to_bytes(&r.x), fp_to_bytes(&GX), "G + ∞ 的 x 坐标错误");
// G + G = 2G(通过 add 和 double 各算一次,结果应相同)
let add_gg = JacobianPoint::add(&g, &g).to_affine().unwrap();
let double_g = g.double().to_affine().unwrap();
assert_eq!(
fp_to_bytes(&add_gg.x),
fp_to_bytes(&double_g.x),
"add(G,G) != double(G) 的 x 坐标"
);
assert_eq!(
fp_to_bytes(&add_gg.y),
fp_to_bytes(&double_g.y),
"add(G,G) != double(G) 的 y 坐标"
);
// G + (-G) = ∞(互反点,y 取负)
let g_neg = JacobianPoint {
x: g.x,
y: fp_neg(&g.y),
z: g.z,
};
assert!(
JacobianPoint::add(&g, &g_neg).is_infinity(),
"G + (-G) 应为无穷远点"
);
}
}
+50
View File
@@ -0,0 +1,50 @@
//! SM2 错误类型
//!
//! SM2 Error types used by all public APIs.
use core::fmt;
/// SM2 操作的统一错误类型
///
/// Unified error type for all SM2 operations.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
/// 私钥不合法(d ∉ [1, n-2]/ Invalid private key (d ∉ [1, n-2])
InvalidPrivateKey,
/// 公钥不合法(格式错误或不在曲线上)/ Invalid public key (bad format or not on curve)
InvalidPublicKey,
/// 签名格式不合法 / Invalid signature format
InvalidSignature,
/// 验签失败 / Signature verification failed
VerifyFailed,
/// 解密失败(C3 校验不通过)/ Decryption failed (C3 check failed)
DecryptFailed,
/// 点在无穷远处 / Point at infinity
PointAtInfinity,
/// 输入长度不合法 / Invalid input length
InvalidInputLength,
/// 密钥交换失败 / Key exchange failed
KeyExchangeFailed,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidPrivateKey => f.write_str("SM2: invalid private key"),
Error::InvalidPublicKey => f.write_str("SM2: invalid public key"),
Error::InvalidSignature => f.write_str("SM2: invalid signature format"),
Error::VerifyFailed => f.write_str("SM2: signature verification failed"),
Error::DecryptFailed => f.write_str("SM2: decryption failed"),
Error::PointAtInfinity => f.write_str("SM2: point at infinity"),
Error::InvalidInputLength => f.write_str("SM2: invalid input length"),
Error::KeyExchangeFailed => f.write_str("SM2: key exchange failed"),
}
}
}
/// Bridge to `signature::Error` for `Signer`/`Verifier` trait impls.
impl From<Error> for signature::Error {
fn from(_: Error) -> Self {
signature::Error::new()
}
}
+251
View File
@@ -0,0 +1,251 @@
//! SM2 sm2p256v1 素域 Fp 与标量域 Fn
//!
//! 曲线参数来自 GB/T 32918.1-2016 附录 A。
//! 所有算术通过 `crypto-bigint` 的 `ConstMontyForm` 实现,常量时间。
use crypto_bigint::{impl_modulus, modular::ConstMontyForm, U256};
// ── 模数定义 ──────────────────────────────────────────────────────────────────
// SM2 素数域模数 p
// p = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF
impl_modulus!(
Sm2FieldModulus,
U256,
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF"
);
// SM2 群阶 n
// n = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123
impl_modulus!(
Sm2GroupOrder,
U256,
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123"
);
/// SM2 素域元素(基于 Montgomery 形式的常量时间运算)
pub type Fp = ConstMontyForm<Sm2FieldModulus, { U256::LIMBS }>;
/// SM2 标量域元素(群阶 n 上的模运算)
pub type Fn = ConstMontyForm<Sm2GroupOrder, { U256::LIMBS }>;
// ── 曲线参数常量(GB/T 32918.1-2016 附录 A)─────────────────────────────────
/// 曲线系数 a = p - 3
pub const CURVE_A: Fp = Fp::new(&U256::from_be_hex(
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
));
/// 曲线系数 b
pub const CURVE_B: Fp = Fp::new(&U256::from_be_hex(
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
));
/// 基点 G 的 x 坐标
pub const GX: Fp = Fp::new(&U256::from_be_hex(
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
));
/// 基点 G 的 y 坐标
pub const GY: Fp = Fp::new(&U256::from_be_hex(
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0",
));
/// 域模数 p(用于坐标范围检查)
pub const FIELD_MODULUS: U256 =
U256::from_be_hex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF");
/// 群阶 n(用于标量范围检查)
pub const GROUP_ORDER: U256 =
U256::from_be_hex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123");
/// 群阶 n - 1(私钥合法性检查:d ∈ [1, n-2] → d < n-1
pub const GROUP_ORDER_MINUS_1: U256 =
U256::from_be_hex("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54122");
// ── Fp 工具函数 ───────────────────────────────────────────────────────────────
/// 从大端字节构造 Fp(调用方保证 bytes 表示的值 < p
#[inline]
pub fn fp_from_bytes(bytes: &[u8; 32]) -> Fp {
Fp::new(&U256::from_be_slice(bytes))
}
/// 将 Fp 元素转为大端字节
#[inline]
pub fn fp_to_bytes(a: &Fp) -> [u8; 32] {
a.retrieve().to_be_bytes()
}
/// 从大端字节构造 Fn(标量,调用方保证值 < n)
#[inline]
pub fn fn_from_bytes(bytes: &[u8; 32]) -> Fn {
Fn::new(&U256::from_be_slice(bytes))
}
/// 将 Fn 元素转为大端字节
#[inline]
pub fn fn_to_bytes(a: &Fn) -> [u8; 32] {
a.retrieve().to_be_bytes()
}
/// Fp 加法(模 p
#[inline]
pub fn fp_add(a: &Fp, b: &Fp) -> Fp {
a.add(b)
}
/// Fp 减法(模 p
#[inline]
pub fn fp_sub(a: &Fp, b: &Fp) -> Fp {
a.sub(b)
}
/// Fp 乘法(Montgomery 乘,常量时间)
#[inline]
pub fn fp_mul(a: &Fp, b: &Fp) -> Fp {
a.mul(b)
}
/// Fp 取负(模 p
#[inline]
pub fn fp_neg(a: &Fp) -> Fp {
a.neg()
}
/// Fp 平方(常量时间)
#[inline]
pub fn fp_square(a: &Fp) -> Fp {
a.square()
}
/// Fp 求逆(Bernstein-Yang 算法,常量时间)
/// 返回 None 当且仅当 a == 0
pub fn fp_inv(a: &Fp) -> Option<Fp> {
let inv = a.inv();
// CtOption 转换为 Option
if bool::from(inv.is_some()) {
// Reason: ConstantTimeEq 保证此 unwrap 不可能 panicis_some 为真)
Some(inv.unwrap())
} else {
None
}
}
/// Fp 平方根(用于点解压缩)
///
/// SM2 素数 p ≡ 3 (mod 4),故 sqrt(a) = a^((p+1)/4) mod p。
/// 若结果的平方 ≠ a,则 a 不是二次剩余,返回 None。
pub fn fp_sqrt(a: &Fp) -> Option<Fp> {
// (p+1)/4 = 3FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFC000000040000000000000000
// p = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 00000000 FFFFFFFFFFFFFFFF
// p+1 = FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 00000001 0000000000000000
// /4 (右移2位) = 3FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFC0000000 40000000 00000000
let exp = U256::from_be_hex("3FFFFFFFBFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC00000004000000000000000");
let candidate = a.pow(&exp);
// 验证 candidate^2 == a(常量时间比较,ConstMontyForm 的 PartialEq 是常量时间)
if candidate.square() == *a {
Some(candidate)
} else {
None
}
}
/// Fn 加法(模 n
#[inline]
pub fn fn_add(a: &Fn, b: &Fn) -> Fn {
a.add(b)
}
/// Fn 减法(模 n
#[inline]
pub fn fn_sub(a: &Fn, b: &Fn) -> Fn {
a.sub(b)
}
/// Fn 乘法(模 nMontgomery 形式)
#[inline]
pub fn fn_mul(a: &Fn, b: &Fn) -> Fn {
a.mul(b)
}
/// Fn 取负(模 n
#[inline]
pub fn fn_neg(a: &Fn) -> Fn {
a.neg()
}
/// Fn 求逆(Bernstein-Yang 算法,常量时间)
/// 返回 None 当且仅当 a == 0
pub fn fn_inv(a: &Fn) -> Option<Fn> {
let inv = a.inv();
if bool::from(inv.is_some()) {
Some(inv.unwrap())
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fp_add_sub_symmetric() {
let a = fp_from_bytes(&[
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
]);
let b = fp_from_bytes(&[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
]);
let sum = fp_add(&a, &b);
let diff = fp_sub(&sum, &b);
assert_eq!(fp_to_bytes(&diff), fp_to_bytes(&a));
}
#[test]
fn test_fp_mul_by_one() {
let gx = GX;
let result = fp_mul(&gx, &Fp::ONE);
assert_eq!(fp_to_bytes(&result), fp_to_bytes(&gx));
}
#[test]
fn test_fp_inv_roundtrip() {
let two = fp_from_bytes(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 2,
]);
let inv = fp_inv(&two).expect("2 的逆元应存在");
assert_eq!(fp_mul(&two, &inv), Fp::ONE);
}
#[test]
fn test_fp_zero_has_no_inv() {
assert!(fp_inv(&Fp::ZERO).is_none());
}
#[test]
fn test_fp_sqrt_of_four() {
let four = fp_from_bytes(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 4,
]);
let root = fp_sqrt(&four).expect("4 应有平方根");
assert_eq!(fp_square(&root), four);
}
#[test]
fn test_fn_inv_roundtrip() {
let three = fn_from_bytes(&[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 3,
]);
let inv = fn_inv(&three).expect("3^-1 应存在");
assert_eq!(fn_mul(&three, &inv), Fn::ONE);
}
}
+67
View File
@@ -0,0 +1,67 @@
//! SM2 KDF 密钥派生函数(GB/T 32918.4-2016 §5.4.3
//!
//! KDF(Z, klen) = ‖_{i=1}^{⌈klen/32⌉} SM3(Z ‖ CT_i)
//! 其中 CT_i 为 32-bit 大端计数器,从 1 开始。
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
// Reason: sm3 sub-crate 只导出 Digest API,通过内联包装器使用
fn sm3_hash(parts: &[&[u8]]) -> [u8; 32] {
use sm3::Digest;
let mut h = sm3::Sm3::new();
for part in parts {
h.update(part);
}
h.finalize().into()
}
/// SM2/SM9 KDF 密钥派生函数
///
/// # 参数
/// - `z`: 输入密钥材料(共享点坐标等)
/// - `klen`: 期望输出字节数
///
/// # 返回
/// 长度为 `klen` 的派生密钥字节序列(`alloc` feature 下可用)
#[cfg(feature = "alloc")]
pub fn kdf(z: &[u8], klen: usize) -> Vec<u8> {
let mut result = Vec::with_capacity(klen + 32);
let mut counter: u32 = 1;
while result.len() < klen {
// 每轮: SM3(Z || CT_i)
result.extend_from_slice(&sm3_hash(&[z, &counter.to_be_bytes()]));
counter += 1;
}
result.truncate(klen);
result
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
#[test]
fn test_kdf_length() {
let z = b"test input";
assert_eq!(kdf(z, 32).len(), 32);
assert_eq!(kdf(z, 48).len(), 48);
assert_eq!(kdf(z, 1).len(), 1);
}
#[test]
fn test_kdf_deterministic() {
let z = b"shared secret";
assert_eq!(kdf(z, 32), kdf(z, 32));
}
#[test]
fn test_kdf_different_lengths() {
// 64 字节输出应与两次 32 字节拼接一致(即第一块完全相同)
let z = b"input";
let k32 = kdf(z, 32);
let k64 = kdf(z, 64);
assert_eq!(&k64[..32], &k32[..]);
}
}
+628
View File
@@ -0,0 +1,628 @@
//! SM2 密钥交换协议(GB/T 32918.3-2016
//!
//! 提供两种密钥交换方式:
//! - `ecdh`: 简单 SM2-ECDH 共享密钥计算(适配 TLS/rustls
//! - `exchange_a` / `exchange_b`: 完整 GB/T 32918.3 密钥交换协议(带确认哈希)
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crypto_bigint::{Zero, U256};
use rand_core::RngCore;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::error::Error;
use crate::ec::{AffinePoint, JacobianPoint};
use crate::field::{fn_add, fn_mul, fp_to_bytes, Fn, GROUP_ORDER_MINUS_1};
use crate::get_z;
use sm3::Digest as _;
// Reason: sm3 sub-crate 只导出 Digest API;通过此包装器实现流式哈希
struct Sm3H(sm3::Sm3);
impl Sm3H {
fn new() -> Self { Sm3H(sm3::Sm3::new()) }
fn update(&mut self, data: &[u8]) { self.0.update(data); }
fn finalize(self) -> [u8; 32] { self.0.finalize().into() }
}
// ── x̄ 辅助函数(GB/T 32918.3 核心运算)─────────────────────────────────────────
/// 计算 x̄ = 2^w + (x & (2^w - 1)),其中 w = ⌈(⌈log2(n)⌉ / 2)⌉ - 1 = 127
///
/// 对 SM2 256 位群阶,w=127。在大端 32 字节表示中:
/// - 清除高 128 位(bytes[0..16]),保留低 128 位
/// - 设 bytes[16] 的 bit7 = 1(即加 2^127
fn x_bar(x_bytes: &[u8; 32]) -> U256 {
let mut buf = [0u8; 32];
// Reason: 保留 x 的低 128 位(bytes[16..32]),高 128 位清零
buf[16..32].copy_from_slice(&x_bytes[16..32]);
// 设 bit 127bytes[16] 的最高位)
buf[16] |= 0x80;
U256::from_be_slice(&buf)
}
// ── EphemeralKey(临时密钥对)────────────────────────────────────────────────────
/// SM2 密钥交换临时密钥对(离开作用域自动清零)
///
/// 用于密钥交换协议中的临时私钥和对应公钥。
#[derive(Zeroize, ZeroizeOnDrop)]
pub struct EphemeralKey {
r_bytes: [u8; 32],
#[zeroize(skip)]
r_point: [u8; 65],
}
impl EphemeralKey {
/// 生成临时密钥对
pub fn generate<R: RngCore>(rng: &mut R) -> Self {
loop {
let mut r_bytes = [0u8; 32];
rng.fill_bytes(&mut r_bytes);
let r = U256::from_be_slice(&r_bytes);
if bool::from(r.is_zero()) || r >= GROUP_ORDER_MINUS_1 {
r_bytes.zeroize();
continue;
}
let r_jac = JacobianPoint::scalar_mul_g(&r);
// Reason: r 在合法范围内,scalar_mul_g 不会产生无穷远点
let r_aff = r_jac.to_affine().expect("valid r produces valid point");
return EphemeralKey {
r_bytes,
r_point: r_aff.to_bytes(),
};
}
}
/// 从指定标量创建临时密钥对(测试用)
pub fn from_scalar(r: &U256) -> Result<Self, Error> {
if bool::from(r.is_zero()) || *r >= GROUP_ORDER_MINUS_1 {
return Err(Error::InvalidPrivateKey);
}
let r_jac = JacobianPoint::scalar_mul_g(r);
let r_aff = r_jac.to_affine().map_err(|_| Error::InvalidPrivateKey)?;
Ok(EphemeralKey {
r_bytes: r.to_be_bytes(),
r_point: r_aff.to_bytes(),
})
}
/// 获取临时公钥(发送给对方)
pub fn public_key(&self) -> &[u8; 65] {
&self.r_point
}
}
// ── 简单 ECDH ──────────────────────────────────────────────────────────────────
/// 简单 SM2-ECDH 共享密钥计算
///
/// 计算 shared = my_priv · peer_pub,返回共享点的 x 坐标(32 字节)。
/// 适用于 TLS/rustls 等只需要原始 ECDH 共享密钥的场景。
///
/// # 参数
/// - `my_priv`: 己方私钥
/// - `peer_pub`: 对方公钥(65 字节,04||x||y
///
/// # 错误
/// - `InvalidPublicKey`: 公钥格式错误或不在曲线上
/// - `PointAtInfinity`: 共享点为无穷远(不应发生于合法输入)
pub fn ecdh(my_priv: &crate::PrivateKey, peer_pub: &[u8; 65]) -> Result<[u8; 32], Error> {
let peer = AffinePoint::from_bytes(peer_pub)?;
let d = U256::from_be_slice(my_priv.as_bytes());
let peer_jac = JacobianPoint::from_affine(&peer);
let shared = JacobianPoint::scalar_mul(&d, &peer_jac);
let shared_aff = shared.to_affine()?;
Ok(fp_to_bytes(&shared_aff.x))
}
/// 从变长切片执行 SM2-ECDHrustls `ActiveKeyExchange::complete` 适配)
///
/// 等同于 `ecdh`,但接受 `&[u8]` 而非 `&[u8; 65]`,省去调用方的长度转换。
///
/// # 错误
/// - `InvalidInputLength`: peer_pub 长度不等于 65
/// - `InvalidPublicKey` / `PointAtInfinity`: 同 `ecdh`
pub fn ecdh_from_slice(
my_priv: &crate::PrivateKey,
peer_pub: &[u8],
) -> Result<[u8; 32], Error> {
let pub_fixed: &[u8; 65] = peer_pub.try_into().map_err(|_| Error::InvalidInputLength)?;
ecdh(my_priv, pub_fixed)
}
// ── 完整密钥交换协议(GB/T 32918.3)──────────────────────────────────────────────
/// 密钥交换结果
#[cfg(feature = "alloc")]
pub struct ExchangeResult {
/// 协商出的共享密钥
pub key: Vec<u8>,
/// 己方确认哈希(发给对方验证)
pub s_self: [u8; 32],
/// 对方确认哈希(用于验证对方发来的值)
pub s_peer: [u8; 32],
}
/// 发起方 A 执行密钥交换
///
/// # 参数
/// - `klen`: 期望密钥长度(字节)
/// - `id_a`: 发起方用户 ID
/// - `id_b`: 响应方用户 ID
/// - `pri_key_a`: 发起方私钥
/// - `pub_key_a`: 发起方公钥(65 字节)
/// - `pub_key_b`: 响应方公钥(65 字节)
/// - `eph_key_a`: 发起方临时密钥
/// - `r_b`: 响应方临时公钥(65 字节)
#[cfg(feature = "alloc")]
#[allow(clippy::too_many_arguments)]
pub fn exchange_a(
klen: usize,
id_a: &[u8],
id_b: &[u8],
pri_key_a: &crate::PrivateKey,
pub_key_a: &[u8; 65],
pub_key_b: &[u8; 65],
eph_key_a: &EphemeralKey,
r_b: &[u8; 65],
) -> Result<ExchangeResult, Error> {
compute_shared(
true, klen, id_a, id_b, pri_key_a, pub_key_a, pub_key_b, eph_key_a, r_b,
)
}
/// 响应方 B 执行密钥交换
///
/// # 参数
/// - `klen`: 期望密钥长度(字节)
/// - `id_a`: 发起方用户 ID
/// - `id_b`: 响应方用户 ID
/// - `pri_key_b`: 响应方私钥
/// - `pub_key_a`: 发起方公钥(65 字节)
/// - `pub_key_b`: 响应方公钥(65 字节)
/// - `eph_key_b`: 响应方临时密钥
/// - `r_a`: 发起方临时公钥(65 字节)
#[cfg(feature = "alloc")]
#[allow(clippy::too_many_arguments)]
pub fn exchange_b(
klen: usize,
id_a: &[u8],
id_b: &[u8],
pri_key_b: &crate::PrivateKey,
pub_key_a: &[u8; 65],
pub_key_b: &[u8; 65],
eph_key_b: &EphemeralKey,
r_a: &[u8; 65],
) -> Result<ExchangeResult, Error> {
compute_shared(
false, klen, id_a, id_b, pri_key_b, pub_key_a, pub_key_b, eph_key_b, r_a,
)
}
/// 内部共享计算
///
/// `is_initiator`: true 表示发起方 Afalse 表示响应方 B
#[cfg(feature = "alloc")]
#[allow(clippy::too_many_arguments)]
fn compute_shared(
is_initiator: bool,
klen: usize,
id_a: &[u8],
id_b: &[u8],
pri_key_self: &crate::PrivateKey,
pub_key_a: &[u8; 65],
pub_key_b: &[u8; 65],
eph_key_self: &EphemeralKey,
r_peer: &[u8; 65],
) -> Result<ExchangeResult, Error> {
// 计算 ZA、ZB
let z_a = get_z(id_a, pub_key_a);
let z_b = get_z(id_b, pub_key_b);
// 解析临时公钥坐标
let r_self_aff = AffinePoint::from_bytes(eph_key_self.public_key())?;
let r_peer_aff = AffinePoint::from_bytes(r_peer)?;
// 计算 x̄_self 和 x̄_peer
let x_self_bytes = fp_to_bytes(&r_self_aff.x);
let x_peer_bytes = fp_to_bytes(&r_peer_aff.x);
let x_bar_self = x_bar(&x_self_bytes);
let x_bar_peer = x_bar(&x_peer_bytes);
// t = (d_self + x̄_self · r_self) mod n
let d_self = U256::from_be_slice(pri_key_self.as_bytes());
let r_self = U256::from_be_slice(&eph_key_self.r_bytes);
let t_fn = fn_add(
&Fn::new(&d_self),
&fn_mul(&Fn::new(&x_bar_self), &Fn::new(&r_self)),
);
// V/U = t · (peer_pub + x̄_peer · R_peer)
// Reason: 先计算 x̄_peer · R_peer(标量乘),再加 peer_pub(仿射点)
let peer_pub_bytes = if is_initiator { pub_key_b } else { pub_key_a };
let peer_pub_aff = AffinePoint::from_bytes(peer_pub_bytes)?;
let peer_pub_jac = JacobianPoint::from_affine(&peer_pub_aff);
let r_peer_jac = JacobianPoint::from_affine(&r_peer_aff);
let x_bar_peer_r = JacobianPoint::scalar_mul(&x_bar_peer, &r_peer_jac);
let combined = JacobianPoint::add(&peer_pub_jac, &x_bar_peer_r);
let t = t_fn.retrieve();
let v_point = JacobianPoint::scalar_mul(&t, &combined);
let v_aff = v_point.to_affine().map_err(|_| Error::KeyExchangeFailed)?;
let xv = fp_to_bytes(&v_aff.x);
let yv = fp_to_bytes(&v_aff.y);
// K = KDF(xV || yV || ZA || ZB, klen)
let mut kdf_input = Vec::with_capacity(32 + 32 + 32 + 32);
kdf_input.extend_from_slice(&xv);
kdf_input.extend_from_slice(&yv);
kdf_input.extend_from_slice(&z_a);
kdf_input.extend_from_slice(&z_b);
let key = crate::kdf::kdf(&kdf_input, klen);
// KDF 输出全零时返回错误(防弱密钥)
if key.iter().all(|&b| b == 0) {
return Err(Error::KeyExchangeFailed);
}
// 确认哈希
// (x1,y1) 始终是 RA(发起方),(x2,y2) 始终是 RB(响应方)
let (x1, y1, x2, y2) = if is_initiator {
(
fp_to_bytes(&r_self_aff.x),
fp_to_bytes(&r_self_aff.y),
fp_to_bytes(&r_peer_aff.x),
fp_to_bytes(&r_peer_aff.y),
)
} else {
(
fp_to_bytes(&r_peer_aff.x),
fp_to_bytes(&r_peer_aff.y),
fp_to_bytes(&r_self_aff.x),
fp_to_bytes(&r_self_aff.y),
)
};
// 内部哈希 hash_v = SM3(xV || ZA || ZB || x1 || y1 || x2 || y2)
let mut h = Sm3H::new();
h.update(&xv);
h.update(&z_a);
h.update(&z_b);
h.update(&x1);
h.update(&y1);
h.update(&x2);
h.update(&y2);
let hash_v = h.finalize();
// S1 = SM3(0x02 || yV || hash_v) — 己方若为 B,则 S1 是己方确认值
let s1 = {
let mut h = Sm3H::new();
h.update(&[0x02]);
h.update(&yv);
h.update(&hash_v);
h.finalize()
};
// SA = SM3(0x03 || yV || hash_v)
let sa = {
let mut h = Sm3H::new();
h.update(&[0x03]);
h.update(&yv);
h.update(&hash_v);
h.finalize()
};
// Reason: 发起方 A 的确认哈希是 SA(0x03),响应方 B 的确认哈希是 S1(0x02)
let (s_self, s_peer) = if is_initiator {
(sa, s1) // A 发送 SA 给 B 验证,A 验证 B 发来的 S1
} else {
(s1, sa) // B 发送 S1 给 A 验证,B 验证 A 发来的 SA
};
Ok(ExchangeResult {
key,
s_self,
s_peer,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::PrivateKey;
#[allow(dead_code)]
struct FakeRng(#[allow(dead_code)] [u8; 32]);
impl RngCore for FakeRng {
fn next_u32(&mut self) -> u32 {
0
}
fn next_u64(&mut self) -> u64 {
0
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
for (i, b) in dest.iter_mut().enumerate() {
*b = self.0[i % 32];
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
#[test]
fn test_x_bar() {
// x = 1(低 128 位只有 bit 0
let mut x_bytes = [0u8; 32];
x_bytes[31] = 0x01;
let result = x_bar(&x_bytes);
// 期望 2^127 + 1
let mut expected = [0u8; 32];
expected[16] = 0x80;
expected[31] = 0x01;
assert_eq!(result, U256::from_be_slice(&expected));
}
#[test]
fn test_x_bar_high_bits_cleared() {
// x 有高 128 位数据,应被清除
let x_bytes = [0xFFu8; 32];
let result = x_bar(&x_bytes);
// 低 128 位全 1 + 2^127 设置位 = 0x80 FF...FF 后 16 字节加上 2^127
// 高 16 字节应为 0bytes[16] = 0xFF | 0x80 = 0xFF
let mut expected = [0u8; 32];
expected[16..32].copy_from_slice(&[0xFF; 16]);
expected[16] |= 0x80; // 已经是 0xFF,不变
assert_eq!(result, U256::from_be_slice(&expected));
}
#[test]
fn test_ecdh_roundtrip() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let d_b: [u8; 32] = [
0x59, 0x27, 0x6e, 0x27, 0xd5, 0x06, 0x86, 0x1a, 0x16, 0x68, 0x0f, 0x3a, 0xd9, 0xc0,
0x2d, 0xcc, 0xef, 0x3c, 0xc1, 0xfa, 0x3c, 0xdb, 0xe4, 0xce, 0x6d, 0x54, 0xb8, 0x0d,
0xea, 0xc1, 0xbc, 0x21,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
let pri_b = PrivateKey::from_bytes(&d_b).unwrap();
let pub_a = pri_a.public_key();
let pub_b = pri_b.public_key();
// A 用 B 的公钥算 ECDH,B 用 A 的公钥算 ECDH,结果应一致
let shared_a = ecdh(&pri_a, &pub_b).unwrap();
let shared_b = ecdh(&pri_b, &pub_a).unwrap();
assert_eq!(shared_a, shared_b);
}
#[test]
fn test_ecdh_invalid_pubkey() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
// 无效公钥(全零 y 坐标不在曲线上)
let mut bad_pub = [0x04u8; 65];
bad_pub[1] = 0x01;
assert!(ecdh(&pri_a, &bad_pub).is_err());
}
#[test]
fn test_ecdh_from_slice_length_check() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
// 长度不对应报 InvalidInputLength
assert!(ecdh_from_slice(&pri_a, &[0x04u8; 64]).is_err());
assert!(ecdh_from_slice(&pri_a, &[0x04u8; 66]).is_err());
}
#[test]
fn test_ecdh_from_slice_equals_ecdh() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let d_b: [u8; 32] = [
0x59, 0x27, 0x6e, 0x27, 0xd5, 0x06, 0x86, 0x1a, 0x16, 0x68, 0x0f, 0x3a, 0xd9, 0xc0,
0x2d, 0xcc, 0xef, 0x3c, 0xc1, 0xfa, 0x3c, 0xdb, 0xe4, 0xce, 0x6d, 0x54, 0xb8, 0x0d,
0xea, 0xc1, 0xbc, 0x21,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
let pri_b = PrivateKey::from_bytes(&d_b).unwrap();
let pub_b = pri_b.public_key();
let r1 = ecdh(&pri_a, &pub_b).unwrap();
let r2 = ecdh_from_slice(&pri_a, &pub_b).unwrap();
assert_eq!(r1, r2);
}
#[cfg(feature = "alloc")]
#[test]
fn test_exchange_roundtrip() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let d_b: [u8; 32] = [
0x59, 0x27, 0x6e, 0x27, 0xd5, 0x06, 0x86, 0x1a, 0x16, 0x68, 0x0f, 0x3a, 0xd9, 0xc0,
0x2d, 0xcc, 0xef, 0x3c, 0xc1, 0xfa, 0x3c, 0xdb, 0xe4, 0xce, 0x6d, 0x54, 0xb8, 0x0d,
0xea, 0xc1, 0xbc, 0x21,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
let pri_b = PrivateKey::from_bytes(&d_b).unwrap();
let pub_a = pri_a.public_key();
let pub_b = pri_b.public_key();
let id_a = b"Alice@test.com";
let id_b = b"Bob@test.com";
// 生成临时密钥
let ra_scalar =
U256::from_be_hex("83A2C9C8B96E5AF70BD480B472409A9A327257F1EBB73F5B073354B248668563");
let rb_scalar =
U256::from_be_hex("33FE21940342161C55619C4A0C060293D543C80AF19748CE176D83477DE71C80");
let eph_a = EphemeralKey::from_scalar(&ra_scalar).unwrap();
let eph_b = EphemeralKey::from_scalar(&rb_scalar).unwrap();
let result_a = exchange_a(
16,
id_a,
id_b,
&pri_a,
&pub_a,
&pub_b,
&eph_a,
eph_b.public_key(),
)
.unwrap();
let result_b = exchange_b(
16,
id_a,
id_b,
&pri_b,
&pub_a,
&pub_b,
&eph_b,
eph_a.public_key(),
)
.unwrap();
// 协商出的密钥应相同
assert_eq!(result_a.key, result_b.key);
assert!(!result_a.key.is_empty());
}
#[cfg(feature = "alloc")]
#[test]
fn test_exchange_confirmation() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let d_b: [u8; 32] = [
0x59, 0x27, 0x6e, 0x27, 0xd5, 0x06, 0x86, 0x1a, 0x16, 0x68, 0x0f, 0x3a, 0xd9, 0xc0,
0x2d, 0xcc, 0xef, 0x3c, 0xc1, 0xfa, 0x3c, 0xdb, 0xe4, 0xce, 0x6d, 0x54, 0xb8, 0x0d,
0xea, 0xc1, 0xbc, 0x21,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
let pri_b = PrivateKey::from_bytes(&d_b).unwrap();
let pub_a = pri_a.public_key();
let pub_b = pri_b.public_key();
let id_a = b"1234567812345678";
let id_b = b"1234567812345678";
let ra_scalar =
U256::from_be_hex("83A2C9C8B96E5AF70BD480B472409A9A327257F1EBB73F5B073354B248668563");
let rb_scalar =
U256::from_be_hex("33FE21940342161C55619C4A0C060293D543C80AF19748CE176D83477DE71C80");
let eph_a = EphemeralKey::from_scalar(&ra_scalar).unwrap();
let eph_b = EphemeralKey::from_scalar(&rb_scalar).unwrap();
let result_a = exchange_a(
16,
id_a,
id_b,
&pri_a,
&pub_a,
&pub_b,
&eph_a,
eph_b.public_key(),
)
.unwrap();
let result_b = exchange_b(
16,
id_a,
id_b,
&pri_b,
&pub_a,
&pub_b,
&eph_b,
eph_a.public_key(),
)
.unwrap();
// 确认哈希交叉验证:A.s_peer == B.s_selfB.s_peer == A.s_self
assert_eq!(result_a.s_peer, result_b.s_self);
assert_eq!(result_b.s_peer, result_a.s_self);
}
#[cfg(feature = "alloc")]
#[test]
fn test_exchange_different_ids() {
let d_a: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
let d_b: [u8; 32] = [
0x59, 0x27, 0x6e, 0x27, 0xd5, 0x06, 0x86, 0x1a, 0x16, 0x68, 0x0f, 0x3a, 0xd9, 0xc0,
0x2d, 0xcc, 0xef, 0x3c, 0xc1, 0xfa, 0x3c, 0xdb, 0xe4, 0xce, 0x6d, 0x54, 0xb8, 0x0d,
0xea, 0xc1, 0xbc, 0x21,
];
let pri_a = PrivateKey::from_bytes(&d_a).unwrap();
let pri_b = PrivateKey::from_bytes(&d_b).unwrap();
let pub_a = pri_a.public_key();
let pub_b = pri_b.public_key();
let ra_scalar =
U256::from_be_hex("83A2C9C8B96E5AF70BD480B472409A9A327257F1EBB73F5B073354B248668563");
let rb_scalar =
U256::from_be_hex("33FE21940342161C55619C4A0C060293D543C80AF19748CE176D83477DE71C80");
// 使用不同 ID 组合
let eph_a1 = EphemeralKey::from_scalar(&ra_scalar).unwrap();
let eph_b1 = EphemeralKey::from_scalar(&rb_scalar).unwrap();
let result_1 = exchange_a(
16,
b"ID_A_1",
b"ID_B_1",
&pri_a,
&pub_a,
&pub_b,
&eph_a1,
eph_b1.public_key(),
)
.unwrap();
let eph_a2 = EphemeralKey::from_scalar(&ra_scalar).unwrap();
let eph_b2 = EphemeralKey::from_scalar(&rb_scalar).unwrap();
let result_2 = exchange_a(
16,
b"ID_A_2",
b"ID_B_2",
&pri_a,
&pub_a,
&pub_b,
&eph_a2,
eph_b2.public_key(),
)
.unwrap();
// 不同 ID 应产生不同密钥
assert_ne!(result_1.key, result_2.key);
}
}
+796
View File
@@ -0,0 +1,796 @@
//! SM2 椭圆曲线公钥密码算法(GB/T 32918.1-5-2016
//!
//! 本 crate 提供符合 GB/T 32918-2016 的纯 Rust、`no_std` 实现:
//!
//! - **密钥生成**[`generate_keypair`]
//! - **数字签名/验签**[`SigningKey`] / [`VerifyingKey`](实现 `signature::Signer/Verifier`
//! - **公钥加密/解密**[`encrypt`] / [`decrypt`](需 `alloc` feature
//! - **密钥交换**[`key_exchange::ecdh`] / [`key_exchange::exchange_a`]
//! - **DER 编解码**[`der`]
//!
//! ## 安全性声明
//!
//! - 所有私钥操作均为常量时间(Montgomery 域算术 + `subtle::ConditionallySelectable`
//! - 私钥离开作用域后自动清零([`ZeroizeOnDrop`]
//! - 标量乘法固定迭代 256 位,不跳过前导零
//! - `sign_with_k` 危险接口需启用 `hazmat` feature
//!
//! ## 快速开始
//!
//! ```rust
//! use sm2::{SigningKey, VerifyingKey, DEFAULT_ID};
//! use sm2::signature::{Signer, Verifier};
//! use rand_core::OsRng;
//!
//! // 生成密钥对
//! let (pri, pub_bytes) = sm2::generate_keypair(&mut OsRng);
//! let signing = SigningKey::new(pri, DEFAULT_ID);
//! let verifying = VerifyingKey::new(pub_bytes, DEFAULT_ID);
//!
//! // 签名
//! let msg = b"hello SM2";
//! let sig = signing.sign(msg);
//!
//! // 验签
//! verifying.verify(msg, &sig).expect("验签应通过");
//! ```
//!
//! ---
//!
//! SM2 elliptic curve public-key cryptography (GB/T 32918.1-5-2016).
//!
//! This crate provides a pure-Rust, `no_std` implementation of:
//!
//! - **Key generation**: [`generate_keypair`]
//! - **Signing / Verification**: [`SigningKey`] / [`VerifyingKey`]
//! (implement `signature::Signer` / `Verifier`)
//! - **Public-key encryption / decryption**: [`encrypt`] / [`decrypt`]
//! (requires `alloc` feature)
//! - **Key exchange**: [`key_exchange::ecdh`] / [`key_exchange::exchange_a`]
//! - **DER encoding / decoding**: [`der`]
//!
//! ## Security
//!
//! - All secret-dependent operations are constant-time
//! - Private keys are zeroized on drop ([`ZeroizeOnDrop`])
//! - Scalar multiplication iterates all 256 bits regardless of scalar value
//! - `sign_with_k` (dangerous raw-k API) requires the `hazmat` feature
#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod der;
pub mod ec;
pub mod error;
pub mod field;
pub mod kdf;
pub mod key_exchange;
mod rfc6979;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crypto_bigint::{Zero, U256};
use rand_core::RngCore;
use subtle::ConstantTimeEq;
use zeroize::{Zeroize, ZeroizeOnDrop};
pub use error::Error;
pub use signature;
use crate::ec::{multi_scalar_mul, AffinePoint, JacobianPoint};
use crate::field::{
fn_add, fn_inv, fn_mul, fn_sub, fp_to_bytes, Fn, CURVE_A, CURVE_B, GROUP_ORDER,
GROUP_ORDER_MINUS_1, GX, GY,
};
// ── 内部 SM3 包装 ─────────────────────────────────────────────────────────────
/// 内部轻量哈希上下文(包装 sm3::Sm3 的 Digest API
///
/// Thin wrapper around `sm3::Sm3` exposing a streaming `update`/`finalize` API
/// identical to the original `Sm3Hasher`, so callers need no refactoring.
struct Sm3H(sm3::Sm3);
impl Sm3H {
fn new() -> Self {
use sm3::Digest;
Sm3H(sm3::Sm3::new())
}
fn update(&mut self, data: &[u8]) {
use sm3::Digest;
self.0.update(data);
}
fn finalize(self) -> [u8; 32] {
use sm3::Digest;
self.0.finalize().into()
}
}
// ── 常量 ──────────────────────────────────────────────────────────────────────
/// SM2 默认用户可辨别标识(GB/T 32918.2-2016 §A.2 示例值)
///
/// Default user distinguishable identifier (example from GB/T 32918.2-2016 §A.2).
pub const DEFAULT_ID: &[u8] = b"1234567812345678";
// ── 私钥类型 ──────────────────────────────────────────────────────────────────
/// SM2 私钥(32 字节,离开作用域自动清零)
///
/// SM2 private key (32 bytes). Automatically zeroized on drop.
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct PrivateKey {
bytes: [u8; 32],
}
impl PrivateKey {
/// 从字节构造私钥(验证 d ∈ [1, n-2])
///
/// Construct from bytes, validating d ∈ [1, n-2].
pub fn from_bytes(bytes: &[u8; 32]) -> Result<Self, Error> {
let d = U256::from_be_slice(bytes);
if bool::from(d.is_zero()) || d >= GROUP_ORDER_MINUS_1 {
return Err(Error::InvalidPrivateKey);
}
Ok(PrivateKey { bytes: *bytes })
}
/// 以字节引用访问私钥
///
/// Access the private key bytes by reference.
pub fn as_bytes(&self) -> &[u8; 32] {
&self.bytes
}
/// 计算对应公钥(65 字节,04||x||y
///
/// Derive the corresponding public key (65 bytes, uncompressed: 04||x||y).
pub fn public_key(&self) -> [u8; 65] {
let d = U256::from_be_slice(&self.bytes);
let pub_jac = JacobianPoint::scalar_mul_g(&d);
// Reason: 私钥合法性已在构造时验证,scalar_mul_g 结果不会是无穷远点
let pub_aff = pub_jac
.to_affine()
.expect("valid private key produces valid public key");
pub_aff.to_bytes()
}
}
// ── 密钥生成 ──────────────────────────────────────────────────────────────────
/// 生成 SM2 密钥对(私钥 + 公钥 65 字节)
///
/// Generate a SM2 key pair (private key + 65-byte public key).
/// Conforms to GB/T 32918.1-2016 §6.1.
pub fn generate_keypair<R: RngCore>(rng: &mut R) -> (PrivateKey, [u8; 65]) {
loop {
let mut d_bytes = [0u8; 32];
rng.fill_bytes(&mut d_bytes);
let d = U256::from_be_slice(&d_bytes);
if bool::from(d.is_zero()) || d >= GROUP_ORDER_MINUS_1 {
d_bytes.zeroize();
continue;
}
// Reason: 私钥满足范围约束,不会失败
let priv_key = PrivateKey { bytes: d_bytes };
let pub_key = priv_key.public_key();
return (priv_key, pub_key);
}
}
// ── Z 值计算(GB/T 32918.2-2016 §5.5)────────────────────────────────────────
/// 计算用户标识的 Z 值
///
/// Z = SM3(ENTL || ID || a || b || Gx || Gy || Px || Py)
///
/// Compute Z-value for the user identity. Conforms to GB/T 32918.2-2016 §5.5.
pub fn get_z(id: &[u8], pub_key: &[u8; 65]) -> [u8; 32] {
let entl = (id.len() * 8) as u16;
let mut h = Sm3H::new();
h.update(&entl.to_be_bytes());
h.update(id);
h.update(&fp_to_bytes(&CURVE_A));
h.update(&fp_to_bytes(&CURVE_B));
h.update(&fp_to_bytes(&GX));
h.update(&fp_to_bytes(&GY));
h.update(&pub_key[1..33]); // Px
h.update(&pub_key[33..65]); // Py
h.finalize()
}
/// 计算消息摘要 e = SM3(Z || M)
///
/// Compute message digest e = SM3(Z || M). Conforms to GB/T 32918.2-2016 §5.5.
pub fn get_e(z: &[u8; 32], msg: &[u8]) -> [u8; 32] {
let mut h = Sm3H::new();
h.update(z);
h.update(msg);
h.finalize()
}
// ── 数字签名(GB/T 32918.2-2016 §6.2)───────────────────────────────────────
/// SM2 签名(使用指定随机数 k)
///
/// Sign using a specified nonce k. **Only expose under `hazmat` feature — misusing k leaks the private key.**
///
/// Sign with a fixed nonce k (for test vectors / hazmat use only).
/// Requires the `hazmat` feature gate.
#[cfg(feature = "hazmat")]
pub fn sign_with_k(e: &[u8; 32], pri_key: &PrivateKey, k: &U256) -> Result<[u8; 64], Error> {
sign_with_k_inner(e, pri_key, k)
}
/// 内部签名实现(供 `sign` 和 `hazmat::sign_with_k` 共用)
fn sign_with_k_inner(e: &[u8; 32], pri_key: &PrivateKey, k: &U256) -> Result<[u8; 64], Error> {
let d = U256::from_be_slice(pri_key.as_bytes());
let kg_aff = JacobianPoint::scalar_mul_g(k)
.to_affine()
.map_err(|_| Error::InvalidSignature)?;
let x1 = fp_to_bytes(&kg_aff.x);
let e_val = U256::from_be_slice(e);
let x1_val = U256::from_be_slice(&x1);
let r_fn = fn_add(&Fn::new(&e_val), &Fn::new(&x1_val));
let r = r_fn.retrieve();
if bool::from(r.is_zero()) {
return Err(Error::InvalidSignature);
}
if fn_add(&r_fn, &Fn::new(k)).retrieve().is_zero().into() {
return Err(Error::InvalidSignature);
}
let d_fn = Fn::new(&d);
let one_plus_d = fn_add(&Fn::ONE, &d_fn);
let inv = fn_inv(&one_plus_d).ok_or(Error::InvalidPrivateKey)?;
let rd = fn_mul(&r_fn, &d_fn);
let s_fn = fn_mul(&inv, &fn_sub(&Fn::new(k), &rd));
let s = s_fn.retrieve();
if bool::from(s.is_zero()) {
return Err(Error::InvalidSignature);
}
let mut sig = [0u8; 64];
sig[..32].copy_from_slice(&r.to_be_bytes());
sig[32..].copy_from_slice(&s.to_be_bytes());
Ok(sig)
}
/// SM2 签名(随机 k,标准接口)
///
/// Sign with random nonce k. Accepts pre-computed digest `e = SM3(Z||M)`.
pub fn sign<R: RngCore>(e: &[u8; 32], pri_key: &PrivateKey, rng: &mut R) -> [u8; 64] {
loop {
let mut k_bytes = [0u8; 32];
rng.fill_bytes(&mut k_bytes);
let k = U256::from_be_slice(&k_bytes);
k_bytes.zeroize();
if bool::from(k.is_zero()) || k >= GROUP_ORDER {
continue;
}
if let Ok(sig) = sign_with_k_inner(e, pri_key, &k) {
return sig;
}
}
}
/// SM2 确定性签名(RFC 6979,使用 HMAC-SM3 生成 k
///
/// Sign with deterministic nonce k derived via RFC 6979.
/// Accepts pre-computed digest `e = SM3(Z||M)`.
///
/// # 安全关键点
///
/// 此函数不依赖外部 RNG,消除了 RNG 故障或偏差导致私钥泄露的风险。
/// 对于相同的 (私钥, 消息摘要) 对,签名结果完全确定。
pub fn sign_deterministic(e: &[u8; 32], pri_key: &PrivateKey) -> [u8; 64] {
// Reason: RFC 6979 保证生成的 k 总是满足 0 < k < n
// 并且对于合法私钥,sign_with_k_inner 总会成功(极罕见的 r=0/s=0 情况由 RFC 6979 循环避免)。
// 如果 sign_with_k_inner 失败(理论上极罕见),我们使用不同输入再试。
let k = rfc6979::generate_k(pri_key.as_bytes(), e);
// RFC 6979 生成的 k 在几乎所有情况下都有效,直接调用
if let Ok(sig) = sign_with_k_inner(e, pri_key, &k) {
return sig;
}
// 极罕见的 fallback:用 e+pri_key 的不同组合再生成一个 k
// (实际上 RFC 6979 的循环设计保证不会到这里)
let mut alt_input = [0u8; 32];
for (i, (&a, &b)) in e.iter().zip(pri_key.as_bytes().iter()).enumerate() {
alt_input[i] = a.wrapping_add(b).wrapping_add(1);
}
let k2 = rfc6979::generate_k(pri_key.as_bytes(), &alt_input);
sign_with_k_inner(e, pri_key, &k2).expect("RFC 6979 fallback must succeed")
}
/// SM2 签名(便捷接口,自动计算 Z 值与消息摘要)
///
/// Convenience signing: auto-computes Z = SM3(ENTL||ID||...) and e = SM3(Z||M).
pub fn sign_message<R: RngCore>(
msg: &[u8],
id: &[u8],
pri_key: &PrivateKey,
rng: &mut R,
) -> [u8; 64] {
let pub_key = pri_key.public_key();
let z = get_z(id, &pub_key);
let e = get_e(&z, msg);
sign(&e, pri_key, rng)
}
/// SM2 确定性签名便捷接口(RFC 6979,无需 RNG
///
/// Convenience deterministic signing: auto-computes Z and e, then uses RFC 6979.
pub fn sign_message_deterministic(msg: &[u8], id: &[u8], pri_key: &PrivateKey) -> [u8; 64] {
let pub_key = pri_key.public_key();
let z = get_z(id, &pub_key);
let e = get_e(&z, msg);
sign_deterministic(&e, pri_key)
}
// ── 签名验证(GB/T 32918.2-2016 §6.3)───────────────────────────────────────
/// SM2 验签
///
/// Verify a SM2 signature. Accepts pre-computed digest `e = SM3(Z||M)`.
pub fn verify(e: &[u8; 32], pub_key: &[u8; 65], sig: &[u8; 64]) -> Result<(), Error> {
let r = U256::from_be_slice(&sig[..32]);
let s = U256::from_be_slice(&sig[32..]);
let n = GROUP_ORDER;
if bool::from(r.is_zero()) || r >= n || bool::from(s.is_zero()) || s >= n {
return Err(Error::InvalidSignature);
}
let t_fn = fn_add(&Fn::new(&r), &Fn::new(&s));
let t = t_fn.retrieve();
if bool::from(t.is_zero()) {
return Err(Error::VerifyFailed);
}
let pa = AffinePoint::from_bytes(pub_key)?;
let point = multi_scalar_mul(&s, &t, &pa)?;
let e_val = U256::from_be_slice(e);
let px_val = U256::from_be_slice(&fp_to_bytes(&point.x));
let r_check = fn_add(&Fn::new(&e_val), &Fn::new(&px_val)).retrieve();
// Reason: 常量时间比较,防时序侧信道
if r.to_be_bytes().ct_eq(&r_check.to_be_bytes()).unwrap_u8() != 1 {
return Err(Error::VerifyFailed);
}
Ok(())
}
/// SM2 验签(便捷接口,自动计算 Z 值与消息要)
///
/// Convenience verification: auto-computes Z and e.
pub fn verify_message(
msg: &[u8],
id: &[u8],
pub_key: &[u8; 65],
sig: &[u8; 64],
) -> Result<(), Error> {
let z = get_z(id, pub_key);
let e = get_e(&z, msg);
verify(&e, pub_key, sig)
}
// ── 公钥加密(GB/T 32918.4-2016 §7.1)──────────────────────────────────────
/// SM2 公钥加密
///
/// SM2 public-key encryption. Output format: C1||C3||C2 (GB/T 32918.4-2016 §6.1).
/// Requires `alloc` feature.
#[cfg(feature = "alloc")]
pub fn encrypt<R: RngCore>(
pub_key: &[u8; 65],
message: &[u8],
rng: &mut R,
) -> Result<Vec<u8>, Error> {
let pa = AffinePoint::from_bytes(pub_key)?;
loop {
let mut k_bytes = [0u8; 32];
rng.fill_bytes(&mut k_bytes);
let k = U256::from_be_slice(&k_bytes);
k_bytes.zeroize();
if bool::from(k.is_zero()) || k >= GROUP_ORDER {
continue;
}
let c1_aff = match JacobianPoint::scalar_mul_g(&k).to_affine() {
Ok(p) => p,
Err(_) => continue,
};
let c1 = c1_aff.to_bytes();
let pa_jac = JacobianPoint::from_affine(&pa);
let kpa_aff = match JacobianPoint::scalar_mul(&k, &pa_jac).to_affine() {
Ok(p) => p,
Err(_) => continue,
};
let x2 = fp_to_bytes(&kpa_aff.x);
let y2 = fp_to_bytes(&kpa_aff.y);
let mut z_input = [0u8; 64];
z_input[..32].copy_from_slice(&x2);
z_input[32..].copy_from_slice(&y2);
let t = kdf::kdf(&z_input, message.len());
if t.iter().all(|&b| b == 0) {
continue;
}
let c2: Vec<u8> = message.iter().zip(t.iter()).map(|(&m, &k)| m ^ k).collect();
let mut h = Sm3H::new();
h.update(&x2);
h.update(message);
h.update(&y2);
let c3 = h.finalize();
let mut output = Vec::with_capacity(65 + 32 + message.len());
output.extend_from_slice(&c1);
output.extend_from_slice(&c3);
output.extend_from_slice(&c2);
return Ok(output);
}
}
// ── 公钥解密(GB/T 32918.4-2016 §7.2)──────────────────────────────────────
/// SM2 公钥解密(新格式 C1||C3||C2
///
/// SM2 public-key decryption (format C1||C3||C2). Requires `alloc` feature.
#[cfg(feature = "alloc")]
pub fn decrypt(pri_key: &PrivateKey, ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
if ciphertext.len() < 97 {
return Err(Error::InvalidInputLength);
}
let d = U256::from_be_slice(pri_key.as_bytes());
let c1_bytes: [u8; 65] = ciphertext[0..65].try_into().unwrap();
let c1 = AffinePoint::from_bytes(&c1_bytes)?;
let c3_expected: [u8; 32] = ciphertext[65..97].try_into().unwrap();
let c2 = &ciphertext[97..];
let c1_jac = JacobianPoint::from_affine(&c1);
let dc1_aff = JacobianPoint::scalar_mul(&d, &c1_jac).to_affine()?;
let x2 = fp_to_bytes(&dc1_aff.x);
let y2 = fp_to_bytes(&dc1_aff.y);
let mut z_input = [0u8; 64];
z_input[..32].copy_from_slice(&x2);
z_input[32..].copy_from_slice(&y2);
let t = kdf::kdf(&z_input, c2.len());
if t.iter().all(|&b| b == 0) {
return Err(Error::DecryptFailed);
}
let m: Vec<u8> = c2.iter().zip(t.iter()).map(|(&c, &k)| c ^ k).collect();
let mut h = Sm3H::new();
h.update(&x2);
h.update(&m);
h.update(&y2);
let c3_computed = h.finalize();
// Reason: 先验证 C3 再返回明文,防止 chosen-ciphertext 攻击
if c3_expected.ct_eq(&c3_computed).unwrap_u8() != 1 {
return Err(Error::DecryptFailed);
}
Ok(m)
}
// ── signature::Signer / Verifier trait 实现 ──────────────────────────────────
/// SM2 签名结果(r||s64 字节)
///
/// SM2 signature (r||s, 64 bytes).
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Sm2Signature {
bytes: [u8; 64],
}
impl Sm2Signature {
/// 从 64 字节原始 r||s 构造签名
///
/// Construct from raw 64-byte r||s.
pub fn from_bytes(bytes: [u8; 64]) -> Self {
Sm2Signature { bytes }
}
/// 以字节切片返回签名
///
/// Return the signature as a byte slice.
pub fn as_bytes(&self) -> &[u8; 64] {
&self.bytes
}
}
impl AsRef<[u8]> for Sm2Signature {
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
// ── SigningKey ────────────────────────────────────────────────────────────────
/// SM2 签名密钥(私钥 + 用户标识)
///
/// SM2 signing key: wraps a private key with a user identity string.
/// Implements [`signature::Signer<Sm2Signature>`].
pub struct SigningKey<'id> {
private_key: PrivateKey,
/// 用户可辨别标识 / User distinguishable identifier
pub id: &'id [u8],
}
impl<'id> SigningKey<'id> {
/// 构造签名密钥
///
/// Construct a signing key from a private key and user ID.
pub fn new(private_key: PrivateKey, id: &'id [u8]) -> Self {
SigningKey { private_key, id }
}
/// 获取对应的公钥字节(65 字节,04||x||y
///
/// Derive the corresponding public key bytes (65 bytes, uncompressed).
pub fn public_key_bytes(&self) -> [u8; 65] {
self.private_key.public_key()
}
}
impl<'id> signature::Signer<Sm2Signature> for SigningKey<'id> {
fn try_sign(&self, msg: &[u8]) -> Result<Sm2Signature, signature::Error> {
// Reason: sign_message 需要 RngCore;此处用 OsRng 退化实现
// 在 no_std 环境中,若无 OsRng 可用,调用方应直接调用 sign/sign_message
use rand_core::OsRng;
let sig_bytes = sign_message(msg, self.id, &self.private_key, &mut OsRng);
Ok(Sm2Signature { bytes: sig_bytes })
}
}
// ── VerifyingKey ──────────────────────────────────────────────────────────────
/// SM2 验证密钥(公钥 + 用户标识)
///
/// SM2 verifying key: wraps a public key with a user identity string.
/// Implements [`signature::Verifier<Sm2Signature>`].
pub struct VerifyingKey<'id> {
public_key: [u8; 65],
/// 用户可辨别标识 / User distinguishable identifier
pub id: &'id [u8],
}
impl<'id> VerifyingKey<'id> {
/// 构造验证密钥
///
/// Construct a verifying key from a public key and user ID.
pub fn new(public_key: [u8; 65], id: &'id [u8]) -> Self {
VerifyingKey { public_key, id }
}
/// 验证公钥是否在 SM2 曲线上
///
/// Returns `Ok(())` if the public key is a valid SM2 curve point.
pub fn validate(&self) -> Result<(), Error> {
AffinePoint::from_bytes(&self.public_key).map(|_| ())
}
}
impl<'id> signature::Verifier<Sm2Signature> for VerifyingKey<'id> {
fn verify(&self, msg: &[u8], signature: &Sm2Signature) -> Result<(), signature::Error> {
verify_message(msg, self.id, &self.public_key, &signature.bytes)
.map_err(signature::Error::from)
}
}
// ── 单元测试 ──────────────────────────────────────────────────────────────────
#[cfg(test)]
mod tests {
use super::*;
// 共用测试私钥(来自 libsmx 内部测试)
const D_BYTES: [u8; 32] = [
0x39, 0x45, 0x20, 0x8f, 0x7b, 0x21, 0x44, 0xb1, 0x3f, 0x36, 0xe3, 0x8a, 0xc6, 0xd3,
0x9f, 0x95, 0x88, 0x93, 0x93, 0x69, 0x28, 0x60, 0xb5, 0x1a, 0x42, 0xfb, 0x81, 0xef,
0x4d, 0xf7, 0xc5, 0xb8,
];
const K_BYTES: [u8; 32] = [
0x59, 0x27, 0x6e, 0x27, 0xd5, 0x06, 0x86, 0x1a, 0x16, 0x68, 0x0f, 0x3a, 0xd9, 0xc0,
0x2d, 0xcc, 0xef, 0x3c, 0xc1, 0xfa, 0x3c, 0xdb, 0xe4, 0xce, 0x6d, 0x54, 0xb8, 0x0d,
0xea, 0xc1, 0xbc, 0x21,
];
// 测试专用的确定性 RNG(使用固定字节池)
struct FakeRng([u8; 32]);
impl rand_core::RngCore for FakeRng {
fn next_u32(&mut self) -> u32 { 0 }
fn next_u64(&mut self) -> u64 { 0 }
fn fill_bytes(&mut self, dest: &mut [u8]) {
for (i, b) in dest.iter_mut().enumerate() {
*b = self.0[i % 32];
}
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
self.fill_bytes(dest);
Ok(())
}
}
#[test]
fn test_private_key_from_bytes_valid() {
PrivateKey::from_bytes(&D_BYTES).expect("合法私钥应成功构造");
}
#[test]
fn test_private_key_from_bytes_zero() {
assert!(PrivateKey::from_bytes(&[0u8; 32]).is_err(), "全零私钥应拒绝");
}
#[test]
fn test_public_key_on_curve() {
let pri = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_bytes = pri.public_key();
let point = AffinePoint::from_bytes(&pub_bytes).expect("公钥应在曲线上");
assert!(point.is_on_curve());
}
#[test]
fn test_get_z_deterministic() {
let pub_key = [0x04u8; 65];
let z1 = get_z(DEFAULT_ID, &pub_key);
let z2 = get_z(DEFAULT_ID, &pub_key);
assert_eq!(z1, z2);
}
#[test]
fn test_sign_verify_roundtrip() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let msg = b"hello sm2";
let z = get_z(DEFAULT_ID, &pub_key);
let e = get_e(&z, msg);
let k = U256::from_be_slice(&K_BYTES);
let sig = sign_with_k_inner(&e, &pri_key, &k).expect("签名应成功");
verify(&e, &pub_key, &sig).expect("验签应通过");
}
#[test]
fn test_sign_message_verify_message() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let mut rng = FakeRng(K_BYTES);
let msg = b"hello sign_message";
let sig = sign_message(msg, DEFAULT_ID, &pri_key, &mut rng);
verify_message(msg, DEFAULT_ID, &pub_key, &sig).expect("便捷验签应通过");
}
#[test]
fn test_verify_rejects_tampered_sig() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let msg = b"hello sm2";
let z = get_z(DEFAULT_ID, &pub_key);
let e = get_e(&z, msg);
let k = U256::from_be_slice(&K_BYTES);
let mut sig = sign_with_k_inner(&e, &pri_key, &k).unwrap();
sig[0] ^= 0x01;
assert!(verify(&e, &pub_key, &sig).is_err());
}
#[test]
fn test_verify_rejects_wrong_id() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let mut rng = FakeRng(K_BYTES);
let msg = b"hello sign_message";
let sig = sign_message(msg, DEFAULT_ID, &pri_key, &mut rng);
assert!(verify_message(msg, b"wrong-id", &pub_key, &sig).is_err());
}
#[cfg(feature = "alloc")]
#[test]
fn test_encrypt_decrypt_roundtrip() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let msg = b"Hello, SM2 encryption!";
let mut rng = FakeRng(K_BYTES);
let ciphertext = encrypt(&pub_key, msg, &mut rng).expect("加密应成功");
let plaintext = decrypt(&pri_key, &ciphertext).expect("解密应成功");
assert_eq!(plaintext, msg);
}
#[cfg(feature = "alloc")]
#[test]
fn test_decrypt_rejects_tampered_ciphertext() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let mut rng = FakeRng(K_BYTES);
let mut ct = encrypt(&pub_key, b"test", &mut rng).unwrap();
ct[70] ^= 0xFF;
assert!(decrypt(&pri_key, &ct).is_err());
}
// ── signature trait 测试 ────────────────────────────────────────────────
#[test]
fn test_signing_key_verifying_key_roundtrip() {
use signature::{Signer, Verifier};
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_bytes = pri_key.public_key();
let signing = SigningKey::new(pri_key, DEFAULT_ID);
let verifying = VerifyingKey::new(pub_bytes, DEFAULT_ID);
let msg = b"signature trait roundtrip";
let sig = signing.sign(msg);
verifying.verify(msg, &sig).expect("SigningKey/VerifyingKey 验签应通过");
}
#[test]
fn test_verifying_key_validate() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_bytes = pri_key.public_key();
let vk = VerifyingKey::new(pub_bytes, DEFAULT_ID);
assert!(vk.validate().is_ok());
}
/// RFC 6979 确定性签名:结果可被 verify 通过
#[test]
fn test_sign_deterministic_verify() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let msg = b"RFC 6979 deterministic test";
let z = get_z(DEFAULT_ID, &pub_key);
let e = get_e(&z, msg);
let sig = sign_deterministic(&e, &pri_key);
verify(&e, &pub_key, &sig).expect("deterministic sign must verify");
}
/// RFC 6979 确定性签名:相同输入总产生相同签名
#[test]
fn test_sign_deterministic_reproducible() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let msg = b"reproducibility test";
let z = get_z(DEFAULT_ID, &pub_key);
let e = get_e(&z, msg);
let sig1 = sign_deterministic(&e, &pri_key);
let sig2 = sign_deterministic(&e, &pri_key);
assert_eq!(sig1, sig2, "RFC 6979 signatures must be reproducible");
verify(&e, &pub_key, &sig1).expect("must verify");
}
/// sign_message_deterministic 便捷接口
#[test]
fn test_sign_message_deterministic() {
let pri_key = PrivateKey::from_bytes(&D_BYTES).unwrap();
let pub_key = pri_key.public_key();
let msg = b"sign_message_deterministic test";
let sig = sign_message_deterministic(msg, DEFAULT_ID, &pri_key);
verify_message(msg, DEFAULT_ID, &pub_key, &sig).expect("deterministic message sig must verify");
}
}
+176
View File
@@ -0,0 +1,176 @@
//! RFC 6979 确定性 k 值生成(使用 HMAC-SM3
//!
//! 实现 RFC 6979 §3.2 的 HMAC-DRBG,以 SM3 作为哈希函数。
//!
//! # 安全关键点
//!
//! 此模块消除了 SM2 签名对外部 RNG 的依赖。对于相同的 (私钥, 消息摘要) 对,
//! 生成的 k 值完全确定,从根本上消除了 RNG 故障或偏差导致私钥泄露的风险。
//!
//! # 参考
//!
//! - RFC 6979 §3.2: <https://www.rfc-editor.org/rfc/rfc6979#section-3.2>
use crypto_bigint::{Zero, U256};
use sm3::Digest;
use zeroize::Zeroize;
use crate::field::GROUP_ORDER;
// SM3 输出长度(字节)
const HASH_LEN: usize = 32;
/// 内部 HMAC-SM3 实现(仅供 RFC 6979 使用)
///
/// HMAC(K, m) = SM3((K ^ opad) || SM3((K ^ ipad) || m))
///
/// Reason: sm3 sub-crate 没有导出 HMAC,我们在内部实现以避免新增依赖。
/// 块大小 64 字节,与 SM3 的 BlockSize 一致。
struct HmacSm3 {
/// 外层密钥 K ^ opad(已预处理)
opad_key: [u8; 64],
/// 内层哈希上下文(已吸收 K ^ ipad)
inner: sm3::Sm3,
}
impl HmacSm3 {
/// 以 key 初始化 HMAC-SM3 上下文
fn new(key: &[u8; 32]) -> Self {
let mut ipad_key = [0x36u8; 64];
let mut opad_key = [0x5cu8; 64];
// Reason: key 长度 32 字节 < 块大小 64,直接 XOR 前 32 字节
for (i, &b) in key.iter().enumerate() {
ipad_key[i] ^= b;
opad_key[i] ^= b;
}
let mut inner = sm3::Sm3::new();
inner.update(&ipad_key);
ipad_key.zeroize();
HmacSm3 { opad_key, inner }
}
/// 向内层哈希追加数据
fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
/// 计算 HMAC 值(消耗 self
fn finalize(self) -> [u8; HASH_LEN] {
// Reason: Drop trait 阻止直接 move self.innerclone 一次即可
let inner_hash: [u8; HASH_LEN] = self.inner.clone().finalize().into();
let mut outer = sm3::Sm3::new();
outer.update(&self.opad_key);
outer.update(&inner_hash);
outer.finalize().into()
}
}
impl Drop for HmacSm3 {
fn drop(&mut self) {
self.opad_key.zeroize();
}
}
/// HMAC-SM3 一次性计算:`HMAC(key, msg1 || msg2 || ...)`
fn hmac(key: &[u8; 32], parts: &[&[u8]]) -> [u8; HASH_LEN] {
let mut mac = HmacSm3::new(key);
for part in parts {
mac.update(part);
}
mac.finalize()
}
/// RFC 6979 §3.2 确定性 k 值生成
///
/// 输入:
/// - `x`: 私钥字节(32 字节,big-endian
/// - `h1`: 消息摘要 e32 字节,已经过 Z||M 预处理的 SM3 输出)
///
/// 输出:满足 `0 < k < n` 的确定性标量 k
///
/// # 安全关键点
///
/// 对于相同的 (x, h1) 输入,此函数总是返回相同的 k,
/// 从根本上消除了签名过程对 RNG 质量的依赖。
pub(crate) fn generate_k(x: &[u8; 32], h1: &[u8; 32]) -> U256 {
// RFC 6979 §3.2 步骤 b/c: 初始化 V 和 K
//
// V = 0x01 0x01 ... 0x01 (hlen 个字节)
// K = 0x00 0x00 ... 0x00 (hlen 个字节)
let mut v = [0x01u8; HASH_LEN];
let mut k = [0x00u8; HASH_LEN];
// 步骤 d: K = HMAC_K(V || 0x00 || x || h1)
k = hmac(&k, &[&v, &[0x00u8], x, h1]);
// 步骤 e: V = HMAC_K(V)
v = hmac(&k, &[&v]);
// 步骤 f: K = HMAC_K(V || 0x01 || x || h1)
k = hmac(&k, &[&v, &[0x01u8], x, h1]);
// 步骤 g: V = HMAC_K(V)
v = hmac(&k, &[&v]);
// 步骤 h: 循环生成候选 k
loop {
// h2: V = HMAC_K(V)
v = hmac(&k, &[&v]);
// bits2int(V): 直接作为 big-endian 256-bit 整数
let candidate = U256::from_be_slice(&v);
// 检查 0 < k < ngroup order
// Reason: CT 比较 — candidate.is_zero() 和 candidate >= GROUP_ORDER
let is_zero: bool = candidate.is_zero().into();
let ge_n: bool = (candidate >= GROUP_ORDER).into();
if !is_zero && !ge_n {
// 找到合法 k,清零临时数据后返回
v.zeroize();
k.zeroize();
return candidate;
}
// 步骤 h3 更新(不满足时继续)
k = hmac(&k, &[&v, &[0x00u8]]);
v = hmac(&k, &[&v]);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::field::GROUP_ORDER;
/// 确定性验证:相同输入总产生相同 k
#[test]
fn test_generate_k_deterministic() {
let x = [0x01u8; 32];
let h1 = [0x02u8; 32];
let k1 = generate_k(&x, &h1);
let k2 = generate_k(&x, &h1);
assert_eq!(k1, k2, "RFC 6979 k must be deterministic");
}
/// k 必须在有效范围 (0, n)
#[test]
fn test_generate_k_range() {
let x = [0x42u8; 32];
let h1 = [0xABu8; 32];
let k = generate_k(&x, &h1);
assert!(!bool::from(k.is_zero()), "k must not be zero");
assert!(k < GROUP_ORDER, "k must be less than group order");
}
/// 不同消息产生不同 k
#[test]
fn test_generate_k_different_msgs() {
let x = [0x01u8; 32];
let h1 = [0x01u8; 32];
let h2 = [0x02u8; 32];
let k1 = generate_k(&x, &h1);
let k2 = generate_k(&x, &h2);
assert_ne!(k1, k2, "different messages must produce different k values");
}
}