add encryption with Traditional PKWARE
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index d5bf580..225728c 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..f545903 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -453,6 +453,14 @@
zip_source_free(src_final);
return -1;
}
+ if (de->encryption_method == ZIP_EM_TRAD_PKWARE && !(flags & ZIP_GPBF_DATA_DESCRIPTOR)) {
+ // calculate crc from src in advance for Traditional PKWARE Encryption.
+ if (zip_source_pkware_calc_crc(za, src, src_tmp->ud) < 0) {
+ zip_source_free(src_tmp);
+ zip_source_free(src_final);
+ return -1;
+ }
+ }
zip_source_free(src_final);
src_final = src_tmp;
diff --git a/lib/zip_crypto.h b/lib/zip_crypto.h
index f46c7d8..de728a0 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 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..99a2ff0
--- /dev/null
+++ b/lib/zip_pkware.c
@@ -0,0 +1,144 @@
+/*
+ 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
+
+
+struct _zip_trad_pkware {
+ zip_uint32_t key[3];
+};
+
+
+void
+update_keys(zip_trad_pkware_t *ctx, Bytef b) {
+ 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;
+}
+
+
+Bytef
+decrypt_byte(zip_trad_pkware_t *ctx) {
+ zip_uint16_t tmp;
+ tmp = (zip_uint16_t) (ctx->key[2] | 2);
+ tmp = (zip_uint16_t) (((zip_uint32_t) tmp * (tmp ^ 1)) >> 8);
+ return (Bytef)tmp;
+}
+
+
+zip_trad_pkware_t *
+_zip_pkware_new(zip_error_t *error) {
+ zip_trad_pkware_t *ctx;
+
+ if ((ctx = (zip_trad_pkware_t *) malloc(sizeof(*ctx))) == NULL) {
+ zip_error_set(error, ZIP_ER_MEMORY, 0);
+ return NULL;
+ }
+ ctx->key[0] = PKWARE_KEY0;
+ ctx->key[1] = PKWARE_KEY1;
+ ctx->key[2] = PKWARE_KEY2;
+
+ return ctx;
+}
+
+
+void
+encrypt(zip_trad_pkware_t *ctx, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len, int update_only) {
+ zip_uint64_t i;
+ Bytef b;
+ Bytef tmp;
+
+ for (i = 0; i < len; i++) {
+ b = in[i];
+
+ if (update_only) {
+ update_keys(ctx, b);
+ } else {
+ /* encrypt next byte */
+ tmp = decrypt_byte(ctx);
+ update_keys(ctx, b);
+ b ^= tmp;
+ }
+
+ /* store cleartext */
+ if (out) {
+ out[i] = b;
+ }
+ }
+}
+
+
+void
+decrypt(zip_trad_pkware_t *ctx, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len, int update_only) {
+ zip_uint64_t i;
+ Bytef b;
+ Bytef tmp;
+
+ for (i = 0; i < len; i++) {
+ b = in[i];
+
+ if (!update_only) {
+ /* decrypt next byte */
+ tmp = decrypt_byte(ctx);
+ b ^= tmp;
+ }
+
+ /* store cleartext */
+ if (out) {
+ out[i] = b;
+ }
+
+ update_keys(ctx, b);
+ }
+}
+
+
+void
+_zip_pkware_free(zip_trad_pkware_t *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+
+ free(ctx);
+}
diff --git a/lib/zip_source_pkware.c b/lib/zip_source_pkware.c
deleted file mode 100644
index 1b59d7a..0000000
--- a/lib/zip_source_pkware.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- zip_source_pkware.c -- Traditional PKWARE de/encryption routines
- Copyright (C) 2009-2019 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"
-
-struct trad_pkware {
- 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 *);
-
-
-zip_source_t *
-zip_source_pkware(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_ENCODE) {
- zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
- return NULL;
- }
-
- if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) {
- zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
- 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);
- return NULL;
- }
-
- return s2;
-}
-
-
-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];
- struct zip_stat st;
- zip_int64_t n;
- unsigned short dostime, dosdate;
-
- if ((n = zip_source_read(src, header, HEADERLEN)) < 0) {
- _zip_error_set_from_source(&ctx->error, src);
- return -1;
- }
-
- if (n != HEADERLEN) {
- zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
- return -1;
- }
-
- decrypt(ctx, header, header, HEADERLEN, 0);
-
- if (zip_source_stat(src, &st) < 0) {
- /* stat failed, skip password validation */
- return 0;
- }
-
- _zip_u2d_time(st.mtime, &dostime, &dosdate);
-
- if (header[HEADERLEN - 1] != st.crc >> 24 && header[HEADERLEN - 1] != dostime >> 8) {
- zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0);
- return -1;
- }
-
- return 0;
-}
-
-
-static zip_int64_t
-pkware_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
- struct trad_pkware *ctx;
- zip_int64_t n;
-
- ctx = (struct trad_pkware *)ud;
-
- switch (cmd) {
- case ZIP_SOURCE_OPEN:
- if (decrypt_header(src, ctx) < 0)
- return -1;
- return 0;
-
- case ZIP_SOURCE_READ:
- if ((n = zip_source_read(src, data, len)) < 0) {
- _zip_error_set_from_source(&ctx->error, src);
- return -1;
- }
-
- decrypt((struct trad_pkware *)ud, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n, 0);
- return n;
-
- case ZIP_SOURCE_CLOSE:
- return 0;
-
- case ZIP_SOURCE_STAT: {
- zip_stat_t *st;
-
- st = (zip_stat_t *)data;
-
- 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;
-
- 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, len);
-
- case ZIP_SOURCE_FREE:
- pkware_free(ctx);
- return 0;
-
- default:
- zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
- return -1;
- }
-}
-
-
-static void
-pkware_free(struct trad_pkware *ctx) {
- free(ctx);
-}
diff --git a/lib/zip_source_pkware_decode.c b/lib/zip_source_pkware_decode.c
new file mode 100644
index 0000000..81bddea
--- /dev/null
+++ b/lib/zip_source_pkware_decode.c
@@ -0,0 +1,201 @@
+/*
+ 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>
+
+ 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 {
+ zip_trad_pkware_t *pkware_ctx;
+ zip_error_t error;
+};
+
+
+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 struct trad_pkware *trad_pkware_new(zip_error_t *error);
+static void trad_pkware_free(struct trad_pkware *);
+
+
+zip_source_t *
+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;
+
+ 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_ENCODE) {
+ zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
+ return NULL;
+ }
+
+ if ((ctx = trad_pkware_new(&za->error)) == NULL) {
+ return NULL;
+ }
+
+ decrypt(ctx->pkware_ctx, NULL, (const zip_uint8_t *) password, strlen(password), 1);
+
+ if ((s2 = zip_source_layered(za, src, pkware_decrypt, ctx)) == NULL) {
+ trad_pkware_free(ctx);
+ return NULL;
+ }
+
+ return s2;
+}
+
+
+static int
+decrypt_header(zip_source_t *src, struct trad_pkware *ctx) {
+ zip_uint8_t header[PKWARE_HEADERLEN];
+ struct zip_stat st;
+ zip_int64_t n;
+ unsigned short dostime, dosdate;
+
+ if ((n = zip_source_read(src, header, PKWARE_HEADERLEN)) < 0) {
+ _zip_error_set_from_source(&ctx->error, src);
+ return -1;
+ }
+
+ if (n != PKWARE_HEADERLEN) {
+ zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
+ return -1;
+ }
+
+ decrypt(ctx->pkware_ctx, header, header, PKWARE_HEADERLEN, 0);
+
+ if (zip_source_stat(src, &st) < 0) {
+ /* stat failed, skip password validation */
+ return 0;
+ }
+
+ _zip_u2d_time(st.mtime, &dostime, &dosdate);
+
+ if (header[PKWARE_HEADERLEN - 1] != st.crc >> 24 && header[PKWARE_HEADERLEN - 1] != dostime >> 8) {
+ zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static zip_int64_t
+pkware_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
+ struct trad_pkware *ctx;
+ zip_int64_t n;
+
+ ctx = (struct trad_pkware *) ud;
+
+ switch (cmd) {
+ case ZIP_SOURCE_OPEN:
+ if (decrypt_header(src, ctx) < 0) {
+ return -1;
+ }
+ return 0;
+
+ case ZIP_SOURCE_READ:
+ if ((n = zip_source_read(src, data, len)) < 0) {
+ _zip_error_set_from_source(&ctx->error, src);
+ return -1;
+ }
+
+ decrypt(ctx->pkware_ctx, (zip_uint8_t *) data, (zip_uint8_t *) data, (zip_uint64_t) n, 0);
+ return n;
+
+ case ZIP_SOURCE_CLOSE:
+ return 0;
+
+ case ZIP_SOURCE_STAT: {
+ zip_stat_t *st;
+
+ st = (zip_stat_t *) data;
+
+ 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 -= 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, len);
+
+ 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(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->pkware_ctx = _zip_pkware_new(error)) == NULL) {
+ return NULL;
+ }
+ zip_error_init(&ctx->error);
+
+ return ctx;
+}
+
+
+static void trad_pkware_free(struct trad_pkware *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+
+ _zip_pkware_free(ctx->pkware_ctx);
+ ctx->pkware_ctx = NULL;
+ free(ctx);
+}
diff --git a/lib/zip_source_pkware_encode.c b/lib/zip_source_pkware_encode.c
new file mode 100644
index 0000000..8b634a0
--- /dev/null
+++ b/lib/zip_source_pkware_encode.c
@@ -0,0 +1,285 @@
+/*
+ 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 {
+ zip_trad_pkware_t *pkware_ctx;
+ zip_int32_t flags;
+ zip_uint8_t header[PKWARE_HEADERLEN];
+ zip_buffer_t *buffer;
+ bool eof;
+ zip_uint32_t crc;
+ 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 struct trad_pkware *trad_pkware_new(zip_error_t *error);
+static void trad_pkware_free(struct trad_pkware *);
+
+
+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(&za->error)) == NULL) {
+ return NULL;
+ }
+ ctx->flags = flags;
+
+ // initialize keys
+ encrypt(ctx->pkware_ctx, NULL, (const zip_uint8_t *) password, strlen(password), 1);
+
+ if ((s2 = zip_source_layered(za, src, pkware_encrypt, ctx)) == NULL) {
+ trad_pkware_free(ctx);
+ return NULL;
+ }
+
+ return s2;
+}
+
+
+int
+zip_source_pkware_calc_crc(zip_t *za, zip_source_t *src, void *ud) {
+ zip_uint8_t buf[BUFSIZE];
+ zip_int64_t n;
+ zip_source_t *crc_src;
+ zip_stat_t st;
+ struct trad_pkware *ctx;
+
+ ctx = (struct trad_pkware *) ud;
+
+ // create crc source
+ if ((crc_src = zip_source_crc(za, src, 0)) == NULL) {
+ zip_source_free(src);
+ return -1;
+ }
+
+ if (zip_source_open(crc_src) < 0) {
+ _zip_error_set_from_source(&za->error, crc_src);
+ return -1;
+ }
+
+ while ((n = zip_source_read(crc_src, buf, sizeof(buf))) > 0) {
+ // just read and calculate crc32.
+ }
+ if (n < 0) {
+ _zip_error_set_from_source(&za->error, crc_src);
+ zip_source_close(crc_src);
+ zip_source_free(crc_src);
+ return -1;
+ }
+
+ // fetch crc calc result
+ if (zip_source_stat(crc_src, &st) != 0) {
+ _zip_error_set_from_source(&za->error, crc_src);
+ zip_source_close(crc_src);
+ zip_source_free(crc_src);
+ return -1;
+ }
+
+ zip_source_close(crc_src);
+ zip_source_free(crc_src);
+
+ ctx->crc = st.crc;
+ return 0;
+}
+
+
+static int
+encrypt_header(zip_source_t *src, struct trad_pkware *ctx) {
+ struct zip_stat st;
+ unsigned short dostime, dosdate;
+
+ // generate random bytes
+ if (!zip_secure_random(ctx->header, PKWARE_HEADERLEN)) {
+ zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
+ return -1;
+ }
+
+ if (zip_source_stat(src, &st) != 0) {
+ _zip_error_set_from_source(&ctx->error, src);
+ return -1;
+ }
+
+ if (ctx->flags & ZIP_GPBF_DATA_DESCRIPTOR) {
+ // If bit 3 is set, use modification time for check bytes.
+ _zip_u2d_time(st.mtime, &dostime, &dosdate);
+ ctx->header[PKWARE_HEADERLEN - 2] = (zip_uint8_t) (dostime & 0xff);
+ ctx->header[PKWARE_HEADERLEN - 1] = (zip_uint8_t) ((dostime >> 8) & 0xff);
+ } else {
+ // if bit 3 is unset, use crc for check bytes.
+ ctx->header[PKWARE_HEADERLEN - 2] = (zip_uint8_t) ((ctx->crc >> 16) & 0xff);
+ ctx->header[PKWARE_HEADERLEN - 1] = (zip_uint8_t) ((ctx->crc >> 24) & 0xff);
+ }
+
+ encrypt(ctx->pkware_ctx, ctx->header, ctx->header, PKWARE_HEADERLEN, 0);
+ if ((ctx->buffer = _zip_buffer_new(ctx->header, PKWARE_HEADERLEN)) == NULL) {
+ trad_pkware_free(ctx);
+ zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
+ return -1;
+ }
+ 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;
+ // create header values
+ 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;
+ }
+
+ encrypt(ctx->pkware_ctx, (zip_uint8_t *) data, (zip_uint8_t *) data, (zip_uint64_t) n, 0);
+
+ if ((zip_uint64_t) n < length) {
+ ctx->eof = true;
+ _zip_pkware_free(ctx->pkware_ctx);
+ ctx->pkware_ctx = NULL;
+ }
+
+ return buffer_n + n;
+
+ case ZIP_SOURCE_CLOSE:
+ 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 += 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(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->pkware_ctx = _zip_pkware_new(error)) == NULL) {
+ return NULL;
+ }
+ zip_error_init(&ctx->error);
+
+ return ctx;
+}
+
+
+static void
+trad_pkware_free(struct trad_pkware *ctx) {
+ if (ctx == NULL) {
+ return;
+ }
+
+ _zip_pkware_free(ctx->pkware_ctx);
+ ctx->pkware_ctx = NULL;
+ free(ctx);
+}
diff --git a/lib/zipint.h b/lib/zipint.h
index 721d36e..3fbc5f0 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -170,7 +170,9 @@
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_pkware_calc_crc(zip_t *za, zip_source_t *src, void *ud);
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,9 @@
struct _zip_winzip_aes;
typedef struct _zip_winzip_aes zip_winzip_aes_t;
+struct _zip_trad_pkware;
+typedef struct _zip_trad_pkware zip_trad_pkware_t;
+
extern const char *const _zip_err_str[];
extern const int _zip_nerr_str;
extern const int _zip_err_type[];
@@ -576,6 +581,13 @@
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 update_keys(zip_trad_pkware_t *ctx, Bytef b);
+Bytef decrypt_byte(zip_trad_pkware_t *ctx);
+void encrypt(zip_trad_pkware_t *ctx, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len, int update_only);
+void decrypt(zip_trad_pkware_t *ctx, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len, int update_only);
+zip_trad_pkware_t *_zip_pkware_new(zip_error_t *error);
+void _zip_pkware_free(zip_trad_pkware_t *ctx);
+
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..1b11a08
--- /dev/null
+++ b/regress/encrypt-pkware-noentropy.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 be30ab1..9f6a71e 100644
--- a/src/ziptool.c
+++ b/src/ziptool.c
@@ -663,6 +663,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)
@@ -834,6 +836,7 @@
"\tstore\n");
fprintf(out, "\nSupported compression methods are:\n"
"\tnone\n"
+ "\tTRAD-PKWARE\n"
"\tAES-128\n"
"\tAES-192\n"
"\tAES-256\n");