| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /*
- zip_crypto_win.c -- Windows Crypto API wrapper.
- Copyright (C) 2018-2021 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 <limits.h>
- #include "zipint.h"
- #include "zip_crypto.h"
- #define WIN32_LEAN_AND_MEAN
- #define NOCRYPT
- #include <windows.h>
- #include <bcrypt.h>
- #pragma comment(lib, "bcrypt.lib")
- /*
- This code is using the Cryptography API: Next Generation (CNG)
- https://docs.microsoft.com/en-us/windows/desktop/seccng/cng-portal
- This API is supported on
- - Windows Vista or later (client OS)
- - Windows Server 2008 (server OS)
- - Windows Embedded Compact 2013 (don't know about Windows Embedded Compact 7)
- The code was developed for Windows Embedded Compact 2013 (WEC2013),
- but should be working for all of the above mentioned OSes.
- There are 2 restrictions for WEC2013, Windows Vista and Windows Server 2008:
- 1.) The function "BCryptDeriveKeyPBKDF2" is not available
- I found some code which is implementing this function using the deprecated Crypto API here:
- https://www.idrix.fr/Root/content/view/37/54/
- I took this code and converted it to the newer CNG API. The original code was more
- flexible, but this is not needed here so i refactored it a bit and just kept what is needed.
- The define "HAS_BCRYPTDERIVEKEYPBKDF2" controls whether "BCryptDeriveKeyPBKDF2"
- of the CNG API is used or not. This define must not be set if you are compiling for WEC2013 or Windows Vista.
- 2.) "BCryptCreateHash" can't manage the memory needed for the hash object internally
- On Windows 7 or later it is possible to pass NULL for the hash object buffer.
- This is not supported on WEC2013, so we have to handle the memory allocation/deallocation ourselves.
- There is no #ifdef to control that, because this is working for all supported OSes.
- */
- #if !defined(WINCE) && !defined(__MINGW32__)
- #define HAS_BCRYPTDERIVEKEYPBKDF2
- #endif
- #ifdef HAS_BCRYPTDERIVEKEYPBKDF2
- bool
- _zip_crypto_pbkdf2(const zip_uint8_t *key, zip_uint64_t key_length, const zip_uint8_t *salt, zip_uint16_t salt_length, zip_uint16_t iterations, zip_uint8_t *output, zip_uint16_t output_length) {
- BCRYPT_ALG_HANDLE hAlgorithm = NULL;
- bool result;
- if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG))) {
- return false;
- }
- result = BCRYPT_SUCCESS(BCryptDeriveKeyPBKDF2(hAlgorithm, (PUCHAR)key, (ULONG)key_length, (PUCHAR)salt, salt_length, iterations, output, output_length, 0));
- BCryptCloseAlgorithmProvider(hAlgorithm, 0);
- return result;
- }
- #else
- #include <math.h>
- #define DIGEST_SIZE 20
- #define BLOCK_SIZE 64
- typedef struct {
- BCRYPT_ALG_HANDLE hAlgorithm;
- BCRYPT_HASH_HANDLE hInnerHash;
- BCRYPT_HASH_HANDLE hOuterHash;
- ULONG cbHashObject;
- PUCHAR pbInnerHash;
- PUCHAR pbOuterHash;
- } PRF_CTX;
- static void
- hmacFree(PRF_CTX *pContext) {
- if (pContext->hOuterHash)
- BCryptDestroyHash(pContext->hOuterHash);
- if (pContext->hInnerHash)
- BCryptDestroyHash(pContext->hInnerHash);
- free(pContext->pbOuterHash);
- free(pContext->pbInnerHash);
- if (pContext->hAlgorithm)
- BCryptCloseAlgorithmProvider(pContext->hAlgorithm, 0);
- }
- static BOOL
- hmacPrecomputeDigest(BCRYPT_HASH_HANDLE hHash, PUCHAR pbPassword, DWORD cbPassword, BYTE mask) {
- BYTE buffer[BLOCK_SIZE];
- DWORD i;
- if (cbPassword > BLOCK_SIZE) {
- return FALSE;
- }
- memset(buffer, mask, sizeof(buffer));
- for (i = 0; i < cbPassword; ++i) {
- buffer[i] = (char)(pbPassword[i] ^ mask);
- }
- return BCRYPT_SUCCESS(BCryptHashData(hHash, buffer, sizeof(buffer), 0));
- }
- static BOOL
- hmacInit(PRF_CTX *pContext, PUCHAR pbPassword, DWORD cbPassword) {
- BOOL bStatus = FALSE;
- ULONG cbResult;
- BYTE key[DIGEST_SIZE];
- if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&pContext->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, 0)) || !BCRYPT_SUCCESS(BCryptGetProperty(pContext->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&pContext->cbHashObject, sizeof(pContext->cbHashObject), &cbResult, 0)) || ((pContext->pbInnerHash = malloc(pContext->cbHashObject)) == NULL) || ((pContext->pbOuterHash = malloc(pContext->cbHashObject)) == NULL) || !BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hInnerHash, pContext->pbInnerHash, pContext->cbHashObject, NULL, 0, 0)) || !BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &pContext->hOuterHash, pContext->pbOuterHash, pContext->cbHashObject, NULL, 0, 0))) {
- goto hmacInit_end;
- }
- if (cbPassword > BLOCK_SIZE) {
- BCRYPT_HASH_HANDLE hHash = NULL;
- PUCHAR pbHashObject = malloc(pContext->cbHashObject);
- if (pbHashObject == NULL) {
- goto hmacInit_end;
- }
- bStatus = BCRYPT_SUCCESS(BCryptCreateHash(pContext->hAlgorithm, &hHash, pbHashObject, pContext->cbHashObject, NULL, 0, 0)) && BCRYPT_SUCCESS(BCryptHashData(hHash, pbPassword, cbPassword, 0)) && BCRYPT_SUCCESS(BCryptGetProperty(hHash, BCRYPT_HASH_LENGTH, (PUCHAR)&cbPassword, sizeof(cbPassword), &cbResult, 0)) && BCRYPT_SUCCESS(BCryptFinishHash(hHash, key, cbPassword, 0));
- if (hHash)
- BCryptDestroyHash(hHash);
- free(pbHashObject);
- if (!bStatus) {
- goto hmacInit_end;
- }
- pbPassword = key;
- }
- bStatus = hmacPrecomputeDigest(pContext->hInnerHash, pbPassword, cbPassword, 0x36) && hmacPrecomputeDigest(pContext->hOuterHash, pbPassword, cbPassword, 0x5C);
- hmacInit_end:
- if (bStatus == FALSE)
- hmacFree(pContext);
- return bStatus;
- }
- static BOOL
- hmacCalculateInternal(BCRYPT_HASH_HANDLE hHashTemplate, PUCHAR pbData, DWORD cbData, PUCHAR pbOutput, DWORD cbOutput, DWORD cbHashObject) {
- BOOL success = FALSE;
- BCRYPT_HASH_HANDLE hHash = NULL;
- PUCHAR pbHashObject = malloc(cbHashObject);
- if (pbHashObject == NULL) {
- return FALSE;
- }
- if (BCRYPT_SUCCESS(BCryptDuplicateHash(hHashTemplate, &hHash, pbHashObject, cbHashObject, 0))) {
- success = BCRYPT_SUCCESS(BCryptHashData(hHash, pbData, cbData, 0)) && BCRYPT_SUCCESS(BCryptFinishHash(hHash, pbOutput, cbOutput, 0));
- BCryptDestroyHash(hHash);
- }
- free(pbHashObject);
- return success;
- }
- static BOOL
- hmacCalculate(PRF_CTX *pContext, PUCHAR pbData, DWORD cbData, PUCHAR pbDigest) {
- DWORD cbResult;
- DWORD cbHashObject;
- return BCRYPT_SUCCESS(BCryptGetProperty(pContext->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&cbHashObject, sizeof(cbHashObject), &cbResult, 0)) && hmacCalculateInternal(pContext->hInnerHash, pbData, cbData, pbDigest, DIGEST_SIZE, cbHashObject) && hmacCalculateInternal(pContext->hOuterHash, pbDigest, DIGEST_SIZE, pbDigest, DIGEST_SIZE, cbHashObject);
- }
- static void
- myxor(LPBYTE ptr1, LPBYTE ptr2, DWORD dwLen) {
- while (dwLen--)
- *ptr1++ ^= *ptr2++;
- }
- BOOL
- pbkdf2(PUCHAR pbPassword, ULONG cbPassword, PUCHAR pbSalt, ULONG cbSalt, DWORD cIterations, PUCHAR pbDerivedKey, ULONG cbDerivedKey) {
- BOOL bStatus = FALSE;
- DWORD l, r, dwULen, i, j;
- BYTE Ti[DIGEST_SIZE];
- BYTE V[DIGEST_SIZE];
- LPBYTE U = malloc(max((cbSalt + 4), DIGEST_SIZE));
- PRF_CTX prfCtx = {0};
- if (U == NULL) {
- return FALSE;
- }
- if (pbPassword == NULL || cbPassword == 0 || pbSalt == NULL || cbSalt == 0 || cIterations == 0 || pbDerivedKey == NULL || cbDerivedKey == 0) {
- free(U);
- return FALSE;
- }
- if (!hmacInit(&prfCtx, pbPassword, cbPassword)) {
- goto PBKDF2_end;
- }
- l = (DWORD)ceil((double)cbDerivedKey / (double)DIGEST_SIZE);
- r = cbDerivedKey - (l - 1) * DIGEST_SIZE;
- for (i = 1; i <= l; i++) {
- ZeroMemory(Ti, DIGEST_SIZE);
- for (j = 0; j < cIterations; j++) {
- if (j == 0) {
- /* construct first input for PRF */
- memcpy(U, pbSalt, cbSalt);
- U[cbSalt] = (BYTE)((i & 0xFF000000) >> 24);
- U[cbSalt + 1] = (BYTE)((i & 0x00FF0000) >> 16);
- U[cbSalt + 2] = (BYTE)((i & 0x0000FF00) >> 8);
- U[cbSalt + 3] = (BYTE)((i & 0x000000FF));
- dwULen = cbSalt + 4;
- }
- else {
- memcpy(U, V, DIGEST_SIZE);
- dwULen = DIGEST_SIZE;
- }
- if (!hmacCalculate(&prfCtx, U, dwULen, V)) {
- goto PBKDF2_end;
- }
- myxor(Ti, V, DIGEST_SIZE);
- }
- if (i != l) {
- memcpy(&pbDerivedKey[(i - 1) * DIGEST_SIZE], Ti, DIGEST_SIZE);
- }
- else {
- /* Take only the first r bytes */
- memcpy(&pbDerivedKey[(i - 1) * DIGEST_SIZE], Ti, r);
- }
- }
- bStatus = TRUE;
- PBKDF2_end:
- hmacFree(&prfCtx);
- free(U);
- return bStatus;
- }
- bool
- _zip_crypto_pbkdf2(const zip_uint8_t *key, zip_uint64_t key_length, const zip_uint8_t *salt, zip_uint16_t salt_length, zip_uint16_t iterations, zip_uint8_t *output, zip_uint16_t output_length) {
- return (key_length <= ZIP_UINT32_MAX) && pbkdf2((PUCHAR)key, (ULONG)key_length, (PUCHAR)salt, salt_length, iterations, output, output_length);
- }
- #endif
- struct _zip_crypto_aes_s {
- BCRYPT_ALG_HANDLE hAlgorithm;
- BCRYPT_KEY_HANDLE hKey;
- ULONG cbKeyObject;
- PUCHAR pbKeyObject;
- };
- _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 = (_zip_crypto_aes_t *)calloc(1, sizeof(*aes));
- ULONG cbResult;
- ULONG key_length = key_size / 8;
- if (aes == NULL) {
- zip_error_set(error, ZIP_ER_MEMORY, 0);
- return NULL;
- }
- if (!BCRYPT_SUCCESS(BCryptOpenAlgorithmProvider(&aes->hAlgorithm, BCRYPT_AES_ALGORITHM, NULL, 0))) {
- _zip_crypto_aes_free(aes);
- return NULL;
- }
- if (!BCRYPT_SUCCESS(BCryptSetProperty(aes->hAlgorithm, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0))) {
- _zip_crypto_aes_free(aes);
- return NULL;
- }
- if (!BCRYPT_SUCCESS(BCryptGetProperty(aes->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&aes->cbKeyObject, sizeof(aes->cbKeyObject), &cbResult, 0))) {
- _zip_crypto_aes_free(aes);
- return NULL;
- }
- aes->pbKeyObject = malloc(aes->cbKeyObject);
- if (aes->pbKeyObject == NULL) {
- _zip_crypto_aes_free(aes);
- zip_error_set(error, ZIP_ER_MEMORY, 0);
- return NULL;
- }
- if (!BCRYPT_SUCCESS(BCryptGenerateSymmetricKey(aes->hAlgorithm, &aes->hKey, aes->pbKeyObject, aes->cbKeyObject, (PUCHAR)key, key_length, 0))) {
- _zip_crypto_aes_free(aes);
- return NULL;
- }
- return aes;
- }
- void
- _zip_crypto_aes_free(_zip_crypto_aes_t *aes) {
- if (aes == NULL) {
- return;
- }
- if (aes->hKey != NULL) {
- BCryptDestroyKey(aes->hKey);
- }
- if (aes->pbKeyObject != NULL) {
- free(aes->pbKeyObject);
- }
- if (aes->hAlgorithm != NULL) {
- BCryptCloseAlgorithmProvider(aes->hAlgorithm, 0);
- }
- free(aes);
- }
- bool
- _zip_crypto_aes_encrypt_block(_zip_crypto_aes_t *aes, const zip_uint8_t *in, zip_uint8_t *out) {
- ULONG cbResult;
- NTSTATUS status = BCryptEncrypt(aes->hKey, (PUCHAR)in, ZIP_CRYPTO_AES_BLOCK_LENGTH, NULL, NULL, 0, (PUCHAR)out, ZIP_CRYPTO_AES_BLOCK_LENGTH, &cbResult, 0);
- return BCRYPT_SUCCESS(status);
- }
- struct _zip_crypto_hmac_s {
- BCRYPT_ALG_HANDLE hAlgorithm;
- BCRYPT_HASH_HANDLE hHash;
- DWORD cbHashObject;
- PUCHAR pbHashObject;
- DWORD cbHash;
- PUCHAR pbHash;
- };
- /* https://code.msdn.microsoft.com/windowsdesktop/Hmac-Computation-Sample-11fe8ec1/sourcecode?fileId=42820&pathId=283874677 */
- _zip_crypto_hmac_t *
- _zip_crypto_hmac_new(const zip_uint8_t *secret, zip_uint64_t secret_length, zip_error_t *error) {
- NTSTATUS status;
- ULONG cbResult;
- _zip_crypto_hmac_t *hmac;
- if (secret_length > INT_MAX) {
- zip_error_set(error, ZIP_ER_INVAL, 0);
- return NULL;
- }
- hmac = (_zip_crypto_hmac_t *)calloc(1, sizeof(*hmac));
- if (hmac == NULL) {
- zip_error_set(error, ZIP_ER_MEMORY, 0);
- return NULL;
- }
- status = BCryptOpenAlgorithmProvider(&hmac->hAlgorithm, BCRYPT_SHA1_ALGORITHM, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
- if (!BCRYPT_SUCCESS(status)) {
- _zip_crypto_hmac_free(hmac);
- return NULL;
- }
- status = BCryptGetProperty(hmac->hAlgorithm, BCRYPT_OBJECT_LENGTH, (PUCHAR)&hmac->cbHashObject, sizeof(hmac->cbHashObject), &cbResult, 0);
- if (!BCRYPT_SUCCESS(status)) {
- _zip_crypto_hmac_free(hmac);
- return NULL;
- }
- hmac->pbHashObject = malloc(hmac->cbHashObject);
- if (hmac->pbHashObject == NULL) {
- _zip_crypto_hmac_free(hmac);
- zip_error_set(error, ZIP_ER_MEMORY, 0);
- return NULL;
- }
- status = BCryptGetProperty(hmac->hAlgorithm, BCRYPT_HASH_LENGTH, (PUCHAR)&hmac->cbHash, sizeof(hmac->cbHash), &cbResult, 0);
- if (!BCRYPT_SUCCESS(status)) {
- _zip_crypto_hmac_free(hmac);
- return NULL;
- }
- hmac->pbHash = malloc(hmac->cbHash);
- if (hmac->pbHash == NULL) {
- _zip_crypto_hmac_free(hmac);
- zip_error_set(error, ZIP_ER_MEMORY, 0);
- return NULL;
- }
- status = BCryptCreateHash(hmac->hAlgorithm, &hmac->hHash, hmac->pbHashObject, hmac->cbHashObject, (PUCHAR)secret, (ULONG)secret_length, 0);
- if (!BCRYPT_SUCCESS(status)) {
- _zip_crypto_hmac_free(hmac);
- return NULL;
- }
- return hmac;
- }
- void
- _zip_crypto_hmac_free(_zip_crypto_hmac_t *hmac) {
- if (hmac == NULL) {
- return;
- }
- if (hmac->hHash != NULL) {
- BCryptDestroyHash(hmac->hHash);
- }
- if (hmac->pbHash != NULL) {
- free(hmac->pbHash);
- }
- if (hmac->pbHashObject != NULL) {
- free(hmac->pbHashObject);
- }
- if (hmac->hAlgorithm) {
- BCryptCloseAlgorithmProvider(hmac->hAlgorithm, 0);
- }
- free(hmac);
- }
- bool
- _zip_crypto_hmac(_zip_crypto_hmac_t *hmac, zip_uint8_t *data, zip_uint64_t length) {
- if (hmac == NULL || length > ULONG_MAX) {
- return false;
- }
- return BCRYPT_SUCCESS(BCryptHashData(hmac->hHash, data, (ULONG)length, 0));
- }
- bool
- _zip_crypto_hmac_output(_zip_crypto_hmac_t *hmac, zip_uint8_t *data) {
- if (hmac == NULL) {
- return false;
- }
- return BCRYPT_SUCCESS(BCryptFinishHash(hmac->hHash, data, hmac->cbHash, 0));
- }
- ZIP_EXTERN bool
- zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) {
- return BCRYPT_SUCCESS(BCryptGenRandom(NULL, buffer, length, BCRYPT_USE_SYSTEM_PREFERRED_RNG));
- }
|