Coverage Report

Created: 2025-08-26 06:44

/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,   mz_stream_pkcrypt_is_open,        mz_stream_pkcrypt_read,
33
    mz_stream_pkcrypt_write,  mz_stream_pkcrypt_tell,           mz_stream_pkcrypt_seek,
34
    mz_stream_pkcrypt_close,  mz_stream_pkcrypt_error,          mz_stream_pkcrypt_create,
35
    mz_stream_pkcrypt_delete, mz_stream_pkcrypt_get_prop_int64, mz_stream_pkcrypt_set_prop_int64};
36
37
/***************************************************************************/
38
39
typedef struct mz_stream_pkcrypt_s {
40
    mz_stream stream;
41
    int32_t error;
42
    int16_t initialized;
43
    uint8_t buffer[UINT16_MAX];
44
    int64_t total_in;
45
    int64_t max_total_in;
46
    int64_t total_out;
47
    uint32_t keys[3]; /* keys defining the pseudo-random sequence */
48
    uint8_t verify1;
49
    uint8_t verify2;
50
    uint16_t verify_version;
51
    const char *password;
52
} mz_stream_pkcrypt;
53
54
/***************************************************************************/
55
56
#define mz_stream_pkcrypt_decode(strm, c)                                                                              \
57
1.29M
    (mz_stream_pkcrypt_update_keys(strm, c ^= mz_stream_pkcrypt_decrypt_byte(strm)))
58
59
#define mz_stream_pkcrypt_encode(strm, c, t)                                                                           \
60
26.8M
    (t = mz_stream_pkcrypt_decrypt_byte(strm), mz_stream_pkcrypt_update_keys(strm, (uint8_t)c), (uint8_t)(t ^ (c)))
61
62
/***************************************************************************/
63
64
28.1M
static uint8_t mz_stream_pkcrypt_decrypt_byte(void *stream) {
65
28.1M
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
66
67
28.1M
    unsigned temp; /* POTENTIAL BUG:  temp*(temp^1) may overflow in an */
68
                   /* unpredictable manner on 16-bit systems; not a problem */
69
                   /* with any known compiler so far, though. */
70
71
28.1M
    temp = pkcrypt->keys[2] | 2;
72
28.1M
    return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff);
73
28.1M
}
74
75
28.2M
static uint8_t mz_stream_pkcrypt_update_keys(void *stream, uint8_t c) {
76
28.2M
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
77
28.2M
    uint8_t buf = c;
78
79
28.2M
    pkcrypt->keys[0] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[0], &buf, 1);
80
81
28.2M
    pkcrypt->keys[1] += pkcrypt->keys[0] & 0xff;
82
28.2M
    pkcrypt->keys[1] *= 134775813L;
83
28.2M
    pkcrypt->keys[1] += 1;
84
85
28.2M
    buf = (uint8_t)(pkcrypt->keys[1] >> 24);
86
28.2M
    pkcrypt->keys[2] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[2], &buf, 1);
87
88
28.2M
    return (uint8_t)c;
89
28.2M
}
90
91
2.81k
static void mz_stream_pkcrypt_init_keys(void *stream, const char *password) {
92
2.81k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
93
94
2.81k
    pkcrypt->keys[0] = 305419896L;
95
2.81k
    pkcrypt->keys[1] = 591751049L;
96
2.81k
    pkcrypt->keys[2] = 878082192L;
97
98
22.5k
    while (*password != 0) {
99
19.7k
        mz_stream_pkcrypt_update_keys(stream, (uint8_t)*password);
100
19.7k
        password += 1;
101
19.7k
    }
102
2.81k
}
103
104
/***************************************************************************/
105
106
2.81k
int32_t mz_stream_pkcrypt_open(void *stream, const char *path, int32_t mode) {
107
2.81k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
108
2.81k
    uint16_t t = 0;
109
2.81k
    int16_t i = 0;
110
2.81k
    uint8_t verify1 = 0;
111
2.81k
    uint8_t verify2 = 0;
112
2.81k
    uint8_t header[MZ_PKCRYPT_HEADER_SIZE];
113
2.81k
    const char *password = path;
114
115
2.81k
    pkcrypt->total_in = 0;
116
2.81k
    pkcrypt->total_out = 0;
117
2.81k
    pkcrypt->initialized = 0;
118
119
2.81k
    if (mz_stream_is_open(pkcrypt->stream.base) != MZ_OK)
120
0
        return MZ_OPEN_ERROR;
121
122
2.81k
    if (!password)
123
2.81k
        password = pkcrypt->password;
124
2.81k
    if (!password)
125
0
        return MZ_PARAM_ERROR;
126
127
2.81k
    mz_stream_pkcrypt_init_keys(stream, password);
128
129
2.81k
    if (mode & MZ_OPEN_MODE_WRITE) {
130
        /* First generate RAND_HEAD_LEN - 2 random bytes. */
131
191
        mz_crypt_rand(header, MZ_PKCRYPT_HEADER_SIZE - 2);
132
133
        /* Encrypt random header (last two bytes is high word of crc) */
134
2.10k
        for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++)
135
1.91k
            header[i] = mz_stream_pkcrypt_encode(stream, header[i], t);
136
137
191
        header[i++] = mz_stream_pkcrypt_encode(stream, pkcrypt->verify1, t);
138
191
        header[i++] = mz_stream_pkcrypt_encode(stream, pkcrypt->verify2, t);
139
140
191
        if (mz_stream_write(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header))
141
0
            return MZ_WRITE_ERROR;
142
143
191
        pkcrypt->total_out += MZ_PKCRYPT_HEADER_SIZE;
144
2.62k
    } else if (mode & MZ_OPEN_MODE_READ) {
145
2.62k
        if (mz_stream_read(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header))
146
27
            return MZ_READ_ERROR;
147
148
28.6k
        for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++)
