重构: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
+22
View File
@@ -0,0 +1,22 @@
[package]
name = "sm4"
version = "0.1.0"
edition = "2021"
rust-version = "1.83.0"
license = "Apache-2.0"
description = "SM4 (ShangMi 4) block cipher with constant-time bitslice S-box — GB/T 32907-2016"
repository = "https://github.com/kintaiW/libsmx"
categories = ["cryptography", "no-std::no-alloc"]
keywords = ["crypto", "cipher", "sm4", "shangmi", "block-cipher"]
[dependencies]
cipher = { workspace = true }
zeroize = { workspace = true }
[dev-dependencies]
hex-literal = { workspace = true }
cipher = { workspace = true, features = ["dev"] }
[features]
default = []
zeroize = []
+286
View File
@@ -0,0 +1,286 @@
//! SM4 S-box constants and internal cipher functions (GB/T 32907-2016).
//!
//! All operations are constant-time boolean-circuit implementations.
// ── System constants ──────────────────────────────────────────────────────────
/// System parameter FK (GB/T 32907 §A.1)
pub(super) const FK: [u32; 4] = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
/// Constant key CK (GB/T 32907 §A.1)
#[rustfmt::skip]
pub(super) const CK: [u32; 32] = [
0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279,
];
// ── Boolean-circuit S-box ─────────────────────────────────────────────────────
/// SM4 S-box — pure boolean circuit (zero memory access, cache-timing immune).
///
/// Input/output linear layers + GF(2^4) inversion in boolean circuit form.
/// Source: emmansun/sm4bs (sbox64), extracted and validated against all 256 values.
///
/// ⚠️ Security: Uses only AND/XOR/OR/NOT. No table lookups, no memory reads.
#[allow(dead_code)]
#[inline]
pub(super) fn sbox_ct(x: u8) -> u8 {
let b0 = x & 1;
let b1 = (x >> 1) & 1;
let b2 = (x >> 2) & 1;
let b3 = (x >> 3) & 1;
let b4 = (x >> 4) & 1;
let b5 = (x >> 5) & 1;
let b6 = (x >> 6) & 1;
let b7 = (x >> 7) & 1;
let t1 = b7 ^ b5;
let t2 = 1 ^ (b5 ^ b1);
let g5 = 1 ^ b0;
let t3 = 1 ^ (b0 ^ t2);
let t4 = b6 ^ b2;
let t5 = b3 ^ t3;
let t6 = b4 ^ t1;
let t7 = b1 ^ t5;
let t8 = b1 ^ t4;
let t9 = t6 ^ t8;
let t10 = t6 ^ t7;
let t11 = 1 ^ (b3 ^ t1);
let t12 = 1 ^ (b6 ^ t9);
let g0 = t10;
let g1 = t7;
let g2 = t4 ^ t10;
let g3 = t5;
let g4 = t2;
let g6 = t11 ^ t2;
let g7 = t12 ^ (t11 ^ t2);
let m0 = t6; let m1 = t3; let m2 = t8;
let m3 = t3 ^ t12; let m4 = t4; let m5 = t11;
let m6 = b1; let m7 = t11 ^ m3; let m8 = t9; let m9 = t12;
let t2t = m0 & m1; let t3t = g0 & g4; let t4t = g3 & g7;
let t7t = g3 | g7; let t11t = m4 & m5; let t10t = m3 & m2;
let t12t = m3 | m2; let t6t = g6 | g2; let t9t = m6 | m7;
let t5t = m8 & m9; let t8t = m8 | m9;
let t14t = t3t ^ t2t; let t16t = t5t ^ t14t;
let t20t = t16t ^ t7t; let t17t = t9t ^ t10t;
let t18t = t11t ^ t12t;
let p2 = t20t ^ t18t; let p0 = t6t ^ t16t;
let t1t = g5 & g1; let t13t = t1t ^ t2t;
let t15t = t13t ^ t4t;
let p3 = (t6t ^ t15t) ^ t17t; let p1 = t8t ^ t15t;
let t0m = p1 & p2; let t1m = p3 & p0; let t2m = p0 & p2;
let t3m = p1 & p3; let t4m = t0m & t2m;
let t5m = t1m ^ t3m; let t6m = t5m | p0; let t7m = t2m | p3;
let l3 = t4m ^ t6m; let t9m = t7m ^ t3m;
let l0 = t0m ^ t9m; let t11m = p2 | t5m;
let l1 = t11m ^ t1m; let t12m = p1 | t2m; let l2 = t12m ^ t5m;
let k4 = l2 ^ l3; let k3 = l1 ^ l3; let k2 = l0 ^ l2;
let k0 = l0 ^ l1; let k1 = k2 ^ k3;
let e0 = m1 & k0; let e1 = g5 & l1; let r0 = e0 ^ e1;
let e2 = g4 & l0; let r1 = e2 ^ e1;
let e3 = m7 & k3; let e4 = m5 & k2; let r2 = e3 ^ e4;
let e5 = m3 & k1; let r3 = e5 ^ e4;
let e6 = m9 & k4; let e7 = g7 & l3; let r4 = e6 ^ e7;
let e8 = g6 & l2; let r5 = e8 ^ e7;
let e9 = m0 & k0; let e10 = g1 & l1; let r6 = e9 ^ e10;
let e11 = g0 & l0; let r7 = e11 ^ e10;
let e12 = m6 & k3; let e13 = m4 & k2; let r8 = e12 ^ e13;
let e14 = m2 & k1; let r9 = e14 ^ e13;
let e15 = m8 & k4; let e16 = g3 & l3; let r10 = e15 ^ e16;
let e17 = g2 & l2; let r11 = e17 ^ e16;
let t1o = r7 ^ r9; let t2o = r1 ^ t1o; let t3o = r3 ^ t2o;
let t4o = r5 ^ r3; let t5o = r4 ^ t4o; let t6o = r0 ^ r4;
let t7o = r11 ^ r7;
let b5o = t1o ^ t4o; let b2o = t1o ^ t6o;
let t10o = r2 ^ t5o; let b3o = r10 ^ r8;
let b1o = 1 ^ (t3o ^ b3o); let b6o = t10o ^ b1o;
let b4o = 1 ^ (t3o ^ t7o); let b0o = t6o ^ b4o;
let b7o = 1 ^ (r10 ^ r6);
b0o | (b1o << 1) | (b2o << 2) | (b3o << 3) | (b4o << 4) | (b5o << 5) | (b6o << 6) | (b7o << 7)
}
/// SM4 τ transform: 4-byte u32 bitslice S-box (constant-time, 4-way parallel).
///
/// Packs 4 bytes' bit-planes into 4 u32 lanes, runs the boolean circuit once,
/// then unpacks — equivalent to ~3-4x speedup over 4 independent `sbox_ct` calls.
///
/// ⚠️ Security: Inherits full constant-time properties of `sbox_ct`.
#[inline]
pub(super) fn tau(a: u32) -> u32 {
let bytes = a.to_be_bytes();
// Pack: bits[i] low 4 = bit-i of [byte0, byte1, byte2, byte3]
let mut bits = [0u32; 8];
for (i, bit) in bits.iter_mut().enumerate() {
*bit = ((bytes[0] >> i) & 1) as u32
| (((bytes[1] >> i) & 1) as u32) << 1
| (((bytes[2] >> i) & 1) as u32) << 2
| (((bytes[3] >> i) & 1) as u32) << 3;
}
let [b0, b1, b2, b3, b4, b5, b6, b7] = bits;
// Boolean circuit (identical to sbox_ct, but NOT uses 0xF instead of 1)
let t1 = b7 ^ b5;
let t2 = 0xF ^ (b5 ^ b1);
let g5 = 0xF ^ b0;
let t3 = 0xF ^ (b0 ^ t2);
let t4 = b6 ^ b2;
let t5 = b3 ^ t3;
let t6 = b4 ^ t1;
let t7 = b1 ^ t5;
let t8 = b1 ^ t4;
let t9 = t6 ^ t8;
let t10 = t6 ^ t7;
let t11 = 0xF ^ (b3 ^ t1);
let t12 = 0xF ^ (b6 ^ t9);
let g0 = t10; let g1 = t7; let g2 = t4 ^ t10; let g3 = t5;
let g4 = t2; let g6 = t11 ^ t2; let g7 = t12 ^ (t11 ^ t2);
let m0 = t6; let m1 = t3; let m2 = t8; let m3 = t3 ^ t12;
let m4 = t4; let m5 = t11; let m6 = b1; let m7 = t11 ^ m3;
let m8 = t9; let m9 = t12;
let t2t = m0 & m1; let t3t = g0 & g4; let t4t = g3 & g7;
let t7t = g3 | g7; let t11t = m4 & m5; let t10t = m3 & m2;
let t12t = m3 | m2; let t6t = g6 | g2; let t9t = m6 | m7;
let t5t = m8 & m9; let t8t = m8 | m9;
let t14t = t3t ^ t2t; let t16t = t5t ^ t14t;
let t20t = t16t ^ t7t; let t17t = t9t ^ t10t;
let t18t = t11t ^ t12t;
let p2 = t20t ^ t18t; let p0 = t6t ^ t16t;
let t1t = g5 & g1; let t13t = t1t ^ t2t;
let t15t = t13t ^ t4t;
let p3 = (t6t ^ t15t) ^ t17t; let p1 = t8t ^ t15t;
let t0m = p1 & p2; let t1m = p3 & p0; let t2m = p0 & p2;
let t3m = p1 & p3; let t4m = t0m & t2m;
let t5m = t1m ^ t3m; let t6m = t5m | p0; let t7m = t2m | p3;
let l3 = t4m ^ t6m; let t9m = t7m ^ t3m;
let l0 = t0m ^ t9m; let t11m = p2 | t5m;
let l1 = t11m ^ t1m; let t12m = p1 | t2m; let l2 = t12m ^ t5m;
let k4 = l2 ^ l3; let k3 = l1 ^ l3; let k2 = l0 ^ l2;
let k0 = l0 ^ l1; let k1 = k2 ^ k3;
let e0 = m1 & k0; let e1 = g5 & l1; let r0 = e0 ^ e1;
let e2 = g4 & l0; let r1 = e2 ^ e1;
let e3 = m7 & k3; let e4 = m5 & k2; let r2 = e3 ^ e4;
let e5 = m3 & k1; let r3 = e5 ^ e4;
let e6 = m9 & k4; let e7 = g7 & l3; let r4 = e6 ^ e7;
let e8 = g6 & l2; let r5 = e8 ^ e7;
let e9 = m0 & k0; let e10 = g1 & l1; let r6 = e9 ^ e10;
let e11 = g0 & l0; let r7 = e11 ^ e10;
let e12 = m6 & k3; let e13 = m4 & k2; let r8 = e12 ^ e13;
let e14 = m2 & k1; let r9 = e14 ^ e13;
let e15 = m8 & k4; let e16 = g3 & l3; let r10 = e15 ^ e16;
let e17 = g2 & l2; let r11 = e17 ^ e16;
let t1o = r7 ^ r9; let t2o = r1 ^ t1o; let t3o = r3 ^ t2o;
let t4o = r5 ^ r3; let t5o = r4 ^ t4o; let t6o = r0 ^ r4;
let t7o = r11 ^ r7;
let b5o = t1o ^ t4o; let b2o = t1o ^ t6o;
let t10o = r2 ^ t5o; let b3o = r10 ^ r8;
let b1o = 0xF ^ (t3o ^ b3o); let b6o = t10o ^ b1o;
let b4o = 0xF ^ (t3o ^ t7o); let b0o = t6o ^ b4o;
let b7o = 0xF ^ (r10 ^ r6);
// Unpack: 8 u32 low-4 bits -> 4 output bytes
let ob = [b0o, b1o, b2o, b3o, b4o, b5o, b6o, b7o];
let mut out = [0u8; 4];
for (i, &v) in ob.iter().enumerate() {
out[0] |= ((v & 1) as u8) << i;
out[1] |= (((v >> 1) & 1) as u8) << i;
out[2] |= (((v >> 2) & 1) as u8) << i;
out[3] |= (((v >> 3) & 1) as u8) << i;
}
u32::from_be_bytes(out)
}
// ── Round functions ───────────────────────────────────────────────────────────
/// Encryption round function T (GB/T 32907 §6.2.1)
#[inline]
pub(super) fn t_enc(a: u32) -> u32 {
let b = tau(a);
b ^ b.rotate_left(2) ^ b.rotate_left(10) ^ b.rotate_left(18) ^ b.rotate_left(24)
}
/// Key expansion round function T' (GB/T 32907 §6.2.2)
#[inline]
fn t_key(a: u32) -> u32 {
let b = tau(a);
b ^ b.rotate_left(13) ^ b.rotate_left(23)
}
// ── Key expansion ─────────────────────────────────────────────────────────────
/// SM4 key expansion (GB/T 32907 §6.2.2)
pub(super) fn expand_key(key: &[u8; 16], rk: &mut [u32; 32]) {
let mk = [
u32::from_be_bytes(key[0..4].try_into().unwrap()),
u32::from_be_bytes(key[4..8].try_into().unwrap()),
u32::from_be_bytes(key[8..12].try_into().unwrap()),
u32::from_be_bytes(key[12..16].try_into().unwrap()),
];
let mut k = [mk[0] ^ FK[0], mk[1] ^ FK[1], mk[2] ^ FK[2], mk[3] ^ FK[3]];
for i in 0..32 {
let tmp = k[(i + 1) % 4] ^ k[(i + 2) % 4] ^ k[(i + 3) % 4] ^ CK[i];
rk[i] = k[i % 4] ^ t_key(tmp);
k[i % 4] = rk[i];
}
}
// ── Block load/store ──────────────────────────────────────────────────────────
#[inline]
pub(super) fn load_block(b: &[u8; 16]) -> [u32; 4] {
[
u32::from_be_bytes(b[0..4].try_into().unwrap()),
u32::from_be_bytes(b[4..8].try_into().unwrap()),
u32::from_be_bytes(b[8..12].try_into().unwrap()),
u32::from_be_bytes(b[12..16].try_into().unwrap()),
]
}
#[inline]
pub(super) fn store_block(b: &mut [u8; 16], x: &[u32; 4]) {
b[0..4].copy_from_slice(&x[0].to_be_bytes());
b[4..8].copy_from_slice(&x[1].to_be_bytes());
b[8..12].copy_from_slice(&x[2].to_be_bytes());
b[12..16].copy_from_slice(&x[3].to_be_bytes());
}
// ── Encryption / Decryption rounds ────────────────────────────────────────────
/// 32-round SM4 encryption (round keys in forward order)
pub(super) fn encrypt_rounds(x: &mut [u32; 4], rk: &[u32; 32]) {
for &rk_i in rk.iter() {
let tmp = x[1] ^ x[2] ^ x[3] ^ rk_i;
let next = x[0] ^ t_enc(tmp);
x[0] = x[1]; x[1] = x[2]; x[2] = x[3]; x[3] = next;
}
x.reverse(); // GB/T 32907 §6.2.1: output = (X35, X34, X33, X32)
}
/// 32-round SM4 decryption (round keys in reverse order)
pub(super) fn decrypt_rounds(x: &mut [u32; 4], rk: &[u32; 32]) {
for i in (0..32).rev() {
let tmp = x[1] ^ x[2] ^ x[3] ^ rk[i];
let next = x[0] ^ t_enc(tmp);
x[0] = x[1]; x[1] = x[2]; x[2] = x[3]; x[3] = next;
}
x.reverse();
}
+254
View File
@@ -0,0 +1,254 @@
//! SM4 block cipher (GB/T 32907-2016).
//!
//! This crate provides a [`cipher::BlockCipherEncrypt`] / [`cipher::BlockCipherDecrypt`]
//! compatible SM4 implementation suitable for use in the RustCrypto ecosystem.
//!
//! ## Security
//!
//! SM4 is standardised by the Chinese National Standard (GB/T 32907-2016).
//! This implementation uses a **boolean-circuit bitslice S-box** (zero memory
//! accesses) rather than a lookup table, making it immune to cache-timing
//! side-channel attacks.
//!
//! ## Usage
//!
//! ```rust
//! use sm4::Sm4;
//! use sm4::cipher::{BlockCipherEncrypt, BlockCipherDecrypt, KeyInit};
//!
//! let key = [0u8; 16];
//! let plaintext = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
//! 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10];
//! let expected = [0x68, 0x1e, 0xdf, 0x34, 0xd2, 0x06, 0x96, 0x5e,
//! 0x86, 0xb3, 0xe9, 0x4f, 0x53, 0x6e, 0x42, 0x46];
//!
//! let cipher = Sm4::new(&plaintext.into());
//! let mut block = plaintext.into();
//! cipher.encrypt_block(&mut block);
//! assert_eq!(block[..], expected[..]);
//! ```
#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
mod consts;
pub use cipher::{self, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
use cipher::{
AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherEncBackend,
BlockCipherEncClosure, BlockSizeUser, InOut, KeySizeUser, ParBlocksSizeUser,
consts::{U1, U16},
};
use core::fmt;
use zeroize::{Zeroize, ZeroizeOnDrop};
// ── Key type alias ────────────────────────────────────────────────────────────
/// SM4 key type: 128 bits (16 bytes).
pub type Sm4Key = cipher::Key<Sm4>;
// ── Sm4 struct ────────────────────────────────────────────────────────────────
/// SM4 block cipher (GB/T 32907-2016).
///
/// Implements [`BlockCipherEncrypt`] and [`BlockCipherDecrypt`] from the
/// `cipher` crate. Construct with [`KeyInit::new`].
#[derive(Clone)]
pub struct Sm4 {
/// 32 round keys derived from the 128-bit master key.
rk: [u32; 32],
}
impl Drop for Sm4 {
fn drop(&mut self) {
self.rk.zeroize();
}
}
impl ZeroizeOnDrop for Sm4 {}
// ── KeySizeUser / BlockSizeUser / ParBlocksSizeUser ───────────────────────────
impl KeySizeUser for Sm4 {
/// SM4 requires a 128-bit (16-byte) key.
type KeySize = U16;
}
impl BlockSizeUser for Sm4 {
/// SM4 operates on 128-bit (16-byte) blocks.
type BlockSize = U16;
}
impl ParBlocksSizeUser for Sm4 {
/// No parallel blocks: each block is processed independently.
type ParBlocksSize = U1;
}
// ── KeyInit ───────────────────────────────────────────────────────────────────
impl KeyInit for Sm4 {
fn new(key: &Sm4Key) -> Self {
let mut rk = [0u32; 32];
// Reason: Sm4Key = Array<u8, U16> which Derefs to [u8; 16] via as_slice().
consts::expand_key(<&[u8; 16]>::try_from(key.as_slice()).unwrap(), &mut rk);
Self { rk }
}
}
// ── AlgorithmName / Debug ─────────────────────────────────────────────────────
impl AlgorithmName for Sm4 {
fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Sm4")
}
}
impl fmt::Debug for Sm4 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Sm4 { ... }")
}
}
// ── BlockCipherEncrypt ────────────────────────────────────────────────────────
impl BlockCipherEncrypt for Sm4 {
fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
f.call(&Sm4EncBackend(self));
}
}
// ── BlockCipherDecrypt ────────────────────────────────────────────────────────
impl BlockCipherDecrypt for Sm4 {
fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
f.call(&Sm4DecBackend(self));
}
}
// ── Encryption backend ────────────────────────────────────────────────────────
struct Sm4EncBackend<'a>(&'a Sm4);
impl BlockSizeUser for Sm4EncBackend<'_> {
type BlockSize = U16;
}
impl ParBlocksSizeUser for Sm4EncBackend<'_> {
type ParBlocksSize = U1;
}
impl BlockCipherEncBackend for Sm4EncBackend<'_> {
#[inline]
fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let mut x =
consts::load_block(<&[u8; 16]>::try_from(block.get_in().as_slice()).unwrap());
consts::encrypt_rounds(&mut x, &self.0.rk);
consts::store_block(
<&mut [u8; 16]>::try_from(block.get_out().as_mut_slice()).unwrap(),
&x,
);
}
}
// ── Decryption backend ────────────────────────────────────────────────────────
struct Sm4DecBackend<'a>(&'a Sm4);
impl BlockSizeUser for Sm4DecBackend<'_> {
type BlockSize = U16;
}
impl ParBlocksSizeUser for Sm4DecBackend<'_> {
type ParBlocksSize = U1;
}
impl BlockCipherDecBackend for Sm4DecBackend<'_> {
#[inline]
fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
let mut x =
consts::load_block(<&[u8; 16]>::try_from(block.get_in().as_slice()).unwrap());
consts::decrypt_rounds(&mut x, &self.0.rk);
consts::store_block(
<&mut [u8; 16]>::try_from(block.get_out().as_mut_slice()).unwrap(),
&x,
);
}
}
// ── Tests ─────────────────────────────────────────────────────────────────────
#[cfg(test)]
mod tests {
use super::*;
use cipher::{BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
use hex_literal::hex;
/// GB/T 32907-2016 Appendix A, Example 1
/// Key = 0123456789ABCDEFFEDCBA9876543210
/// Plaintext = 0123456789ABCDEFFEDCBA9876543210
/// Ciphertext = 681EDF34D206965E86B3E94F536E4246
#[test]
fn test_vector_appendix_a() {
let key = hex!("0123456789ABCDEFFEDCBA9876543210");
let plaintext = hex!("0123456789ABCDEFFEDCBA9876543210");
let ciphertext = hex!("681EDF34D206965E86B3E94F536E4246");
let cipher = Sm4::new(&key.into());
let mut block: Block<Sm4> = plaintext.into();
cipher.encrypt_block(&mut block);
assert_eq!(block[..], ciphertext[..], "encryption mismatch");
cipher.decrypt_block(&mut block);
assert_eq!(block[..], plaintext[..], "decryption mismatch");
}
/// GB/T 32907-2016 Appendix A, Example 2: 1 000 000 iterations
/// Repeatedly encrypt the same plaintext 10^6 times.
/// Result must be 595298C7C6FD271F0402F804C33D3F66.
#[test]
#[ignore = "slow (1M iterations)"]
fn test_vector_1m_iterations() {
let key = hex!("0123456789ABCDEFFEDCBA9876543210");
let expected = hex!("595298C7C6FD271F0402F804C33D3F66");
let cipher = Sm4::new(&key.into());
let mut block: Block<Sm4> = hex!("0123456789ABCDEFFEDCBA9876543210").into();
for _ in 0..1_000_000 {
cipher.encrypt_block(&mut block);
}
assert_eq!(block[..], expected[..]);
}
/// All-zero key, all-zero block: encrypt then decrypt must restore plaintext.
#[test]
fn test_all_zeros_roundtrip() {
let key = [0u8; 16];
let plaintext = [0u8; 16];
let cipher = Sm4::new(&key.into());
let mut block: Block<Sm4> = plaintext.into();
cipher.encrypt_block(&mut block);
cipher.decrypt_block(&mut block);
assert_eq!(block[..], plaintext[..]);
}
/// Arbitrary key/plaintext: roundtrip must restore the original.
#[test]
fn test_roundtrip() {
let key = hex!("FEDCBA98765432100123456789ABCDEF");
let plaintext = hex!("AABBCCDDEEFF00112233445566778899");
let cipher = Sm4::new(&key.into());
let mut block: Block<Sm4> = plaintext.into();
cipher.encrypt_block(&mut block);
assert_ne!(block[..], plaintext[..], "ciphertext must differ from plaintext");
cipher.decrypt_block(&mut block);
assert_eq!(block[..], plaintext[..], "roundtrip must restore plaintext");
}
}