Coverage Report

Created: 2026-02-26 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/minizip-ng/mz_strm_pkcrypt.c
Line
Count
Source
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
6.67M
    (mz_stream_pkcrypt_update_keys(strm, c ^= mz_stream_pkcrypt_decrypt_byte(strm)))
58
59
#define mz_stream_pkcrypt_encode(strm, c, t)                                                                           \
60
0
    (t = mz_stream_pkcrypt_decrypt_byte(strm), mz_stream_pkcrypt_update_keys(strm, (uint8_t)c), (uint8_t)(t ^ (c)))
61
62
/***************************************************************************/
63
64
6.66M
static uint8_t mz_stream_pkcrypt_decrypt_byte(void *stream) {
65
6.66M
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
66
67
6.66M
    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
6.66M
    temp = pkcrypt->keys[2] | 2;
72
6.66M
    return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff);
73
6.66M
}
74
75
6.73M
static uint8_t mz_stream_pkcrypt_update_keys(void *stream, uint8_t c) {
76
6.73M
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
77
6.73M
    uint8_t buf = c;
78
79
6.73M
    pkcrypt->keys[0] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[0], &buf, 1);
80
81
6.73M
    pkcrypt->keys[1] += pkcrypt->keys[0] & 0xff;
82
6.73M
    pkcrypt->keys[1] *= 134775813L;
83
6.73M
    pkcrypt->keys[1] += 1;
84
85
6.73M
    buf = (uint8_t)(pkcrypt->keys[1] >> 24);
86
6.73M
    pkcrypt->keys[2] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[2], &buf, 1);
87
88
6.73M
    return (uint8_t)c;
89
6.73M
}
90
91
9.12k
static void mz_stream_pkcrypt_init_keys(void *stream, const char *password) {
92
9.12k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
93
94
9.12k
    pkcrypt->keys[0] = 305419896L;
95
9.12k
    pkcrypt->keys[1] = 591751049L;
96
9.12k
    pkcrypt->keys[2] = 878082192L;
97
98
73.0k
    while (*password != 0) {
99
63.8k
        mz_stream_pkcrypt_update_keys(stream, (uint8_t)*password);
100
63.8k
        password += 1;
101
63.8k
    }
102
9.12k
}
103
104
/***************************************************************************/
105
106
9.12k
int32_t mz_stream_pkcrypt_open(void *stream, const char *path, int32_t mode) {
107
9.12k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
108
9.12k
    uint16_t t = 0;
109
9.12k
    int16_t i = 0;
110
9.12k
    uint8_t verify1 = 0;
111
9.12k
    uint8_t verify2 = 0;
112
9.12k
    uint8_t header[MZ_PKCRYPT_HEADER_SIZE];
113
9.12k
    const char *password = path;
114
115
9.12k
    pkcrypt->total_in = 0;
116
9.12k
    pkcrypt->total_out = 0;
117
9.12k
    pkcrypt->initialized = 0;
118
119
9.12k
    if (mz_stream_is_open(pkcrypt->stream.base) != MZ_OK)
120
0
        return MZ_OPEN_ERROR;
121
122
9.12k
    if (!password)
123
9.12k
        password = pkcrypt->password;
124
9.12k
    if (!password)
125
0
        return MZ_PARAM_ERROR;
126
127
9.12k
    mz_stream_pkcrypt_init_keys(stream, password);
128
129
9.12k
    if (mode & MZ_OPEN_MODE_WRITE) {
130
        /* First generate RAND_HEAD_LEN - 2 random bytes. */
131
0
        mz_crypt_rand(header, MZ_PKCRYPT_HEADER_SIZE - 2);
132
133
        /* Encrypt random header (last two bytes is high word of crc) */
134
0
        for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++)
135
0
            header[i] = mz_stream_pkcrypt_encode(stream, header[i], t);
136
137
0
        header[i++] = mz_stream_pkcrypt_encode(stream, pkcrypt->verify1, t);
138
0
        header[i++] = mz_stream_pkcrypt_encode(stream, pkcrypt->verify2, t);
139
140
0
        if (mz_stream_write(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header))
141
0
            return MZ_WRITE_ERROR;
142
143
0
        pkcrypt->total_out += MZ_PKCRYPT_HEADER_SIZE;
144
9.12k
    } else if (mode & MZ_OPEN_MODE_READ) {
145
9.12k
        if (mz_stream_read(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header))
146
36
            return MZ_READ_ERROR;
147
148
99.9k
        for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++)
149
90.8k
            header[i] = mz_stream_pkcrypt_decode(stream, header[i]);
150
151
9.08k
        verify1 = mz_stream_pkcrypt_decode(stream, header[i++]);
152
9.08k
        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
9.08k
        if (verify2 != pkcrypt->verify2)
157
77
            return MZ_PASSWORD_ERROR;
158
9.01k
        if (pkcrypt->verify_version < 2) {
159
376
            if (verify1 != pkcrypt->verify1)
160
14
                return MZ_PASSWORD_ERROR;
161
376
        }
162
163
8.99k
        pkcrypt->total_in += MZ_PKCRYPT_HEADER_SIZE;
164
8.99k
    }
165
166
8.99k
    pkcrypt->initialized = 1;
167
8.99k
    return MZ_OK;
