[PATCH v2 6/7] rsaenh: Allow importing bigger RC2 keys.

Paul Gofman wine at gitlab.winehq.org
Mon May 16 12:31:31 CDT 2022


From: Paul Gofman <pgofman at codeweavers.com>

Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
 dlls/rsaenh/rsaenh.c       |  43 +++++++-
 dlls/rsaenh/tests/rsaenh.c | 218 +++++++++++++++++++++++++++++++++++++
 2 files changed, 260 insertions(+), 1 deletion(-)

diff --git a/dlls/rsaenh/rsaenh.c b/dlls/rsaenh/rsaenh.c
index 8a11da5f3f8..2dcc85cad7b 100644
--- a/dlls/rsaenh/rsaenh.c
+++ b/dlls/rsaenh/rsaenh.c
@@ -25,6 +25,7 @@
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -2627,6 +2628,23 @@ BOOL WINAPI RSAENH_CPEncrypt(HCRYPTPROV hProv, HCRYPTKEY hKey, HCRYPTHASH hHash,
         return FALSE;
     }
 
+    if (pCryptKey->aiAlgid == CALG_RC2)
+    {
+        const PROV_ENUMALGS_EX *info;
+
+        if (!(info = get_algid_info(hProv, pCryptKey->aiAlgid)))
+        {
+            FIXME("Can't get algid info.\n");
+            SetLastError(NTE_BAD_KEY);
+            return FALSE;
+        }
+        if (pCryptKey->dwKeyLen > info->dwMaxLen / 8)
+        {
+            SetLastError(NTE_BAD_KEY);
+            return FALSE;
+        }
+    }
+
     if (pCryptKey->dwState == RSAENH_KEYSTATE_IDLE) 
         pCryptKey->dwState = RSAENH_KEYSTATE_ENCRYPTING;
 
@@ -3318,7 +3336,30 @@ static BOOL import_symmetric_key(HCRYPTPROV hProv, const BYTE *pbData, DWORD dwD
         return FALSE;
     }
 
-    *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, dwKeyLen<<19, &pCryptKey);
+    if (pBlobHeader->aiKeyAlg == CALG_RC2)
+    {
+        const PROV_ENUMALGS_EX *info;
+
+        info = get_algid_info(hProv, CALG_RC2);
+        assert(info);
+        if (!dwKeyLen)
+            dwKeyLen = info->dwDefaultLen;
+        if (dwKeyLen < info->dwMinLen / 8 || dwKeyLen > 128)
+        {
+            WARN("Invalid RC2 key, len %ld.\n", dwKeyLen);
+            *phKey = (HCRYPTKEY)INVALID_HANDLE_VALUE;
+            SetLastError(NTE_BAD_DATA);
+        }
+        else if ((*phKey = alloc_key(hProv, pBlobHeader->aiKeyAlg, 0, dwKeyLen << 3, &pCryptKey))
+                  != (HCRYPTKEY)INVALID_HANDLE_VALUE && info->dwDefaultLen == 40 && dwKeyLen > info->dwMaxLen / 8)
+        {
+            pCryptKey->dwEffectiveKeyLen = 40;
+        }
+    }
+    else
+    {
+        *phKey = new_key(hProv, pBlobHeader->aiKeyAlg, dwKeyLen<<19, &pCryptKey);
+    }
     if (*phKey == (HCRYPTKEY)INVALID_HANDLE_VALUE)
     {
         free(pbDecrypted);
diff --git a/dlls/rsaenh/tests/rsaenh.c b/dlls/rsaenh/tests/rsaenh.c
index 30c56722c12..6d784e2245d 100644
--- a/dlls/rsaenh/tests/rsaenh.c
+++ b/dlls/rsaenh/tests/rsaenh.c
@@ -3985,6 +3985,221 @@ err:
     }
 }
 
