From 6b569a451f1259384d68ba882e536643c834445c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Bernon?= Date: Tue, 10 Nov 2020 14:42:27 +0100 Subject: [PATCH] bcrypt: Implement 3DES cipher support. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is used by CoD: WWII multiplayer mode to login to its servers. Signed-off-by: RĂ©mi Bernon Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/bcrypt/bcrypt_internal.h | 1 + dlls/bcrypt/bcrypt_main.c | 65 +++++++++++++++++++++++++++++++++++ dlls/bcrypt/gnutls.c | 13 +++++++ dlls/bcrypt/tests/bcrypt.c | 62 +++++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index 463672db470..d5a54aad92b 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -113,6 +113,7 @@ struct object enum alg_id { /* cipher */ + ALG_ID_3DES, ALG_ID_AES, /* hash */ diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 85d06f6001e..380d9105e48 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -101,6 +101,7 @@ static const struct } builtin_algorithms[] = { + { BCRYPT_3DES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, 522, 0, 0 }, { BCRYPT_AES_ALGORITHM, BCRYPT_CIPHER_INTERFACE, 654, 0, 0 }, { BCRYPT_SHA256_ALGORITHM, BCRYPT_HASH_INTERFACE, 286, 32, 512 }, { BCRYPT_SHA384_ALGORITHM, BCRYPT_HASH_INTERFACE, 382, 48, 1024 }, @@ -426,6 +427,7 @@ struct hash struct hash_impl inner; }; +#define BLOCK_LENGTH_3DES 8 #define BLOCK_LENGTH_AES 16 static NTSTATUS generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) @@ -467,6 +469,46 @@ static NTSTATUS generic_alg_property( enum alg_id id, const WCHAR *prop, UCHAR * return STATUS_NOT_IMPLEMENTED; } +static NTSTATUS get_3des_property( enum mode_id mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) + { + *ret_size = sizeof(ULONG); + if (size < sizeof(ULONG)) return STATUS_BUFFER_TOO_SMALL; + if (buf) *(ULONG *)buf = BLOCK_LENGTH_3DES; + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) + { + const WCHAR *str; + switch (mode) + { + case MODE_ID_CBC: str = BCRYPT_CHAIN_MODE_CBC; break; + default: return STATUS_NOT_IMPLEMENTED; + } + + *ret_size = 64; + if (size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + memcpy( buf, str, (lstrlenW(str) + 1) * sizeof(WCHAR) ); + return STATUS_SUCCESS; + } + if (!wcscmp( prop, BCRYPT_KEY_LENGTHS )) + { + BCRYPT_KEY_LENGTHS_STRUCT *key_lengths = (void *)buf; + *ret_size = sizeof(*key_lengths); + if (key_lengths && size < *ret_size) return STATUS_BUFFER_TOO_SMALL; + if (key_lengths) + { + key_lengths->dwMinLength = 192; + key_lengths->dwMaxLength = 192; + key_lengths->dwIncrement = 0; + } + return STATUS_SUCCESS; + } + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + static NTSTATUS get_aes_property( enum mode_id mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) @@ -556,6 +598,9 @@ static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop switch (alg->id) { + case ALG_ID_3DES: + return get_3des_property( alg->mode, prop, buf, size, ret_size ); + case ALG_ID_AES: return get_aes_property( alg->mode, prop, buf, size, ret_size ); @@ -577,6 +622,23 @@ static NTSTATUS set_alg_property( struct algorithm *alg, const WCHAR *prop, UCHA { switch (alg->id) { + case ALG_ID_3DES: + if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) + { + if (!wcscmp( (WCHAR *)value, BCRYPT_CHAIN_MODE_CBC )) + { + alg->mode = MODE_ID_CBC; + return STATUS_SUCCESS; + } + else + { + FIXME( "unsupported mode %s\n", debugstr_w((WCHAR *)value) ); + return STATUS_NOT_SUPPORTED; + } + } + FIXME( "unsupported 3des algorithm property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; + case ALG_ID_AES: if (!wcscmp( prop, BCRYPT_CHAINING_MODE )) { @@ -624,6 +686,9 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA { switch (key->alg_id) { + case ALG_ID_3DES: + return get_3des_property( key->u.s.mode, prop, buf, size, ret_size ); + case ALG_ID_AES: if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) return STATUS_NOT_SUPPORTED; return get_aes_property( key->u.s.mode, prop, buf, size, ret_size ); diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 5ed51e8704c..e85085499ca 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -491,6 +491,7 @@ static NTSTATUS CDECL key_symmetric_init( struct key *key ) switch (key->alg_id) { + case ALG_ID_3DES: case ALG_ID_AES: return STATUS_SUCCESS; @@ -504,6 +505,18 @@ static gnutls_cipher_algorithm_t get_gnutls_cipher( const struct key *key ) { switch (key->alg_id) { + case ALG_ID_3DES: + WARN( "handle block size\n" ); + switch (key->u.s.mode) + { + case MODE_ID_CBC: + return GNUTLS_CIPHER_3DES_CBC; + default: + break; + } + FIXME( "3DES mode %u with key length %u not supported\n", key->u.s.mode, key->u.s.secret_len ); + return GNUTLS_CIPHER_UNKNOWN; + case ALG_ID_AES: WARN( "handle block size\n" ); switch (key->u.s.mode) diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 0ae4b5dad53..456727d04a9 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -639,6 +639,67 @@ static void test_aes(void) ok(ret == STATUS_SUCCESS, "got %08x\n", ret); } +static void test_3des(void) +{ + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; + BCRYPT_ALG_HANDLE alg; + ULONG size, len; + UCHAR mode[64]; + NTSTATUS ret; + + alg = NULL; + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_3DES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(alg != NULL, "alg not set\n"); + + len = size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(len, "expected non-zero len\n"); + ok(size == sizeof(len), "got %u\n", size); + + len = size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&len, sizeof(len), &size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(len == 8, "got %u\n", len); + ok(size == sizeof(len), "got %u\n", size); + + size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, 0, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); + ok(size == 64, "got %u\n", size); + + size = 0; + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode) - 1, &size, 0); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); + ok(size == 64, "got %u\n", size); + + size = 0; + memset(mode, 0, sizeof(mode)); + ret = pBCryptGetProperty(alg, BCRYPT_CHAINING_MODE, mode, sizeof(mode), &size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(!lstrcmpW((const WCHAR *)mode, BCRYPT_CHAIN_MODE_CBC), "got %s\n", wine_dbgstr_w((const WCHAR *)mode)); + ok(size == 64, "got %u\n", size); + + size = 0; + memset(&key_lengths, 0, sizeof(key_lengths)); + ret = pBCryptGetProperty(alg, BCRYPT_KEY_LENGTHS, (UCHAR*)&key_lengths, sizeof(key_lengths), &size, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + ok(size == sizeof(key_lengths), "got %u\n", size); + ok(key_lengths.dwMinLength == 192, "Expected 192, got %d\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 192, "Expected 192, got %d\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 0, "Expected 0, got %d\n", key_lengths.dwIncrement); + + memcpy(mode, BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM)); + ret = pBCryptSetProperty(alg, BCRYPT_CHAINING_MODE, mode, 0, 0); + ok(ret == STATUS_NOT_SUPPORTED, "got %08x\n", ret); + + test_alg_name(alg, L"3DES"); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); +} + static void test_BCryptGenerateSymmetricKey(void) { static UCHAR secret[] = @@ -2730,6 +2791,7 @@ START_TEST(bcrypt) test_BcryptHash(); test_BcryptDeriveKeyPBKDF2(); test_rng(); + test_3des(); test_aes(); test_BCryptGenerateSymmetricKey(); test_BCryptEncrypt();