[go: nahoru, domu]

blob: 0f34335cb4a47f2946feea064764552c2993381a [file] [log] [blame]
David LeGare132eccb2022-04-11 16:44:12 +00001//! A counter mode (CTR) for AES to work with the encryption used in zip files.
2//!
3//! This was implemented since the zip specification requires the mode to not use a nonce and uses a
4//! different byte order (little endian) than NIST (big endian).
5//! See [AesCtrZipKeyStream](./struct.AesCtrZipKeyStream.html) for more information.
6
7use aes::cipher::generic_array::GenericArray;
8use aes::{BlockEncrypt, NewBlockCipher};
9use byteorder::WriteBytesExt;
10use std::{any, fmt};
11
12/// Internal block size of an AES cipher.
13const AES_BLOCK_SIZE: usize = 16;
14
15/// AES-128.
16#[derive(Debug)]
17pub struct Aes128;
18/// AES-192
19#[derive(Debug)]
20pub struct Aes192;
21/// AES-256.
22#[derive(Debug)]
23pub struct Aes256;
24
25/// An AES cipher kind.
26pub trait AesKind {
27 /// Key type.
28 type Key: AsRef<[u8]>;
29 /// Cipher used to decrypt.
30 type Cipher;
31}
32
33impl AesKind for Aes128 {
34 type Key = [u8; 16];
35 type Cipher = aes::Aes128;
36}
37
38impl AesKind for Aes192 {
39 type Key = [u8; 24];
40 type Cipher = aes::Aes192;
41}
42
43impl AesKind for Aes256 {
44 type Key = [u8; 32];
45 type Cipher = aes::Aes256;
46}
47
48/// An AES-CTR key stream generator.
49///
50/// Implements the slightly non-standard AES-CTR variant used by WinZip AES encryption.
51///
52/// Typical AES-CTR implementations combine a nonce with a 64 bit counter. WinZIP AES instead uses
53/// no nonce and also uses a different byte order (little endian) than NIST (big endian).
54///
55/// The stream implements the `Read` trait; encryption or decryption is performed by XOR-ing the
56/// bytes from the key stream with the ciphertext/plaintext.
57pub struct AesCtrZipKeyStream<C: AesKind> {
58 /// Current AES counter.
59 counter: u128,
60 /// AES cipher instance.
61 cipher: C::Cipher,
62 /// Stores the currently available keystream bytes.
63 buffer: [u8; AES_BLOCK_SIZE],
64 /// Number of bytes already used up from `buffer`.
65 pos: usize,
66}
67
68impl<C> fmt::Debug for AesCtrZipKeyStream<C>
69where
70 C: AesKind,
71{
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(
74 f,
75 "AesCtrZipKeyStream<{}>(counter: {})",
76 any::type_name::<C>(),
77 self.counter
78 )
79 }
80}
81
82impl<C> AesCtrZipKeyStream<C>
83where
84 C: AesKind,
85 C::Cipher: NewBlockCipher,
86{
87 /// Creates a new zip variant AES-CTR key stream.
88 ///
89 /// # Panics
90 ///
91 /// This panics if `key` doesn't have the correct size for cipher `C`.
92 pub fn new(key: &[u8]) -> AesCtrZipKeyStream<C> {
93 AesCtrZipKeyStream {
94 counter: 1,
95 cipher: C::Cipher::new(GenericArray::from_slice(key)),
96 buffer: [0u8; AES_BLOCK_SIZE],
97 pos: AES_BLOCK_SIZE,
98 }
99 }
100}
101
102impl<C> AesCipher for AesCtrZipKeyStream<C>
103where
104 C: AesKind,
105 C::Cipher: BlockEncrypt,
106{
107 /// Decrypt or encrypt `target`.
108 #[inline]
109 fn crypt_in_place(&mut self, mut target: &mut [u8]) {
110 while !target.is_empty() {
111 if self.pos == AES_BLOCK_SIZE {
112 // Note: AES block size is always 16 bytes, same as u128.
113 self.buffer
114 .as_mut()
115 .write_u128::<byteorder::LittleEndian>(self.counter)
116 .expect("did not expect u128 le conversion to fail");
117 self.cipher
118 .encrypt_block(GenericArray::from_mut_slice(&mut self.buffer));
119 self.counter += 1;
120 self.pos = 0;
121 }
122
123 let target_len = target.len().min(AES_BLOCK_SIZE - self.pos);
124
125 xor(
126 &mut target[0..target_len],
127 &self.buffer[self.pos..(self.pos + target_len)],
128 );
129 target = &mut target[target_len..];
130 self.pos += target_len;
131 }
132 }
133}
134
135/// This trait allows using generic AES ciphers with different key sizes.
136pub trait AesCipher {
137 fn crypt_in_place(&mut self, target: &mut [u8]);
138}
139
140/// XORs a slice in place with another slice.
141#[inline]
142fn xor(dest: &mut [u8], src: &[u8]) {
143 assert_eq!(dest.len(), src.len());
144
145 for (lhs, rhs) in dest.iter_mut().zip(src.iter()) {
146 *lhs ^= *rhs;
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::{Aes128, Aes192, Aes256, AesCipher, AesCtrZipKeyStream, AesKind};
153 use aes::{BlockEncrypt, NewBlockCipher};
154
155 /// Checks whether `crypt_in_place` produces the correct plaintext after one use and yields the
156 /// cipertext again after applying it again.
157 fn roundtrip<Aes>(key: &[u8], ciphertext: &mut [u8], expected_plaintext: &[u8])
158 where
159 Aes: AesKind,
160 Aes::Cipher: NewBlockCipher + BlockEncrypt,
161 {
162 let mut key_stream = AesCtrZipKeyStream::<Aes>::new(key);
163
164 let mut plaintext: Vec<u8> = ciphertext.to_vec();
165 key_stream.crypt_in_place(plaintext.as_mut_slice());
166 assert_eq!(plaintext, expected_plaintext.to_vec());
167
168 // Round-tripping should yield the ciphertext again.
169 let mut key_stream = AesCtrZipKeyStream::<Aes>::new(key);
170 key_stream.crypt_in_place(&mut plaintext);
171 assert_eq!(plaintext, ciphertext.to_vec());
172 }
173
174 #[test]
175 #[should_panic]
176 fn new_with_wrong_key_size() {
177 AesCtrZipKeyStream::<Aes128>::new(&[1, 2, 3, 4, 5]);
178 }
179
180 // The data used in these tests was generated with p7zip without any compression.
181 // It's not possible to recreate the exact same data, since a random salt is used for encryption.
182 // `7z a -phelloworld -mem=AES256 -mx=0 aes256_40byte.zip 40byte_data.txt`
183 #[test]
184 fn crypt_aes_256_0_byte() {
185 let mut ciphertext = [];
186 let expected_plaintext = &[];
187 let key = [
188 0x0b, 0xec, 0x2e, 0xf2, 0x46, 0xf0, 0x7e, 0x35, 0x16, 0x54, 0xe0, 0x98, 0x10, 0xb3,
189 0x18, 0x55, 0x24, 0xa3, 0x9e, 0x0e, 0x40, 0xe7, 0x92, 0xad, 0xb2, 0x8a, 0x48, 0xf4,
190 0x5c, 0xd0, 0xc0, 0x54,
191 ];
192
193 roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext);
194 }
195
196 #[test]
197 fn crypt_aes_128_5_byte() {
198 let mut ciphertext = [0x98, 0xa9, 0x8c, 0x26, 0x0e];
199 let expected_plaintext = b"asdf\n";
200 let key = [
201 0xe0, 0x25, 0x7b, 0x57, 0x97, 0x6a, 0xa4, 0x23, 0xab, 0x94, 0xaa, 0x44, 0xfd, 0x47,
202 0x4f, 0xa5,
203 ];
204
205 roundtrip::<Aes128>(&key, &mut ciphertext, expected_plaintext);
206 }
207
208 #[test]
209 fn crypt_aes_192_5_byte() {
210 let mut ciphertext = [0x36, 0x55, 0x5c, 0x61, 0x3c];
211 let expected_plaintext = b"asdf\n";
212 let key = [
213 0xe4, 0x4a, 0x88, 0x52, 0x8f, 0xf7, 0x0b, 0x81, 0x7b, 0x75, 0xf1, 0x74, 0x21, 0x37,
214 0x8c, 0x90, 0xad, 0xbe, 0x4a, 0x65, 0xa8, 0x96, 0x0e, 0xcc,
215 ];
216
217 roundtrip::<Aes192>(&key, &mut ciphertext, expected_plaintext);
218 }
219
220 #[test]
221 fn crypt_aes_256_5_byte() {
222 let mut ciphertext = [0xc2, 0x47, 0xc0, 0xdc, 0x56];
223 let expected_plaintext = b"asdf\n";
224 let key = [
225 0x79, 0x5e, 0x17, 0xf2, 0xc6, 0x3d, 0x28, 0x9b, 0x4b, 0x4b, 0xbb, 0xa9, 0xba, 0xc9,
226 0xa5, 0xee, 0x3a, 0x4f, 0x0f, 0x4b, 0x29, 0xbd, 0xe9, 0xb8, 0x41, 0x9c, 0x41, 0xa5,
227 0x15, 0xb2, 0x86, 0xab,
228 ];
229
230 roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext);
231 }
232
233 #[test]
234 fn crypt_aes_128_40_byte() {
235 let mut ciphertext = [
236 0xcf, 0x72, 0x6b, 0xa1, 0xb2, 0x0f, 0xdf, 0xaa, 0x10, 0xad, 0x9c, 0x7f, 0x6d, 0x1c,
237 0x8d, 0xb5, 0x16, 0x7e, 0xbb, 0x11, 0x69, 0x52, 0x8c, 0x89, 0x80, 0x32, 0xaa, 0x76,
238 0xa6, 0x18, 0x31, 0x98, 0xee, 0xdd, 0x22, 0x68, 0xb7, 0xe6, 0x77, 0xd2,
239 ];
240 let expected_plaintext = b"Lorem ipsum dolor sit amet, consectetur\n";
241 let key = [
242 0x43, 0x2b, 0x6d, 0xbe, 0x05, 0x76, 0x6c, 0x9e, 0xde, 0xca, 0x3b, 0xf8, 0xaf, 0x5d,
243 0x81, 0xb6,
244 ];
245
246 roundtrip::<Aes128>(&key, &mut ciphertext, expected_plaintext);
247 }
248
249 #[test]
250 fn crypt_aes_192_40_byte() {
251 let mut ciphertext = [
252 0xa6, 0xfc, 0x52, 0x79, 0x2c, 0x6c, 0xfe, 0x68, 0xb1, 0xa8, 0xb3, 0x07, 0x52, 0x8b,
253 0x82, 0xa6, 0x87, 0x9c, 0x72, 0x42, 0x3a, 0xf8, 0xc6, 0xa9, 0xc9, 0xfb, 0x61, 0x19,
254 0x37, 0xb9, 0x56, 0x62, 0xf4, 0xfc, 0x5e, 0x7a, 0xdd, 0x55, 0x0a, 0x48,
255 ];
256 let expected_plaintext = b"Lorem ipsum dolor sit amet, consectetur\n";
257 let key = [
258 0xac, 0x92, 0x41, 0xba, 0xde, 0xd9, 0x02, 0xfe, 0x40, 0x92, 0x20, 0xf6, 0x56, 0x03,
259 0xfe, 0xae, 0x1b, 0xba, 0x01, 0x97, 0x97, 0x79, 0xbb, 0xa6,
260 ];
261
262 roundtrip::<Aes192>(&key, &mut ciphertext, expected_plaintext);
263 }
264
265 #[test]
266 fn crypt_aes_256_40_byte() {
267 let mut ciphertext = [
268 0xa9, 0x99, 0xbd, 0xea, 0x82, 0x9b, 0x8f, 0x2f, 0xb7, 0x52, 0x2f, 0x6b, 0xd8, 0xf6,
269 0xab, 0x0e, 0x24, 0x51, 0x9e, 0x18, 0x0f, 0xc0, 0x8f, 0x54, 0x15, 0x80, 0xae, 0xbc,
270 0xa0, 0x5c, 0x8a, 0x11, 0x8d, 0x14, 0x7e, 0xc5, 0xb4, 0xae, 0xd3, 0x37,
271 ];
272 let expected_plaintext = b"Lorem ipsum dolor sit amet, consectetur\n";
273 let key = [
274 0x64, 0x7c, 0x7a, 0xde, 0xf0, 0xf2, 0x61, 0x49, 0x1c, 0xf1, 0xf1, 0xe3, 0x37, 0xfc,
275 0xe1, 0x4d, 0x4a, 0x77, 0xd4, 0xeb, 0x9e, 0x3d, 0x75, 0xce, 0x9a, 0x3e, 0x10, 0x50,
276 0xc2, 0x07, 0x36, 0xb6,
277 ];
278
279 roundtrip::<Aes256>(&key, &mut ciphertext, expected_plaintext);
280 }
281}