Coverage Report

Created: 2026-02-14 07:09

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