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