Add support for mbed TLS as crypto backend.

Contributed by Michael Balzer.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c19b45f..bea4878 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -7,10 +7,12 @@
 
 PROJECT(libzip C)
 
-OPTION(ENABLE_GNUTLS "Enable use of GnuTLS" ON)
-OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
 OPTION(ENABLE_COMMONCRYPTO "Enable use of CommonCrypto" ON)
+OPTION(ENABLE_GNUTLS "Enable use of GnuTLS" ON)
+OPTION(ENABLE_MBEDTLS "Enable use of mbed TLS" ON)
+OPTION(ENABLE_OPENSSL "Enable use of OpenSSL" ON)
 OPTION(ENABLE_WINDOWS_CRYPTO "Enable use of Windows cryptography libraries" ON)
+
 OPTION(ENABLE_BZIP2 "Enable use of BZip2" ON)
 
 OPTION(BUILD_TOOLS "Build tools in the src directory (zipcmp, zipmerge, ziptool)" ON)
@@ -38,6 +40,12 @@
 ELSE()
   SET(GNUTLS_FOUND FALSE)
 ENDIF()
+IF(ENABLE_MBEDTLS)
+  FIND_PATH(MBEDTLS_INCLUDE_DIR mbedtls/aes.h)
+  FIND_LIBRARY(MBEDTLS_LIBRARIES NAMES mbedtls)
+ELSE()
+  SET(MBEDTLS_LIBRARIES FALSE)
+ENDIF()
 IF(ENABLE_OPENSSL)
   INCLUDE(FindOpenSSL)
 ELSE()
@@ -223,10 +231,15 @@
   SET (HAVE_OPENSSL 1)
   INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
   SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${OPENSSL_LIBRARIES})
+ELSEIF (MBEDTLS_LIBRARIES)
+  SET (HAVE_CRYPTO 1)
+  SET (HAVE_MBEDTLS 1)
+  INCLUDE_DIRECTORIES(${MBEDTLS_INCLUDE_DIR})
+  SET (OPTIONAL_LIBRARY ${OPTIONAL_LIBRARY} ${MBEDTLS_LIBRARIES})
 ENDIF()
 
 IF (NOT HAVE_CRYPTO)
-  MESSAGE(WARNING "-- neither OpenSSL, GnuTLS, Common Crypto nor Windows Cryptography found; AES support disabled")
+  MESSAGE(WARNING "-- neither Common Crypto, GnuTLS, mbed TLS, OpenSSL, nor Windows Cryptography found; AES support disabled")
 ENDIF()
 
 IF(MSVC)
diff --git a/INSTALL.md b/INSTALL.md
index 053174c..eef900e 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -12,13 +12,14 @@
 listed in order of preference:
 
 - Apple's CommonCrypto (available on macOS and iOS)
