Coverage Report

Created: 2024-09-06 06:34

/src/minizip-ng/mz_strm_wzaes.c
Line
Count
Source (jump to first uncovered line)
1
/* mz_strm_wzaes.c -- Stream for WinZip AES 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-2010 Brian Gladman, Worcester, UK
7
8
   This program is distributed under the terms of the same license as zlib.
9
   See the accompanying LICENSE file for the full text of the license.
10
*/
11
12
#include "mz.h"
13
#include "mz_crypt.h"
14
#include "mz_strm.h"
15
#include "mz_strm_wzaes.h"
16
17
/***************************************************************************/
18
19
13.0k
#define MZ_AES_KEY_LENGTH(STRENGTH)  (8 * (STRENGTH & 3) + 8)
20
13.0k
#define MZ_AES_KEYING_ITERATIONS     (1000)
21
26.1k
#define MZ_AES_SALT_LENGTH(STRENGTH) (4 * (STRENGTH & 3) + 4)
22
#define MZ_AES_SALT_LENGTH_MAX       (16)
23
13.1k
#define MZ_AES_PW_LENGTH_MAX         (128)
24
91.5k
#define MZ_AES_PW_VERIFY_SIZE        (2)
25
13.0k
#define MZ_AES_AUTHCODE_SIZE         (10)
26
27
/***************************************************************************/
28
29
static mz_stream_vtbl mz_stream_wzaes_vtbl = {
30
    mz_stream_wzaes_open,
31
    mz_stream_wzaes_is_open,
32
    mz_stream_wzaes_read,
33
    mz_stream_wzaes_write,
34
    mz_stream_wzaes_tell,
35
    mz_stream_wzaes_seek,
36
    mz_stream_wzaes_close,
37
    mz_stream_wzaes_error,
38
    mz_stream_wzaes_create,
39
    mz_stream_wzaes_delete,
40
    mz_stream_wzaes_get_prop_int64,
41
    mz_stream_wzaes_set_prop_int64
42
};
43
44
/***************************************************************************/
45
46
typedef struct mz_stream_wzaes_s {
47
    mz_stream       stream;
48
    int32_t         mode;
49
    int32_t         error;
50
    int16_t         initialized;
51
    uint8_t         buffer[UINT16_MAX];
52
    int64_t         total_in;
53
    int64_t         max_total_in;
54
    int64_t         total_out;
55
    uint8_t         strength;
56
    const char      *password;
57
    void            *aes;
58
    uint32_t        crypt_pos;
59
    uint8_t         crypt_block[MZ_AES_BLOCK_SIZE];
60
    void            *hmac;
61
    uint8_t         nonce[MZ_AES_BLOCK_SIZE];
62
} mz_stream_wzaes;
63
64
/***************************************************************************/
65
66
13.1k
int32_t mz_stream_wzaes_open(void *stream, const char *path, int32_t mode) {
67
13.1k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
68
13.1k
    uint16_t salt_length = 0;
69
13.1k
    uint16_t password_length = 0;
70
13.1k
    uint16_t key_length = 0;
71
13.1k
    uint8_t kbuf[2 * MZ_AES_KEY_LENGTH_MAX + MZ_AES_PW_VERIFY_SIZE];
72
13.1k
    uint8_t verify[MZ_AES_PW_VERIFY_SIZE];
73
13.1k
    uint8_t verify_expected[MZ_AES_PW_VERIFY_SIZE];
74
13.1k
    uint8_t salt_value[MZ_AES_SALT_LENGTH_MAX];
75
13.1k
    const char *password = path;
76
77
13.1k
    wzaes->total_in = 0;
78
13.1k
    wzaes->total_out = 0;
79
13.1k
    wzaes->initialized = 0;
80
81
13.1k
    if (mz_stream_is_open(wzaes->stream.base) != MZ_OK)
82
0
        return MZ_OPEN_ERROR;
83
84
13.1k
    if (!password)
85
13.1k
        password = wzaes->password;
86
13.1k
    if (!password)
87
0
        return MZ_PARAM_ERROR;
88
13.1k
    password_length = (uint16_t)strlen(password);
89
13.1k
    if (password_length > MZ_AES_PW_LENGTH_MAX)
90
0
        return MZ_PARAM_ERROR;
91
92
13.1k
    if (wzaes->strength < 1 || wzaes->strength > 3)
93
7
        return MZ_PARAM_ERROR;
94
95
13.0k
    key_length = MZ_AES_KEY_LENGTH(wzaes->strength);
96
13.0k
    salt_length = MZ_AES_SALT_LENGTH(wzaes->strength);
97
98
13.0k
    if (mode & MZ_OPEN_MODE_WRITE) {
99
0
        mz_crypt_rand(salt_value, salt_length);
100
13.0k
    } else if (mode & MZ_OPEN_MODE_READ) {
101
13.0k
        if (mz_stream_read(wzaes->stream.base, salt_value, salt_length) != salt_length)
102
5
            return MZ_READ_ERROR;
103
13.0k
    }
104
105
    /* Derive the encryption and authentication keys and the password verifier */
106
13.0k
    mz_crypt_pbkdf2((uint8_t *)password, password_length, salt_value, salt_length,
107
13.0k
        MZ_AES_KEYING_ITERATIONS, kbuf, 2 * key_length + MZ_AES_PW_VERIFY_SIZE);
108
109
    /* Initialize the buffer pos */
110
13.0k
    wzaes->crypt_pos = MZ_AES_BLOCK_SIZE;
111
112
    /* Use fixed zeroed IV/nonce for CTR mode */
113
13.0k
    memset(wzaes->nonce, 0, sizeof(wzaes->nonce));
114
115
    /* Initialize for encryption using key 1 */
116
13.0k
    mz_crypt_aes_reset(wzaes->aes);
117
13.0k
    mz_crypt_aes_set_encrypt_key(wzaes->aes, kbuf, key_length, NULL, 0);
118
119
    /* Initialize for authentication using key 2 */
120
13.0k
    mz_crypt_hmac_reset(wzaes->hmac);
121
13.0k
    mz_crypt_hmac_set_algorithm(wzaes->hmac, MZ_HASH_SHA1);
122
13.0k
    mz_crypt_hmac_init(wzaes->hmac, kbuf + key_length, key_length);
123
124
13.0k
    memcpy(verify, kbuf + (2 * key_length), MZ_AES_PW_VERIFY_SIZE);
125
126
13.0k
    if (mode & MZ_OPEN_MODE_WRITE) {
127
0
        if (mz_stream_write(wzaes->stream.base, salt_value, salt_length) != salt_length)
128
0
            return MZ_WRITE_ERROR;
129
130
0
        wzaes->total_out += salt_length;
131
132
0
        if (mz_stream_write(wzaes->stream.base, verify, MZ_AES_PW_VERIFY_SIZE) != MZ_AES_PW_VERIFY_SIZE)
133
0
            return MZ_WRITE_ERROR;
134
135
0
        wzaes->total_out += MZ_AES_PW_VERIFY_SIZE;
136
13.0k
    } else if (mode & MZ_OPEN_MODE_READ) {
137
13.0k
        wzaes->total_in += salt_length;
138
139
13.0k
        if (mz_stream_read(wzaes->stream.base, verify_expected, MZ_AES_PW_VERIFY_SIZE) != MZ_AES_PW_VERIFY_SIZE)
140
2
            return MZ_READ_ERROR;
141
142
13.0k
        wzaes->total_in += MZ_AES_PW_VERIFY_SIZE;
143
144
13.0k
        if (memcmp(verify_expected, verify, MZ_AES_PW_VERIFY_SIZE) != 0)
145
51
            return MZ_PASSWORD_ERROR;
146
13.0k
    }
147
148
13.0k
    wzaes->mode = mode;
149
13.0k
    wzaes->initialized = 1;
150
151
13.0k
    return MZ_OK;
152
13.0k
}
153
154
38.3k
int32_t mz_stream_wzaes_is_open(void *stream) {
155
38.3k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
156
38.3k
    if (!wzaes->initialized)
157
0
        return MZ_OPEN_ERROR;
158
38.3k
    return MZ_OK;
159
38.3k
}
160
161
7.03k
static int32_t mz_stream_wzaes_ctr_encrypt(void *stream, uint8_t *buf, int32_t size) {
162
7.03k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
163
7.03k
    uint32_t pos = wzaes->crypt_pos;
164
7.03k
    uint32_t i = 0;
165
7.03k
    int32_t err = MZ_OK;
166
167
6.97M
    while (i < (uint32_t)size) {
168
6.96M
        if (pos == MZ_AES_BLOCK_SIZE) {
169
435k
            uint32_t j = 0;
170
171
            /* Increment encryption nonce */
172
435k
            while (j < 8 && !++wzaes->nonce[j])
173
0
                j += 1;
174
175
            /* Encrypt the nonce using ECB mode to form next xor buffer */
176
435k
            memcpy(wzaes->crypt_block, wzaes->nonce, MZ_AES_BLOCK_SIZE);
177
435k
            mz_crypt_aes_encrypt(wzaes->aes, NULL, 0, wzaes->crypt_block, sizeof(wzaes->crypt_block));
178
435k
            pos = 0;
179
435k
        }
180
181
6.96M
        buf[i++] ^= wzaes->crypt_block[pos++];
182
6.96M
    }
183
184
7.03k
    wzaes->crypt_pos = pos;
185
7.03k
    return err;
186
7.03k
}
187
188
12.6k
int32_t mz_stream_wzaes_read(void *stream, void *buf, int32_t size) {
189
12.6k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
190
12.6k
    int64_t max_total_in = 0;
191
12.6k
    int32_t bytes_to_read = size;
192
12.6k
    int32_t read = 0;
193
194
12.6k
    max_total_in = wzaes->max_total_in - MZ_AES_FOOTER_SIZE;
195
12.6k
    if ((int64_t)bytes_to_read > (max_total_in - wzaes->total_in))
196
5.61k
        bytes_to_read = (int32_t)(max_total_in - wzaes->total_in);
197
198
12.6k
    read = mz_stream_read(wzaes->stream.base, buf, bytes_to_read);
199
200
12.6k
    if (read > 0) {
201
7.03k
        mz_crypt_hmac_update(wzaes->hmac, (uint8_t *)buf, read);
202
7.03k
        mz_stream_wzaes_ctr_encrypt(stream, (uint8_t *)buf, read);
203
204
7.03k
        wzaes->total_in += read;
205
7.03k
    }
206
207
12.6k
    return read;
208
12.6k
}
209
210
0
int32_t mz_stream_wzaes_write(void *stream, const void *buf, int32_t size) {
211
0
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
212
0
    const uint8_t *buf_ptr = (const uint8_t *)buf;
213
0
    int32_t bytes_to_write = sizeof(wzaes->buffer);
214
0
    int32_t total_written = 0;
215
0
    int32_t written = 0;
216
217
0
    if (size < 0)
218
0
        return MZ_PARAM_ERROR;
219
220
0
    do {
221
0
        if (bytes_to_write > (size - total_written))
222
0
            bytes_to_write = (size - total_written);
223
224
0
        memcpy(wzaes->buffer, buf_ptr, bytes_to_write);
225
0
        buf_ptr += bytes_to_write;
226
227
0
        mz_stream_wzaes_ctr_encrypt(stream, (uint8_t *)wzaes->buffer, bytes_to_write);
228
0
        mz_crypt_hmac_update(wzaes->hmac, wzaes->buffer, bytes_to_write);
229
230
0
        written = mz_stream_write(wzaes->stream.base, wzaes->buffer, bytes_to_write);
231
0
        if (written < 0)
232
0
            return written;
233
234
0
        total_written += written;
235
0
    } while (total_written < size && written > 0);
236
237
0
    wzaes->total_out += total_written;
238
0
    return total_written;
239
0
}
240
241
0
int64_t mz_stream_wzaes_tell(void *stream) {
242
0
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
243
0
    return mz_stream_tell(wzaes->stream.base);
244
0
}
245
246
0
int32_t mz_stream_wzaes_seek(void *stream, int64_t offset, int32_t origin) {
247
0
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
248
0
    return mz_stream_seek(wzaes->stream.base, offset, origin);
249
0
}
250
251
0
int32_t mz_stream_wzaes_close(void *stream) {
252
0
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
253
0
    uint8_t expected_hash[MZ_AES_AUTHCODE_SIZE];
254
0
    uint8_t computed_hash[MZ_HASH_SHA1_SIZE];
255
256
0
    mz_crypt_hmac_end(wzaes->hmac, computed_hash, sizeof(computed_hash));
257
258
0
    if (wzaes->mode & MZ_OPEN_MODE_WRITE) {
259
0
        if (mz_stream_write(wzaes->stream.base, computed_hash, MZ_AES_AUTHCODE_SIZE) != MZ_AES_AUTHCODE_SIZE)
260
0
            return MZ_WRITE_ERROR;
261
262
0
        wzaes->total_out += MZ_AES_AUTHCODE_SIZE;
263
0
    } else if (wzaes->mode & MZ_OPEN_MODE_READ) {
264
0
        if (mz_stream_read(wzaes->stream.base, expected_hash, MZ_AES_AUTHCODE_SIZE) != MZ_AES_AUTHCODE_SIZE)
265
0
            return MZ_READ_ERROR;
266
267
0
        wzaes->total_in += MZ_AES_AUTHCODE_SIZE;
268
269
        /* If entire entry was not read this will fail */
270
0
        if (memcmp(computed_hash, expected_hash, MZ_AES_AUTHCODE_SIZE) != 0)
271
0
            return MZ_CRC_ERROR;
272
0
    }
273
274
0
    wzaes->initialized = 0;
275
0
    return MZ_OK;
276
0
}
277
278
0
int32_t mz_stream_wzaes_error(void *stream) {
279
0
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
280
0
    return wzaes->error;
281
0
}
282
283
13.1k
void mz_stream_wzaes_set_password(void *stream, const char *password) {
284
13.1k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
285
13.1k
    wzaes->password = password;
286
13.1k
}
287
288
13.1k
void mz_stream_wzaes_set_strength(void *stream, uint8_t strength) {
289
13.1k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
290
13.1k
    wzaes->strength = strength;
291
13.1k
}
292
293
26.0k
int32_t mz_stream_wzaes_get_prop_int64(void *stream, int32_t prop, int64_t *value) {
294
26.0k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
295
26.0k
    switch (prop) {
296
0
    case MZ_STREAM_PROP_TOTAL_IN:
297
0
        *value = wzaes->total_in;
298
0
        break;
299
0
    case MZ_STREAM_PROP_TOTAL_OUT:
300
0
        *value = wzaes->total_out;
301
0
        break;
302
0
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
303
0
        *value = wzaes->max_total_in;
304
0
        break;
305
13.0k
    case MZ_STREAM_PROP_HEADER_SIZE:
306
13.0k
        *value = MZ_AES_SALT_LENGTH((int64_t)wzaes->strength) + MZ_AES_PW_VERIFY_SIZE;
307
13.0k
        break;
308
13.0k
    case MZ_STREAM_PROP_FOOTER_SIZE:
309
13.0k
        *value = MZ_AES_AUTHCODE_SIZE;
310
13.0k
        break;
311
0
    default:
312
0
        return MZ_EXIST_ERROR;
313
26.0k
    }
314
26.0k
    return MZ_OK;
315
26.0k
}
316
317
13.0k
int32_t mz_stream_wzaes_set_prop_int64(void *stream, int32_t prop, int64_t value) {
318
13.0k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)stream;
319
13.0k
    switch (prop) {
320
13.0k
    case MZ_STREAM_PROP_TOTAL_IN_MAX:
321
13.0k
        wzaes->max_total_in = value;
322
13.0k
        break;
323
0
    default:
324
0
        return MZ_EXIST_ERROR;
325
13.0k
    }
