Winzip AES encryption support for writing.
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index cfd47f3..7393970 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt
@@ -83,6 +83,7 @@ zip_file_rename.c zip_file_replace.c zip_file_set_comment.c + zip_file_set_encryption.c zip_file_set_external_attributes.c zip_file_set_mtime.c zip_file_strerror.c @@ -140,6 +141,8 @@ zip_source_tell.c zip_source_tell_write.c zip_source_window.c + zip_source_winzip_aes_decode.c + zip_source_winzip_aes_encode.c zip_source_write.c zip_source_zip.c zip_source_zip_new.c @@ -157,6 +160,7 @@ IF(WIN32) SET(LIBZIP_OPSYS_FILES + zip_random_win32.c zip_source_win32a.c zip_source_win32handle.c zip_source_win32utf8.c @@ -164,6 +168,7 @@ ) ELSE(WIN32) SET(LIBZIP_OPSYS_FILES + zip_random_unix.c zip_source_file.c ) ENDIF(WIN32)
diff --git a/lib/Makefile.am b/lib/Makefile.am index d4b97bb..04740a2 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am
@@ -12,18 +12,20 @@ libzip_la_LIBADD=@LTLIBOBJS@ if WIN32_HOST -IO_SOURCES=\ +OS_SOURCES=\ + zip_random_win32.c \ zip_source_win32a.c \ zip_source_win32handle.c \ zip_source_win32utf8.c \ zip_source_win32w.c else -IO_SOURCES=\ +OS_SOURCES=\ + zip_random_unix.c \ zip_source_file.c endif libzip_la_SOURCES=\ - ${IO_SOURCES} \ + ${OS_SOURCES} \ gladman-fcrypt.c \ zip_add.c \ zip_add_dir.c \ @@ -55,6 +57,7 @@ zip_file_rename.c \ zip_file_replace.c \ zip_file_set_comment.c \ + zip_file_set_encryption.c \ zip_file_set_external_attributes.c \ zip_file_set_mtime.c \ zip_file_strerror.c \ @@ -113,7 +116,8 @@ zip_source_tell.c \ zip_source_tell_write.c \ zip_source_window.c \ - zip_source_winzip_aes.c \ + zip_source_winzip_aes_decode.c \ + zip_source_winzip_aes_encode.c \ zip_source_write.c \ zip_source_zip.c \ zip_source_zip_new.c \
diff --git a/lib/zip.h b/lib/zip.h index 804dcea..97f01e5 100644 --- a/lib/zip.h +++ b/lib/zip.h
@@ -358,6 +358,7 @@ ZIP_EXTERN int zip_file_rename(zip_t *, zip_uint64_t, const char *, zip_flags_t); ZIP_EXTERN int zip_file_replace(zip_t *, zip_uint64_t, zip_source_t *, zip_flags_t); ZIP_EXTERN int zip_file_set_comment(zip_t *, zip_uint64_t, const char *, zip_uint16_t, zip_flags_t); +ZIP_EXTERN int zip_file_set_encryption(zip_t *, zip_uint64_t, zip_uint16_t, const char *); ZIP_EXTERN int zip_file_set_external_attributes(zip_t *, zip_uint64_t, zip_flags_t, zip_uint8_t, zip_uint32_t); ZIP_EXTERN int zip_file_set_mtime(zip_t *, zip_uint64_t, time_t, zip_flags_t); ZIP_EXTERN const char *zip_file_strerror(zip_file_t *);
diff --git a/lib/zip_buffer.c b/lib/zip_buffer.c index f622650..a59a020 100644 --- a/lib/zip_buffer.c +++ b/lib/zip_buffer.c
@@ -140,6 +140,19 @@ } +zip_uint64_t +_zip_buffer_read(zip_buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length) +{ + if (_zip_buffer_left(buffer) < length) { + length = _zip_buffer_left(buffer); + } + + memcpy(data, _zip_buffer_get(buffer, length), length); + + return length; +} + + zip_buffer_t * _zip_buffer_new(zip_uint8_t *data, zip_uint64_t size) {
diff --git a/lib/zip_close.c b/lib/zip_close.c index ac536f1..fd5a107 100644 --- a/lib/zip_close.c +++ b/lib/zip_close.c
@@ -134,7 +134,7 @@ i = filelist[j].idx; entry = za->entry+i; - new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD)); + new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_ENCRYPTION_METHOD)); /* create new local directory entry */ if (entry->changes == NULL) { @@ -259,6 +259,10 @@ st.valid &= ~ZIP_STAT_COMP_SIZE; } + if ((st.valid & ZIP_STAT_ENCRYPTION_METHOD) == 0) { + st.valid |= ZIP_STAT_ENCRYPTION_METHOD; + st.encryption_method = ZIP_EM_NONE; + } flags = ZIP_EF_LOCAL; @@ -290,8 +294,30 @@ bool needs_crc = (st.comp_method == ZIP_CM_STORE) || needs_decompress; bool needs_compress = needs_recompress && (de->comp_method != ZIP_CM_STORE); + bool needs_reencrypt = needs_recompress || (de->changed & ZIP_DIRENT_PASSWORD) || (de->encryption_method != st.encryption_method); + bool needs_decrypt = needs_reencrypt && (st.encryption_method != ZIP_EM_NONE); + bool needs_encrypt = needs_reencrypt && (de->encryption_method != ZIP_EM_NONE); + src_final = src; zip_source_keep(src_final); + + if (needs_decrypt) { + zip_encryption_implementation impl; + + if ((impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + zip_source_free(src_final); + return -1; + } + if ((src_tmp = impl(za, src_final, st.encryption_method, ZIP_CODEC_DECODE, NULL)) == NULL) { + /* error set by impl */ + zip_source_free(src_final); + return -1; + } + + zip_source_free(src_final); + src_final = src_tmp; + } if (needs_decompress) { zip_compression_implementation comp_impl; @@ -337,7 +363,26 @@ zip_source_free(src_final); src_final = src_tmp; } + + if (needs_encrypt) { + zip_encryption_implementation impl; + + if ((impl = _zip_get_encryption_implementation(de->encryption_method, ZIP_CODEC_ENCODE)) == NULL) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + zip_source_free(src_final); + return -1; + } + if ((src_tmp = impl(za, src_final, de->encryption_method, ZIP_CODEC_ENCODE, de->password)) == NULL) { + /* error set by impl */ + zip_source_free(src_final); + return -1; + } + + zip_source_free(src_final); + src_final = src_tmp; + } + if ((offdata = zip_source_tell_write(za->src)) < 0) { return -1;
diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c index 6fcc6c7..4337bde 100644 --- a/lib/zip_dirent.c +++ b/lib/zip_dirent.c
@@ -252,6 +252,13 @@ _zip_string_free(zde->comment); zde->comment = NULL; } + if (!zde->cloned || zde->changed & ZIP_DIRENT_PASSWORD) { + if (zde->password) { + _zip_crypto_clear(zde->password, strlen(zde->password)); + } + free(zde->password); + zde->password = NULL; + } } @@ -290,6 +297,7 @@ de->ext_attrib = ZIP_EXT_ATTRIB_DEFAULT; de->offset = 0; de->encryption_method = ZIP_EM_NONE; + de->password = NULL; } @@ -758,6 +766,7 @@ zip_uint32_t ef_total_size; bool is_zip64; bool is_really_zip64; + bool is_winzip_aes; zip_uint8_t buf[CDENTRYSIZE]; zip_buffer_t *buffer; @@ -788,8 +797,16 @@ } } + if (de->encryption_method == ZIP_EM_NONE) { + de->bitflags &= ~ZIP_GPBF_ENCRYPTED; + } + else { + de->bitflags |= ZIP_GPBF_ENCRYPTED; + } + is_really_zip64 = _zip_dirent_needs_zip64(de, flags); is_zip64 = (flags & (ZIP_FL_LOCAL|ZIP_FL_FORCE_ZIP64)) == (ZIP_FL_LOCAL|ZIP_FL_FORCE_ZIP64) || is_really_zip64; + is_winzip_aes = de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256; if (is_zip64) { zip_uint8_t ef_zip64[EFZIP64SIZE]; @@ -833,6 +850,35 @@ ef = ef64; } + if (is_winzip_aes) { + zip_uint8_t data[EF_WINZIP_AES_SIZE]; + zip_buffer_t *ef_buffer = _zip_buffer_new(data, sizeof(data)); + zip_extra_field_t *ef_winzip; + + if (ef_buffer == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + _zip_ef_free(ef); + return -1; + } + + _zip_buffer_put_16(ef_buffer, 2); + _zip_buffer_put(ef_buffer, "AE", 2); + _zip_buffer_put_8(ef_buffer, (de->encryption_method & 0xff)); + _zip_buffer_put_16(ef_buffer, de->comp_method); + + if (!_zip_buffer_ok(ef_buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(ef_buffer); + _zip_ef_free(ef); + return -1; + } + + ef_winzip = _zip_ef_new(ZIP_EF_WINZIP_AES, EF_WINZIP_AES_SIZE, data, ZIP_EF_BOTH); + _zip_buffer_free(ef_buffer); + ef_winzip->next = ef; + ef = ef_winzip; + } + if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); _zip_ef_free(ef); @@ -846,13 +892,23 @@ } _zip_buffer_put_16(buffer, (zip_uint16_t)(is_really_zip64 ? 45 : de->version_needed)); _zip_buffer_put_16(buffer, de->bitflags&0xfff9); /* clear compression method specific flags */ - _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + if (is_winzip_aes) { + _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES); + } + else { + _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + } _zip_u2d_time(de->last_mod, &dostime, &dosdate); _zip_buffer_put_16(buffer, dostime); _zip_buffer_put_16(buffer, dosdate); - _zip_buffer_put_32(buffer, de->crc); + if (is_winzip_aes && de->uncomp_size < 20) { + _zip_buffer_put_32(buffer, 0); + } + else { + _zip_buffer_put_32(buffer, de->crc); + } if (((flags & ZIP_FL_LOCAL) == ZIP_FL_LOCAL) && ((de->comp_size >= ZIP_UINT32_MAX) || (de->uncomp_size >= ZIP_UINT32_MAX))) { /* In local headers, if a ZIP64 EF is written, it MUST contain
diff --git a/lib/zip_file_set_encryption.c b/lib/zip_file_set_encryption.c new file mode 100644 index 0000000..9ef09f3 --- /dev/null +++ b/lib/zip_file_set_encryption.c
@@ -0,0 +1,115 @@ +/* + zip_file_set_encryption.c -- set encryption for file in archive + Copyright (C) 2016 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 "zipint.h" + +#include <stdlib.h> +#include <string.h> + +ZIP_EXTERN int +zip_file_set_encryption(zip_t *za, zip_uint64_t idx, zip_uint16_t method, const char *password) +{ + zip_entry_t *e; + zip_uint16_t old_method; + + if (idx >= za->nentry) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (ZIP_IS_RDONLY(za)) { + zip_error_set(&za->error, ZIP_ER_RDONLY, 0); + return -1; + } + + if (_zip_get_encryption_implementation(method, ZIP_CODEC_ENCODE) == NULL) { + zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); + return -1; + } + + e = za->entry+idx; + + old_method = (e->orig == NULL ? ZIP_EM_NONE : e->orig->encryption_method); + + if (method == old_method && password == NULL) { + if (e->changes) { + if (e->changes->changed & ZIP_DIRENT_PASSWORD) { + _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); + free(e->changes->password); + e->changes->password = e->orig->password; + } + e->changes->changed &= ~(ZIP_DIRENT_ENCRYPTION_METHOD|ZIP_DIRENT_PASSWORD); + if (e->changes->changed == 0) { + _zip_dirent_free(e->changes); + e->changes = NULL; + } + } + } + else { + char *our_password = NULL; + + if (password) { + if ((our_password = strdup(password)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + if (e->changes == NULL) { + if ((e->changes=_zip_dirent_clone(e->orig)) == NULL) { + _zip_crypto_clear(our_password, strlen(our_password)); + free(our_password); + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + e->changes->encryption_method = method; + e->changes->changed |= ZIP_DIRENT_ENCRYPTION_METHOD; + if (password) { + e->changes->password = our_password; + e->changes->changed |= ZIP_DIRENT_PASSWORD; + } + else { + if (e->changes->changed & ZIP_DIRENT_PASSWORD) { + _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); + free(e->changes->password); + e->changes->password = e->orig->password; + e->changes->changed &= ~ZIP_DIRENT_PASSWORD; + } + } + } + + return 0; +}
diff --git a/lib/zip_get_compression_implementation.c b/lib/zip_get_compression_implementation.c index c1120d3..cd96339 100644 --- a/lib/zip_get_compression_implementation.c +++ b/lib/zip_get_compression_implementation.c
@@ -36,7 +36,7 @@ zip_compression_implementation -_zip_get_compression_implementation(zip_int32_t cm) +_zip_get_compression_implementation(zip_int32_t cm, int operation) { if (cm == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(cm)) return zip_source_deflate;
diff --git a/lib/zip_get_encryption_implementation.c b/lib/zip_get_encryption_implementation.c index 96e939c..f8af7b0 100644 --- a/lib/zip_get_encryption_implementation.c +++ b/lib/zip_get_encryption_implementation.c
@@ -1,6 +1,6 @@ /* zip_get_encryption_implementation.c -- get encryption implementation - Copyright (C) 2009-2014 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2016 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> @@ -36,16 +36,19 @@ zip_encryption_implementation -_zip_get_encryption_implementation(zip_uint16_t em) +_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; case ZIP_EM_AES_128: case ZIP_EM_AES_192: case ZIP_EM_AES_256: - return zip_source_winzip_aes; + return operation == ZIP_CODEC_DECODE ? zip_source_winzip_aes_decode : zip_source_winzip_aes_encode; default: return NULL;
diff --git a/lib/zip_random_unix.c b/lib/zip_random_unix.c new file mode 100644 index 0000000..ee016ac --- /dev/null +++ b/lib/zip_random_unix.c
@@ -0,0 +1,55 @@ +/* + zip_random_unix.c -- fill the user's buffer with random stuff (Unix version) + Copyright (C) 2016 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 "zipint.h" + +#include <fcntl.h> + +bool +zip_random(zip_uint8_t *buffer, zip_uint16_t length) +{ + int fd; + int len_ret; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { + return false; + } + + if (read(fd, buffer, length) != length) { + close(fd); + return false; + } + + close(fd); + return true; +}
diff --git a/lib/zip_random_unix.c~ b/lib/zip_random_unix.c~ new file mode 100644 index 0000000..1a7fab2 --- /dev/null +++ b/lib/zip_random_unix.c~
@@ -0,0 +1,51 @@ +/* + zip_random.c -- fill the user's buffer with random stuff + Copyright (C) 2016 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. +*/ + +bool +zip_random(zip_uint8_t *buffer, zip_uint16_t length) +{ + int fd; + int len_ret; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { + return false; + } + + if (read(fd, buffer, length) != length) { + close(fd); + return false; + } + + close(fd); + return true; +}
diff --git a/lib/zip_random_win32.c b/lib/zip_random_win32.c new file mode 100644 index 0000000..ca1fe5e --- /dev/null +++ b/lib/zip_random_win32.c
@@ -0,0 +1,39 @@ +/* + zip_random_win32.c -- fill the user's buffer with random stuff (Windows version) + Copyright (C) 2016 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. +*/ + +bool +zip_random(zip_uint8_t *buffer, zip_uint16_t length) +{ + /* TODO: implement */ + return false; +}
diff --git a/lib/zip_random_win32.c~ b/lib/zip_random_win32.c~ new file mode 100644 index 0000000..1a7fab2 --- /dev/null +++ b/lib/zip_random_win32.c~
@@ -0,0 +1,51 @@ +/* + zip_random.c -- fill the user's buffer with random stuff + Copyright (C) 2016 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. +*/ + +bool +zip_random(zip_uint8_t *buffer, zip_uint16_t length) +{ + int fd; + int len_ret; + + if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { + return false; + } + + if (read(fd, buffer, length) != length) { + close(fd); + return false; + } + + close(fd); + return true; +}
diff --git a/lib/zip_source_error.c b/lib/zip_source_error.c index e09199b..e9f050d 100644 --- a/lib/zip_source_error.c +++ b/lib/zip_source_error.c
@@ -40,3 +40,9 @@ { return &src->error; } + +bool +_zip_source_had_error(zip_source_t *src) +{ + return zip_source_error(src)->zip_err != ZIP_ER_OK; +}
diff --git a/lib/zip_source_function.c b/lib/zip_source_function.c index 1d4be93..345959e 100644 --- a/lib/zip_source_function.c +++ b/lib/zip_source_function.c
@@ -94,6 +94,7 @@ src->source_archive = NULL; src->refcount = 1; zip_error_init(&src->error); + src->eof = false; return src; }
diff --git a/lib/zip_source_open.c b/lib/zip_source_open.c index ec5e39d..a621995 100644 --- a/lib/zip_source_open.c +++ b/lib/zip_source_open.c
@@ -67,6 +67,8 @@ } } + src->eof = false; + _zip_error_clear(&src->error); src->open_count++; return 0;
diff --git a/lib/zip_source_read.c b/lib/zip_source_read.c index 061a6f9..88edd65 100644 --- a/lib/zip_source_read.c +++ b/lib/zip_source_read.c
@@ -38,6 +38,9 @@ zip_int64_t zip_source_read(zip_source_t *src, void *data, zip_uint64_t len) { + zip_uint64_t bytes_read; + zip_int64_t n; + if (src->source_closed) { return -1; } @@ -46,5 +49,39 @@ return -1; } - return _zip_source_call(src, data, len, ZIP_SOURCE_READ); + if (_zip_source_had_error(src)) { + return -1; + } + + if (_zip_source_eof(src)) { + return 0; + } + + bytes_read = 0; + while (bytes_read < len) { + if ((n = _zip_source_call(src, data + bytes_read, len - bytes_read, ZIP_SOURCE_READ)) < 0) { + if (bytes_read == 0) { + return -1; + } + else { + return bytes_read; + } + } + + if (n == 0) { + src->eof = 1; + break; + } + + bytes_read += (zip_uint64_t)n; + } + + return (zip_int64_t)bytes_read; +} + + +bool +_zip_source_eof(zip_source_t *src) +{ + return src->eof; }
diff --git a/lib/zip_source_winzip_aes.c b/lib/zip_source_winzip_aes_decode.c similarity index 98% rename from lib/zip_source_winzip_aes.c rename to lib/zip_source_winzip_aes_decode.c index 66a6a00..e083f95 100644 --- a/lib/zip_source_winzip_aes.c +++ b/lib/zip_source_winzip_aes_decode.c
@@ -63,7 +63,7 @@ zip_source_t * -zip_source_winzip_aes(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) +zip_source_winzip_aes_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { zip_source_t *s2; int mode = 0;
diff --git a/lib/zip_source_winzip_aes.c b/lib/zip_source_winzip_aes_encode.c similarity index 65% copy from lib/zip_source_winzip_aes.c copy to lib/zip_source_winzip_aes_encode.c index 66a6a00..eab2964 100644 --- a/lib/zip_source_winzip_aes.c +++ b/lib/zip_source_winzip_aes_encode.c
@@ -47,31 +47,31 @@ struct winzip_aes { char *password; int mode; + zip_uint16_t encryption_method; - zip_uint64_t data_length; - zip_uint64_t current_position; + zip_uint8_t data[MAX_HEADER_LENGTH]; + zip_buffer_t *buffer; fcrypt_ctx fcrypt_ctx; + bool eof; zip_error_t error; }; -static int decrypt_header(zip_source_t *src, struct winzip_aes *ctx); +static int encrypt_header(zip_source_t *src, struct winzip_aes *ctx); static void winzip_aes_free(struct winzip_aes *); -static zip_int64_t winzip_aes_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd); -static struct winzip_aes * winzip_aes_new(int mode, const char *password); +static zip_int64_t winzip_aes_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd); +static struct winzip_aes * winzip_aes_new(int mode, zip_uint16_t encryption_method, const char *password); zip_source_t * -zip_source_winzip_aes(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) +zip_source_winzip_aes_encode(zip_t *za, zip_source_t *src, zip_uint16_t encryption_method, int flags, const char *password) { zip_source_t *s2; int mode = 0; - zip_stat_t st; - zip_uint64_t aux_length; struct winzip_aes *ctx; - switch (em) { + switch (encryption_method) { case ZIP_EM_AES_128: mode = 1; break; @@ -87,31 +87,13 @@ 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 (zip_source_stat(src, &st) != 0) { - _zip_error_set_from_source(&za->error, src); - return NULL; - } - - aux_length = PWD_VER_LENGTH + salt_length[mode] + HMAC_LENGTH; - - if ((st.valid & ZIP_STAT_COMP_SIZE) == 0 || st.comp_size < aux_length) { - zip_error_set(&za->error, ZIP_ER_OPNOTSUPP, 0); - return NULL; - } - - if ((ctx = winzip_aes_new(mode, password)) == NULL) { + if ((ctx = winzip_aes_new(mode, encryption_method, password)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return NULL; } - ctx->data_length = st.comp_size - aux_length; - - if ((s2 = zip_source_layered(za, src, winzip_aes_decrypt, ctx)) == NULL) { + if ((s2 = zip_source_layered(za, src, winzip_aes_encrypt, ctx)) == NULL) { winzip_aes_free(ctx); return NULL; } @@ -121,33 +103,23 @@ static int -decrypt_header(zip_source_t *src, struct winzip_aes *ctx) +encrypt_header(zip_source_t *src, struct winzip_aes *ctx) { - zip_uint8_t header[MAX_HEADER_LENGTH]; - zip_uint8_t password_verification[PWD_VER_LENGTH]; - zip_uint8_t headerlen; - zip_int64_t n; - unsigned char key[16]; - - headerlen = PWD_VER_LENGTH + salt_length[ctx->mode]; - if ((n=zip_source_read(src, header, headerlen)) < 0) { - _zip_error_set_from_source(&ctx->error, src); + if (!zip_random(ctx->data, salt_length[ctx->mode])) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); return -1; - } + } - if (n != headerlen) { - zip_error_set(&ctx->error, ZIP_ER_EOF, 0); - return -1; - } - - if (_zip_fcrypt_init(ctx->mode, ctx->password, strlen(ctx->password), header, password_verification, &ctx->fcrypt_ctx) != 0) { + if (_zip_fcrypt_init(ctx->mode, ctx->password, strlen(ctx->password), ctx->data, ctx->data+salt_length[ctx->mode], &ctx->fcrypt_ctx) != 0) { zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); return -1; } - if (memcmp(password_verification, header + salt_length[ctx->mode], PWD_VER_LENGTH) != 0) { - zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0); + + if ((ctx->buffer = _zip_buffer_new(ctx->data, salt_length[ctx->mode] + PWD_VER_LENGTH)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); return -1; } + return 0; } @@ -173,43 +145,62 @@ static zip_int64_t -winzip_aes_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) +winzip_aes_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip_source_cmd_t cmd) { struct winzip_aes *ctx; - zip_int64_t n; + zip_int64_t ret; + zip_uint64_t buffer_n, offset, n; ctx = (struct winzip_aes *)ud; switch (cmd) { case ZIP_SOURCE_OPEN: - if (decrypt_header(src, ctx) < 0) { + ctx->eof = false; + if (encrypt_header(src, ctx) < 0) { return -1; } - ctx->current_position = 0; return 0; case ZIP_SOURCE_READ: - if (len > ctx->data_length - ctx->current_position) { - len = ctx->data_length - ctx->current_position; + buffer_n = 0; + + if (ctx->buffer) { + buffer_n = _zip_buffer_read(ctx->buffer, data, length); + + data += buffer_n; + length -= buffer_n; + + if (_zip_buffer_eof(ctx->buffer)) { + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + } } - if (len == 0) { - if (!verify_hmac(src, ctx)) { - return -1; - } - return 0; + if (ctx->eof) { + return buffer_n; } - - if ((n=zip_source_read(src, data, len)) < 0) { + + if ((ret = zip_source_read(src, data, length)) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } - ctx->current_position += n; - /* TODO: loop if len > UINT_MAX */ - _zip_fcrypt_decrypt(data, n, &ctx->fcrypt_ctx); + n = (zip_uint64_t)ret; + for (offset = 0; offset < n; offset += ZIP_MIN(n - offset, UINT_MAX)) { + _zip_fcrypt_encrypt(data + offset, ZIP_MIN(n - offset, UINT_MAX), &ctx->fcrypt_ctx); + } - return n; + if (n < length) { + ctx->eof = true; + _zip_fcrypt_end(ctx->data, &ctx->fcrypt_ctx); + if ((ctx->buffer = _zip_buffer_new(ctx->data, HMAC_LENGTH)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + /* TODO: return partial read? */ + return -1; + } + } + + return (zip_int64_t)(buffer_n + n); case ZIP_SOURCE_CLOSE: return 0; @@ -219,11 +210,10 @@ zip_stat_t *st; st = (zip_stat_t *)data; - - st->encryption_method = ZIP_EM_NONE; + st->encryption_method = ctx->encryption_method; st->valid |= ZIP_STAT_ENCRYPTION_METHOD; if (st->valid & ZIP_STAT_COMP_SIZE) { - st->comp_size -= 12 + salt_length[ctx->mode]; + st->comp_size += 12 + salt_length[ctx->mode]; } return 0; @@ -233,7 +223,7 @@ 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); + return zip_error_to_data(&ctx->error, data, length); case ZIP_SOURCE_FREE: winzip_aes_free(ctx); @@ -257,12 +247,13 @@ _zip_crypto_clear(ctx->password, strlen(ctx->password)); free(ctx->password); zip_error_fini(&ctx->error); + _zip_buffer_free(ctx->buffer); free(ctx); } static struct winzip_aes * -winzip_aes_new(int mode, const char *password) { +winzip_aes_new(int mode, zip_uint16_t encryption_method, const char *password) { struct winzip_aes *ctx; if ((ctx = (struct winzip_aes *)malloc(sizeof(*ctx))) == NULL) { @@ -275,8 +266,11 @@ } ctx->mode = mode; + ctx->encryption_method = encryption_method; + ctx->buffer = NULL; zip_error_init(&ctx->error); + ctx->eof = false; return ctx; }
diff --git a/lib/zip_source_zip_new.c b/lib/zip_source_zip_new.c index 40f1195..3d6c342 100644 --- a/lib/zip_source_zip_new.c +++ b/lib/zip_source_zip_new.c
@@ -85,7 +85,7 @@ zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0); return NULL; } - if ((enc_impl=_zip_get_encryption_implementation(st.encryption_method)) == NULL) { + if ((enc_impl=_zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) { zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); return NULL; } @@ -94,7 +94,7 @@ comp_impl = NULL; if ((flags & ZIP_FL_COMPRESSED) == 0) { if (st.comp_method != ZIP_CM_STORE) { - if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) { + if ((comp_impl=_zip_get_compression_implementation(st.comp_method, ZIP_CODEC_DECODE)) == NULL) { zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); return NULL; }
diff --git a/lib/zipint.h b/lib/zipint.h index fe62208..6d16caf 100644 --- a/lib/zipint.h +++ b/lib/zipint.h
@@ -64,6 +64,7 @@ #define CDBUFSIZE (MAXCOMLEN+EOCDLEN+EOCD64LOCLEN) #define BUFSIZE 8192 #define EFZIP64SIZE 28 +#define EF_WINZIP_AES_SIZE 7 #define ZIP_CM_REPLACED_DEFAULT (-2) #define ZIP_CM_WINZIP_AES 99 /* Winzip AES encrypted */ @@ -98,8 +99,8 @@ typedef zip_source_t *(*zip_compression_implementation)(zip_t *, zip_source_t *, zip_int32_t, int); typedef zip_source_t *(*zip_encryption_implementation)(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); -zip_compression_implementation _zip_get_compression_implementation(zip_int32_t); -zip_encryption_implementation _zip_get_encryption_implementation(zip_uint16_t); +zip_compression_implementation _zip_get_compression_implementation(zip_int32_t method, int operation); +zip_encryption_implementation _zip_get_encryption_implementation(zip_uint16_t method, int operation); @@ -120,7 +121,8 @@ 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); -zip_source_t *zip_source_winzip_aes(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_winzip_aes_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_winzip_aes_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); /* error source for layered sources */ @@ -211,13 +213,15 @@ /* zip archive directory entry (central or local) */ -#define ZIP_DIRENT_COMP_METHOD 0x0001u -#define ZIP_DIRENT_FILENAME 0x0002u -#define ZIP_DIRENT_COMMENT 0x0004u -#define ZIP_DIRENT_EXTRA_FIELD 0x0008u -#define ZIP_DIRENT_ATTRIBUTES 0x0010u -#define ZIP_DIRENT_LAST_MOD 0x0020u -#define ZIP_DIRENT_ALL 0xffffu +#define ZIP_DIRENT_COMP_METHOD 0x0001u +#define ZIP_DIRENT_FILENAME 0x0002u +#define ZIP_DIRENT_COMMENT 0x0004u +#define ZIP_DIRENT_EXTRA_FIELD 0x0008u +#define ZIP_DIRENT_ATTRIBUTES 0x0010u +#define ZIP_DIRENT_LAST_MOD 0x0020u +#define ZIP_DIRENT_ENCRYPTION_METHOD 0x0040u +#define ZIP_DIRENT_PASSWORD 0x0080u +#define ZIP_DIRENT_ALL ZIP_UINT32_MAX struct zip_dirent { zip_uint32_t changed; @@ -243,6 +247,7 @@ zip_uint64_t offset; /* (c) offset of local header */ zip_uint16_t encryption_method; /* encryption method, computed from other fields */ + char *password; /* file specific encryption password */ }; /* zip archive central directory */ @@ -288,6 +293,7 @@ bool source_closed; /* set if source archive is closed */ zip_t *source_archive; /* zip archive we're reading from, NULL if not from archive */ unsigned int refcount; + bool eof; /* EOF reached */ }; #define ZIP_SOURCE_IS_OPEN_READING(src) ((src)->open_count > 0) @@ -340,6 +346,8 @@ extern const int _zip_nerr_str; extern const int _zip_err_type[]; +#define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b)) +#define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b)) #define ZIP_ENTRY_CHANGED(e, f) ((e)->changes && ((e)->changes->changed & (f))) @@ -380,6 +388,7 @@ int _zip_buffer_put_32(zip_buffer_t *buffer, zip_uint32_t i); int _zip_buffer_put_64(zip_buffer_t *buffer, zip_uint64_t i); int _zip_buffer_put_8(zip_buffer_t *buffer, zip_uint8_t i); +zip_uint64_t _zip_buffer_read(zip_buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length); int _zip_buffer_skip(zip_buffer_t *buffer, zip_uint64_t length); int _zip_buffer_set_offset(zip_buffer_t *buffer, zip_uint64_t offset); zip_uint64_t _zip_buffer_size(zip_buffer_t *buffer); @@ -443,6 +452,8 @@ zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *); +bool zip_random(zip_uint8_t *buffer, zip_uint16_t length); + int _zip_read(zip_source_t *src, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error); int _zip_read_at_offset(zip_source_t *src, zip_uint64_t offset, unsigned char *b, size_t length, zip_error_t *error); zip_uint8_t *_zip_read_data(zip_buffer_t *buffer, zip_source_t *src, size_t length, bool nulp, zip_error_t *error); @@ -453,8 +464,10 @@ void _zip_set_open_error(int *zep, const zip_error_t *err, int ze); zip_int64_t _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command); +bool _zip_source_eof(zip_source_t *); zip_source_t *_zip_source_file_or_p(const char *, FILE *, zip_uint64_t, zip_int64_t, const zip_stat_t *, zip_error_t *error); void _zip_source_invalidate(zip_source_t *src); +bool _zip_source_had_error(zip_source_t *); zip_source_t *_zip_source_new(zip_error_t *error); int _zip_source_set_source_archive(zip_source_t *, zip_t *); zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_error_t *error);