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