Winzip AES encryption support for writing.
diff --git a/TODO.md b/TODO.md
index 928a97c..902ac6b 100644
--- a/TODO.md
+++ b/TODO.md
@@ -35,10 +35,9 @@
 # Features
 
 * Winzip AES support
-  * encryption
-    * don't write CRC if file too small
-	* write EF 0x9901
-  * test cases: correct pw, wrong pw, 128/192/256, <=20, >20
+  * test cases decryption: correct password, wrong password, no password, 128/192/256, <=20, >20
+  * test cases encryption: no password, default password, file-specific password, 128/192/256, <=20, >20
+  * Windows random
   * document existence
 * xz support
 * consistently use `_zip_crypto_clear()` for passwords
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);
diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt
index a8a8e3d..9f893f5 100644
--- a/man/CMakeLists.txt
+++ b/man/CMakeLists.txt
@@ -31,6 +31,9 @@
   zip_file_get_error.mdoc
   zip_file_rename.mdoc
   zip_file_set_comment.mdoc
+  zip_file_set_encryption.mdoc
+  zip_file_set_external_attributes.mdoc
+  zip_file_set_mtime.mdoc
   zip_file_strerror.mdoc
   zip_fopen.mdoc
   zip_fopen_encrypted.mdoc
diff --git a/man/Makefile.am b/man/Makefile.am
index 2ceb27c..4e0cc8e 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -40,6 +40,7 @@
 	zip_file_get_external_attributes.mdoc \
 	zip_file_rename.mdoc \
 	zip_file_set_comment.mdoc \
+	zip_file_set_encryption.mdoc \
 	zip_file_set_external_attributes.mdoc \
 	zip_file_set_mtime.mdoc \
 	zip_file_strerror.mdoc \
diff --git a/man/zip_file_set_encryption.mdoc b/man/zip_file_set_encryption.mdoc
new file mode 100644
index 0000000..14fdf98
--- /dev/null
+++ b/man/zip_file_set_encryption.mdoc
@@ -0,0 +1,112 @@
+.\" zip_file_set_encryption.mdoc -- set encryption method for file
+.\" Copyright (C) 2016 Dieter Baron and Thomas Klausner
+.\"
+.\" This file is part of libzip, a library to manipulate ZIP files.
+.\" 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.
+.\"
+.Dd December 16, 2016
+.Dt ZIP_FILE_SET_ENCRYPTION 3
+.Os
+.Sh NAME
+.Nm zip_file_set_encryption
+.Nd set encryption method for file in zip
+.Sh LIBRARY
+libzip (-lzip)
+.Sh SYNOPSIS
+.In zip.h
+.Ft int
+.Fn zip_file_set_encryption "zip_t *archive" "zip_uint64_t index" \
+"zip_int32_t method" "const char *password"
+.Sh DESCRIPTION
+The
+.Fn zip_file_set_encryption
+function sets the encryption method for the file at position
+.Ar index
+in the zip archive to
+.Ar method
+using the password
+.Ar password .
+The
+.Ar method
+is the same as returned by
+.Xr zip_stat 3 .
+For the
+.Ar method
+argument, currently only the following values are supported:
+.Bl -tag -width ZIP_CM_DEFLATE_XX
+.It Dv ZIP_EM_NONE
+No encryption.
+.It Dv ZIP_EM_AES_128
+Winzip AES-128 encryption.
+.It Dv ZIP_EM_AES_192
+Winzip AES-192 encryption.
+.It Dv ZIP_EM_AES_256
+Winzip AES-256 encryption.
+.El
+.Pp
+If
+.Ar password
+is
+.Dv NULL ,
+the default password provided by
+.Xr zip_set_default_password 3
+is used.
+.Pp
+The current encryption method for a file in a zip archive can be
+determined using
+.Xr zip_stat 3 .
+.Sh RETURN VALUES
+Upon successful completion 0 is returned.
+Otherwise, \-1 is returned and the error information in
+.Ar archive
+is set to indicate the error.
+.Sh ERRORS
+.Fn zip_file_set_encryption
+fails if:
+.Bl -tag -width Er
+.It Bq Er ZIP_ER_ENCRNOTSUPP
+Unsupported compression method requested.
+.It Bq Er ZIP_ER_INVAL
+.Ar index
+is not a valid file index in
+.Ar archive ,
+or the argument combination is invalid.
+.It Bq Er ZIP_ER_MEMORY
+Required memory could not be allocated.
+.It Bq Er ZIP_ER_RDONLY
+Read-only zip file, no changes allowed.
+.El
+.Sh SEE ALSO
+.Xr libzip 3 ,
+.Xr zip_set_default_password 3 ,
+.Xr zip_stat 3
+.Sh AUTHORS
+.An -nosplit
+.An Dieter Baron Aq Mt dillo@nih.at
+and
+.An Thomas Klausner Aq Mt tk@giga.or.at
diff --git a/man/zip_source_function.mdoc b/man/zip_source_function.mdoc
index ae8b865..4cdfd44 100644
--- a/man/zip_source_function.mdoc
+++ b/man/zip_source_function.mdoc
@@ -1,5 +1,5 @@
 .\" zip_source_function.mdoc -- create data source from function
