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