326
13.0k
    return MZ_OK;
327
13.0k
}
328
329
13.1k
void *mz_stream_wzaes_create(void) {
330
13.1k
    mz_stream_wzaes *wzaes = (mz_stream_wzaes *)calloc(1, sizeof(mz_stream_wzaes));
331
13.1k
    if (wzaes) {
332
13.1k
        wzaes->stream.vtbl = &mz_stream_wzaes_vtbl;
333
13.1k
        wzaes->strength = MZ_AES_STRENGTH_256;
334
335
13.1k
        wzaes->hmac = mz_crypt_hmac_create();
336
13.1k
        if (!wzaes->hmac) {
337
0
            free(wzaes);
338
0
            return NULL;
339
0
        }
340
13.1k
        wzaes->aes = mz_crypt_aes_create();
341
13.1k
        if (!wzaes->aes) {
342
0
            mz_crypt_hmac_delete(&wzaes->hmac);
343
0
            free(wzaes);
344
0
            return NULL;
345
0
        }
346
13.1k
    }
347
13.1k
    return wzaes;
348
13.1k
}
349
350
13.1k
void mz_stream_wzaes_delete(void **stream) {
351
13.1k
    mz_stream_wzaes *wzaes = NULL;
352
13.1k
    if (!stream)
353
0
        return;
354
13.1k
    wzaes = (mz_stream_wzaes *)*stream;
355
13.1k
    if (wzaes) {
356
13.1k
        mz_crypt_aes_delete(&wzaes->aes);
357
13.1k
        mz_crypt_hmac_delete(&wzaes->hmac);
358
13.1k
        free(wzaes);
359
13.1k
    }
360
13.1k
    *stream = NULL;
361
13.1k
}
362
363
0
void *mz_stream_wzaes_get_interface(void) {
364
0
    return (void *)&mz_stream_wzaes_vtbl;
365
0
}