+static void test_rc2_import(void)
+{
+    static const DWORD test_lengths[] = { 128 + 8, 256, 512, 1024 };
+    static const DWORD mac_results[ARRAY_SIZE(test_lengths)][2] =
+    {
+        {0xc13650cf, 0xa3e93efb},
+        {0x6f7be248, 0x444b38b2},
+        {0x2c3534d2, 0x29fca10c},
+        {0x2c3534d2, 0x29fca10c},
+    };
+    static const DWORD mac_baseprov_results[ARRAY_SIZE(test_lengths)][2] =
+    {
+        {0x7e94a244, 0x12a9ae17},
+        {0x2aaa6719, 0xa671d9a6},
+        {0x2e730ce2, 0x9ebe6016},
+        {0x2e730ce2, 0x9ebe6016},
+    };
+    static const DWORD mac_results_old[ARRAY_SIZE(test_lengths)][2] =
+    {
+        {0xebe2155a, 0xab2f58b7},
+        {0x4394ccb2, 0xbe5c629b},
+        {0xd7bc2195, 0x63fb2785},
+        {0xd7bc2195, 0x63fb2785},
+    };
+    static const DWORD mac_baseprov_results_old[ARRAY_SIZE(test_lengths)][2] =
+    {
+        {0xe2ed2dec, 0x5ae837ed},
+        {0x12f4b193, 0xe3f6afc1},
+        {0x04d7f905, 0x686d357b},
+        {0x04d7f905, 0x686d357b},
+    };
+    static const DWORD hmac_results[ARRAY_SIZE(test_lengths)][4] =
+    {
+        {0x2f44586d, 0x76d04c9f, 0xdae8fc03, 0x27e870bd},
+        {0xbcde3186, 0xd9892cd5, 0x578c89f5, 0xc2cba8e5},
+        {0xad2bf1cc, 0xae4e2c1f, 0xd1599a67, 0x0167d802},
+        {0x6ee75968, 0x52a0eff5, 0x75340d85, 0x87b64962},
+    };
+    static const DWORD decrypt_baseprov_results[ARRAY_SIZE(test_lengths)][2] =
+    {
+        {0x48c4ff05, 0x9d880b90},
+        {0xcfee0629, 0xfeab04a1},
+        {0x23c9336c, 0x7f67b26e},
+        {0x23c9336c, 0x7f67b26e},
+    };
+    static const DWORD decrypt_results[ARRAY_SIZE(test_lengths)][2] =
+    {
+        {0xb37e1be2, 0x1ed67048},
+        {0x7c7d0b04, 0x3a74bb76},
+        {0x7bfd232c, 0x93d52c4f},
+        {0x7bfd232c, 0x93d52c4f},
+    };
+
+    struct key_blob
+    {
+        BLOBHEADER h;
+        union
+        {
+            DWORD key_size;
+            DWORD alg_id;
+        } param;
+        BYTE data[512];
+    }
+    key_data;
+    const DWORD *results, *broken_results;
+    HCRYPTKEY exchange_key, key;
+    DWORD len, value, expected;
+    DWORD test_length;
+    BYTE data[2048];
+    HCRYPTPROV prov;
+    HCRYPTHASH hash;
+    unsigned int i;
+    HMAC_INFO hmac;
+    BOOL ret;
+
+    ret = CryptAcquireContextA(&prov, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_NEWKEYSET);
+    ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+    /* CryptGenKey() favours advertised key length limit. */
+    ret = CryptGenKey(prov, CALG_RC2, 128 << 16, &key);
+    ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+    CryptDestroyKey(key);
+
+    ret = CryptGenKey(prov, CALG_RC2, (128 + 8) << 16, &key);
+    ok(!ret, "Expected failure.\n");
+
+    CryptReleaseContext(prov, 0);
+
+    ret = CryptGetUserKey(hProv, AT_KEYEXCHANGE, &exchange_key);
+    ok(ret, "CryptGetUserKey failed.\n");
+
+    memset(&key_data.h, 0, sizeof(key_data.h));
+    key_data.h.bVersion = CUR_BLOB_VERSION;
+    key_data.h.aiKeyAlg = CALG_RC2;
+
+    for (i = 0; i < ARRAY_SIZE(test_lengths); ++i)
+    {
+        memset(key_data.data, 0xcc, sizeof(key_data.data));
+        test_length = test_lengths[i];
+        winetest_push_context("length %lu", test_length);
+        len = min(test_length / 8, 64);
+
+
+        ret = CryptEncrypt(exchange_key, 0, TRUE, 0, key_data.data, &len, sizeof(key_data) - 12);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        memset(key_data.data + len, 0xee, sizeof(key_data.data) - len);
+
+        /* Importing a larger key as plaintext fails. */
+        key_data.h.bType = PLAINTEXTKEYBLOB;
+        key_data.param.key_size = test_length / 8;
+        ret = CryptImportKey(hProv, (BYTE *)&key_data, offsetof(struct key_blob, data[test_length / 8]), 0, 0, &key);
+        ok(!ret, "Expected failure.\n");
+
+        /* Importing a larger key as SIMPLEBLOB succeeds. */
+        key_data.h.bType = SIMPLEBLOB;
+        key_data.param.alg_id = CALG_RSA_KEYX;
+        ret = CryptImportKey(hProv, (BYTE *)&key_data, offsetof(struct key_blob, data[len]),
+                exchange_key, 0, &key);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+        /* Effective key length is a public key decrypted length except for the base provider
+         * where it is default length. */
+        len = sizeof(value);
+        value = 0xdeadbeef;
+        ret = CryptGetKeyParam(key, KP_EFFECTIVE_KEYLEN, (BYTE *)&value, &len, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        expected = BASE_PROV ? 40 : test_length;
+        expected = min(expected, 512);
+        ok(value == expected, "Unexpected value %lu, expected %lu.\n", value, expected);
+
+        expected = min(test_length, 512);
+        ret = CryptGetKeyParam(key, KP_KEYLEN, (BYTE *)&value, &len, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        ok(value == expected, "Unexpected value %lu, expected %lu.\n", value, expected);
+
+        /* The resulting key is not good for encryption. */
+        memset(data, 0xcc, 8);
+        len = 8;
+        ret = CryptEncrypt(key, 0, FALSE, 0, data, &len, sizeof(data));
+        ok(!ret, "Expected failure.\n");
+        ok(GetLastError() == NTE_BAD_KEY, "Unexpected error %#lx.\n", GetLastError());
+        len = 8;
+        ret = CryptEncrypt(key, 0, TRUE, 0, data, &len, sizeof(data));
+        ok(!ret, "Expected failure.\n");
+        ok(GetLastError() == NTE_BAD_KEY, "Unexpected error %#lx.\n", GetLastError());
+        /* But decryption works. */
+        len = 8;
+        ret = CryptDecrypt(key, 0, FALSE, 0, data, &len);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        ok(len == 8, "Unexpected len %lu.\n", len);
+        results = BASE_PROV ? decrypt_baseprov_results[i] : decrypt_results[i];
+        ok(!memcmp(data, results, len), "Data does not match.\n");
+
+        ret = CryptCreateHash(hProv, CALG_MAC, key, 0, &hash);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+        data[0] = 0;
+        ret = CryptHashData(hash, data, 1, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+        len = sizeof(value);
+        ret = CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *)&value, &len, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        ok(value == 8, "Unexpected value %lu.\n", value);
+
+        len = 32;
+        memset(data, 0xcc, 8);
+        ret = CryptGetHashParam(hash, HP_HASHVAL, data, &len, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        ok(len == 8, "Unexpected len %lu.\n", len);
+
+        results = BASE_PROV ? mac_baseprov_results[i] : mac_results[i];
+        broken_results = BASE_PROV ? mac_baseprov_results_old[i] : mac_results_old[i];
+        /* Hash state is affected by key state before Win8. */
+        ok(!memcmp(data, results, len) || broken(!memcmp(data, broken_results, len)), "Hash does not match.\n");
+
+        CryptDestroyHash(hash);
+
+        ret = CryptCreateHash(hProv, CALG_HMAC, key, 0, &hash);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        memset(&hmac, 0, sizeof(hmac));
+        hmac.HashAlgid = CALG_MD5;
+        memset(data, 0, sizeof(data));
+        hmac.pbInnerString = data;
+        hmac.cbInnerString = test_length;
+        hmac.pbOuterString = data;
+        hmac.cbOuterString = test_length;
+
+        ret = CryptSetHashParam(hash, HP_HMAC_INFO, (BYTE *)&hmac, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+        data[0] = 0;
+        ret = CryptHashData(hash, data, 1, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+
+        len = sizeof(value);
+        ret = CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *)&value, &len, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        ok(value == 16, "Unexpected value %lu.\n", value);
+
+        len = 16;
+        ret = CryptGetHashParam(hash, HP_HASHVAL, data, &len, 0);
+        ok(ret, "Failed, error %#lx.\n", GetLastError());
+        ok(len == 16, "Unexpected len %lu.\n", len);
+
+        ok(!memcmp(data, hmac_results[i], len), "Hash does not match.\n");
+
+        CryptDestroyHash(hash);
+        CryptDestroyKey(key);
+        winetest_pop_context();
+    }
+    CryptDestroyKey(exchange_key);
+}
+
 START_TEST(rsaenh)
 {
     for (iProv = 0; iProv < ARRAY_SIZE(szProviders); iProv++)
@@ -4016,6 +4231,7 @@ START_TEST(rsaenh)
         test_import_hmac();
         test_enum_container();
         if(!BASE_PROV) test_key_derivation(STRONG_PROV ? "STRONG" : "ENH");
+        test_rc2_import();
         clean_up_base_environment();
     }
 
@@ -4026,10 +4242,12 @@ START_TEST(rsaenh)
     test_rsa_round_trip();
     if (!init_aes_environment())
         return;
+    trace("Testing AES provider.\n");
     test_aes(128);
     test_aes(192);
     test_aes(256);
     test_sha2();
     test_key_derivation("AES");
+    test_rc2_import();
     clean_up_aes_environment();
 }
-- 
GitLab


https://gitlab.winehq.org/wine/wine/-/merge_requests/68



More information about the wine-devel mailing list