Skip to main content

encryption/guomi/
zuc256.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! 国密 ZUC-256 流密码(祖冲之密码算法 ZUC-256 规范 v1.1,2018-01)+
4//! HMAC-SHA256(encrypt-then-MAC);载荷:`v1 | IV(23) | MAC(32) | 密文(与密钥流异或)`。
5//! 密钥 32 字节(256 位),IV 定长 23 字节(184 位)——ZUC-256 规范的取值,
6//! 均与 ZUC-128(16 字节密钥 / 16 字节 IV)不同,两者不可混用密钥材料。
7//!
8//! 注:GB/T 33133.2-2021 是「祖冲之序列密码算法 第2部分:保密性算法」
9//! (128 位密钥版本),ZUC-256 的规范文本为上述 ZUC256-version1.1 文档,
10//! 尚无对应 GB/T 编号;定值向量的出处见测试模块。
11
12use rand_core::{OsRng, RngCore};
13use zuc::zuc256::Zuc256Keystream;
14
15use crate::contract::{Identified, SymmetricCipher};
16use crate::error::Result;
17use crate::internal::etm::{self, EtmKeys};
18
19const LABEL: &str = "ZUC-256";
20const IV_LEN: usize = 23;
21
22/// ZUC-256 加密器。
23#[derive(Debug)]
24pub struct Zuc256Encryptor {
25    keys: EtmKeys<32>,
26}
27
28impl Zuc256Encryptor {
29    pub const IDENTIFIER: &'static str = "zuc-256";
30
31    pub fn new(key: [u8; 32]) -> Self {
32        Self {
33            keys: EtmKeys::new(key),
34        }
35    }
36
37    pub fn from_slice(key: &[u8]) -> Result<Self> {
38        Ok(Self {
39            keys: EtmKeys::from_slice(key, LABEL)?,
40        })
41    }
42}
43
44impl Identified for Zuc256Encryptor {
45    fn identifier(&self) -> &str {
46        Self::IDENTIFIER
47    }
48}
49
50impl SymmetricCipher for Zuc256Encryptor {
51    fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>> {
52        let mut iv = [0u8; IV_LEN];
53        OsRng.fill_bytes(&mut iv);
54        // 注:底层按 ZUC-256 规范取 iv[22] 的低 6 bit,有效 IV 熵为 182 bit。
55
56        let mut buffer = plaintext.to_vec();
57        xor_keystream(self.keys.cipher(), &iv, &mut buffer);
58
59        Ok(etm::pack(&iv, &buffer, self.keys.mac()))
60    }
61
62    fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>> {
63        let (iv, body) = etm::unpack_and_verify(ciphertext, IV_LEN, self.keys.mac(), LABEL)?;
64        // unpack_and_verify 已保证 IV 长度恰为 23。
65        let iv: &[u8; IV_LEN] = iv.try_into().expect("iv length was verified");
66
67        let mut buffer = body.to_vec();
68        xor_keystream(self.keys.cipher(), iv, &mut buffer);
69        Ok(buffer)
70    }
71}
72
73/// 密钥流异或(大端 32 位字;尾部不足一字时取该字前缀字节,
74/// 与 zuc128_xor_inplace 的字节对齐语义一致)。
75///
76/// ponytail: 与 ZUC-128 一致,单帧载荷全量驻留内存、无流式接口——
77/// 能驻留内存的载荷远达不到规范的用量上限;真要分片加密再改增量接口。
78fn xor_keystream(key: &[u8; 32], iv: &[u8; IV_LEN], data: &mut [u8]) {
79    let mut keystream = Zuc256Keystream::new(key, iv);
80    for chunk in data.chunks_mut(4) {
81        let word = keystream.generate().to_be_bytes();
82        for (byte, ks) in chunk.iter_mut().zip(word.iter()) {
83            *byte ^= ks;
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::error::Error;
92
93    /// 定值测试(KAT):ZUC256-version1.1 规范文档附带的测试向量——
94    /// <http://www.is.cas.cn/ztzl2016/zouchongzhi/201801/W020180416526664982687.pdf>
95    /// (zuc 0.4.1 内嵌的 EXAMPLE1/EXAMPLE2 同源;全零向量亦见于《密码学报》
96    /// 2018 年第 5 卷第 2 期 ZUC-256 一文,出处一致,非自造)。
97    /// 往返测试抓不出「密钥流整体错位」——这两个向量是规格符合性的锚点。
98    static EXAMPLES: &[(&[u8; 32], &[u8; IV_LEN], [u32; 20])] = &[
99        (
100            &[0u8; 32],
101            &[0u8; IV_LEN],
102            [
103                0x58d0_3ad6,
104                0x2e03_2ce2,
105                0xdafc_683a,
106                0x39bd_cb03,
107                0x52a2_bc67,
108                0xf1b7_de74,
109                0x163c_e3a1,
110                0x01ef_5558,
111                0x9639_d75b,
112                0x95fa_681b,
113                0x7f09_0df7,
114                0x5639_1ccc,
115                0x903b_7612,
116                0x744d_544c,
117                0x17bc_3fad,
118                0x8b16_3b08,
119                0x2178_7c0b,
120                0x9777_5bb8,
121                0x4943_c6bb,
122                0xe8ad_8afd,
123            ],
124        ),
125        (
126            &[0xffu8; 32],
127            &[0xffu8; IV_LEN],
128            [
129                0x3356_cbae,
130                0xd1a1_c18b,
131                0x6baa_4ffe,
132                0x343f_777c,
133                0x9e15_128f,
134                0x251a_b65b,
135                0x949f_7b26,
136                0xef71_57f2,
137                0x96dd_2fa9,
138                0xdf95_e3ee,
139                0x7a5b_e02e,
140                0xc32b_a585,
141                0x505a_f316,
142                0xc2f9_ded2,
143                0x7cdb_d935,
144                0xe441_ce11,
145                0x15fd_0a80,
146                0xbb7a_ef67,
147                0x6898_9416,
148                0xb8fa_c8c2,
149            ],
150        ),
151    ];
152
153    #[test]
154    fn matches_standard_keystream_vectors() {
155        for (key, iv, expected) in EXAMPLES {
156            // 全零缓冲区异或后即密钥流本身。
157            let mut buffer = [0u8; 80];
158            xor_keystream(key, iv, &mut buffer);
159            for (word, want) in buffer.as_chunks::<4>().0.iter().zip(expected.iter()) {
160                assert_eq!(u32::from_be_bytes(*word), *want);
161            }
162        }
163    }
164
165    #[test]
166    fn roundtrip_including_partial_word_tail() {
167        let encryptor = Zuc256Encryptor::new([3u8; 32]);
168        // 覆盖 4 字节外的尾巴(密钥流按字生成,长度不足时取前缀字节)。
169        for plaintext in [
170            b"".as_slice(),
171            b"a",
172            b"abc",
173            b"abcd",
174            b"abcde",
175            &[0xffu8; 40],
176        ] {
177            let blob = encryptor.encrypt(plaintext).unwrap();
178            assert_eq!(blob.len(), 2 + IV_LEN + etm::MAC_LEN + plaintext.len());
179            assert_eq!(encryptor.decrypt(&blob).unwrap(), plaintext);
180        }
181    }
182
183    #[test]
184    fn ciphertext_differs_from_plaintext_and_tamper_fails() {
185        let encryptor = Zuc256Encryptor::new([3u8; 32]);
186        let blob = encryptor.encrypt(b"0123456789").unwrap();
187        assert_ne!(&blob[57..], b"0123456789");
188
189        // 改密文末字节。
190        let mut tampered = blob.clone();
191        let last = tampered.len() - 1;
192        tampered[last] ^= 0x01;
193        assert_eq!(
194            encryptor.decrypt(&tampered).unwrap_err(),
195            Error::MacVerificationFailed { label: LABEL }
196        );
197
198        // 改 MAC 字节。
199        let mut tampered = blob.clone();
200        tampered[2 + IV_LEN] ^= 0x01;
201        assert_eq!(
202            encryptor.decrypt(&tampered).unwrap_err(),
203            Error::MacVerificationFailed { label: LABEL }
204        );
205    }
206
207    #[test]
208    fn every_encrypt_uses_a_fresh_iv() {
209        let encryptor = Zuc256Encryptor::new([3u8; 32]);
210        let first = encryptor.encrypt(b"same").unwrap();
211        let second = encryptor.encrypt(b"same").unwrap();
212        assert_ne!(first, second);
213        assert_ne!(first[2..2 + IV_LEN], second[2..2 + IV_LEN]);
214    }
215
216    #[test]
217    fn wrong_key_and_bad_frame_fail() {
218        let blob = Zuc256Encryptor::new([3u8; 32]).encrypt(b"x").unwrap();
219        let other = Zuc256Encryptor::new([4u8; 32]);
220        assert_eq!(
221            other.decrypt(&blob).unwrap_err(),
222            Error::MacVerificationFailed { label: LABEL }
223        );
224        assert_eq!(
225            other.decrypt(b"v9....").unwrap_err(),
226            Error::InvalidPrefix { label: LABEL }
227        );
228        assert_eq!(
229            other.decrypt(b"v1short").unwrap_err(),
230            Error::TooShort { label: LABEL }
231        );
232    }
233
234    #[test]
235    fn key_length_is_checked() {
236        assert_eq!(
237            Zuc256Encryptor::from_slice(&[0u8; 16]).unwrap_err(),
238            Error::InvalidKeyLength {
239                label: LABEL,
240                expected: 32,
241                got: 16
242            }
243        );
244    }
245
246    #[test]
247    fn identifier_matches_registered_name() {
248        assert_eq!(Zuc256Encryptor::new([0u8; 32]).identifier(), "zuc-256");
249    }
250}