blob: fc0b208993957e9d848616c13f5e5b4e2ab6cbb6 [file] [log] [blame]
Thomas Klausner796c5962016-12-02 15:01:14 +01001/*
2 zip_source_winzip_aes.c -- Winzip AES de/encryption routines
Thomas Klausnerdf77dc62016-12-31 13:40:32 +01003 Copyright (C) 2009-2016 Dieter Baron and Thomas Klausner
Thomas Klausner796c5962016-12-02 15:01:14 +01004
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
7
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
10 are met:
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in
15 the documentation and/or other materials provided with the
16 distribution.
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
19 written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32*/
33
34
35#include <stdlib.h>
36#include <string.h>
37
38#include "zipint.h"
39
40#include "gladman-fcrypt.h"
41
42#define MAX_HEADER_LENGTH (16+PWD_VER_LENGTH)
43#define HMAC_LENGTH 10
44
Thomas Klausner7b867f52016-12-24 09:43:48 +010045static unsigned int salt_length[] = { 0, 8, 12, 16 };
Thomas Klausner796c5962016-12-02 15:01:14 +010046
47struct winzip_aes {
48 char *password;
Thomas Klausnerf7032622017-01-30 17:50:41 +010049 unsigned int mode;
Thomas Klausner796c5962016-12-02 15:01:14 +010050
51 zip_uint64_t data_length;
52 zip_uint64_t current_position;
53
54 fcrypt_ctx fcrypt_ctx;
55 zip_error_t error;
56};
57
58
59static int decrypt_header(zip_source_t *src, struct winzip_aes *ctx);
60static void winzip_aes_free(struct winzip_aes *);
61static zip_int64_t winzip_aes_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
Thomas Klausnerf7032622017-01-30 17:50:41 +010062static struct winzip_aes * winzip_aes_new(unsigned int mode, const char *password);
Thomas Klausner796c5962016-12-02 15:01:14 +010063
64
65zip_source_t *
Thomas Klausner7ed6a922016-12-16 15:50:12 +010066zip_source_winzip_aes_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password)
Thomas Klausner796c5962016-12-02 15:01:14 +010067{
68 zip_source_t *s2;
Thomas Klausnerf7032622017-01-30 17:50:41 +010069 unsigned int mode = 0;
Thomas Klausner796c5962016-12-02 15:01:14 +010070 zip_stat_t st;
71 zip_uint64_t aux_length;
72 struct winzip_aes *ctx;
73
74 switch (em) {
75 case ZIP_EM_AES_128:
76 mode = 1;
77 break;
78 case ZIP_EM_AES_192:
79 mode = 2;
80 break;
81 case ZIP_EM_AES_256:
82 mode = 3;
83 break;
84 }
85
86 if (password == NULL || src == NULL || mode == 0) {
87 zip_error_set(&za->error, ZIP_ER_INVAL, 0);
88 return NULL;
89 }
90 if (flags & ZIP_CODEC_ENCODE) {
91 zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
92 return NULL;
93 }
94
Dieter Baron04337f32017-01-29 10:32:37 +010095 if (strlen(password) > UINT_MAX) {
96 zip_error_set(&za->error, ZIP_ER_INVAL, 0); /* TODO: better error code? (password too long) */
97 return NULL;
98 }
99
Thomas Klausner796c5962016-12-02 15:01:14 +0100100 if (zip_source_stat(src, &st) != 0) {
101 _zip_error_set_from_source(&za->error, src);
102 return NULL;
103 }
104
105 aux_length = PWD_VER_LENGTH + salt_length[mode] + HMAC_LENGTH;
106
107 if ((st.valid & ZIP_STAT_COMP_SIZE) == 0 || st.comp_size < aux_length) {
108 zip_error_set(&za->error, ZIP_ER_OPNOTSUPP, 0);
109 return NULL;
110 }
111
112 if ((ctx = winzip_aes_new(mode, password)) == NULL) {
113 zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
114 return NULL;
115 }
116
117 ctx->data_length = st.comp_size - aux_length;
118
119 if ((s2 = zip_source_layered(za, src, winzip_aes_decrypt, ctx)) == NULL) {
120 winzip_aes_free(ctx);
121 return NULL;
122 }
123
124 return s2;
125}
126
127
128static int
129decrypt_header(zip_source_t *src, struct winzip_aes *ctx)
130{
131 zip_uint8_t header[MAX_HEADER_LENGTH];
132 zip_uint8_t password_verification[PWD_VER_LENGTH];
Dieter Baron04337f32017-01-29 10:32:37 +0100133 unsigned int headerlen;
Thomas Klausner796c5962016-12-02 15:01:14 +0100134 zip_int64_t n;
Thomas Klausner796c5962016-12-02 15:01:14 +0100135
136 headerlen = PWD_VER_LENGTH + salt_length[ctx->mode];
137 if ((n=zip_source_read(src, header, headerlen)) < 0) {
138 _zip_error_set_from_source(&ctx->error, src);
139 return -1;
140 }
141
142 if (n != headerlen) {
143 zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
144 return -1;
145 }
146
Dieter Baron04337f32017-01-29 10:32:37 +0100147 if (_zip_fcrypt_init(ctx->mode, (unsigned char *)ctx->password, (unsigned int)strlen(ctx->password), header, password_verification, &ctx->fcrypt_ctx) != 0) {
Thomas Klausner796c5962016-12-02 15:01:14 +0100148 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
149 return -1;
150 }
151 if (memcmp(password_verification, header + salt_length[ctx->mode], PWD_VER_LENGTH) != 0) {
152 zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0);
153 return -1;
154 }
155 return 0;
156}
157
158
159static bool
160verify_hmac(zip_source_t *src, struct winzip_aes *ctx)
161{
162 unsigned char computed[HMAC_LENGTH], from_file[HMAC_LENGTH];
163 if (zip_source_read(src, from_file, HMAC_LENGTH) < HMAC_LENGTH) {
164 _zip_error_set_from_source(&ctx->error, src);
165 return false;
166 }
167
168 _zip_fcrypt_end(computed, &ctx->fcrypt_ctx);
169
170 if (memcmp(from_file, computed, HMAC_LENGTH) != 0) {
171 zip_error_set(&ctx->error, ZIP_ER_CRC, 0);
172 return false;
173 }
174
175 return true;
176}
177
178
179static zip_int64_t
180winzip_aes_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
181{
182 struct winzip_aes *ctx;
183 zip_int64_t n;
Thomas Klausnerc71b1812016-12-17 12:51:14 +0100184 zip_uint64_t total, offset;
Thomas Klausner796c5962016-12-02 15:01:14 +0100185
186 ctx = (struct winzip_aes *)ud;
187
188 switch (cmd) {
189 case ZIP_SOURCE_OPEN:
190 if (decrypt_header(src, ctx) < 0) {
191 return -1;
192 }
193 ctx->current_position = 0;
194 return 0;
195
196 case ZIP_SOURCE_READ:
197 if (len > ctx->data_length - ctx->current_position) {
198 len = ctx->data_length - ctx->current_position;
199 }
200
201 if (len == 0) {
202 if (!verify_hmac(src, ctx)) {
203 return -1;
204 }
205 return 0;
206 }
207
208 if ((n=zip_source_read(src, data, len)) < 0) {
209 _zip_error_set_from_source(&ctx->error, src);
210 return -1;
211 }
Thomas Klausner7b867f52016-12-24 09:43:48 +0100212 ctx->current_position += (zip_uint64_t)n;
Thomas Klausner796c5962016-12-02 15:01:14 +0100213
Thomas Klausnerc71b1812016-12-17 12:51:14 +0100214 total = (zip_uint64_t)n;
215 for (offset = 0; offset < total; offset += ZIP_MIN(total - offset, UINT_MAX)) {
Dieter Baron04337f32017-01-29 10:32:37 +0100216 _zip_fcrypt_decrypt((zip_uint8_t *)data + offset, (unsigned int)ZIP_MIN(total - offset, UINT_MAX), &ctx->fcrypt_ctx);
Thomas Klausnerc71b1812016-12-17 12:51:14 +0100217 }
Thomas Klausner796c5962016-12-02 15:01:14 +0100218
219 return n;
220
221 case ZIP_SOURCE_CLOSE:
222 return 0;
223
224 case ZIP_SOURCE_STAT:
225 {
226 zip_stat_t *st;
227
228 st = (zip_stat_t *)data;
229
230 st->encryption_method = ZIP_EM_NONE;
231 st->valid |= ZIP_STAT_ENCRYPTION_METHOD;
232 if (st->valid & ZIP_STAT_COMP_SIZE) {
233 st->comp_size -= 12 + salt_length[ctx->mode];
234 }
235
236 return 0;
237 }
238
239 case ZIP_SOURCE_SUPPORTS:
240 return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
241
242 case ZIP_SOURCE_ERROR:
243 return zip_error_to_data(&ctx->error, data, len);
244
245 case ZIP_SOURCE_FREE:
246 winzip_aes_free(ctx);
247 return 0;
248
249 default:
250 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
251 return -1;
252 }
253}
254
255
256static void
257winzip_aes_free(struct winzip_aes *ctx)
258{
259 if (ctx == NULL) {
260 return;
261 }
262
263 _zip_crypto_clear(&ctx->fcrypt_ctx, sizeof(ctx->fcrypt_ctx));
264 _zip_crypto_clear(ctx->password, strlen(ctx->password));
265 free(ctx->password);
266 zip_error_fini(&ctx->error);
267 free(ctx);
268}
269
270
271static struct winzip_aes *
Thomas Klausnerf7032622017-01-30 17:50:41 +0100272winzip_aes_new(unsigned int mode, const char *password) {
Thomas Klausner796c5962016-12-02 15:01:14 +0100273 struct winzip_aes *ctx;
274
275 if ((ctx = (struct winzip_aes *)malloc(sizeof(*ctx))) == NULL) {
276 return NULL;
277 }
278
279 if ((ctx->password = strdup(password)) == NULL) {
280 free(ctx);
281 return NULL;
282 }
283
284 ctx->mode = mode;
285
286 zip_error_init(&ctx->error);
287
288 return ctx;
289}