-.\" Copyright (C) 2004-2014 Dieter Baron and Thomas Klausner
+.\" Copyright (C) 2004-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>
@@ -29,7 +29,7 @@
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd November 13, 2014
+.Dd December 16, 2016
 .Dt ZIP_SOURCE_FUNCTION 3
 .Os
 .Sh NAME
@@ -146,7 +146,7 @@
 .Ar len .
 Return the number of bytes placed into
 .Ar data
-on success.
+on success, and zero for end-of-file.
 .Ss Dv ZIP_SOURCE_REMOVE
 Remove the underlying file.
 This is called if a zip archive is empty when closed.
diff --git a/man/zip_source_read.mdoc b/man/zip_source_read.mdoc
index c9a29b9..7fb5d96 100644
--- a/man/zip_source_read.mdoc
+++ b/man/zip_source_read.mdoc
@@ -1,5 +1,5 @@
 .\" zip_source_read.mdoc -- read data from zip source
-.\" Copyright (C) 2014 Dieter Baron and Thomas Klausner
+.\" Copyright (C) 2014-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>
@@ -29,7 +29,7 @@
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd November 18, 2014
+.Dd December 16, 2016
 .Dt ZIP_SOURCE_READ 3
 .Os
 .Sh NAME
@@ -58,6 +58,7 @@
 first.
 .Sh RETURN VALUES
 Upon successful completion the number of bytes read is returned.
+Upon reading end-of-file, zero is returned.
 Otherwise, \-1 is returned and the error information in
 .Ar source
 is set to indicate the error.
diff --git a/man/ziptool.mdoc b/man/ziptool.mdoc
index ec7a751..4eca252 100644
--- a/man/ziptool.mdoc
+++ b/man/ziptool.mdoc
@@ -29,7 +29,7 @@
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd August 25, 2016
+.Dd December 16, 2016
 .Dt ZIPTOOL 1
 .Os
 .Sh NAME
@@ -256,6 +256,13 @@
 Currently,
 .Ar compression_flags
 are ignored.
+.It Cm set_file_encryption Ar index method password
+Set file encryption method for archive entry
+.Ar index
+to
+.Ar method
+with password
+.Ar password .
 .It Cm set_file_mtime Ar index timestamp
 Set file modification time for archive entry
 .Ar index
@@ -304,6 +311,19 @@
 .It
 .Dv store
 .El
+.Ss Encryption Methods
+Some commands take encryption method arguments.
+Supported methods are:
+.Bl -bullet -compact -offset indent
+.It
+.Dv none
+.It
+.Dv AES-128
+.It
+.Dv AES-192
+.It
+.Dv AES-256
+.El
 .Sh EXIT STATUS
 .Ex -std
 .Sh EXAMPLES
