/src/minizip-ng/mz_strm_pkcrypt.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* mz_strm_pkcrypt.c -- Code for traditional PKWARE 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-2005 Gilles Vollant |
7 | | Modifications for Info-ZIP crypting |
8 | | https://www.winimage.com/zLibDll/minizip.html |
9 | | Copyright (C) 2003 Terry Thorsen |
10 | | |
11 | | This code is a modified version of crypting code in Info-ZIP distribution |
12 | | |
13 | | Copyright (C) 1990-2000 Info-ZIP. All rights reserved. |
14 | | |
15 | | This program is distributed under the terms of the same license as zlib. |
16 | | See the accompanying LICENSE file for the full text of the license. |
17 | | |
18 | | This encryption code is a direct transcription of the algorithm from |
19 | | Roger Schlafly, described by Phil Katz in the file appnote.txt. This |
20 | | file (appnote.txt) is distributed with the PKZIP program (even in the |
21 | | version without encryption capabilities). |
22 | | */ |
23 | | |
24 | | #include "mz.h" |
25 | | #include "mz_crypt.h" |
26 | | #include "mz_strm.h" |
27 | | #include "mz_strm_pkcrypt.h" |
28 | | |
29 | | /***************************************************************************/ |
30 | | |
31 | | static mz_stream_vtbl mz_stream_pkcrypt_vtbl = { |
32 | | mz_stream_pkcrypt_open, |
33 | | mz_stream_pkcrypt_is_open, |
34 | | mz_stream_pkcrypt_read, |
35 | | mz_stream_pkcrypt_write, |
36 | | mz_stream_pkcrypt_tell, |
37 | | mz_stream_pkcrypt_seek, |
38 | | mz_stream_pkcrypt_close, |
39 | | mz_stream_pkcrypt_error, |
40 | | mz_stream_pkcrypt_create, |
41 | | mz_stream_pkcrypt_delete, |
42 | | mz_stream_pkcrypt_get_prop_int64, |
43 | | mz_stream_pkcrypt_set_prop_int64 |
44 | | }; |
45 | | |
46 | | /***************************************************************************/ |
47 | | |
48 | | typedef struct mz_stream_pkcrypt_s { |
49 | | mz_stream stream; |
50 | | int32_t error; |
51 | | int16_t initialized; |
52 | | uint8_t buffer[UINT16_MAX]; |
53 | | int64_t total_in; |
54 | | int64_t max_total_in; |
55 | | int64_t total_out; |
56 | | uint32_t keys[3]; /* keys defining the pseudo-random sequence */ |
57 | | uint8_t verify1; |
58 | | uint8_t verify2; |
59 | | const char *password; |
60 | | } mz_stream_pkcrypt; |
61 | | |
62 | | /***************************************************************************/ |
63 | | |
64 | | #define mz_stream_pkcrypt_decode(strm, c) \ |
65 | 1.40M | (mz_stream_pkcrypt_update_keys(strm, \ |
66 | 1.40M | c ^= mz_stream_pkcrypt_decrypt_byte(strm))) |
67 | | |
68 | | #define mz_stream_pkcrypt_encode(strm, c, t) \ |
69 | 0 | (t = mz_stream_pkcrypt_decrypt_byte(strm), \ |
70 | 0 | mz_stream_pkcrypt_update_keys(strm, (uint8_t)c), (uint8_t)(t^(c))) |
71 | | |
72 | | /***************************************************************************/ |
73 | | |
74 | 1.39M | static uint8_t mz_stream_pkcrypt_decrypt_byte(void *stream) { |
75 | 1.39M | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
76 | | |
77 | 1.39M | unsigned temp; /* POTENTIAL BUG: temp*(temp^1) may overflow in an */ |
78 | | /* unpredictable manner on 16-bit systems; not a problem */ |
79 | | /* with any known compiler so far, though. */ |
80 | | |
81 | 1.39M | temp = pkcrypt->keys[2] | 2; |
82 | 1.39M | return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff); |
83 | 1.39M | } |
84 | | |
85 | 1.43M | static uint8_t mz_stream_pkcrypt_update_keys(void *stream, uint8_t c) { |
86 | 1.43M | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
87 | 1.43M | uint8_t buf = c; |
88 | | |
89 | 1.43M | pkcrypt->keys[0] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[0], &buf, 1); |
90 | | |
91 | 1.43M | pkcrypt->keys[1] += pkcrypt->keys[0] & 0xff; |
92 | 1.43M | pkcrypt->keys[1] *= 134775813L; |
93 | 1.43M | pkcrypt->keys[1] += 1; |
94 | | |
95 | 1.43M | buf = (uint8_t)(pkcrypt->keys[1] >> 24); |
96 | 1.43M | pkcrypt->keys[2] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[2], &buf, 1); |
97 | | |
98 | 1.43M | return (uint8_t)c; |
99 | 1.43M | } |
100 | | |
101 | 5.20k | static void mz_stream_pkcrypt_init_keys(void *stream, const char *password) { |
102 | 5.20k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
103 | | |
104 | 5.20k | pkcrypt->keys[0] = 305419896L; |
105 | 5.20k | pkcrypt->keys[1] = 591751049L; |
106 | 5.20k | pkcrypt->keys[2] = 878082192L; |
107 | | |
108 | 41.6k | while (*password != 0) { |
109 | 36.4k | mz_stream_pkcrypt_update_keys(stream, (uint8_t)*password); |
110 | 36.4k | password += 1; |
111 | 36.4k | } |
112 | 5.20k | } |
113 | | |
114 | | /***************************************************************************/ |
115 | | |
116 | 5.20k | int32_t mz_stream_pkcrypt_open(void *stream, const char *path, int32_t mode) { |
117 | 5.20k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
118 | 5.20k | uint16_t t = 0; |
119 | 5.20k | int16_t i = 0; |
120 | 5.20k | uint8_t verify1 = 0; |
121 | 5.20k | uint8_t verify2 = 0; |
122 | 5.20k | uint8_t header[MZ_PKCRYPT_HEADER_SIZE]; |
123 | 5.20k | const char *password = path; |
124 | | |
125 | 5.20k | pkcrypt->total_in = 0; |
126 | 5.20k | pkcrypt->total_out = 0; |
127 | 5.20k | pkcrypt->initialized = 0; |
128 | | |
129 | 5.20k | if (mz_stream_is_open(pkcrypt->stream.base) != MZ_OK) |
130 | 0 | return MZ_OPEN_ERROR; |
131 | | |
132 | 5.20k | if (!password) |
133 | 5.20k | password = pkcrypt->password; |
134 | 5.20k | if (!password) |
135 | 0 | return MZ_PARAM_ERROR; |
136 | | |
137 | 5.20k | mz_stream_pkcrypt_init_keys(stream, password); |
138 | | |
139 | 5.20k | if (mode & MZ_OPEN_MODE_WRITE) { |
140 | | /* First generate RAND_HEAD_LEN - 2 random bytes. */ |
141 | 0 | mz_crypt_rand(header, MZ_PKCRYPT_HEADER_SIZE - 2); |
142 | | |
143 | | /* Encrypt random header (last two bytes is high word of crc) */ |
144 | 0 | for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++) |
145 | 0 | header[i] = mz_stream_pkcrypt_encode(stream, header[i], t); |
146 | |
|
147 | 0 | header[i++] = mz_stream_pkcrypt_encode(stream, pkcrypt->verify1, t); |
148 | 0 | header[i++] = mz_stream_pkcrypt_encode(stream, pkcrypt->verify2, t); |
149 | |
|
150 | 0 | if (mz_stream_write(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header)) |
151 | 0 | return MZ_WRITE_ERROR; |
152 | | |
153 | 0 | pkcrypt->total_out += MZ_PKCRYPT_HEADER_SIZE; |
154 | 5.20k | } else if (mode & MZ_OPEN_MODE_READ) { |
155 | 5.20k | if (mz_stream_read(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header)) |
156 | 71 | return MZ_READ_ERROR; |
157 | | |
158 | 56.4k | for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++) |
159 | 51.3k | header[i] = mz_stream_pkcrypt_decode(stream, header[i]); |
160 | | |
161 | 5.13k | verify1 = mz_stream_pkcrypt_decode(stream, header[i++]); |
162 | 5.13k | verify2 = mz_stream_pkcrypt_decode(stream, header[i++]); |
163 | | |
164 | | /* Older versions used 2 byte check, newer versions use 1 byte check. */ |
165 | 5.13k | MZ_UNUSED(verify1); |
166 | 5.13k | if ((verify2 != 0) && (verify2 != pkcrypt->verify2)) |
167 | 41 | return MZ_PASSWORD_ERROR; |
168 | | |
169 | 5.09k | pkcrypt->total_in += MZ_PKCRYPT_HEADER_SIZE; |
170 | 5.09k | } |
171 | | |
172 | 5.09k | pkcrypt->initialized = 1; |
173 | 5.09k | return MZ_OK; |
174 | 5.20k | } |
175 | | |
176 | 13.9k | int32_t mz_stream_pkcrypt_is_open(void *stream) { |
177 | 13.9k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
178 | 13.9k | if (!pkcrypt->initialized) |
179 | 0 | return MZ_OPEN_ERROR; |
180 | 13.9k | return MZ_OK; |
181 | 13.9k | } |
182 | | |
183 | 4.44k | int32_t mz_stream_pkcrypt_read(void *stream, void *buf, int32_t size) { |
184 | 4.44k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
185 | 4.44k | uint8_t *buf_ptr = (uint8_t *)buf; |
186 | 4.44k | int32_t bytes_to_read = size; |
187 | 4.44k | int32_t read = 0; |
188 | 4.44k | int32_t i = 0; |
189 | | |
190 | 4.44k | if ((int64_t)bytes_to_read > (pkcrypt->max_total_in - pkcrypt->total_in)) |
191 | 1.74k | bytes_to_read = (int32_t)(pkcrypt->max_total_in - pkcrypt->total_in); |
192 | | |
193 | 4.44k | read = mz_stream_read(pkcrypt->stream.base, buf, bytes_to_read); |
194 | | |
195 | 1.34M | for (i = 0; i < read; i++) |
196 | 1.33M | buf_ptr[i] = mz_stream_pkcrypt_decode(stream, buf_ptr[i]); |
197 | | |
198 | 4.44k | if (read > 0) |
199 | 2.64k | pkcrypt->total_in += read; |
200 | | |
201 | 4.44k | return read; |
202 | 4.44k | } |
203 | | |
204 | 0 | int32_t mz_stream_pkcrypt_write(void *stream, const void *buf, int32_t size) { |
205 | 0 | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
206 | 0 | const uint8_t *buf_ptr = (const uint8_t *)buf; |
207 | 0 | int32_t bytes_to_write = sizeof(pkcrypt->buffer); |
208 | 0 | int32_t total_written = 0; |
209 | 0 | int32_t written = 0; |
210 | 0 | int32_t i = 0; |
211 | 0 | uint16_t t = 0; |
212 | |
|
213 | 0 | if (size < 0) |
214 | 0 | return MZ_PARAM_ERROR; |
215 | | |
216 | 0 | do { |
217 | 0 | if (bytes_to_write > (size - total_written)) |
218 | 0 | bytes_to_write = (size - total_written); |
219 | |
|
220 | 0 | for (i = 0; i < bytes_to_write; i += 1) { |
221 | 0 | pkcrypt->buffer[i] = mz_stream_pkcrypt_encode(stream, *buf_ptr, t); |
222 | 0 | buf_ptr += 1; |
223 | 0 | } |
224 | |
|
225 | 0 | written = mz_stream_write(pkcrypt->stream.base, pkcrypt->buffer, bytes_to_write); |
226 | 0 | if (written < 0) |
227 | 0 | return written; |
228 | | |
229 | 0 | total_written += written; |
230 | 0 | } while (total_written < size && written > 0); |
231 | | |
232 | 0 | pkcrypt->total_out += total_written; |
233 | 0 | return total_written; |
234 | 0 | } |
235 | | |
236 | 0 | int64_t mz_stream_pkcrypt_tell(void *stream) { |
237 | 0 | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
238 | 0 | return mz_stream_tell(pkcrypt->stream.base); |
239 | 0 | } |
240 | | |
241 | 0 | int32_t mz_stream_pkcrypt_seek(void *stream, int64_t offset, int32_t origin) { |
242 | 0 | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
243 | 0 | return mz_stream_seek(pkcrypt->stream.base, offset, origin); |
244 | 0 | } |
245 | | |
246 | 0 | int32_t mz_stream_pkcrypt_close(void *stream) { |
247 | 0 | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
248 | 0 | pkcrypt->initialized = 0; |
249 | 0 | return MZ_OK; |
250 | 0 | } |
251 | | |
252 | 0 | int32_t mz_stream_pkcrypt_error(void *stream) { |
253 | 0 | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
254 | 0 | return pkcrypt->error; |
255 | 0 | } |
256 | | |
257 | 5.20k | void mz_stream_pkcrypt_set_password(void *stream, const char *password) { |
258 | 5.20k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
259 | 5.20k | pkcrypt->password = password; |
260 | 5.20k | } |
261 | | |
262 | 5.20k | void mz_stream_pkcrypt_set_verify(void *stream, uint8_t verify1, uint8_t verify2) { |
263 | 5.20k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
264 | 5.20k | pkcrypt->verify1 = verify1; |
265 | 5.20k | pkcrypt->verify2 = verify2; |
266 | 5.20k | } |
267 | | |
268 | 0 | void mz_stream_pkcrypt_get_verify(void *stream, uint8_t *verify1, uint8_t *verify2) { |
269 | 0 | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
270 | 0 | *verify1 = pkcrypt->verify1; |
271 | 0 | *verify2 = pkcrypt->verify2; |
272 | 0 | } |
273 | | |
274 | 10.1k | int32_t mz_stream_pkcrypt_get_prop_int64(void *stream, int32_t prop, int64_t *value) { |
275 | 10.1k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
276 | 10.1k | switch (prop) { |
277 | 0 | case MZ_STREAM_PROP_TOTAL_IN: |
278 | 0 | *value = pkcrypt->total_in; |
279 | 0 | break; |
280 | 0 | case MZ_STREAM_PROP_TOTAL_OUT: |
281 | 0 | *value = pkcrypt->total_out; |
282 | 0 | break; |
283 | 0 | case MZ_STREAM_PROP_TOTAL_IN_MAX: |
284 | 0 | *value = pkcrypt->max_total_in; |
285 | 0 | break; |
286 | 5.09k | case MZ_STREAM_PROP_HEADER_SIZE: |
287 | 5.09k | *value = MZ_PKCRYPT_HEADER_SIZE; |
288 | 5.09k | break; |
289 | 5.09k | case MZ_STREAM_PROP_FOOTER_SIZE: |
290 | 5.09k | *value = 0; |
291 | 5.09k | break; |
292 | 0 | default: |
293 | 0 | return MZ_EXIST_ERROR; |
294 | 10.1k | } |
295 | 10.1k | return MZ_OK; |
296 | 10.1k | } |
297 | | |
298 | 5.09k | int32_t mz_stream_pkcrypt_set_prop_int64(void *stream, int32_t prop, int64_t value) { |
299 | 5.09k | mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream; |
300 | 5.09k | switch (prop) { |
301 | 5.09k | case MZ_STREAM_PROP_TOTAL_IN_MAX: |
302 | 5.09k | pkcrypt->max_total_in = value; |
303 | 5.09k | break; |
304 | 0 | default: |
305 | 0 | return MZ_EXIST_ERROR; |
306 | 5.09k | } |
307 | 5.09k | return MZ_OK; |
308 | 5.09k | } |
309 | | |
310 | 5.20k | void *mz_stream_pkcrypt_create(void **stream) { |
311 | 5.20k | mz_stream_pkcrypt *pkcrypt = NULL; |
312 | | |
313 | 5.20k | pkcrypt = (mz_stream_pkcrypt *)calloc(1, sizeof(mz_stream_pkcrypt)); |
314 | 5.20k | if (pkcrypt) |
315 | 5.20k | pkcrypt->stream.vtbl = &mz_stream_pkcrypt_vtbl; |
316 | 5.20k | if (stream) |
317 | 5.20k | *stream = pkcrypt; |
318 | | |
319 | 5.20k | return pkcrypt; |
320 | 5.20k | } |
321 | | |
322 | 5.20k | void mz_stream_pkcrypt_delete(void **stream) { |
323 | 5.20k | mz_stream_pkcrypt *pkcrypt = NULL; |
324 | 5.20k | if (!stream) |
325 | 0 | return; |
326 | 5.20k | pkcrypt = (mz_stream_pkcrypt *)*stream; |
327 | 5.20k | if (pkcrypt) |
328 | 5.20k | free(pkcrypt); |
329 | 5.20k | *stream = NULL; |
330 | 5.20k | } |
331 | | |
332 | 0 | void *mz_stream_pkcrypt_get_interface(void) { |
333 | 0 | return (void *)&mz_stream_pkcrypt_vtbl; |
334 | 0 | } |