/src/minizip-ng/mz_strm_wzaes.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* mz_strm_wzaes.c -- Stream for WinZip AES encryption |
2 | | part of the minizip-ng project |
3 | | |
4 | | Copyright (C) Nathan Moinvaziri |
5 | | https://github.com/zlib-ng/minizip-ng |
6 | | Copyright (C) 1998-2010 Brian Gladman, Worcester, UK |
7 | | |
8 | | This program is distributed under the terms of the same license as zlib. |
9 | | See the accompanying LICENSE file for the full text of the license. |
10 | | */ |
11 | | |
12 | | #include "mz.h" |
13 | | #include "mz_crypt.h" |
14 | | #include "mz_strm.h" |
15 | | #include "mz_strm_wzaes.h" |
16 | | |
17 | | /***************************************************************************/ |
18 | | |
19 | 13.3k | #define MZ_AES_KEY_LENGTH(STRENGTH) (8 * (STRENGTH & 3) + 8) |
20 | 13.3k | #define MZ_AES_KEYING_ITERATIONS (1000) |
21 | 26.6k | #define MZ_AES_SALT_LENGTH(STRENGTH) (4 * (STRENGTH & 3) + 4) |
22 | | #define MZ_AES_SALT_LENGTH_MAX (16) |
23 | 13.3k | #define MZ_AES_PW_LENGTH_MAX (128) |
24 | 93.2k | #define MZ_AES_PW_VERIFY_SIZE (2) |
25 | 13.2k | #define MZ_AES_AUTHCODE_SIZE (10) |
26 | | |
27 | | /***************************************************************************/ |
28 | | |
29 | | static mz_stream_vtbl mz_stream_wzaes_vtbl = { |
30 | | mz_stream_wzaes_open, mz_stream_wzaes_is_open, mz_stream_wzaes_read, mz_stream_wzaes_write, |
31 | | mz_stream_wzaes_tell, mz_stream_wzaes_seek, mz_stream_wzaes_close, mz_stream_wzaes_error, |
32 | | mz_stream_wzaes_create, mz_stream_wzaes_delete, mz_stream_wzaes_get_prop_int64, mz_stream_wzaes_set_prop_int64}; |
33 | | |
34 | | /***************************************************************************/ |
35 | | |
36 | | typedef struct mz_stream_wzaes_s { |
37 | | mz_stream stream; |
38 | | int32_t mode; |
39 | | int32_t error; |
40 | | int16_t initialized; |
41 | | uint8_t buffer[UINT16_MAX]; |
42 | | int64_t total_in; |
43 | | int64_t max_total_in; |
44 | | int64_t total_out; |
45 | | uint8_t strength; |
46 | | const char *password; |
47 | | void *aes; |
48 | | uint32_t crypt_pos; |
49 | | uint8_t crypt_block[MZ_AES_BLOCK_SIZE]; |
50 | | void *hmac; |
51 | | uint8_t nonce[MZ_AES_BLOCK_SIZE]; |
52 | | } mz_stream_wzaes; |
53 | | |
54 | | /***************************************************************************/ |
55 | | |
56 | 13.3k | int32_t mz_stream_wzaes_open(void *stream, const char *path, int32_t mode) { |
57 | 13.3k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
58 | 13.3k | uint16_t salt_length = 0; |
59 | 13.3k | uint16_t password_length = 0; |
60 | 13.3k | uint16_t key_length = 0; |
61 | 13.3k | uint8_t kbuf[2 * MZ_AES_KEY_LENGTH_MAX + MZ_AES_PW_VERIFY_SIZE]; |
62 | 13.3k | uint8_t verify[MZ_AES_PW_VERIFY_SIZE]; |
63 | 13.3k | uint8_t verify_expected[MZ_AES_PW_VERIFY_SIZE]; |
64 | 13.3k | uint8_t salt_value[MZ_AES_SALT_LENGTH_MAX]; |
65 | 13.3k | const char *password = path; |
66 | | |
67 | 13.3k | wzaes->total_in = 0; |
68 | 13.3k | wzaes->total_out = 0; |
69 | 13.3k | wzaes->initialized = 0; |
70 | | |
71 | 13.3k | if (mz_stream_is_open(wzaes->stream.base) != MZ_OK) |
72 | 0 | return MZ_OPEN_ERROR; |
73 | | |
74 | 13.3k | if (!password) |
75 | 13.3k | password = wzaes->password; |
76 | 13.3k | if (!password) |
77 | 0 | return MZ_PARAM_ERROR; |
78 | 13.3k | password_length = (uint16_t)strlen(password); |
79 | 13.3k | if (password_length > MZ_AES_PW_LENGTH_MAX) |
80 | 0 | return MZ_PARAM_ERROR; |
81 | | |
82 | 13.3k | if (wzaes->strength < 1 || wzaes->strength > 3) |
83 | 14 | return MZ_PARAM_ERROR; |
84 | | |
85 | 13.3k | key_length = MZ_AES_KEY_LENGTH(wzaes->strength); |
86 | 13.3k | salt_length = MZ_AES_SALT_LENGTH(wzaes->strength); |
87 | | |
88 | 13.3k | if (mode & MZ_OPEN_MODE_WRITE) { |
89 | 0 | mz_crypt_rand(salt_value, salt_length); |
90 | 13.3k | } else if (mode & MZ_OPEN_MODE_READ) { |
91 | 13.3k | if (mz_stream_read(wzaes->stream.base, salt_value, salt_length) != salt_length) |
92 | 5 | return MZ_READ_ERROR; |
93 | 13.3k | } |
94 | | |
95 | | /* Derive the encryption and authentication keys and the password verifier */ |
96 | 13.3k | mz_crypt_pbkdf2((uint8_t *)password, password_length, salt_value, salt_length, MZ_AES_KEYING_ITERATIONS, kbuf, |
97 | 13.3k | 2 * key_length + MZ_AES_PW_VERIFY_SIZE); |
98 | | |
99 | | /* Initialize the buffer pos */ |
100 | 13.3k | wzaes->crypt_pos = MZ_AES_BLOCK_SIZE; |
101 | | |
102 | | /* Use fixed zeroed IV/nonce for CTR mode */ |
103 | 13.3k | memset(wzaes->nonce, 0, sizeof(wzaes->nonce)); |
104 | | |
105 | | /* Initialize for encryption using key 1 */ |
106 | 13.3k | mz_crypt_aes_reset(wzaes->aes); |
107 | 13.3k | mz_crypt_aes_set_encrypt_key(wzaes->aes, kbuf, key_length, NULL, 0); |
108 | | |
109 | | /* Initialize for authentication using key 2 */ |
110 | 13.3k | mz_crypt_hmac_reset(wzaes->hmac); |
111 | 13.3k | mz_crypt_hmac_set_algorithm(wzaes->hmac, MZ_HASH_SHA1); |
112 | 13.3k | mz_crypt_hmac_init(wzaes->hmac, kbuf + key_length, key_length); |
113 | | |
114 | 13.3k | memcpy(verify, kbuf + (2 * key_length), MZ_AES_PW_VERIFY_SIZE); |
115 | | |
116 | 13.3k | if (mode & MZ_OPEN_MODE_WRITE) { |
117 | 0 | if (mz_stream_write(wzaes->stream.base, salt_value, salt_length) != salt_length) |
118 | 0 | return MZ_WRITE_ERROR; |
119 | | |
120 | 0 | wzaes->total_out += salt_length; |
121 | |
|
122 | 0 | if (mz_stream_write(wzaes->stream.base, verify, MZ_AES_PW_VERIFY_SIZE) != MZ_AES_PW_VERIFY_SIZE) |
123 | 0 | return MZ_WRITE_ERROR; |
124 | | |
125 | 0 | wzaes->total_out += MZ_AES_PW_VERIFY_SIZE; |
126 | 13.3k | } else if (mode & MZ_OPEN_MODE_READ) { |
127 | 13.3k | wzaes->total_in += salt_length; |
128 | | |
129 | 13.3k | if (mz_stream_read(wzaes->stream.base, verify_expected, MZ_AES_PW_VERIFY_SIZE) != MZ_AES_PW_VERIFY_SIZE) |
130 | 3 | return MZ_READ_ERROR; |
131 | | |
132 | 13.3k | wzaes->total_in += MZ_AES_PW_VERIFY_SIZE; |
133 | | |
134 | 13.3k | if (memcmp(verify_expected, verify, MZ_AES_PW_VERIFY_SIZE) != 0) |
135 | 46 | return MZ_PASSWORD_ERROR; |
136 | 13.3k | } |
137 | | |
138 | 13.2k | wzaes->mode = mode; |
139 | 13.2k | wzaes->initialized = 1; |
140 | | |
141 | 13.2k | return MZ_OK; |
142 | 13.3k | } |
143 | | |
144 | 39.2k | int32_t mz_stream_wzaes_is_open(void *stream) { |
145 | 39.2k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
146 | 39.2k | if (!wzaes->initialized) |
147 | 0 | return MZ_OPEN_ERROR; |
148 | 39.2k | return MZ_OK; |
149 | 39.2k | } |
150 | | |
151 | 7.07k | static int32_t mz_stream_wzaes_ctr_encrypt(void *stream, uint8_t *buf, int32_t size) { |
152 | 7.07k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
153 | 7.07k | uint32_t pos = wzaes->crypt_pos; |
154 | 7.07k | uint32_t i = 0; |
155 | 7.07k | int32_t err = MZ_OK; |
156 | | |
157 | 7.01M | while (i < (uint32_t)size) { |
158 | 7.00M | if (pos == MZ_AES_BLOCK_SIZE) { |
159 | 438k | uint32_t j = 0; |
160 | | |
161 | | /* Increment encryption nonce */ |
162 | 438k | while (j < 8 && !++wzaes->nonce[j]) |
163 | 0 | j += 1; |
164 | | |
165 | | /* Encrypt the nonce using ECB mode to form next xor buffer */ |
166 | 438k | memcpy(wzaes->crypt_block, wzaes->nonce, MZ_AES_BLOCK_SIZE); |
167 | 438k | mz_crypt_aes_encrypt(wzaes->aes, NULL, 0, wzaes->crypt_block, sizeof(wzaes->crypt_block)); |
168 | 438k | pos = 0; |
169 | 438k | } |
170 | | |
171 | 7.00M | buf[i++] ^= wzaes->crypt_block[pos++]; |
172 | 7.00M | } |
173 | | |
174 | 7.07k | wzaes->crypt_pos = pos; |
175 | 7.07k | return err; |
176 | 7.07k | } |
177 | | |
178 | 12.9k | int32_t mz_stream_wzaes_read(void *stream, void *buf, int32_t size) { |
179 | 12.9k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
180 | 12.9k | int64_t max_total_in = 0; |
181 | 12.9k | int32_t bytes_to_read = size; |
182 | 12.9k | int32_t read = 0; |
183 | | |
184 | 12.9k | max_total_in = wzaes->max_total_in - MZ_AES_FOOTER_SIZE; |
185 | 12.9k | if ((int64_t)bytes_to_read > (max_total_in - wzaes->total_in)) |
186 | 5.91k | bytes_to_read = (int32_t)(max_total_in - wzaes->total_in); |
187 | | |
188 | 12.9k | read = mz_stream_read(wzaes->stream.base, buf, bytes_to_read); |
189 | | |
190 | 12.9k | if (read > 0) { |
191 | 7.07k | mz_crypt_hmac_update(wzaes->hmac, (uint8_t *)buf, read); |
192 | 7.07k | mz_stream_wzaes_ctr_encrypt(stream, (uint8_t *)buf, read); |
193 | | |
194 | 7.07k | wzaes->total_in += read; |
195 | 7.07k | } |
196 | | |
197 | 12.9k | return read; |
198 | 12.9k | } |
199 | | |
200 | 0 | int32_t mz_stream_wzaes_write(void *stream, const void *buf, int32_t size) { |
201 | 0 | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
202 | 0 | const uint8_t *buf_ptr = (const uint8_t *)buf; |
203 | 0 | int32_t bytes_to_write = sizeof(wzaes->buffer); |
204 | 0 | int32_t total_written = 0; |
205 | 0 | int32_t written = 0; |
206 | |
|
207 | 0 | if (size < 0) |
208 | 0 | return MZ_PARAM_ERROR; |
209 | | |
210 | 0 | do { |
211 | 0 | if (bytes_to_write > (size - total_written)) |
212 | 0 | bytes_to_write = (size - total_written); |
213 | |
|
214 | 0 | memcpy(wzaes->buffer, buf_ptr, bytes_to_write); |
215 | 0 | buf_ptr += bytes_to_write; |
216 | |
|
217 | 0 | mz_stream_wzaes_ctr_encrypt(stream, (uint8_t *)wzaes->buffer, bytes_to_write); |
218 | 0 | mz_crypt_hmac_update(wzaes->hmac, wzaes->buffer, bytes_to_write); |
219 | |
|
220 | 0 | written = mz_stream_write(wzaes->stream.base, wzaes->buffer, bytes_to_write); |
221 | 0 | if (written < 0) |
222 | 0 | return written; |
223 | | |
224 | 0 | total_written += written; |
225 | 0 | } while (total_written < size && written > 0); |
226 | | |
227 | 0 | wzaes->total_out += total_written; |
228 | 0 | return total_written; |
229 | 0 | } |
230 | | |
231 | 0 | int64_t mz_stream_wzaes_tell(void *stream) { |
232 | 0 | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
233 | 0 | return mz_stream_tell(wzaes->stream.base); |
234 | 0 | } |
235 | | |
236 | 0 | int32_t mz_stream_wzaes_seek(void *stream, int64_t offset, int32_t origin) { |
237 | 0 | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
238 | 0 | return mz_stream_seek(wzaes->stream.base, offset, origin); |
239 | 0 | } |
240 | | |
241 | 0 | int32_t mz_stream_wzaes_close(void *stream) { |
242 | 0 | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
243 | 0 | uint8_t expected_hash[MZ_AES_AUTHCODE_SIZE]; |
244 | 0 | uint8_t computed_hash[MZ_HASH_SHA1_SIZE]; |
245 | |
|
246 | 0 | mz_crypt_hmac_end(wzaes->hmac, computed_hash, sizeof(computed_hash)); |
247 | |
|
248 | 0 | if (wzaes->mode & MZ_OPEN_MODE_WRITE) { |
249 | 0 | if (mz_stream_write(wzaes->stream.base, computed_hash, MZ_AES_AUTHCODE_SIZE) != MZ_AES_AUTHCODE_SIZE) |
250 | 0 | return MZ_WRITE_ERROR; |
251 | | |
252 | 0 | wzaes->total_out += MZ_AES_AUTHCODE_SIZE; |
253 | 0 | } else if (wzaes->mode & MZ_OPEN_MODE_READ) { |
254 | 0 | if (mz_stream_read(wzaes->stream.base, expected_hash, MZ_AES_AUTHCODE_SIZE) != MZ_AES_AUTHCODE_SIZE) |
255 | 0 | return MZ_READ_ERROR; |
256 | | |
257 | 0 | wzaes->total_in += MZ_AES_AUTHCODE_SIZE; |
258 | | |
259 | | /* If entire entry was not read this will fail */ |
260 | 0 | if (memcmp(computed_hash, expected_hash, MZ_AES_AUTHCODE_SIZE) != 0) |
261 | 0 | return MZ_CRC_ERROR; |
262 | 0 | } |
263 | | |
264 | 0 | wzaes->initialized = 0; |
265 | 0 | return MZ_OK; |
266 | 0 | } |
267 | | |
268 | 0 | int32_t mz_stream_wzaes_error(void *stream) { |
269 | 0 | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
270 | 0 | return wzaes->error; |
271 | 0 | } |
272 | | |
273 | 13.3k | void mz_stream_wzaes_set_password(void *stream, const char *password) { |
274 | 13.3k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
275 | 13.3k | wzaes->password = password; |
276 | 13.3k | } |
277 | | |
278 | 13.3k | void mz_stream_wzaes_set_strength(void *stream, uint8_t strength) { |
279 | 13.3k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
280 | 13.3k | wzaes->strength = strength; |
281 | 13.3k | } |
282 | | |
283 | 26.5k | int32_t mz_stream_wzaes_get_prop_int64(void *stream, int32_t prop, int64_t *value) { |
284 | 26.5k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
285 | 26.5k | switch (prop) { |
286 | 0 | case MZ_STREAM_PROP_TOTAL_IN: |
287 | 0 | *value = wzaes->total_in; |
288 | 0 | break; |
289 | 0 | case MZ_STREAM_PROP_TOTAL_OUT: |
290 | 0 | *value = wzaes->total_out; |
291 | 0 | break; |
292 | 0 | case MZ_STREAM_PROP_TOTAL_IN_MAX: |
293 | 0 | *value = wzaes->max_total_in; |
294 | 0 | break; |
295 | 13.2k | case MZ_STREAM_PROP_HEADER_SIZE: |
296 | 13.2k | *value = MZ_AES_SALT_LENGTH((int64_t)wzaes->strength) + MZ_AES_PW_VERIFY_SIZE; |
297 | 13.2k | break; |
298 | 13.2k | case MZ_STREAM_PROP_FOOTER_SIZE: |
299 | 13.2k | *value = MZ_AES_AUTHCODE_SIZE; |
300 | 13.2k | break; |
301 | 0 | default: |
302 | 0 | return MZ_EXIST_ERROR; |
303 | 26.5k | } |
304 | 26.5k | return MZ_OK; |
305 | 26.5k | } |
306 | | |
307 | 13.2k | int32_t mz_stream_wzaes_set_prop_int64(void *stream, int32_t prop, int64_t value) { |
308 | 13.2k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream; |
309 | 13.2k | switch (prop) { |
310 | 13.2k | case MZ_STREAM_PROP_TOTAL_IN_MAX: |
311 | 13.2k | wzaes->max_total_in = value; |
312 | 13.2k | break; |
313 | 0 | default: |
314 | 0 | return MZ_EXIST_ERROR; |
315 | 13.2k | } |
316 | 13.2k | return MZ_OK; |
317 | 13.2k | } |
318 | | |
319 | 13.3k | void *mz_stream_wzaes_create(void) { |
320 | 13.3k | mz_stream_wzaes *wzaes = (mz_stream_wzaes *)calloc(1, sizeof(mz_stream_wzaes)); |
321 | 13.3k | if (wzaes) { |
322 | 13.3k | wzaes->stream.vtbl = &mz_stream_wzaes_vtbl; |
323 | 13.3k | wzaes->strength = MZ_AES_STRENGTH_256; |
324 | | |
325 | 13.3k | wzaes->hmac = mz_crypt_hmac_create(); |
326 | 13.3k | if (!wzaes->hmac) { |
327 | 0 | free(wzaes); |
328 | 0 | return NULL; |
329 | 0 | } |
330 | 13.3k | wzaes->aes = mz_crypt_aes_create(); |
331 | 13.3k | if (!wzaes->aes) { |
332 | 0 | mz_crypt_hmac_delete(&wzaes->hmac); |
333 | 0 | free(wzaes); |
334 | 0 | return NULL; |
335 | 0 | } |
336 | 13.3k | } |
337 | 13.3k | return wzaes; |
338 | 13.3k | } |
339 | | |
340 | 13.3k | void mz_stream_wzaes_delete(void **stream) { |
341 | 13.3k | mz_stream_wzaes *wzaes = NULL; |
342 | 13.3k | if (!stream) |
343 | 0 | return; |
344 | 13.3k | wzaes = (mz_stream_wzaes *)*stream; |
345 | 13.3k | if (wzaes) { |
346 | 13.3k | mz_crypt_aes_delete(&wzaes->aes); |
347 | 13.3k | mz_crypt_hmac_delete(&wzaes->hmac); |
348 | 13.3k | free(wzaes); |
349 | 13.3k | } |
350 | 13.3k | *stream = NULL; |
351 | 13.3k | } |
352 | | |
353 | 0 | void *mz_stream_wzaes_get_interface(void) { |
354 | 0 | return (void *)&mz_stream_wzaes_vtbl; |
355 | 0 | } |