Merge branch 'tradpkenc' into origin/master
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 83fd054..16394ad 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -110,6 +110,7 @@
zip_name_locate.c
zip_new.c
zip_open.c
+ zip_pkware.c
zip_progress.c
zip_rename.c
zip_replace.c
@@ -136,7 +137,8 @@
zip_source_is_deleted.c
zip_source_layered.c
zip_source_open.c
- zip_source_pkware.c
+ zip_source_pkware_encode.c
+ zip_source_pkware_decode.c
zip_source_read.c
zip_source_remove.c
zip_source_rollback_write.c
diff --git a/lib/zip_close.c b/lib/zip_close.c
index 2a9fed2..8c63324 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -55,6 +55,7 @@
static int copy_data(zip_t *, zip_uint64_t);
static int copy_source(zip_t *, zip_source_t *, zip_int64_t);
static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t);
+static int write_data_descriptor(zip_t *za, const zip_dirent_t *dirent, int is_zip64);
ZIP_EXTERN int
zip_close(zip_t *za) {
@@ -233,8 +234,11 @@
else {
zip_uint64_t offset;
- /* when copying data, all sizes are known -> no data descriptor needed */
- de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
+ if (de->encryption_method != ZIP_EM_TRAD_PKWARE) {
+ /* when copying data, all sizes are known -> no data descriptor needed */
+ /* except for PKWare encryption, where removing the data descriptor breaks password validation */
+ de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
+ }
if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) {
error = 1;
break;
@@ -252,7 +256,15 @@
error = 1;
break;
}
+
+ if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
+ if (write_data_descriptor(za, de, _zip_dirent_needs_zip64(de, 0)) < 0) {
+ error = 1;
+ break;
+ }
+ }
}
+
}
if (!error) {
@@ -453,6 +465,9 @@
zip_source_free(src_final);
return -1;
}
+ if (de->encryption_method == ZIP_EM_TRAD_PKWARE) {
+ de->bitflags |= ZIP_GPBF_DATA_DESCRIPTOR;
+ }
zip_source_free(src_final);
src_final = src_tmp;
@@ -524,6 +539,12 @@
return -1;
}
+ if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
+ if (write_data_descriptor(za, de, is_zip64) < 0) {
+ return -1;
+ }
+ }
+
return 0;
}
@@ -656,3 +677,37 @@
return changed;
}
+
+static int
+write_data_descriptor(zip_t *za, const zip_dirent_t *de, int is_zip64) {
+ zip_buffer_t *buffer = _zip_buffer_new(NULL, MAX_DATA_DESCRIPTOR_LENGTH);
+ int ret = 0;
+
+ if (buffer == NULL) {
+ zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+ return -1;
+ }
+
+ _zip_buffer_put(buffer, DATADES_MAGIC, 4);
+ _zip_buffer_put_32(buffer, de->crc);
+ if (is_zip64) {
+ _zip_buffer_put_64(buffer, de->comp_size);
+ _zip_buffer_put_64(buffer, de->uncomp_size);
+ }
+ else {
+ _zip_buffer_put_32(buffer, de->comp_size);
+ _zip_buffer_put_32(buffer, de->uncomp_size);
+ }
+
+ if (!_zip_buffer_ok(buffer)) {
+ zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+ ret = -1;
+ }
+ else {
+ ret = _zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer));
+ }
+
+ _zip_buffer_free(buffer);
+
+ return ret;
+}
diff --git a/lib/zip_crypto.h b/lib/zip_crypto.h
index f46c7d8..0706a02 100644
--- a/lib/zip_crypto.h
+++ b/lib/zip_crypto.h
@@ -37,6 +37,8 @@
#define ZIP_CRYPTO_SHA1_LENGTH 20
#define ZIP_CRYPTO_AES_BLOCK_LENGTH 16
+#define ZIP_CRYPTO_PKWARE_HEADERLEN 12
+
#if defined(HAVE_WINDOWS_CRYPTO)
#include "zip_crypto_win.h"
#elif defined(HAVE_COMMONCRYPTO)
diff --git a/lib/zip_get_encryption_implementation.c b/lib/zip_get_encryption_implementation.c
index c9d52ad..5ec8db5 100644
--- a/lib/zip_get_encryption_implementation.c
+++ b/lib/zip_get_encryption_implementation.c
@@ -39,19 +39,16 @@
_zip_get_encryption_implementation(zip_uint16_t em, int operation) {
switch (em) {
case ZIP_EM_TRAD_PKWARE:
- if (operation == ZIP_CODEC_ENCODE) {
- return NULL;
- }
- return zip_source_pkware;
+ return operation == ZIP_CODEC_DECODE ? zip_source_pkware_decode : zip_source_pkware_encode;
#if defined(HAVE_CRYPTO)
case ZIP_EM_AES_128:
case ZIP_EM_AES_192:
case ZIP_EM_AES_256:
- return operation == ZIP_CODEC_DECODE ? zip_source_winzip_aes_decode : zip_source_winzip_aes_encode;
+ return operation == ZIP_CODEC_DECODE ? zip_source_winzip_aes_decode : zip_source_winzip_aes_encode;
#endif
default:
- return NULL;
+ return NULL;
}
}
diff --git a/lib/zip_pkware.c b/lib/zip_pkware.c
new file mode 100644
index 0000000..afb0d30
--- /dev/null
+++ b/lib/zip_pkware.c
@@ -0,0 +1,115 @@
+/*
+ zip_pkware.c -- Traditional PKWARE de/encryption backend routines
+ Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner
+
+ This file is part of libzip, a library to manipulate ZIP archives.
+ The authors can be contacted at <libzip@nih.at>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "zipint.h"
+
+#include "zip_crypto.h"
+
+
+#define PKWARE_KEY0 305419896
+#define PKWARE_KEY1 591751049
+#define PKWARE_KEY2 878082192
+
+
+static void
+update_keys(zip_pkware_keys_t *keys, zip_uint8_t b) {
+ keys->key[0] = (zip_uint32_t)crc32(keys->key[0] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL;
+ keys->key[1] = (keys->key[1] + (keys->key[0] & 0xff)) * 134775813 + 1;
+ b = (zip_uint8_t)(keys->key[1] >> 24);
+ keys->key[2] = (zip_uint32_t)crc32(keys->key[2] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL;
+}
+
+
+static zip_uint8_t
+crypt_byte(zip_pkware_keys_t *keys) {
+ zip_uint16_t tmp;
+ tmp = (zip_uint16_t)(keys->key[2] | 2);
+ tmp = (zip_uint16_t)(((zip_uint32_t)tmp * (tmp ^ 1)) >> 8);
+ return (zip_uint8_t)tmp;
+}
+
+
+void
+_zip_pkware_keys_reset(zip_pkware_keys_t *keys) {
+ keys->key[0] = PKWARE_KEY0;
+ keys->key[1] = PKWARE_KEY1;
+ keys->key[2] = PKWARE_KEY2;
+}
+
+
+void
+_zip_pkware_encrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len) {
+ zip_uint64_t i;
+ zip_uint8_t b;
+ zip_uint8_t tmp;
+
+ for (i = 0; i < len; i++) {
+ b = in[i];
+
+ if (out != NULL) {
+ tmp = crypt_byte(keys);
+ update_keys(keys, b);
+ b ^= tmp;
+ out[i] = b;
+ }
+ else {
+ /* during initialization, we're only interested in key updates */
+ update_keys(keys, b);
+ }
+ }
+}
+
+
+void
+_zip_pkware_decrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len) {
+ zip_uint64_t i;
+ zip_uint8_t b;
+ zip_uint8_t tmp;
+
+ for (i = 0; i < len; i++) {
+ b = in[i];
+
+ /* during initialization, we're only interested in key updates */
+ if (out != NULL) {
+ tmp = crypt_byte(keys);
+ b ^= tmp;
+ out[i] = b;
+ }
+
+ update_keys(keys, b);
+ }
+}
diff --git a/lib/zip_source_pkware.c b/lib/zip_source_pkware_decode.c
similarity index 63%
rename from lib/zip_source_pkware.c
rename to lib/zip_source_pkware_decode.c
index 1b59d7a..9389f55 100644
--- a/lib/zip_source_pkware.c
+++ b/lib/zip_source_pkware_decode.c
@@ -1,6 +1,6 @@
/*
- zip_source_pkware.c -- Traditional PKWARE de/encryption routines
- Copyright (C) 2009-2019 Dieter Baron and Thomas Klausner
+ zip_source_pkware_decode.c -- Traditional PKWARE decryption routines
+ Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner
This file is part of libzip, a library to manipulate ZIP archives.
The authors can be contacted at <libzip@nih.at>
@@ -37,25 +37,24 @@
#include "zipint.h"
+#include "zip_crypto.h"
+
+
struct trad_pkware {
+ char *password;
+ zip_pkware_keys_t keys;
zip_error_t error;
- zip_uint32_t key[3];
};
-#define HEADERLEN 12
-#define KEY0 305419896
-#define KEY1 591751049
-#define KEY2 878082192
-
-static void decrypt(struct trad_pkware *, zip_uint8_t *, const zip_uint8_t *, zip_uint64_t, int);
static int decrypt_header(zip_source_t *, struct trad_pkware *);
static zip_int64_t pkware_decrypt(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
-static void pkware_free(struct trad_pkware *);
+static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error);
+static void trad_pkware_free(struct trad_pkware *);
zip_source_t *
-zip_source_pkware(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) {
+zip_source_pkware_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) {
struct trad_pkware *ctx;
zip_source_t *s2;
@@ -68,20 +67,12 @@
return NULL;
}
- if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) {
- zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+ if ((ctx = trad_pkware_new(password, &za->error)) == NULL) {
return NULL;
}
- zip_error_init(&ctx->error);
-
- ctx->key[0] = KEY0;
- ctx->key[1] = KEY1;
- ctx->key[2] = KEY2;
- decrypt(ctx, NULL, (const zip_uint8_t *)password, strlen(password), 1);
-
if ((s2 = zip_source_layered(za, src, pkware_decrypt, ctx)) == NULL) {
- pkware_free(ctx);
+ trad_pkware_free(ctx);
return NULL;
}
@@ -89,62 +80,51 @@
}
-static void
-decrypt(struct trad_pkware *ctx, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len, int update_only) {
- zip_uint16_t tmp;
- zip_uint64_t i;
- Bytef b;
-
- for (i = 0; i < len; i++) {
- b = in[i];
-
- if (!update_only) {
- /* decrypt next byte */
- tmp = (zip_uint16_t)(ctx->key[2] | 2);
- tmp = (zip_uint16_t)(((zip_uint32_t)tmp * (tmp ^ 1)) >> 8);
- b ^= (Bytef)tmp;
- }
-
- /* store cleartext */
- if (out)
- out[i] = b;
-
- /* update keys */
- ctx->key[0] = (zip_uint32_t)crc32(ctx->key[0] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL;
- ctx->key[1] = (ctx->key[1] + (ctx->key[0] & 0xff)) * 134775813 + 1;
- b = (Bytef)(ctx->key[1] >> 24);
- ctx->key[2] = (zip_uint32_t)crc32(ctx->key[2] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL;
- }
-}
-
-
static int
decrypt_header(zip_source_t *src, struct trad_pkware *ctx) {
- zip_uint8_t header[HEADERLEN];
+ zip_uint8_t header[ZIP_CRYPTO_PKWARE_HEADERLEN];
struct zip_stat st;
zip_int64_t n;
- unsigned short dostime, dosdate;
- if ((n = zip_source_read(src, header, HEADERLEN)) < 0) {
+ if ((n = zip_source_read(src, header, ZIP_CRYPTO_PKWARE_HEADERLEN)) < 0) {
_zip_error_set_from_source(&ctx->error, src);
return -1;
}
- if (n != HEADERLEN) {
+ if (n != ZIP_CRYPTO_PKWARE_HEADERLEN) {
zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
return -1;
}
- decrypt(ctx, header, header, HEADERLEN, 0);
+ _zip_pkware_decrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN);
- if (zip_source_stat(src, &st) < 0) {
+ if (zip_source_stat(src, &st)) {
/* stat failed, skip password validation */
return 0;
}
- _zip_u2d_time(st.mtime, &dostime, &dosdate);
+ /* password verification - two ways:
+ * mtime - InfoZIP way, to avoid computing complete CRC before encrypting data
+ * CRC - old PKWare way
+ */
- if (header[HEADERLEN - 1] != st.crc >> 24 && header[HEADERLEN - 1] != dostime >> 8) {
+ bool ok = false;
+
+ if (st.valid & ZIP_STAT_MTIME) {
+ unsigned short dostime, dosdate;
+ _zip_u2d_time(st.mtime, &dostime, &dosdate);
+ if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == dostime >> 8) {
+ ok = true;
+ }
+ }
+
+ if (st.valid & ZIP_STAT_CRC) {
+ if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == st.crc >> 24) {
+ ok = true;
+ }
+ }
+
+ if (!ok && (st.valid & (ZIP_STAT_MTIME | ZIP_STAT_CRC) != 0)) {
zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0);
return -1;
}
@@ -162,8 +142,11 @@
switch (cmd) {
case ZIP_SOURCE_OPEN:
- if (decrypt_header(src, ctx) < 0)
+ _zip_pkware_keys_reset(&ctx->keys);
+ _zip_pkware_decrypt(&ctx->keys, NULL, (const zip_uint8_t *)ctx->password, strlen(ctx->password));
+ if (decrypt_header(src, ctx) < 0) {
return -1;
+ }
return 0;
case ZIP_SOURCE_READ:
@@ -172,7 +155,7 @@
return -1;
}
- decrypt((struct trad_pkware *)ud, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n, 0);
+ _zip_pkware_decrypt(&ctx->keys, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n);
return n;
case ZIP_SOURCE_CLOSE:
@@ -185,9 +168,9 @@
st->encryption_method = ZIP_EM_NONE;
st->valid |= ZIP_STAT_ENCRYPTION_METHOD;
- /* TODO: deduce HEADERLEN from size for uncompressed */
- if (st->valid & ZIP_STAT_COMP_SIZE)
- st->comp_size -= HEADERLEN;
+ if (st->valid & ZIP_STAT_COMP_SIZE) {
+ st->comp_size -= ZIP_CRYPTO_PKWARE_HEADERLEN;
+ }
return 0;
}
@@ -199,7 +182,7 @@
return zip_error_to_data(&ctx->error, data, len);
case ZIP_SOURCE_FREE:
- pkware_free(ctx);
+ trad_pkware_free(ctx);
return 0;
default:
@@ -209,7 +192,33 @@
}
+static struct trad_pkware *
+trad_pkware_new(const char *password, zip_error_t *error) {
+ struct trad_pkware *ctx;
+
+ if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) {
+ zip_error_set(error, ZIP_ER_MEMORY, 0);
+ return NULL;
+ }
+
+ if ((ctx->password = strdup(password)) == NULL) {
+ zip_error_set(error, ZIP_ER_MEMORY, 0);
+ free(ctx);
+ return NULL;
+ }
+
+ zip_error_init(&ctx->error);
+
+ return ctx;
+}
+
+
static void
-pkware_free(struct trad_pkware *ctx) {
+trad_pkware_free(struct trad_pkware *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+
+ free(ctx->password);
free(ctx);
}
diff --git a/lib/zip_source_pkware_encode.c b/lib/zip_source_pkware_encode.c
new file mode 100644
index 0000000..5888806
--- /dev/null
+++ b/lib/zip_source_pkware_encode.c
@@ -0,0 +1,239 @@
+/*
+ zip_source_pkware_encode.c -- Traditional PKWARE encryption routines
+ Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner
+
+ This file is part of libzip, a library to manipulate ZIP archives.
+ The authors can be contacted at <libzip@nih.at>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "zipint.h"
+
+#include "zip_crypto.h"
+
+struct trad_pkware {
+ char *password;
+ zip_pkware_keys_t keys;
+ zip_buffer_t *buffer;
+ bool eof;
+ zip_error_t error;
+};
+
+
+static int encrypt_header(zip_source_t *, struct trad_pkware *);
+static zip_int64_t pkware_encrypt(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
+static void trad_pkware_free(struct trad_pkware *);
+static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error);
+
+
+zip_source_t *
+zip_source_pkware_encode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) {
+ struct trad_pkware *ctx;
+ zip_source_t *s2;
+
+ if (password == NULL || src == NULL || em != ZIP_EM_TRAD_PKWARE) {
+ zip_error_set(&za->error, ZIP_ER_INVAL, 0);
+ return NULL;
+ }
+ if (flags & ZIP_CODEC_DECODE) {
+ zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
+ return NULL;
+ }
+
+ if ((ctx = trad_pkware_new(password, &za->error)) == NULL) {
+ return NULL;
+ }
+
+ if ((s2 = zip_source_layered(za, src, pkware_encrypt, ctx)) == NULL) {
+ trad_pkware_free(ctx);
+ return NULL;
+ }
+
+ return s2;
+}
+
+
+static int
+encrypt_header(zip_source_t *src, struct trad_pkware *ctx) {
+ struct zip_stat st;
+ unsigned short dostime, dosdate;
+ zip_uint8_t *header;
+
+ if (zip_source_stat(src, &st) != 0) {
+ _zip_error_set_from_source(&ctx->error, src);
+ return -1;
+ }
+
+ _zip_u2d_time(st.mtime, &dostime, &dosdate);
+
+ if ((ctx->buffer = _zip_buffer_new(NULL, ZIP_CRYPTO_PKWARE_HEADERLEN)) == NULL) {
+ zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+ return -1;
+ }
+
+ header = _zip_buffer_data(ctx->buffer);
+
+ /* generate header from random bytes and mtime
+ see appnote.iz, XIII. Decryption, Step 2, last paragraph */
+ if (!zip_secure_random(header, ZIP_CRYPTO_PKWARE_HEADERLEN - 1)) {
+ zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
+ _zip_buffer_free(ctx->buffer);
+ ctx->buffer = NULL;
+ return -1;
+ }
+ header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] = (zip_uint8_t)((dostime >> 8) & 0xff);
+
+ _zip_pkware_encrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN);
+
+ return 0;
+}
+
+
+static zip_int64_t
+pkware_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip_source_cmd_t cmd) {
+ struct trad_pkware *ctx;
+ zip_int64_t n;
+ zip_uint64_t buffer_n;
+
+ ctx = (struct trad_pkware *)ud;
+
+ switch (cmd) {
+ case ZIP_SOURCE_OPEN:
+ ctx->eof = false;
+
+ /* initialize keys */
+ _zip_pkware_keys_reset(&ctx->keys);
+ _zip_pkware_encrypt(&ctx->keys, NULL, (const zip_uint8_t *)ctx->password, strlen(ctx->password));
+
+ if (encrypt_header(src, ctx) < 0) {
+ return -1;
+ }
+ return 0;
+
+ case ZIP_SOURCE_READ:
+ buffer_n = 0;
+
+ if (ctx->buffer) {
+ /* write header values to data */
+ buffer_n = _zip_buffer_read(ctx->buffer, data, length);
+ data = (zip_uint8_t *)data + buffer_n;
+ length -= buffer_n;
+
+ if (_zip_buffer_eof(ctx->buffer)) {
+ _zip_buffer_free(ctx->buffer);
+ ctx->buffer = NULL;
+ }
+ }
+
+ if (ctx->eof) {
+ return (zip_int64_t)buffer_n;
+ }
+
+ if ((n = zip_source_read(src, data, length)) < 0) {
+ _zip_error_set_from_source(&ctx->error, src);
+ return -1;
+ }
+
+ _zip_pkware_encrypt(&ctx->keys, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n);
+
+ if ((zip_uint64_t)n < length) {
+ ctx->eof = true;
+ }
+
+ return buffer_n + n;
+
+ case ZIP_SOURCE_CLOSE:
+ _zip_buffer_free(ctx->buffer);
+ ctx->buffer = NULL;
+ return 0;
+
+ case ZIP_SOURCE_STAT: {
+ zip_stat_t *st;
+
+ st = (zip_stat_t *)data;
+ st->encryption_method = ZIP_EM_TRAD_PKWARE;
+ st->valid |= ZIP_STAT_ENCRYPTION_METHOD;
+ if (st->valid & ZIP_STAT_COMP_SIZE) {
+ st->comp_size += ZIP_CRYPTO_PKWARE_HEADERLEN;
+ }
+
+ return 0;
+ }
+
+ case ZIP_SOURCE_SUPPORTS:
+ 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);
+
+ case ZIP_SOURCE_ERROR:
+ return zip_error_to_data(&ctx->error, data, length);
+
+ case ZIP_SOURCE_FREE:
+ trad_pkware_free(ctx);
+ return 0;
+
+ default:
+ zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
+ return -1;
+ }
+}
+
+
+static struct trad_pkware *
+trad_pkware_new(const char *password, zip_error_t *error) {
+ struct trad_pkware *ctx;
+
+ if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) {
+ zip_error_set(error, ZIP_ER_MEMORY, 0);
+ return NULL;
+ }
+
+ if ((ctx->password = strdup(password)) == NULL) {
+ zip_error_set(error, ZIP_ER_MEMORY, 0);
+ free(ctx);
+ return NULL;
+ }
+ ctx->buffer = NULL;
+ zip_error_init(&ctx->error);
+
+ return ctx;
+}
+
+
+static void
+trad_pkware_free(struct trad_pkware *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+
+ free(ctx->password);
+ _zip_buffer_free(ctx->buffer);
+ zip_error_fini(&ctx->error);
+ free(ctx);
+}
diff --git a/lib/zipint.h b/lib/zipint.h
index 06133d1..52f418c 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -69,6 +69,7 @@
#define BUFSIZE 8192
#define EFZIP64SIZE 28
#define EF_WINZIP_AES_SIZE 7
+#define MAX_DATA_DESCRIPTOR_LENGTH 24
#define ZIP_CM_REPLACED_DEFAULT (-2)
#define ZIP_CM_WINZIP_AES 99 /* Winzip AES encrypted */
@@ -170,7 +171,8 @@
zip_source_t *zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t cm);
zip_source_t *zip_source_layered(zip_t *, zip_source_t *, zip_source_layered_callback, void *);
zip_source_t *zip_source_layered_create(zip_source_t *src, zip_source_layered_callback cb, void *ud, zip_error_t *error);
-zip_source_t *zip_source_pkware(zip_t *, zip_source_t *, zip_uint16_t, int, const char *);
+zip_source_t *zip_source_pkware_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *);
+zip_source_t *zip_source_pkware_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *);
int zip_source_remove(zip_source_t *);
zip_int64_t zip_source_supports(zip_source_t *src);
zip_source_t *zip_source_window(zip_t *, zip_source_t *, zip_uint64_t, zip_uint64_t);
@@ -414,6 +416,11 @@
struct _zip_winzip_aes;
typedef struct _zip_winzip_aes zip_winzip_aes_t;
+struct _zip_pkware_keys {
+ zip_uint32_t key[3];
+};
+typedef struct _zip_pkware_keys zip_pkware_keys_t;
+
extern const char *const _zip_err_str[];
extern const int _zip_nerr_str;
extern const int _zip_err_type[];
@@ -576,6 +583,12 @@
void _zip_winzip_aes_free(zip_winzip_aes_t *ctx);
zip_winzip_aes_t *_zip_winzip_aes_new(const zip_uint8_t *password, zip_uint64_t password_length, const zip_uint8_t *salt, zip_uint16_t key_size, zip_uint8_t *password_verify, zip_error_t *error);
+void _zip_pkware_encrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len);
+void _zip_pkware_decrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len);
+zip_pkware_keys_t *_zip_pkware_keys_new(zip_error_t *error);
+void _zip_pkware_keys_free(zip_pkware_keys_t *keys);
+void _zip_pkware_keys_reset(zip_pkware_keys_t *keys);
+
int _zip_changed(const zip_t *, zip_uint64_t *);
const char *_zip_get_name(zip_t *, zip_uint64_t, zip_flags_t, zip_error_t *);
int _zip_local_header_read(zip_t *, int);
diff --git a/regress/CMakeLists.txt b/regress/CMakeLists.txt
index 47c25d8..a4fccb6 100644
--- a/regress/CMakeLists.txt
+++ b/regress/CMakeLists.txt
@@ -112,6 +112,7 @@
encryption-nonrandom-aes128.test
encryption-nonrandom-aes192.test
encryption-nonrandom-aes256.test
+ encryption-nonrandom-pkware.test
encryption-remove.test
extra_add.test
extra_add_multiple.test
diff --git a/regress/encrypt-pkware-noentropy.zip b/regress/encrypt-pkware-noentropy.zip
new file mode 100644
index 0000000..ee0833d
--- /dev/null
+++ b/regress/encrypt-pkware-noentropy.zip
Binary files differ
diff --git a/regress/encrypt_plus_extra.zip b/regress/encrypt_plus_extra.zip
index 4a26309..6936865 100644
--- a/regress/encrypt_plus_extra.zip
+++ b/regress/encrypt_plus_extra.zip
Binary files differ
diff --git a/regress/encryption-nonrandom-pkware.test b/regress/encryption-nonrandom-pkware.test
new file mode 100644
index 0000000..ea2c4b9
--- /dev/null
+++ b/regress/encryption-nonrandom-pkware.test
@@ -0,0 +1,6 @@
+features CRYPTO
+description encrypt file by Traditional PKWARE
+return 0
+preload nonrandomopen.so
+args encrypt.zzip set_file_encryption 1 TRAD-PKWARE no-entropy
+file encrypt.zzip encrypt-none.zip encrypt-pkware-noentropy.zip
diff --git a/src/ziptool.c b/src/ziptool.c
index a2c97a3..9ba3993 100644
--- a/src/ziptool.c
+++ b/src/ziptool.c
@@ -662,6 +662,8 @@
get_encryption_method(const char *arg) {
if (strcmp(arg, "none") == 0)
return ZIP_EM_NONE;
+ else if (strcmp(arg, "TRAD-PKWARE") == 0)
+ return ZIP_EM_TRAD_PKWARE;
else if (strcmp(arg, "AES-128") == 0)
return ZIP_EM_AES_128;
else if (strcmp(arg, "AES-192") == 0)
@@ -833,6 +835,7 @@
"\tstore\n");
fprintf(out, "\nSupported compression methods are:\n"
"\tnone\n"
+ "\tTRAD-PKWARE\n"
"\tAES-128\n"
"\tAES-192\n"
"\tAES-256\n");