168
9.12k
}
169
170
2.07M
int32_t mz_stream_pkcrypt_is_open(void *stream) {
171
2.07M
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
172
2.07M
    if (!pkcrypt->initialized)
173
0
        return MZ_OPEN_ERROR;
174
2.07M
    return MZ_OK;
175
2.07M
}
176
177
2.06M
int32_t mz_stream_pkcrypt_read(void *stream, void *buf, int32_t size) {
178
2.06M
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
179
2.06M
    uint8_t *buf_ptr = (uint8_t *)buf;
180
2.06M
    int32_t bytes_to_read = size;
181
2.06M
    int32_t read = 0;
182
2.06M
    int32_t i = 0;
183
184
2.06M
    if ((int64_t)bytes_to_read > (pkcrypt->max_total_in - pkcrypt->total_in))
185
428
        bytes_to_read = (int32_t)(pkcrypt->max_total_in - pkcrypt->total_in);
186
187
2.06M
    read = mz_stream_read(pkcrypt->stream.base, buf, bytes_to_read);
188
189
8.62M
    for (i = 0; i < read; i++)
190
6.55M
        buf_ptr[i] = mz_stream_pkcrypt_decode(stream, buf_ptr[i]);
191
192
2.06M
    if (read > 0)
193
2.06M
        pkcrypt->total_in += read;
194
195
2.06M
    return read;
196
2.06M
}
197
198
0
int32_t mz_stream_pkcrypt_write(void *stream, const void *buf, int32_t size) {
199
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
200
0
    const uint8_t *buf_ptr = (const uint8_t *)buf;
201
0
    int32_t bytes_to_write = sizeof(pkcrypt->buffer);
202
0
    int32_t total_written = 0;
203
0
    int32_t written = 0;
204
0
    int32_t i = 0;
205
0
    uint16_t t = 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
        for (i = 0; i < bytes_to_write; i += 1) {
215
0
            pkcrypt->buffer[i] = mz_stream_pkcrypt_encode(stream, *buf_ptr, t);
216
0
            buf_ptr += 1;
217
0
        }
218
219
0
        written = mz_stream_write(pkcrypt->stream.base, pkcrypt->buffer, bytes_to_write);
220
0
        if (written < 0)
221
0
            return written;
222
223
0
        total_written += written;
224
0
    } while (total_written < size && written > 0);
225
226
0
    pkcrypt->total_out += total_written;
227
0
    return total_written;
228
0
}
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
0
int32_t mz_stream_pkcrypt_close(void *stream) {
241
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
242
0
    pkcrypt->initialized = 0;
243
0
    return MZ_OK;
244
0
}
245
246
372
int32_t mz_stream_pkcrypt_error(void *stream) {
247
372
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
248
372
    return pkcrypt->error;
249
372
}
250
251
9.12k
void mz_stream_pkcrypt_set_password(void *stream, const char *password) {
252
9.12k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
253
9.12k
    pkcrypt->password = password;
254
9.12k
}
255
256
9.12k
void mz_stream_pkcrypt_set_verify(void *stream, uint8_t verify1, uint8_t verify2, uint16_t version) {
257
9.12k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
258
9.12k
    pkcrypt->verify1 = verify1;
259
9.12k
    pkcrypt->verify2 = verify2;
260
9.12k
    pkcrypt->verify_version = version;
261
9.12k
}
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
17.9k
int32_t mz_stream_pkcrypt_get_prop_int64(void *stream, int32_t prop, int64_t *value) {
271
17.9k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
272
17.9k
    switch (prop) {
273
0
    case MZ_STREAM_PROP_TOTAL_IN:
274
0
        *value = pkcrypt->total_in;
275
0
        break;
276
0
    case MZ_STREAM_PROP_TOTAL_OUT:
277
0
        *value = pkcrypt->total_out;
278
0
        break;
279
0
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
280
0
        *value = pkcrypt->max_total_in;
281
0
        break;
282
8.99k
    case MZ_STREAM_PROP_HEADER_SIZE:
283
8.99k
        *value = MZ_PKCRYPT_HEADER_SIZE;
284
8.99k
        break;
285
8.99k
    case MZ_STREAM_PROP_FOOTER_SIZE:
286
8.99k
        *value = 0;
287
8.99k
        break;
288
0
    default:
289
0
        return MZ_EXIST_ERROR;
290
17.9k
    }
291
17.9k
    return MZ_OK;
292
17.9k
}
293
294
8.99k
int32_t mz_stream_pkcrypt_set_prop_int64(void *stream, int32_t prop, int64_t value) {
295
8.99k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
296
8.99k
    switch (prop) {
297
8.99k
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
298
8.99k
        pkcrypt->max_total_in = value;
299
8.99k
        break;
300
0
    default:
301
0
        return MZ_EXIST_ERROR;
302
8.99k
    }
303
8.99k
    return MZ_OK;
304
8.99k
}
305
306
9.12k
void *mz_stream_pkcrypt_create(void) {
307
9.12k
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)calloc(1, sizeof(mz_stream_pkcrypt));
308
9.12k
    if (pkcrypt)
309
9.12k
        pkcrypt->stream.vtbl = &mz_stream_pkcrypt_vtbl;
310
9.12k
    return pkcrypt;
311
9.12k
}
312
313
9.12k
void mz_stream_pkcrypt_delete(void **stream) {
314
9.12k
    mz_stream_pkcrypt *pkcrypt = NULL;
315
9.12k
    if (!stream)
316
0
        return;
317
9.12k
    pkcrypt = (mz_stream_pkcrypt *)*stream;
318
9.12k
    free(pkcrypt);
319
9.12k
    *stream = NULL;
320
9.12k
}
321
322
0
void *mz_stream_pkcrypt_get_interface(void) {
323
0
    return (void *)&mz_stream_pkcrypt_vtbl;
324
0
}