From: Paul Gofman pgofman@codeweavers.com
--- dlls/rsaenh/rsaenh.c | 47 +++++++++ dlls/rsaenh/tests/rsaenh.c | 196 +++++++++++++++++++++++++++++++++++++ 2 files changed, 243 insertions(+)
diff --git a/dlls/rsaenh/rsaenh.c b/dlls/rsaenh/rsaenh.c index fd39a5b125e..909fcabd54f 100644 --- a/dlls/rsaenh/rsaenh.c +++ b/dlls/rsaenh/rsaenh.c @@ -957,6 +957,15 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK */ break;
+ case CALG_RC2: + if (dwKeyLen % 8 || dwKeyLen < peaAlgidInfo->dwMinLen) + { + WARN("Invalid RC2 key len %ld.\n", dwKeyLen); + SetLastError(NTE_BAD_DATA); + return (HCRYPTKEY)INVALID_HANDLE_VALUE; + } + break; + default: if (dwKeyLen % 8 || dwKeyLen > peaAlgidInfo->dwMaxLen || @@ -1016,6 +1025,9 @@ static HCRYPTKEY new_key(HCRYPTPROV hProv, ALG_ID aiAlgid, DWORD dwFlags, CRYPTK break;
case CALG_RC2: + if (peaAlgidInfo->dwDefaultLen == 40 && dwKeyLen > peaAlgidInfo->dwMaxLen) + pCryptKey->dwEffectiveKeyLen = 40; + /* fallthrough */ case CALG_DES: case CALG_3DES_112: case CALG_3DES: @@ -2619,6 +2631,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;
@@ -3561,6 +3590,24 @@ BOOL WINAPI RSAENH_CPGenKey(HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags, HCRYP break;
case CALG_RC2: + { + const PROV_ENUMALGS_EX *info; + DWORD key_len = HIWORD(dwFlags); + + if (!(info = get_algid_info(hProv, Algid))) + { + SetLastError(NTE_BAD_ALGID); + *phKey = (HCRYPTKEY)INVALID_HANDLE_VALUE; + break; + } + if (key_len > info->dwMaxLen) + { + SetLastError(NTE_BAD_FLAGS); + *phKey = (HCRYPTKEY)INVALID_HANDLE_VALUE; + break; + } + } + /* fallthrough */ case CALG_RC4: case CALG_DES: case CALG_3DES_112: diff --git a/dlls/rsaenh/tests/rsaenh.c b/dlls/rsaenh/tests/rsaenh.c index 30c56722c12..0fe9dda449c 100644 --- a/dlls/rsaenh/tests/rsaenh.c +++ b/dlls/rsaenh/tests/rsaenh.c @@ -3985,6 +3985,199 @@ 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}, + }; + + const DWORD *results, *broken_results; + BYTE key_data[2048], data[2048]; + HCRYPTKEY exchange_key, key; + DWORD len, value, expected; + DWORD test_length; + 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"); + + ((DWORD *)key_data)[0] = 0x00000201; + ((DWORD *)key_data)[1] = CALG_RC2; + ((DWORD *)key_data)[2] = CALG_RSA_KEYX; + + for (i = 0; i < ARRAY_SIZE(test_lengths); ++i) + { + memset(key_data + 12, 0xcc, sizeof(key_data) - 12); + 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 + 12, &len, sizeof(key_data) - 12); + ok(ret, "Failed, error %#lx.\n", GetLastError()); + memset(key_data + 12 + len, 0xee, sizeof(key_data) - 12 - len); + + /* Importing a larger key succeeds. */ + ret = CryptImportKey(hProv, key_data, len + 12, 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 +4209,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 +4220,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(); }