149
26.0k
            header[i] = mz_stream_pkcrypt_decode(stream, header[i]);
150
151
2.60k
        verify1 = mz_stream_pkcrypt_decode(stream, header[i++]);
152
2.60k
        verify2 = mz_stream_pkcrypt_decode(stream, header[i++]);
153
154
        /* PKZIP 2.0 and higher use 1 byte check, older versions used 2 byte check.
155
           See app note section 6.1.6. */
156
2.60k
        if (verify2 != pkcrypt->verify2)
157
81
            return MZ_PASSWORD_ERROR;
158
2.52k
        if (pkcrypt->verify_version < 2) {
159
407
            if (verify1 != pkcrypt->verify1)
160
5
                return MZ_PASSWORD_ERROR;
161
407
        }
162
163
2.51k
        pkcrypt->total_in += MZ_PKCRYPT_HEADER_SIZE;
164
2.51k
    }
165
166
2.70k
    pkcrypt->initialized = 1;
167
2.70k
    return MZ_OK;
168
2.81k
}
169
170
7.83k
int32_t mz_stream_pkcrypt_is_open(void *stream) {
171
7.83k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
172
7.83k
    if (!pkcrypt->initialized)
173
0
        return MZ_OPEN_ERROR;
174
7.83k
    return MZ_OK;
175
7.83k
}
176
177
2.28k
int32_t mz_stream_pkcrypt_read(void *stream, void *buf, int32_t size) {
178
2.28k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
179
2.28k
    uint8_t *buf_ptr = (uint8_t *)buf;
180
2.28k
    int32_t bytes_to_read = size;
181
2.28k
    int32_t read = 0;
182
2.28k
    int32_t i = 0;
183
184
2.28k
    if ((int64_t)bytes_to_read > (pkcrypt->max_total_in - pkcrypt->total_in))
185
682
        bytes_to_read = (int32_t)(pkcrypt->max_total_in - pkcrypt->total_in);
186
187
2.28k
    read = mz_stream_read(pkcrypt->stream.base, buf, bytes_to_read);
188
189
1.26M
    for (i = 0; i < read; i++)
190
1.25M
        buf_ptr[i] = mz_stream_pkcrypt_decode(stream, buf_ptr[i]);
191
192
2.28k
    if (read > 0)
193
1.56k
        pkcrypt->total_in += read;
194
195
2.28k
    return read;
196
2.28k
}
197
198
183
int32_t mz_stream_pkcrypt_write(void *stream, const void *buf, int32_t size) {
199
183
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
200
183
    const uint8_t *buf_ptr = (const uint8_t *)buf;
201
183
    int32_t bytes_to_write = sizeof(pkcrypt->buffer);
202
183
    int32_t total_written = 0;
203
183
    int32_t written = 0;
204
183
    int32_t i = 0;
205
183
    uint16_t t = 0;
206
207
183
    if (size < 0)
208
0
        return MZ_PARAM_ERROR;
209
210
541
    do {
211
541
        if (bytes_to_write > (size - total_written))
212
177
            bytes_to_write = (size - total_written);
213
214
26.8M
        for (i = 0; i < bytes_to_write; i += 1) {
215
26.8M
            pkcrypt->buffer[i] = mz_stream_pkcrypt_encode(stream, *buf_ptr, t);
216
26.8M
            buf_ptr += 1;
217
26.8M
        }
218
219
541
        written = mz_stream_write(pkcrypt->stream.base, pkcrypt->buffer, bytes_to_write);
220
541
        if (written < 0)
221
0
            return written;
222
223
541
        total_written += written;
224
541
    } while (total_written < size && written > 0);
225
226
183
    pkcrypt->total_out += total_written;
227
183
    return total_written;
228
183
}
229
230
0
int64_t mz_stream_pkcrypt_tell(void *stream) {
231
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
232
0
    return mz_stream_tell(pkcrypt->stream.base);
233
0
}
234
235
0
int32_t mz_stream_pkcrypt_seek(void *stream, int64_t offset, int32_t origin) {
236
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
237
0
    return mz_stream_seek(pkcrypt->stream.base, offset, origin);
238
0
}
239
240
191
int32_t mz_stream_pkcrypt_close(void *stream) {
241
191
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
242
191
    pkcrypt->initialized = 0;
243
191
    return MZ_OK;
244
191
}
245
246
0
int32_t mz_stream_pkcrypt_error(void *stream) {
247
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
248
0
    return pkcrypt->error;
249
0
}
250
251
2.81k
void mz_stream_pkcrypt_set_password(void *stream, const char *password) {
252
2.81k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
253
2.81k
    pkcrypt->password = password;
254
2.81k
}
255
256
2.81k
void mz_stream_pkcrypt_set_verify(void *stream, uint8_t verify1, uint8_t verify2, uint16_t version) {
257
2.81k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
258
2.81k
    pkcrypt->verify1 = verify1;
259
2.81k
    pkcrypt->verify2 = verify2;
260
2.81k
    pkcrypt->verify_version = version;
261
2.81k
}
262
263
0
void mz_stream_pkcrypt_get_verify(void *stream, uint8_t *verify1, uint8_t *verify2, uint16_t *version) {
264
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
265
0
    *verify1 = pkcrypt->verify1;
266
0
    *verify2 = pkcrypt->verify2;
267
0
    *version = pkcrypt->verify_version;
268
0
}
269
270
5.22k
int32_t mz_stream_pkcrypt_get_prop_int64(void *stream, int32_t prop, int64_t *value) {
271
5.22k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
272
5.22k
    switch (prop) {
273
0
    case MZ_STREAM_PROP_TOTAL_IN:
274
0
        *value = pkcrypt->total_in;
275
0
        break;
276
191
    case MZ_STREAM_PROP_TOTAL_OUT:
277
191
        *value = pkcrypt->total_out;
278
191
        break;
279
0
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
280
0
        *value = pkcrypt->max_total_in;
281
0
        break;
282
2.51k
    case MZ_STREAM_PROP_HEADER_SIZE:
283
2.51k
        *value = MZ_PKCRYPT_HEADER_SIZE;
284
2.51k
        break;
285
2.51k
    case MZ_STREAM_PROP_FOOTER_SIZE:
286
2.51k
        *value = 0;
287
2.51k
        break;
288
0
    default:
289
0
        return MZ_EXIST_ERROR;
290
5.22k
    }
291
5.22k
    return MZ_OK;
292
5.22k
}
293
294
2.51k
int32_t mz_stream_pkcrypt_set_prop_int64(void *stream, int32_t prop, int64_t value) {
295
2.51k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
296
2.51k
    switch (prop) {
297
2.51k
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
298
2.51k
        pkcrypt->max_total_in = value;
299
2.51k
        break;
300
0
    default:
301
0
        return MZ_EXIST_ERROR;
302
2.51k
    }
303
2.51k
    return MZ_OK;
304
2.51k
}
305
306
2.81k
void *mz_stream_pkcrypt_create(void) {
307
2.81k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)calloc(1, sizeof(mz_stream_pkcrypt));
308
2.81k
    if (pkcrypt)
309
2.81k
        pkcrypt->stream.vtbl = &mz_stream_pkcrypt_vtbl;
310
2.81k
    return pkcrypt;
311
2.81k
}
312
313
2.81k
void mz_stream_pkcrypt_delete(void **stream) {
314
2.81k
    mz_stream_pkcrypt *pkcrypt = NULL;
315
2.81k
    if (!stream)
316
0
        return;
317
2.81k
    pkcrypt = (mz_stream_pkcrypt *)*stream;
318
2.81k
    free(pkcrypt);
319
2.81k
    *stream = NULL;
320
2.81k
}
321
322
0
void *mz_stream_pkcrypt_get_interface(void) {
323
0
    return (void *)&mz_stream_pkcrypt_vtbl;
324
0
}