Coverage Report

Created: 2023-03-26 06:14

/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
0
    (mz_stream_pkcrypt_update_keys(strm,                                    \
66
0
        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
0
static uint8_t mz_stream_pkcrypt_decrypt_byte(void *stream) {
75
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
76
77
0
    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
0
    temp = pkcrypt->keys[2] | 2;
82
0
    return (uint8_t)(((temp * (temp ^ 1)) >> 8) & 0xff);
83
0
}
84
85
0
static uint8_t mz_stream_pkcrypt_update_keys(void *stream, uint8_t c) {
86
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
87
0
    uint8_t buf = c;
88
89
0
    pkcrypt->keys[0] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[0], &buf, 1);
90
91
0
    pkcrypt->keys[1] += pkcrypt->keys[0] & 0xff;
92
0
    pkcrypt->keys[1] *= 134775813L;
93
0
    pkcrypt->keys[1] += 1;
94
95
0
    buf = (uint8_t)(pkcrypt->keys[1] >> 24);
96
0
    pkcrypt->keys[2] = (uint32_t)~mz_crypt_crc32_update(~pkcrypt->keys[2], &buf, 1);
97
98
0
    return (uint8_t)c;
99
0
}
100
101
0
static void mz_stream_pkcrypt_init_keys(void *stream, const char *password) {
102
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
103
104
0
    pkcrypt->keys[0] = 305419896L;
105
0
    pkcrypt->keys[1] = 591751049L;
106
0
    pkcrypt->keys[2] = 878082192L;
107
108
0
    while (*password != 0) {
109
0
        mz_stream_pkcrypt_update_keys(stream, (uint8_t)*password);
110
0
        password += 1;
111
0
    }
112
0
}
113
114
/***************************************************************************/
115
116
0
int32_t mz_stream_pkcrypt_open(void *stream, const char *path, int32_t mode) {
117
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
118
0
    uint16_t t = 0;
119
0
    int16_t i = 0;
120
0
    uint8_t verify1 = 0;
121
0
    uint8_t verify2 = 0;
122
0
    uint8_t header[MZ_PKCRYPT_HEADER_SIZE];
123
0
    const char *password = path;
124
125
0
    pkcrypt->total_in = 0;
126
0
    pkcrypt->total_out = 0;
127
0
    pkcrypt->initialized = 0;
128
129
0
    if (mz_stream_is_open(pkcrypt->stream.base) != MZ_OK)
130
0
        return MZ_OPEN_ERROR;
131
132
0
    if (!password)
133
0
        password = pkcrypt->password;
134
0
    if (!password)
135
0
        return MZ_PARAM_ERROR;
136
137
0
    mz_stream_pkcrypt_init_keys(stream, password);
138
139
0
    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
0
    } else if (mode & MZ_OPEN_MODE_READ) {
155
0
        if (mz_stream_read(pkcrypt->stream.base, header, sizeof(header)) != sizeof(header))
156
0
            return MZ_READ_ERROR;
157
158
0
        for (i = 0; i < MZ_PKCRYPT_HEADER_SIZE - 2; i++)
159
0
            header[i] = mz_stream_pkcrypt_decode(stream, header[i]);
160
161
0
        verify1 = mz_stream_pkcrypt_decode(stream, header[i++]);
162
0
        verify2 = mz_stream_pkcrypt_decode(stream, header[i++]);
163
164
        /* Older versions used 2 byte check, newer versions use 1 byte check. */
165
0
        MZ_UNUSED(verify1);
166
0
        if ((verify2 != 0) && (verify2 != pkcrypt->verify2))
167
0
            return MZ_PASSWORD_ERROR;
168
169
0
        pkcrypt->total_in += MZ_PKCRYPT_HEADER_SIZE;
170
0
    }
171
172
0
    pkcrypt->initialized = 1;
173
0
    return MZ_OK;
174
0
}
175
176
0
int32_t mz_stream_pkcrypt_is_open(void *stream) {
177
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
178
0
    if (!pkcrypt->initialized)
179
0
        return MZ_OPEN_ERROR;
180
0
    return MZ_OK;
181
0
}
182
183
0
int32_t mz_stream_pkcrypt_read(void *stream, void *buf, int32_t size) {
184
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
185
0
    uint8_t *buf_ptr = (uint8_t *)buf;
186
0
    int32_t bytes_to_read = size;
187
0
    int32_t read = 0;
188
0
    int32_t i = 0;
189
190
0
    if ((int64_t)bytes_to_read > (pkcrypt->max_total_in - pkcrypt->total_in))
191
0
        bytes_to_read = (int32_t)(pkcrypt->max_total_in - pkcrypt->total_in);
192
193
0
    read = mz_stream_read(pkcrypt->stream.base, buf, bytes_to_read);
194
195
0
    for (i = 0; i < read; i++)
196
0
        buf_ptr[i] = mz_stream_pkcrypt_decode(stream, buf_ptr[i]);
197
198
0
    if (read > 0)
199
0
        pkcrypt->total_in += read;
200
201
0
    return read;
202
0
}
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
0
void mz_stream_pkcrypt_set_password(void *stream, const char *password) {
258
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
259
0
    pkcrypt->password = password;
260
0
}
261
262
0
void mz_stream_pkcrypt_set_verify(void *stream, uint8_t verify1, uint8_t verify2) {
263
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
264
0
    pkcrypt->verify1 = verify1;
265
0
    pkcrypt->verify2 = verify2;
266
0
}
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
0
int32_t mz_stream_pkcrypt_get_prop_int64(void *stream, int32_t prop, int64_t *value) {
275
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
276
0
    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
0
    case MZ_STREAM_PROP_HEADER_SIZE:
287
0
        *value = MZ_PKCRYPT_HEADER_SIZE;
288
0
        break;
289
0
    case MZ_STREAM_PROP_FOOTER_SIZE:
290
0
        *value = 0;
291
0
        break;
292
0
    default:
293
0
        return MZ_EXIST_ERROR;
294
0
    }
295
0
    return MZ_OK;
296
0
}
297
298
0
int32_t mz_stream_pkcrypt_set_prop_int64(void *stream, int32_t prop, int64_t value) {
299
0
    mz_stream_pkcrypt *pkcrypt = (mz_stream_pkcrypt *)stream;
300
0
    switch (prop) {
301
0
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
302
0
        pkcrypt->max_total_in = value;
303
0
        break;
304
0
    default:
305
0
        return MZ_EXIST_ERROR;
306
0
    }
307
0
    return MZ_OK;
308
0
}
309
310
0
void *mz_stream_pkcrypt_create(void **stream) {
311
0
    mz_stream_pkcrypt *pkcrypt = NULL;
312
313
0
    pkcrypt = (mz_stream_pkcrypt *)calloc(1, sizeof(mz_stream_pkcrypt));
314
0
    if (pkcrypt)
315
0
        pkcrypt->stream.vtbl = &mz_stream_pkcrypt_vtbl;
316
0
    if (stream)
317
0
        *stream = pkcrypt;
318
319
0
    return pkcrypt;
320
0
}
321
322
0
void mz_stream_pkcrypt_delete(void **stream) {
323
0
    mz_stream_pkcrypt *pkcrypt = NULL;
324
0
    if (!stream)
325
0
        return;
326
0
    pkcrypt = (mz_stream_pkcrypt *)*stream;
327
0
    if (pkcrypt)
328
0
        free(pkcrypt);
329
0
    *stream = NULL;
330
0
}
331
332
0
void *mz_stream_pkcrypt_get_interface(void) {
333
0
    return (void *)&mz_stream_pkcrypt_vtbl;
334
0
}