From 82afb0f1ee91ab56124a93872e39615ccd1c7346 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 9 Oct 2015 15:23:15 -0400 Subject: [PATCH] libwinpr-crypto: add generic digest API and OpenSSL compatible key derivation --- winpr/include/winpr/crypto.h | 59 +++++++++++ winpr/libwinpr/crypto/cipher.c | 137 ++++++++++++++++++++++++++ winpr/libwinpr/crypto/crypto.c | 12 +-- winpr/libwinpr/crypto/hash.c | 52 +++++++++- winpr/libwinpr/crypto/test/TestCryptoCipher.c | 62 +++++++++++- 5 files changed, 307 insertions(+), 15 deletions(-) diff --git a/winpr/include/winpr/crypto.h b/winpr/include/winpr/crypto.h index e24a9dd..8af3df2 100644 --- a/winpr/include/winpr/crypto.h +++ b/winpr/include/winpr/crypto.h @@ -803,6 +803,51 @@ WINPR_API int winpr_HMAC(int md, const BYTE* key, size_t keylen, const BYTE* inp #endif /** + * Generic Digest API + */ + +struct _OPENSSL_DIGEST_CTX +{ + const void* digest; + void* engine; + unsigned long flags; + void* md_data; + void* pctx; + void* update; + BYTE winpr_pad[8]; +}; +typedef struct _OPENSSL_DIGEST_CTX OPENSSL_DIGEST_CTX; + +struct _MBEDTLS_DIGEST_CTX +{ + const void* md_info; + void* md_ctx; + void* hmac_ctx; + BYTE winpr_pad[8]; +}; +typedef struct _MBEDTLS_DIGEST_CTX MBEDTLS_DIGEST_CTX; + +union _WINPR_DIGEST_CTX +{ + OPENSSL_DIGEST_CTX openssl; + MBEDTLS_DIGEST_CTX mbedtls; +}; +typedef union _WINPR_DIGEST_CTX WINPR_DIGEST_CTX; + +#ifdef __cplusplus +extern "C" { +#endif + +WINPR_API void winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, int md); +WINPR_API void winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const BYTE* input, size_t ilen); +WINPR_API void winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, BYTE* output); +WINPR_API void winpr_Digest(int md, const BYTE* input, size_t ilen, BYTE* output); + +#ifdef __cplusplus +} +#endif + +/** * Random Number Generation */ @@ -969,4 +1014,18 @@ WINPR_API void winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, BYTE* output, size_t* o } #endif +/** + * Key Generation + */ + +#ifdef __cplusplus +extern "C" { +#endif + +int winpr_openssl_BytesToKey(int cipher, int md, const BYTE* salt, const BYTE* data, int datal, int count, BYTE* key, BYTE* iv); + +#ifdef __cplusplus +} +#endif + #endif /* WINPR_CRYPTO_H */ diff --git a/winpr/libwinpr/crypto/cipher.c b/winpr/libwinpr/crypto/cipher.c index 3194e42..3c267d4 100644 --- a/winpr/libwinpr/crypto/cipher.c +++ b/winpr/libwinpr/crypto/cipher.c @@ -32,6 +32,7 @@ #endif #ifdef WITH_MBEDTLS +#include #include #include #include @@ -75,6 +76,14 @@ void winpr_RC4_Final(WINPR_RC4_CTX* ctx) * Generic Cipher API */ +#ifdef WITH_OPENSSL +extern const EVP_MD* winpr_openssl_get_evp_md(int md); +#endif + +#ifdef WITH_MBEDTLS +extern mbedtls_md_type_t winpr_mbedtls_get_md_type(int md); +#endif + #if defined(WITH_OPENSSL) const EVP_CIPHER* winpr_openssl_get_evp_cipher(int cipher) { @@ -506,3 +515,131 @@ void winpr_Cipher_Final(WINPR_CIPHER_CTX* ctx, BYTE* output, size_t* olen) mbedtls_cipher_free((mbedtls_cipher_context_t*) ctx); #endif } + +/** + * Key Generation + */ + +int winpr_openssl_BytesToKey(int cipher, int md, const BYTE* salt, const BYTE* data, int datal, int count, BYTE* key, BYTE* iv) +{ + /** + * Key and IV generation compatible with OpenSSL EVP_BytesToKey(): + * https://www.openssl.org/docs/manmaster/crypto/EVP_BytesToKey.html + */ + +#if defined(WITH_OPENSSL) + const EVP_MD* evp_md; + const EVP_CIPHER* evp_cipher; + evp_md = winpr_openssl_get_evp_md(md); + evp_cipher = winpr_openssl_get_evp_cipher(cipher); + return EVP_BytesToKey(evp_cipher, evp_md, salt, data, datal, count, key, iv); +#elif defined(WITH_MBEDTLS) + int rv = 0; + BYTE md_buf[64]; + int niv, nkey, addmd = 0; + unsigned int mds = 0, i; + mbedtls_md_context_t ctx; + const mbedtls_md_info_t* md_info; + mbedtls_cipher_type_t cipher_type; + const mbedtls_cipher_info_t* cipher_info; + + mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); + md_info = mbedtls_md_info_from_type(md_type); + + cipher_type = winpr_mbedtls_get_cipher_type(cipher); + cipher_info = mbedtls_cipher_info_from_type(cipher_type); + + nkey = cipher_info->key_bitlen / 8; + niv = cipher_info->iv_size; + + if ((nkey > 64) || (niv > 64)) + return 0; + + if (!data) + return nkey; + + mbedtls_md_init(&ctx); + + while (1) + { + if (mbedtls_md_setup(&ctx, md_info, 0) != 0) + return 0; + + if (mbedtls_md_starts(&ctx) != 0) + return 0; + + if (addmd++) + { + if (mbedtls_md_update(&ctx, &(md_buf[0]), mds) != 0) + goto err; + } + + if (mbedtls_md_update(&ctx, data, datal) != 0) + goto err; + + if (salt) + { + if (mbedtls_md_update(&ctx, salt, 8) != 0) + goto err; + } + + if (mbedtls_md_finish(&ctx, &(md_buf[0])) != 0) + goto err; + + mds = mbedtls_md_get_size(md_info); + + for (i = 1; i < (unsigned int) count; i++) + { + if (mbedtls_md_starts(&ctx) != 0) + goto err; + if (mbedtls_md_update(&ctx, &(md_buf[0]), mds) != 0) + goto err; + if (mbedtls_md_finish(&ctx, &(md_buf[0])) != 0) + goto err; + } + + i = 0; + + if (nkey) + { + while (1) + { + if (nkey == 0) + break; + if (i == mds) + break; + if (key) + *(key++) = md_buf[i]; + nkey--; + i++; + } + } + + if (niv && (i != mds)) + { + while (1) + { + if (niv == 0) + break; + if (i == mds) + break; + if (iv) + *(iv++) = md_buf[i]; + niv--; + i++; + } + } + + if ((nkey == 0) && (niv == 0)) + break; + } + + rv = cipher_info->key_bitlen / 8; +err: + mbedtls_md_free(&ctx); + SecureZeroMemory(&(md_buf[0]), EVP_MAX_MD_SIZE); + return rv; +#endif + + return 0; +} diff --git a/winpr/libwinpr/crypto/crypto.c b/winpr/libwinpr/crypto/crypto.c index 6c692af..9e3064e 100644 --- a/winpr/libwinpr/crypto/crypto.c +++ b/winpr/libwinpr/crypto/crypto.c @@ -144,11 +144,6 @@ #include #include -#ifdef WITH_OPENSSL -#include -#include -#endif - static wListDictionary* g_ProtectedMemoryBlocks = NULL; BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) @@ -165,6 +160,7 @@ BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) if (!g_ProtectedMemoryBlocks) { g_ProtectedMemoryBlocks = ListDictionary_New(TRUE); + if (!g_ProtectedMemoryBlocks) return FALSE; } @@ -181,10 +177,8 @@ BOOL CryptProtectMemory(LPVOID pData, DWORD cbData, DWORD dwFlags) winpr_RAND(pMemBlock->salt, 8); winpr_RAND(randomKey, sizeof(randomKey)); -#ifdef WITH_OPENSSL - EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(), pMemBlock->salt, - randomKey, sizeof(randomKey), 4, pMemBlock->key, pMemBlock->iv); -#endif + winpr_openssl_BytesToKey(WINPR_CIPHER_AES_256_CBC, WINPR_MD_SHA1, + pMemBlock->salt, randomKey, sizeof(randomKey), 4, pMemBlock->key, pMemBlock->iv); SecureZeroMemory(randomKey, sizeof(randomKey)); diff --git a/winpr/libwinpr/crypto/hash.c b/winpr/libwinpr/crypto/hash.c index 9a36897..97e9deb 100644 --- a/winpr/libwinpr/crypto/hash.c +++ b/winpr/libwinpr/crypto/hash.c @@ -166,7 +166,7 @@ void winpr_SHA1(const BYTE* input, size_t ilen, BYTE* output) * HMAC */ -#if defined(WITH_OPENSSL) +#ifdef WITH_OPENSSL const EVP_MD* winpr_openssl_get_evp_md(int md) { const EVP_MD* evp = NULL; @@ -226,7 +226,9 @@ const EVP_MD* winpr_openssl_get_evp_md(int md) return evp; } -#elif defined(WITH_MBEDTLS) +#endif + +#ifdef WITH_MBEDTLS mbedtls_md_type_t winpr_mbedtls_get_md_type(int md) { mbedtls_md_type_t type = MBEDTLS_MD_NONE; @@ -322,3 +324,49 @@ int winpr_HMAC(int md, const BYTE* key, size_t keylen, const BYTE* input, size_t return 0; } +/** + * Generic Digest API + */ + +void winpr_Digest_Init(WINPR_DIGEST_CTX* ctx, int md) +{ +#if defined(WITH_OPENSSL) + const EVP_MD* evp = winpr_openssl_get_evp_md(md); + EVP_MD_CTX_init((EVP_MD_CTX*) ctx); + EVP_DigestInit_ex((EVP_MD_CTX*) ctx, evp, NULL); +#elif defined(WITH_MBEDTLS) + const mbedtls_md_info_t* md_info; + mbedtls_md_type_t md_type = winpr_mbedtls_get_md_type(md); + md_info = mbedtls_md_info_from_type(md_type); + mbedtls_md_init((mbedtls_md_context_t*) ctx); + mbedtls_md_setup((mbedtls_md_context_t*) ctx, md_info, 0); + mbedtls_md_starts((mbedtls_md_context_t*) ctx); +#endif +} + +void winpr_Digest_Update(WINPR_DIGEST_CTX* ctx, const BYTE* input, size_t ilen) +{ +#if defined(WITH_OPENSSL) + EVP_DigestUpdate((EVP_MD_CTX*) ctx, input, ilen); +#elif defined(WITH_MBEDTLS) + mbedtls_md_update((mbedtls_md_context_t*) ctx, input, ilen); +#endif +} + +void winpr_Digest_Final(WINPR_DIGEST_CTX* ctx, BYTE* output) +{ +#if defined(WITH_OPENSSL) + EVP_DigestFinal_ex((EVP_MD_CTX*) ctx, output, NULL); +#elif defined(WITH_MBEDTLS) + mbedtls_md_finish((mbedtls_md_context_t*) ctx, output); + mbedtls_md_free((mbedtls_md_context_t*) ctx); +#endif +} + +void winpr_Digest(int md, const BYTE* input, size_t ilen, BYTE* output) +{ + WINPR_DIGEST_CTX ctx; + winpr_Digest_Init(&ctx, md); + winpr_Digest_Update(&ctx, input, ilen); + winpr_Digest_Final(&ctx, output); +} diff --git a/winpr/libwinpr/crypto/test/TestCryptoCipher.c b/winpr/libwinpr/crypto/test/TestCryptoCipher.c index 7504942..29aa925 100644 --- a/winpr/libwinpr/crypto/test/TestCryptoCipher.c +++ b/winpr/libwinpr/crypto/test/TestCryptoCipher.c @@ -7,7 +7,7 @@ static const BYTE* TEST_RC4_KEY = (BYTE*) "Key"; static const char* TEST_RC4_PLAINTEXT = "Plaintext"; static const BYTE* TEST_RC4_CIPHERTEXT = (BYTE*) "\xBB\xF3\x16\xE8\xD9\x40\xAF\x0A\xD3"; -int test_crypto_cipher_rc4() +BOOL test_crypto_cipher_rc4() { size_t len; BYTE* text; @@ -18,7 +18,7 @@ int test_crypto_cipher_rc4() text = (BYTE*) calloc(1, len); if (!text) - return -1; + return FALSE; winpr_RC4_Init(&ctx, TEST_RC4_KEY, strlen((char*) TEST_RC4_KEY)); winpr_RC4_Update(&ctx, len, (BYTE*) TEST_RC4_PLAINTEXT, text); @@ -37,10 +37,61 @@ int test_crypto_cipher_rc4() free(actual); free(expected); - return -1; + return FALSE; } - return 0; + return TRUE; +} + +static const BYTE* TEST_RAND_DATA = (BYTE*) + "\x1F\xC2\xEE\x4C\xA3\x66\x80\xA2\xCE\xFE\x56\xB4\x9E\x08\x30\x96" + "\x33\x6A\xA9\x6D\x36\xFD\x3C\xB7\x83\x04\x4E\x5E\xDC\x22\xCD\xF3" + "\x48\xDF\x3A\x2A\x61\xF1\xA8\xFA\x1F\xC6\xC7\x1B\x81\xB4\xE1\x0E" + "\xCB\xA2\xEF\xA1\x12\x4A\x83\xE5\x1D\x72\x1D\x2D\x26\xA8\x6B\xC0"; + +static const BYTE* TEST_CIPHER_KEY = (BYTE*) + "\x9D\x7C\xC0\xA1\x94\x3B\x07\x67\x2F\xD3\x83\x10\x51\x83\x38\x0E" + "\x1C\x74\x8C\x4E\x15\x79\xD6\xFF\xE2\xF0\x37\x7F\x8C\xD7\xD2\x13"; + +static const BYTE* TEST_CIPHER_IV = (BYTE*) + "\xFE\xE3\x9F\xF0\xD1\x5E\x37\x0C\xAB\xAB\x9B\x04\xF3\xDB\x99\x15"; + +BOOL test_crypto_cipher_key() +{ + int status; + BYTE key[32]; + BYTE iv[16]; + BYTE salt[8] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 }; + + ZeroMemory(key, sizeof(key)); + ZeroMemory(iv, sizeof(iv)); + + status = winpr_openssl_BytesToKey(WINPR_CIPHER_AES_256_CBC, WINPR_MD_SHA1, + salt, TEST_RAND_DATA, 64, 4, key, iv); + + if (status != 32 || memcmp(key, TEST_CIPHER_KEY, 32) || memcmp(iv, TEST_CIPHER_IV, 16)) + { + char* akstr; + char* ekstr; + char* aivstr; + char* eivstr; + + akstr = winpr_BinToHexString(key, 32, 0); + ekstr = winpr_BinToHexString(TEST_CIPHER_KEY, 32, 0); + + aivstr = winpr_BinToHexString(iv, 16, 0); + eivstr = winpr_BinToHexString(TEST_CIPHER_IV, 16, 0); + + fprintf(stderr, "Unexpected EVP_BytesToKey Key: Actual: %s, Expected: %s\n", akstr, ekstr); + fprintf(stderr, "Unexpected EVP_BytesToKey IV : Actual: %s, Expected: %s\n", aivstr, eivstr); + + free(akstr); + free(ekstr); + free(aivstr); + free(eivstr); + } + + return TRUE; } int TestCryptoCipher(int argc, char* argv[]) @@ -48,5 +99,8 @@ int TestCryptoCipher(int argc, char* argv[]) if (!test_crypto_cipher_rc4()) return -1; + if (!test_crypto_cipher_key()) + return -1; + return 0; } -- 2.7.4