1use 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#[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 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 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
72fn 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 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 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 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 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 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}