libwinpr-crypto: add generic digest API and OpenSSL compatible key derivation
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Fri, 9 Oct 2015 19:23:15 +0000 (15:23 -0400)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Fri, 9 Oct 2015 19:23:15 +0000 (15:23 -0400)
winpr/include/winpr/crypto.h
winpr/libwinpr/crypto/cipher.c
winpr/libwinpr/crypto/crypto.c
winpr/libwinpr/crypto/hash.c
winpr/libwinpr/crypto/test/TestCryptoCipher.c

index e24a9dd..8af3df2 100644 (file)
@@ -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 */
index 3194e42..3c267d4 100644 (file)
@@ -32,6 +32,7 @@
 #endif
 
 #ifdef WITH_MBEDTLS
+#include <mbedtls/md.h>
 #include <mbedtls/aes.h>
 #include <mbedtls/arc4.h>
 #include <mbedtls/des.h>
@@ -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;
+}
index 6c692af..9e3064e 100644 (file)
 #include <winpr/crypto.h>
 #include <winpr/collections.h>
 
-#ifdef WITH_OPENSSL
-#include <openssl/evp.h>
-#include <openssl/aes.h>
-#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));
 
index 9a36897..97e9deb 100644 (file)
@@ -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);
+}
index 7504942..29aa925 100644 (file)
@@ -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;
 }