-- Microsoft Windows Cryptography Framework
 - [GnuTLS](https://www.gnutls.org/)
+- [mbed TLS](https://tls.mbed.org/)
 - [OpenSSL](https://www.openssl.org/) >= 1.0.
+- Microsoft Windows Cryptography Framework
 
 If you don't want a library even if it is installed, you can
 pass `-DENABLE_<LIBRARY>=OFF` to cmake, where `<LIBRARY>` is one of
-`COMMONCRYPTO`, `OPENSSL`, or `GNUTLS`.
+`COMMONCRYPTO`, `GNUTLS`, `MBEDTLS`, or `OPENSSL`.
 
 The basic usage is
 ```sh
diff --git a/NEWS.md b/NEWS.md
index fb93099..debe6e9 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,6 +1,7 @@
 1.5.2 [201X-XX-XX]
 ==================
 * Support systems with small stack size.
+* Support mbed TLS as crypto backend.
 
 1.5.1 [2018-04-11]
 ==================
diff --git a/THANKS b/THANKS
index abdf46c..9db30fa 100644
--- a/THANKS
+++ b/THANKS
@@ -48,6 +48,7 @@
 Martin Buchholz <martinrb@google.com>
 Martin Herkt
 Martin Szulecki <m.szulecki@libimobiledevice.org>
+Michael Balzer
 Michael Beck <mm.beck@gmx.net>
 MichaƂ Janiszewski
 Michal Vyskocil <mvyskocil@suse.cz>
diff --git a/cmake-config.h.in b/cmake-config.h.in
index 2fa8e5b..bf8264c 100644
--- a/cmake-config.h.in
+++ b/cmake-config.h.in
@@ -29,9 +29,10 @@
 #cmakedefine HAVE_GETPROGNAME
 #cmakedefine HAVE_GNUTLS
 #cmakedefine HAVE_LIBBZ2
+#cmakedefine HAVE_MBEDTLS
+#cmakedefine HAVE_MKSTEMP
 #cmakedefine HAVE_OPEN
 #cmakedefine HAVE_OPENSSL
-#cmakedefine HAVE_MKSTEMP
 #cmakedefine HAVE_SETMODE
 #cmakedefine HAVE_SNPRINTF
 #cmakedefine HAVE_SSIZE_T_LIBZIP
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index ee27ee9..8be48a0 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -205,6 +205,9 @@
 ELSEIF(HAVE_OPENSSL)
   SET(LIBZIP_OPTIONAL_FILES ${LIBZIP_OPTIONAL_FILES} zip_crypto_openssl.c
 )
+ELSEIF(HAVE_MBEDTLS)
+  SET(LIBZIP_OPTIONAL_FILES ${LIBZIP_OPTIONAL_FILES} zip_crypto_mbedtls.c
+)
 ENDIF()
 
 IF(HAVE_CRYPTO)
diff --git a/lib/zip_crypto.h b/lib/zip_crypto.h
index 89bd369..be8f918 100644
--- a/lib/zip_crypto.h
+++ b/lib/zip_crypto.h
@@ -45,6 +45,8 @@
 #include "zip_crypto_gnutls.h"
 #elif defined(HAVE_OPENSSL)
 #include "zip_crypto_openssl.h"
+#elif defined(HAVE_MBEDTLS)
+#include "zip_crypto_mbedtls.h"
 #else
 #error "no crypto backend found"
 #endif
diff --git a/lib/zip_crypto_mbedtls.c b/lib/zip_crypto_mbedtls.c
new file mode 100644
index 0000000..8e74fbc
--- /dev/null
+++ b/lib/zip_crypto_mbedtls.c
@@ -0,0 +1,160 @@
+/*
+  zip_crypto_mbedtls.c -- mbed TLS wrapper
+  Copyright (C) 2018 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 "zipint.h"
+#include "zip_crypto.h"
+
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/pkcs5.h>
+
+
+_zip_crypto_aes_t *
+_zip_crypto_aes_new(const zip_uint8_t *key, zip_uint16_t key_size, zip_error_t *error) {
+    _zip_crypto_aes_t *aes;
+
+    if ((aes = (_zip_crypto_aes_t *)malloc(sizeof(*aes))) == NULL) {
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    mbedtls_aes_init(aes);
+    mbedtls_aes_setkey_enc(aes, (const unsigned char *)key, (unsigned int)key_size);
+
+    return aes;
+}
+
+void
+_zip_crypto_aes_free(_zip_crypto_aes_t *aes) {
+    if (aes == NULL) {
+	return;
+    }
+
+    mbedtls_aes_free(aes);
+    free(aes);
+}
+
+
+_zip_crypto_hmac_t *
+_zip_crypto_hmac_new(const zip_uint8_t *secret, zip_uint64_t secret_length, zip_error_t *error) {
+    _zip_crypto_hmac_t *hmac;
+
+    if (secret_length > INT_MAX) {
+	zip_error_set(error, ZIP_ER_INVAL, 0);
+	return NULL;
+    }
+
+    if ((hmac = (_zip_crypto_hmac_t *)malloc(sizeof(*hmac))) == NULL) {
+	zip_error_set(error, ZIP_ER_MEMORY, 0);
+	return NULL;
+    }
+
+    mbedtls_md_init(hmac);
+
+    if (mbedtls_md_setup(hmac, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1) != 0) {
+	zip_error_set(error, ZIP_ER_INTERNAL, 0);
+	free(hmac);
+	return NULL;
+    }
+
+    if (mbedtls_md_hmac_starts(hmac, (const unsigned char *)secret, (size_t)secret_length) != 0) {
+	zip_error_set(error, ZIP_ER_INTERNAL, 0);
+	free(hmac);
+	return NULL;
+    }
+
+    return hmac;
+}
+
+
+void
+_zip_crypto_hmac_free(_zip_crypto_hmac_t *hmac) {
+    if (hmac == NULL) {
+	return;
+    }
+
+    mbedtls_md_free(hmac);
+    free(hmac);
+}
+
+
+bool
+_zip_crypto_pbkdf2(const zip_uint8_t *key, zip_uint64_t key_length, const zip_uint8_t *salt, zip_uint16_t salt_length, int iterations, zip_uint8_t *output, zip_uint64_t output_length) {
+    mbedtls_md_context_t sha1_ctx;
+    bool ok = true;
+
+    mbedtls_md_init(&sha1_ctx);
+
+    if (mbedtls_md_setup(&sha1_ctx, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1) != 0) {
+	ok = false;
+    }
+
+    if (ok && mbedtls_pkcs5_pbkdf2_hmac(&sha1_ctx, (const unsigned char *)key, (size_t)key_length, (const unsigned char *)salt, (size_t)salt_length, (unsigned int)iterations, (uint32_t)output_length, (unsigned char *)output) != 0) {
+	ok = false;
+    }
+
+    mbedtls_md_free(&sha1_ctx);
+    return ok;
+}
+
+
+typedef struct {
+    mbedtls_entropy_context entropy;
+    mbedtls_ctr_drbg_context ctr_drbg;
+} zip_random_context_t;
+
+ZIP_EXTERN bool
+zip_random(zip_uint8_t *buffer, zip_uint16_t length) {
+    static zip_random_context_t *ctx = NULL;
+    const unsigned char *pers = "zip_crypto_mbedtls";
+
+    if (!ctx) {
+	ctx = (zip_random_context_t *)malloc(sizeof(zip_random_context_t));
+	if (!ctx) {
+	    return false;
+	}
+	mbedtls_entropy_init(&ctx->entropy);
+	mbedtls_ctr_drbg_init(&ctx->ctr_drbg);
+	if (mbedtls_ctr_drbg_seed(&ctx->ctr_drbg, mbedtls_entropy_func, &ctx->entropy, pers, strlen(pers)) != 0) {
+	    mbedtls_ctr_drbg_free(&ctx->ctr_drbg);
+	    mbedtls_entropy_free(&ctx->entropy);
+	    free(ctx);
+	    ctx = NULL;
+	    return false;
+	}
+    }
+
+    return mbedtls_ctr_drbg_random(&ctx->ctr_drbg, (unsigned char *)buffer, (size_t)length) == 0;
+}
diff --git a/lib/zip_crypto_mbedtls.h b/lib/zip_crypto_mbedtls.h
new file mode 100644
index 0000000..af22d5b
--- /dev/null
+++ b/lib/zip_crypto_mbedtls.h
@@ -0,0 +1,54 @@
+/*
+  zip_crypto_mbedtls.h -- definitions for mbedtls wrapper
+  Copyright (C) 2018 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.
+*/
+
+#ifndef HAD_ZIP_CRYPTO_MBEDTLS_H
+#define HAD_ZIP_CRYPTO_MBEDTLS_H
+
+#include <mbedtls/aes.h>
+#include <mbedtls/md.h>
+
+#define _zip_crypto_aes_t mbedtls_aes_context
+#define _zip_crypto_hmac_t mbedtls_md_context_t
+
+_zip_crypto_aes_t *_zip_crypto_aes_new(const zip_uint8_t *key, zip_uint16_t key_size, zip_error_t *error);
+#define _zip_crypto_aes_encrypt_block(aes, in, out) (mbedtls_aes_crypt_ecb((aes), MBEDTLS_AES_ENCRYPT, (in), (out)) == 0)
+void _zip_crypto_aes_free(_zip_crypto_aes_t *aes);
+
+_zip_crypto_hmac_t *_zip_crypto_hmac_new(const zip_uint8_t *secret, zip_uint64_t secret_length, zip_error_t *error);
+#define _zip_crypto_hmac(hmac, data, length) (mbedtls_md_hmac_update((hmac), (data), (length)) == 0)
+#define _zip_crypto_hmac_output(hmac, data) (mbedtls_md_hmac_finish((hmac), (data)) == 0)
+void _zip_crypto_hmac_free(_zip_crypto_hmac_t *hmac);
+
+bool _zip_crypto_pbkdf2(const zip_uint8_t *key, zip_uint64_t key_length, const zip_uint8_t *salt, zip_uint16_t salt_length, int iterations, zip_uint8_t *output, zip_uint64_t output_length);
+
+#endif /*  HAD_ZIP_CRYPTO_MBEDTLS_H */