diff --git a/src/ziptool.c b/src/ziptool.c
index a0f4be4..63763ce 100644
--- a/src/ziptool.c
+++ b/src/ziptool.c
@@ -71,6 +71,7 @@
 
 static zip_flags_t get_flags(const char *arg);
 static zip_int32_t get_compression_method(const char *arg);
+static zip_uint16_t get_encryption_method(const char *arg);
 static void hexdump(const zip_uint8_t *data, zip_uint16_t len);
 static zip_t *read_to_memory(const char *archive, int flags, zip_error_t *error, zip_source_t **srcp);
 static zip_source_t *source_nul(zip_t *za, zip_uint64_t length);
@@ -482,6 +483,24 @@
 }
 
 static int
+set_file_encryption(int argc, char *argv[]) {
+    zip_int32_t method;
+    zip_uint64_t idx;
+    char *password;
+    idx = strtoull(argv[0], NULL, 10);
+    method = get_encryption_method(argv[1]);
+    password = argv[2];
+    if (strlen(password) == 0) {
+	password = NULL;
+    }
+    if (zip_file_set_encryption(za, idx, method, password) < 0) {
+	fprintf(stderr, "can't set file encryption method at index '%" PRIu64 "' to `%s': %s\n", idx, argv[1], zip_strerror(za));
+	return -1;
+    }
+    return 0;
+}
+
+static int
 set_file_mtime(int argc, char *argv[]) {
     /* set file last modification time (mtime) */
     time_t mtime;
@@ -617,10 +636,26 @@
         return ZIP_CM_DEFAULT;
     else if (strcmp(arg, "store") == 0)
         return ZIP_CM_STORE;
-    else if (strcmp(arg, "deflate") ==0)
+    else if (strcmp(arg, "deflate") == 0)
         return ZIP_CM_DEFLATE;
-    else if (strcmp(arg, "unknown") ==0)
-        return 99;
+    else if (strcmp(arg, "unknown") == 0)
+        return 100;
+    return 0; /* TODO: error handling */
+}
+
+static zip_uint16_t
+get_encryption_method(const char *arg)
+{
+    if (strcmp(arg, "none") == 0)
+        return ZIP_EM_NONE;
+    else if (strcmp(arg, "AES-128") == 0)
+        return ZIP_EM_AES_128;
+    else if (strcmp(arg, "AES-192") == 0)
+        return ZIP_EM_AES_192;
+    else if (strcmp(arg, "AES-256") == 0)
+        return ZIP_EM_AES_256;
+    else if (strcmp(arg, "unknown") == 0)
+        return 100;
     return 0; /* TODO: error handling */
 }
 
@@ -898,6 +933,7 @@
     { "set_extra", 5, "index extra_id extra_index flags value", "set extra field", set_extra },
     { "set_file_comment", 2, "index comment", "set file comment", set_file_comment },
     { "set_file_compression", 3, "index method compression_flags", "set file compression method", set_file_compression },
+    { "set_file_encryption", 3, "index method password", "set file encryption method", set_file_encryption },
     { "set_file_mtime", 2, "index timestamp", "set file modification time", set_file_mtime },
     { "set_file_mtime_all", 1, "timestamp", "set file modification time for all files", set_file_mtime_all },
     { "set_password", 1, "password", "set default password for encryption", set_password },
@@ -973,6 +1009,11 @@
 	    "\tdefault\n"
 	    "\tdeflate\n"
 	    "\tstore\n");
+    fprintf(out, "\nSupported compression methods are:\n"
+	    "\tnone\n"
+	    "\tAES-128\n"
+	    "\tAES-192\n"
+	    "\tAES-256\n");
     fprintf(out, "\nThe index is zero-based.\n");
     exit(0);
 }