From: Michihiro NAKAJIMA Date: Mon, 13 Oct 2014 05:41:21 +0000 (+0900) Subject: Implement HMAC, PBKDF2 and AES support on Windows using CNG for X-Git-Tag: v3.1.900a~180 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=178bf9b890c71f8a64e1960e5449faa84a84c4eb;p=thirdparty%2Flibarchive.git Implement HMAC, PBKDF2 and AES support on Windows using CNG for Zip encryption and decryption. --- diff --git a/CMakeLists.txt b/CMakeLists.txt index cdd1f0f20..74c83f6f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,8 @@ OPTION(ENABLE_BZip2 "Enable the use of the system found BZip2 library if found" OPTION(ENABLE_EXPAT "Enable the use of the system found EXPAT library if found" ON) OPTION(ENABLE_PCREPOSIX "Enable the use of the system found PCREPOSIX library if found" ON) OPTION(ENABLE_LibGCC "Enable the use of the system found LibGCC library if found" ON) +# CNG is used for encrypt/decrypt Zip archives on Windows. +OPTION(ENABLE_CNG "Enable the use of CNG(Crypto Next Generation)" ON) OPTION(ENABLE_TAR "Enable tar building" ON) OPTION(ENABLE_TAR_SHARED "Enable dynamic build of tar" FALSE) @@ -571,6 +573,11 @@ LA_CHECK_INCLUDE_FILE("utime.h" HAVE_UTIME_H) LA_CHECK_INCLUDE_FILE("wchar.h" HAVE_WCHAR_H) LA_CHECK_INCLUDE_FILE("wctype.h" HAVE_WCTYPE_H) LA_CHECK_INCLUDE_FILE("windows.h" HAVE_WINDOWS_H) +IF(ENABLE_CNG) + LA_CHECK_INCLUDE_FILE("Bcrypt.h" HAVE_BCRYPT_H) +ELSE(ENABLE_CNG) + UNSET(HAVE_BCRYPT_H CACHE) +ENDIF(ENABLE_CNG) # Following files need windwos.h, so we should test it after windows.h test. LA_CHECK_INCLUDE_FILE("wincrypt.h" HAVE_WINCRYPT_H) LA_CHECK_INCLUDE_FILE("winioctl.h" HAVE_WINIOCTL_H) diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index ee48de87a..a1272e18f 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -334,6 +334,9 @@ typedef uint64_t uintmax_t; /* Define to 1 if you have the header file. */ #cmakedefine HAVE_ATTR_XATTR_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_BCRYPT_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_BSDXML_H 1 diff --git a/configure.ac b/configure.ac index 216ab76fa..09e505c39 100644 --- a/configure.ac +++ b/configure.ac @@ -276,6 +276,7 @@ AC_CHECK_HEADERS([sys/param.h sys/poll.h sys/select.h sys/statfs.h sys/statvfs.h AC_CHECK_HEADERS([sys/time.h sys/utime.h sys/utsname.h sys/vfs.h]) AC_CHECK_HEADERS([time.h unistd.h utime.h wchar.h wctype.h]) AC_CHECK_HEADERS([windows.h]) +AC_CHECK_HEADERS([Bcrypt.h]) # check windows.h first; the other headers require it. AC_CHECK_HEADERS([wincrypt.h winioctl.h],[],[], [[#ifdef HAVE_WINDOWS_H diff --git a/libarchive/archive_cryptor.c b/libarchive/archive_cryptor.c index 1d819fd48..94dce5ace 100644 --- a/libarchive/archive_cryptor.c +++ b/libarchive/archive_cryptor.c @@ -44,6 +44,34 @@ pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, return 0; } +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#ifdef _MSC_VER +#pragma comment(lib, "Bcrypt.lib") +#endif + +static int +pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, + size_t salt_len, unsigned rounds, uint8_t *derived_key, + size_t derived_key_len) +{ + NTSTATUS status; + BCRYPT_ALG_HANDLE hAlg; + + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, + MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); + if (!BCRYPT_SUCCESS(status)) + return -1; + + status = BCryptDeriveKeyPBKDF2(hAlg, + (PUCHAR)(uintptr_t)pw, (ULONG)pw_len, + (PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds, + (PUCHAR)derived_key, (ULONG)derived_key_len, 0); + + BCryptCloseAlgorithmProvider(hAlg, 0); + + return (BCRYPT_SUCCESS(status)) ? 0: -1; +} + #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H) static int @@ -124,6 +152,107 @@ aes_ctr_release(archive_crypto_ctx *ctx) return 0; } +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) + +static int +aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) +{ + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_KEY_HANDLE hKey; + DWORD keyObj_len, aes_key_len; + PBYTE keyObj; + ULONG result; + NTSTATUS status; + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + + ctx->hAlg = NULL; + ctx->hKey = NULL; + ctx->keyObj = NULL; + switch (key_len) { + case 16: aes_key_len = 128; break; + case 24: aes_key_len = 192; break; + case 32: aes_key_len = 256; break; + default: return -1; + } + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, + MS_PRIMITIVE_PROVIDER, 0); + if (!BCRYPT_SUCCESS(status)) + return -1; + status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths, + sizeof(key_lengths), &result, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + if (key_lengths.dwMinLength > aes_key_len + || key_lengths.dwMaxLength < aes_key_len) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len, + sizeof(keyObj_len), &result, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len); + if (keyObj == NULL) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, + (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + HeapFree(GetProcessHeap(), 0, keyObj); + return -1; + } + status = BCryptGenerateSymmetricKey(hAlg, &hKey, + keyObj, keyObj_len, + (PUCHAR)(uintptr_t)key, (ULONG)key_len, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + HeapFree(GetProcessHeap(), 0, keyObj); + return -1; + } + + ctx->hAlg = hAlg; + ctx->hKey = hKey; + ctx->keyObj = keyObj; + ctx->keyObj_len = keyObj_len; + ctx->encr_pos = AES_BLOCK_SIZE; + + return 0; +} + +static int +aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) +{ + NTSTATUS status; + ULONG result; + + status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE, + NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE, + &result, 0); + return BCRYPT_SUCCESS(status) ? 0 : -1; +} + +static int +aes_ctr_release(archive_crypto_ctx *ctx) +{ + + if (ctx->hAlg != NULL) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + ctx->hAlg = NULL; + BCryptDestroyKey(ctx->hKey); + ctx->hKey = NULL; + HeapFree(GetProcessHeap(), 0, ctx->keyObj); + ctx->keyObj = NULL; + } + memset(ctx, 0, sizeof(*ctx)); + return 0; +} + #elif defined(HAVE_LIBNETTLE) static int diff --git a/libarchive/archive_cryptor_private.h b/libarchive/archive_cryptor_private.h index 35b3385e0..88c208db6 100644 --- a/libarchive/archive_cryptor_private.h +++ b/libarchive/archive_cryptor_private.h @@ -45,14 +45,16 @@ typedef struct { unsigned encr_pos; } archive_crypto_ctx; -#elif defined(_WIN32) && !defined(__CYGWIN__) +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) +#include #define AES_MAX_KEY_SIZE 32 #define AES_BLOCK_SIZE 16 typedef struct { - int ctx; - uint8_t key[AES_MAX_KEY_SIZE]; - unsigned key_len; + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_KEY_HANDLE hKey; + PBYTE keyObj; + DWORD keyObj_len; uint8_t nonce[AES_BLOCK_SIZE]; uint8_t encr_buf[AES_BLOCK_SIZE]; unsigned encr_pos; @@ -88,6 +90,10 @@ typedef struct { unsigned encr_pos; } archive_crypto_ctx; +#else + +typedef int archive_crypto_ctx; + #endif /* defines */ diff --git a/libarchive/archive_hmac.c b/libarchive/archive_hmac.c index 9e6834ec9..898853bd1 100644 --- a/libarchive/archive_hmac.c +++ b/libarchive/archive_hmac.c @@ -60,6 +60,75 @@ __hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) memset(ctx, 0, sizeof(*ctx)); } +#elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) + +static int +__hmac_sha1_init(archive_hmac_sha1_ctx *ctx, const uint8_t *key, size_t key_len) +{ + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; + DWORD hash_len; + PBYTE hash; + ULONG result; + NTSTATUS status; + + ctx->hAlg = NULL; + status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, + MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); + if (!BCRYPT_SUCCESS(status)) + return -1; + status = BCryptGetProperty(hAlg, BCRYPT_HASH_LENGTH, (PUCHAR)&hash_len, + sizeof(hash_len), &result, 0); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + hash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, hash_len); + if (hash == NULL) { + BCryptCloseAlgorithmProvider(hAlg, 0); + return -1; + } + status = BCryptCreateHash(hAlg, &hHash, NULL, 0, + (PUCHAR)key, (ULONG)key_len, BCRYPT_HASH_REUSABLE_FLAG); + if (!BCRYPT_SUCCESS(status)) { + BCryptCloseAlgorithmProvider(hAlg, 0); + HeapFree(GetProcessHeap(), 0, hash); + return -1; + } + + ctx->hAlg = hAlg; + ctx->hHash = hHash; + ctx->hash_len = hash_len; + ctx->hash = hash; + + return 0; +} + +static void +__hmac_sha1_update(archive_hmac_sha1_ctx *ctx, const uint8_t *data, + size_t data_len) +{ + BCryptHashData(ctx->hHash, (PUCHAR)(uintptr_t)data, (ULONG)data_len, 0); +} + +static void +__hmac_sha1_final(archive_hmac_sha1_ctx *ctx, uint8_t *out, size_t *out_len) +{ + BCryptFinishHash(ctx->hHash, ctx->hash, ctx->hash_len, 0); + if (ctx->hash_len == *out_len) + memcpy(out, ctx->hash, *out_len); +} + +static void +__hmac_sha1_cleanup(archive_hmac_sha1_ctx *ctx) +{ + if (ctx->hAlg != NULL) { + BCryptCloseAlgorithmProvider(ctx->hAlg, 0); + HeapFree(GetProcessHeap(), 0, ctx->hash); + ctx->hAlg = NULL; + } +} + #elif defined(HAVE_LIBNETTLE) static int diff --git a/libarchive/archive_hmac_private.h b/libarchive/archive_hmac_private.h index 223e7ca66..e5be3dc7a 100644 --- a/libarchive/archive_hmac_private.h +++ b/libarchive/archive_hmac_private.h @@ -36,8 +36,15 @@ typedef CCHmacContext archive_hmac_sha1_ctx; #elif defined(_WIN32) && !defined(__CYGWIN__) +#include -typedef int archive_hmac_sha1_ctx; +typedef struct { + BCRYPT_ALG_HANDLE hAlg; + BCRYPT_HASH_HANDLE hHash; + DWORD hash_len; + PBYTE hash; + +} archive_hmac_sha1_ctx; #elif defined(HAVE_LIBNETTLE) #include @@ -49,6 +56,10 @@ typedef struct hmac_sha1_ctx archive_hmac_sha1_ctx; typedef HMAC_CTX archive_hmac_sha1_ctx; +#else + +typedef int archive_hmac_sha1_ctx; + #endif