重构: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:
@@ -0,0 +1,87 @@
|
||||
//! SM2 属性测试 / Property-based tests for SM2
|
||||
//!
|
||||
//! 使用 proptest 验证:任意随机私钥生成的签名均可被对应公钥验证。
|
||||
//! Tests that for arbitrary random key bytes, sign-then-verify always succeeds.
|
||||
|
||||
use libsmx::sm2::{get_e, get_z, sign, verify, PrivateKey, DEFAULT_ID};
|
||||
use proptest::prelude::*;
|
||||
|
||||
proptest! {
|
||||
/// 任意合法私钥:签名后验签必须通过
|
||||
///
|
||||
/// Reason: 使用原始字节数组作为策略输入(proptest 只需字节组具有 Debug),
|
||||
/// 在测试体内调用 from_bytes 过滤非法值,合法时执行验证逻辑。
|
||||
#[test]
|
||||
fn prop_sign_verify_roundtrip(
|
||||
key_bytes in prop::array::uniform32(1u8..=0xFFu8),
|
||||
msg in prop::collection::vec(any::<u8>(), 0..256),
|
||||
) {
|
||||
let pri_key = match PrivateKey::from_bytes(&key_bytes) {
|
||||
Ok(k) => k,
|
||||
Err(_) => return Ok(()), // 非法私钥直接跳过
|
||||
};
|
||||
|
||||
let pub_key = pri_key.public_key();
|
||||
let z = get_z(DEFAULT_ID, &pub_key);
|
||||
let e = get_e(&z, &msg);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let sig = sign(&e, &pri_key, &mut rng);
|
||||
|
||||
prop_assert!(verify(&e, &pub_key, &sig).is_ok(),
|
||||
"sign-then-verify failed for a valid key");
|
||||
}
|
||||
|
||||
/// 不同消息的签名不能交叉验证
|
||||
#[test]
|
||||
fn prop_different_msg_rejected(
|
||||
key_bytes in prop::array::uniform32(1u8..=0xFFu8),
|
||||
msg1 in prop::collection::vec(any::<u8>(), 1..64),
|
||||
msg2 in prop::collection::vec(any::<u8>(), 1..64),
|
||||
) {
|
||||
prop_assume!(msg1 != msg2);
|
||||
|
||||
let pri_key = match PrivateKey::from_bytes(&key_bytes) {
|
||||
Ok(k) => k,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
let pub_key = pri_key.public_key();
|
||||
let z = get_z(DEFAULT_ID, &pub_key);
|
||||
let e1 = get_e(&z, &msg1);
|
||||
let e2 = get_e(&z, &msg2);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let sig1 = sign(&e1, &pri_key, &mut rng);
|
||||
|
||||
// 用 msg1 的签名验证 msg2 应失败
|
||||
prop_assert!(verify(&e2, &pub_key, &sig1).is_err(),
|
||||
"signature for msg1 must not verify msg2");
|
||||
}
|
||||
|
||||
/// 篡改签名任意字节后验签应失败(或恰好产生另一合法签名,极罕见)
|
||||
#[test]
|
||||
fn prop_tampered_sig_no_panic(
|
||||
key_bytes in prop::array::uniform32(1u8..=0xFFu8),
|
||||
msg in prop::collection::vec(any::<u8>(), 1..128),
|
||||
tamper_idx in 0usize..64,
|
||||
tamper_xor in 1u8..=0xFFu8,
|
||||
) {
|
||||
let pri_key = match PrivateKey::from_bytes(&key_bytes) {
|
||||
Ok(k) => k,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
|
||||
let pub_key = pri_key.public_key();
|
||||
let z = get_z(DEFAULT_ID, &pub_key);
|
||||
let e = get_e(&z, &msg);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut sig = sign(&e, &pri_key, &mut rng);
|
||||
|
||||
sig[tamper_idx] ^= tamper_xor;
|
||||
|
||||
// 仅断言不 panic,不断言一定失败(极罕见情况下可能仍然合法)
|
||||
let _ = verify(&e, &pub_key, &sig);
|
||||
}
|
||||
}
|
||||
+34
-18
@@ -1,9 +1,12 @@
|
||||
//! SM4 国标测试向量(GB/T 32907-2016 附录 A)
|
||||
//!
|
||||
//! A.1 示例1:单次 ECB 加密
|
||||
//! A.1 示例1:单次 ECB 加密(单块)
|
||||
//! A.2 示例2:1,000,000 次迭代 ECB 加密(验证算法迭代正确性)
|
||||
//!
|
||||
//! 注:原来使用 `sm4_encrypt_ecb` 的向量测试已迁移为直接使用 `Sm4Key::encrypt_block`,
|
||||
//! 与 RustCrypto 生态的 `sm4` 子 crate 行为一致。
|
||||
|
||||
use libsmx::sm4::{sm4_decrypt_ecb, sm4_encrypt_ecb};
|
||||
use libsmx::sm4::Sm4Key;
|
||||
|
||||
/// GB/T 32907-2016 附录 A.1
|
||||
/// 密钥:0123456789abcdeffedcba9876543210
|
||||
@@ -12,15 +15,20 @@ use libsmx::sm4::{sm4_decrypt_ecb, sm4_encrypt_ecb};
|
||||
#[test]
|
||||
fn test_sm4_ecb_vector_a1_single() {
|
||||
let key = hex::decode("0123456789abcdeffedcba9876543210").unwrap();
|
||||
let plaintext = hex::decode("0123456789abcdeffedcba9876543210").unwrap();
|
||||
let expected_ct = hex::decode("681edf34d206965e86b3e94f536e4246").unwrap();
|
||||
|
||||
let key_arr: [u8; 16] = key.try_into().unwrap();
|
||||
let ct = sm4_encrypt_ecb(&key_arr, &plaintext);
|
||||
assert_eq!(ct, expected_ct, "GB/T 32907 附录 A.1 加密失败");
|
||||
let sm4 = Sm4Key::new(&key_arr);
|
||||
|
||||
let pt = sm4_decrypt_ecb(&key_arr, &ct);
|
||||
assert_eq!(pt, plaintext, "GB/T 32907 附录 A.1 解密失败");
|
||||
let mut block = hex::decode("0123456789abcdeffedcba9876543210").unwrap();
|
||||
let block_arr: &mut [u8; 16] = block.as_mut_slice().try_into().unwrap();
|
||||
|
||||
sm4.encrypt_block(block_arr);
|
||||
|
||||
let expected = hex::decode("681edf34d206965e86b3e94f536e4246").unwrap();
|
||||
assert_eq!(block_arr, expected.as_slice(), "GB/T 32907 附录 A.1 加密失败");
|
||||
|
||||
sm4.decrypt_block(block_arr);
|
||||
let plaintext = hex::decode("0123456789abcdeffedcba9876543210").unwrap();
|
||||
assert_eq!(block_arr, plaintext.as_slice(), "GB/T 32907 附录 A.1 解密失败");
|
||||
}
|
||||
|
||||
/// GB/T 32907-2016 附录 A.2
|
||||
@@ -28,28 +36,36 @@ fn test_sm4_ecb_vector_a1_single() {
|
||||
/// 明文:0123456789abcdeffedcba9876543210(反复迭代 1,000,000 次)
|
||||
/// 密文:595298c7c6fd271f0402f804c33d3f66
|
||||
#[test]
|
||||
#[ignore = "slow (1M iterations)"]
|
||||
fn test_sm4_ecb_vector_a2_million_iterations() {
|
||||
let key: [u8; 16] = hex::decode("0123456789abcdeffedcba9876543210")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
let sm4 = Sm4Key::new(&key);
|
||||
|
||||
let mut data: Vec<u8> = hex::decode("0123456789abcdeffedcba9876543210").unwrap();
|
||||
let mut block: [u8; 16] = hex::decode("0123456789abcdeffedcba9876543210")
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
for _ in 0..1_000_000 {
|
||||
data = sm4_encrypt_ecb(&key, &data);
|
||||
sm4.encrypt_block(&mut block);
|
||||
}
|
||||
|
||||
let expected = hex::decode("595298c7c6fd271f0402f804c33d3f66").unwrap();
|
||||
assert_eq!(data, expected, "GB/T 32907 附录 A.2 百万次迭代失败");
|
||||
assert_eq!(&block, expected.as_slice(), "GB/T 32907 附录 A.2 百万次迭代失败");
|
||||
}
|
||||
|
||||
/// ECB 解密是加密的逆操作(往返测试)
|
||||
/// 加解密往返测试
|
||||
#[test]
|
||||
fn test_sm4_ecb_roundtrip() {
|
||||
fn test_sm4_block_roundtrip() {
|
||||
let key = [0x01u8; 16];
|
||||
let plaintext = b"SM4 ECB test!!!\x00";
|
||||
let ct = sm4_encrypt_ecb(&key, plaintext);
|
||||
let pt = sm4_decrypt_ecb(&key, &ct);
|
||||
assert_eq!(pt.as_slice(), plaintext.as_slice());
|
||||
let sm4 = Sm4Key::new(&key);
|
||||
let plaintext = *b"SM4 ECB test!!!!\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
|
||||
let mut block: [u8; 16] = plaintext[..16].try_into().unwrap();
|
||||
sm4.encrypt_block(&mut block);
|
||||
assert_ne!(block, plaintext[..16], "密文应与明文不同");
|
||||
sm4.decrypt_block(&mut block);
|
||||
assert_eq!(block, plaintext[..16], "解密后应恢复原文");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user