mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-15 19:41:05 +00:00
rsaenh: Allow importing bigger RC2 keys.
Signed-off-by: Paul Gofman <pgofman@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
0ce784300e
commit
06e125ab3a
|
@ -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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue