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