-- v2: bcrypt: Add BCryptKeyDerivation partial implementation (PBKDF2 algorithm). bcrypt: Reorganize hash_handle_from_desc helper so it can be reused. bcrypt: Handle PBKDF2 in BCryptGenerateSymmetricKey. bcrypt: Handle PBKDF2 in BCryptGetProperty. bcrypt: Add PBKDF2 algorithm provider. bcrypt: Fix BcryptDeriveKeyPBKDF2 with NULL salt. include: Add some PBKDF2 related definitions.
From: Piotr Caban piotr@codeweavers.com
--- include/bcrypt.h | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/include/bcrypt.h b/include/bcrypt.h index 54a7c038021..b7d6c161467 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -106,6 +106,7 @@ typedef LONG NTSTATUS; #define BCRYPT_SHA256_ALGORITHM L"SHA256" #define BCRYPT_SHA384_ALGORITHM L"SHA384" #define BCRYPT_SHA512_ALGORITHM L"SHA512" +#define BCRYPT_PBKDF2_ALGORITHM L"PBKDF2"
#define BCRYPT_CHAIN_MODE_NA L"ChainingModeN/A" #define BCRYPT_CHAIN_MODE_CBC L"ChainingModeCBC" @@ -189,6 +190,7 @@ static const WCHAR BCRYPT_SHA1_ALGORITHM[] = {'S','H','A','1',0}; static const WCHAR BCRYPT_SHA256_ALGORITHM[] = {'S','H','A','2','5','6',0}; static const WCHAR BCRYPT_SHA384_ALGORITHM[] = {'S','H','A','3','8','4',0}; static const WCHAR BCRYPT_SHA512_ALGORITHM[] = {'S','H','A','5','1','2',0}; +static const WCHAR BCRYPT_PBKDF2_ALGORITHM[] = {'P','B','K','D','F','2',0};
static const WCHAR BCRYPT_CHAIN_MODE_NA[] = {'C','h','a','i','n','i','n','g','M','o','d','e','N','/','A',0}; static const WCHAR BCRYPT_CHAIN_MODE_CBC[] = {'C','h','a','i','n','i','n','g','M','o','d','e','C','B','C',0}; @@ -226,6 +228,7 @@ static const WCHAR BCRYPT_DH_PARAMETERS[] = {'D','H','P','a','r','a','m','e','t' #define BCRYPT_SECRET_AGREEMENT_OPERATION 0x00000008 #define BCRYPT_SIGNATURE_OPERATION 0x00000010 #define BCRYPT_RNG_OPERATION 0x00000020 +#define BCRYPT_KEY_DERIVATION_OPERATION 0x00000040
#define BCRYPT_CIPHER_INTERFACE 0x00000001 #define BCRYPT_HASH_INTERFACE 0x00000002 @@ -413,9 +416,23 @@ typedef struct _BCRYPT_KEY_DATA_BLOB_HEADER ULONG cbKeyData; } BCRYPT_KEY_DATA_BLOB_HEADER, *PBCRYPT_KEY_DATA_BLOB_HEADER;
-#define KDF_HASH_ALGORITHM 0x00000000 -#define KDF_SECRET_PREPEND 0x00000001 -#define KDF_SECRET_APPEND 0x00000002 +#define KDF_HASH_ALGORITHM 0x00 +#define KDF_SECRET_PREPEND 0x01 +#define KDF_SECRET_APPEND 0x02 +#define KDF_HMAC_KEY 0x03 +#define KDF_TLS_PRF_LABEL 0x04 +#define KDF_TLS_PRF_SEED 0x05 +#define KDF_SECRET_HANDLE 0x06 +#define KDF_TLS_PRF_PROTOCOL 0x07 +#define KDF_ALGORITHMID 0x08 +#define KDF_PARTYUINFO 0x09 +#define KDF_PARTYVINFO 0x0a +#define KDF_SUPPPUBINFO 0x0b +#define KDF_SUPPPRIVINFO 0x0c +#define KDF_LABEL 0x0d +#define KDF_CONTEXT 0x0e +#define KDF_SALT 0x0f +#define KDF_ITERATION_COUNT 0x10
typedef struct _BCryptBuffer { @@ -552,6 +569,7 @@ NTSTATUS WINAPI BCryptHash(BCRYPT_ALG_HANDLE, PUCHAR, ULONG, PUCHAR, ULONG, PUCH NTSTATUS WINAPI BCryptHashData(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, ULONG); NTSTATUS WINAPI BCryptImportKey(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, BCRYPT_KEY_HANDLE *, PUCHAR, ULONG, PUCHAR, ULONG, ULONG); NTSTATUS WINAPI BCryptImportKeyPair(BCRYPT_ALG_HANDLE, BCRYPT_KEY_HANDLE, LPCWSTR, BCRYPT_KEY_HANDLE *, UCHAR *, ULONG, ULONG); +NTSTATUS WINAPI BCryptKeyDerivation(BCRYPT_KEY_HANDLE, BCryptBufferDesc *, UCHAR *, ULONG, ULONG *, ULONG); NTSTATUS WINAPI BCryptOpenAlgorithmProvider(BCRYPT_ALG_HANDLE *, LPCWSTR, LPCWSTR, ULONG); NTSTATUS WINAPI BCryptRemoveContextFunction(ULONG, LPCWSTR, ULONG, LPCWSTR); NTSTATUS WINAPI BCryptSecretAgreement(BCRYPT_KEY_HANDLE, BCRYPT_KEY_HANDLE, BCRYPT_SECRET_HANDLE *, ULONG);
From: Piotr Caban piotr@codeweavers.com
LibTomCrypt hash functions return CRYPT_INVALID_ARG when input buffer is NULL. --- dlls/bcrypt/bcrypt_main.c | 2 +- dlls/bcrypt/tests/bcrypt.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 8a4d595a7d0..bb27cfa00e3 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -2420,7 +2420,7 @@ static NTSTATUS pbkdf2( struct hash *hash, UCHAR *pwd, ULONG pwd_len, UCHAR *sal if (j == 0) { /* use salt || INT(i) */ - if (hash->desc->process( &hash->inner, salt, salt_len )) + if (salt_len && hash->desc->process( &hash->inner, salt, salt_len )) { free( buf ); return STATUS_INVALID_PARAMETER; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index dbb1f0f5bad..af49d76730e 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -486,6 +486,7 @@ static UCHAR dk3[] = "4b007901b765489abead49d926f721d065a429c1"; static UCHAR dk4[] = "364dd6bc200ec7d197f1b85f4a61769010717124"; static UCHAR dk5[] = "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"; static UCHAR dk6[] = "56fa6aa75548099dcc37d7f03425e0c3"; +static UCHAR dk7[] = "8754c32c64b0f524fc50c00f788135de";
static const struct { @@ -503,7 +504,8 @@ static const struct { 8, 4, 4096, 20, password, salt, dk3 }, { 8, 4, 1000000, 20, password, salt, dk4 }, { 24, 36, 4096, 25, long_password, long_salt, dk5 }, - { 9, 5, 4096, 16, password_NUL, salt_NUL, dk6 } + { 9, 5, 4096, 16, password_NUL, salt_NUL, dk6 }, + { 8, 0, 1, 16, password, NULL, dk7 } };
static void test_BcryptDeriveKeyPBKDF2(void)
From: Piotr Caban piotr@codeweavers.com
--- dlls/bcrypt/bcrypt_internal.h | 3 +++ dlls/bcrypt/bcrypt_main.c | 5 ++++- dlls/bcrypt/tests/bcrypt.c | 18 ++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index 20c9a2912a5..866232343c6 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -73,6 +73,9 @@ enum alg_id
/* rng */ ALG_ID_RNG, + + /* key derivation */ + ALG_ID_PBKDF2, };
enum chain_mode diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index bb27cfa00e3..353ef081fd1 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -123,6 +123,7 @@ builtin_algorithms[] = { BCRYPT_ECDSA_P384_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_DSA_ALGORITHM, BCRYPT_SIGNATURE_INTERFACE, 0, 0, 0 }, { BCRYPT_RNG_ALGORITHM, BCRYPT_RNG_INTERFACE, 0, 0, 0 }, + { BCRYPT_PBKDF2_ALGORITHM, BCRYPT_KEY_DERIVATION_INTERFACE, 618, 0, 0 }, };
static inline BOOL is_symmetric_key( const struct key *key ) @@ -156,6 +157,7 @@ static BOOL match_operation_type( ULONG type, ULONG class ) case BCRYPT_SECRET_AGREEMENT_INTERFACE: return type & BCRYPT_SECRET_AGREEMENT_OPERATION; case BCRYPT_SIGNATURE_INTERFACE: return type & BCRYPT_SIGNATURE_OPERATION; case BCRYPT_RNG_INTERFACE: return type & BCRYPT_RNG_OPERATION; + case BCRYPT_KEY_DERIVATION_INTERFACE: return type & BCRYPT_KEY_DERIVATION_OPERATION; default: break; } return FALSE; @@ -168,7 +170,8 @@ NTSTATUS WINAPI BCryptEnumAlgorithms( ULONG type, ULONG *ret_count, BCRYPT_ALGOR BCRYPT_ASYMMETRIC_ENCRYPTION_OPERATION |\ BCRYPT_SECRET_AGREEMENT_OPERATION |\ BCRYPT_SIGNATURE_OPERATION |\ - BCRYPT_RNG_OPERATION; + BCRYPT_RNG_OPERATION |\ + BCRYPT_KEY_DERIVATION_OPERATION; BCRYPT_ALGORITHM_IDENTIFIER *list; ULONG i, j, count = 0;
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index af49d76730e..4d65cd26cb2 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -4370,6 +4370,23 @@ static void test_RC4(void) ok(status == STATUS_SUCCESS, "got %#lx\n", status); }
+static void test_PBKDF2(void) +{ + BCRYPT_ALG_HANDLE alg; + NTSTATUS status; + + status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_PBKDF2_ALGORITHM, NULL, 0); + if (status == STATUS_NOT_FOUND) + { + win_skip("PBKDF2 not available\n"); + return; + } + ok(!status, "got %#lx\n", status); + + status = BCryptCloseAlgorithmProvider(alg, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); +} + START_TEST(bcrypt) { HMODULE module; @@ -4408,6 +4425,7 @@ START_TEST(bcrypt) test_SecretAgreement(); test_rsa_encrypt(); test_RC4(); + test_PBKDF2();
FreeLibrary(module); }
From: Piotr Caban piotr@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 23 +++++++++++++++++++++++ dlls/bcrypt/tests/bcrypt.c | 21 +++++++++++++++++++++ 2 files changed, 44 insertions(+)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 353ef081fd1..35474d67125 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -638,6 +638,26 @@ static NTSTATUS get_dsa_property( enum chain_mode mode, const WCHAR *prop, UCHAR return STATUS_NOT_IMPLEMENTED; }
+static NTSTATUS get_pbkdf2_property( enum chain_mode mode, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + if (!wcscmp( prop, BCRYPT_BLOCK_LENGTH )) return STATUS_NOT_SUPPORTED; + 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 = 0; + key_lengths->dwMaxLength = 16384; + key_lengths->dwIncrement = 8; + } + return STATUS_SUCCESS; + } + FIXME( "unsupported property %s\n", debugstr_w(prop) ); + return STATUS_NOT_IMPLEMENTED; +} + static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { @@ -664,6 +684,9 @@ static NTSTATUS get_alg_property( const struct algorithm *alg, const WCHAR *prop case ALG_ID_DSA: return get_dsa_property( alg->mode, prop, buf, size, ret_size );
+ case ALG_ID_PBKDF2: + return get_pbkdf2_property( alg->mode, prop, buf, size, ret_size ); + default: break; } diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 4d65cd26cb2..640629c4dee 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -4372,8 +4372,10 @@ static void test_RC4(void)
static void test_PBKDF2(void) { + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; BCRYPT_ALG_HANDLE alg; NTSTATUS status; + ULONG val, size;
status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_PBKDF2_ALGORITHM, NULL, 0); if (status == STATUS_NOT_FOUND) @@ -4383,6 +4385,25 @@ static void test_PBKDF2(void) } ok(!status, "got %#lx\n", status);
+ val = size = 0; + status = BCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&val, sizeof(val), &size, 0); + ok(!status, "got %#lx\n", status); + ok(val, "got %lu\n", val); + ok(size == sizeof(val), "got %lu\n", size); + + val = size = 0; + status = BCryptGetProperty(alg, BCRYPT_BLOCK_LENGTH, (UCHAR *)&val, sizeof(val), &size, 0); + ok(status == STATUS_NOT_SUPPORTED, "got %#lx\n", status); + + memset(&key_lengths, 0xfe, sizeof(key_lengths)); + size = 0; + status = BCryptGetProperty(alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size, 0); + ok(!status, "got %#lx\n", status); + ok(size == sizeof(key_lengths), "got %lu\n", size); + ok(key_lengths.dwMinLength == 0, "got %lu\n", key_lengths.dwMinLength); + ok(key_lengths.dwMaxLength == 16384, "got %lu\n", key_lengths.dwMaxLength); + ok(key_lengths.dwIncrement == 8, "got %lu\n", key_lengths.dwIncrement); + status = BCryptCloseAlgorithmProvider(alg, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); }
From: Piotr Caban piotr@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 14 +++++++++++--- dlls/bcrypt/tests/bcrypt.c | 11 +++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 35474d67125..b35b215c540 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -128,7 +128,8 @@ builtin_algorithms[] =
static inline BOOL is_symmetric_key( const struct key *key ) { - return builtin_algorithms[key->alg_id].class == BCRYPT_CIPHER_INTERFACE; + return builtin_algorithms[key->alg_id].class == BCRYPT_CIPHER_INTERFACE + || builtin_algorithms[key->alg_id].class == BCRYPT_KEY_DERIVATION_INTERFACE; }
static inline BOOL is_asymmetric_encryption_key( struct key *key ) @@ -1209,8 +1210,15 @@ static NTSTATUS key_symmetric_generate( struct algorithm *alg, BCRYPT_KEY_HANDLE struct key *key; NTSTATUS status;
- if (!(block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER; - if (!get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size )) + if (alg->id == ALG_ID_PBKDF2 && + !get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size )) + { + if (secret_len > key_lengths.dwMaxLength / 8 || secret_len < key_lengths.dwMinLength / 8) + return STATUS_INVALID_PARAMETER; + block_size = secret_len; + } + else if (!(block_size = get_block_size( alg ))) return STATUS_INVALID_PARAMETER; + else if (!get_alg_property( alg, BCRYPT_KEY_LENGTHS, (UCHAR *)&key_lengths, sizeof(key_lengths), &size )) { if (secret_len > (size = key_lengths.dwMaxLength / 8)) { diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 640629c4dee..272819d3335 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -4374,6 +4374,7 @@ static void test_PBKDF2(void) { BCRYPT_KEY_LENGTHS_STRUCT key_lengths; BCRYPT_ALG_HANDLE alg; + BCRYPT_KEY_HANDLE key; NTSTATUS status; ULONG val, size;
@@ -4404,6 +4405,16 @@ static void test_PBKDF2(void) ok(key_lengths.dwMaxLength == 16384, "got %lu\n", key_lengths.dwMaxLength); ok(key_lengths.dwIncrement == 8, "got %lu\n", key_lengths.dwIncrement);
+ key = 0; + status = BCryptGenerateSymmetricKey(alg, &key, NULL, 0, (UCHAR *)"test", 4, 0); + ok(!status, "got %#lx\n", status); + val = size = 0; + status = BCryptGetProperty(key, BCRYPT_KEY_STRENGTH, (UCHAR *)&val, sizeof(val), &size, 0); + ok(!status, "got %#lx\n", status); + ok(val == strlen("test") * 8, "got %lu\n", val); + + status = BCryptDestroyKey(key); + ok(!status, "got %#lx\n", status); status = BCryptCloseAlgorithmProvider(alg, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); }
From: Piotr Caban piotr@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 51 +++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index b35b215c540..f9dc1bcfe02 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -2605,42 +2605,47 @@ static NTSTATUS derive_key_raw( struct secret *secret, UCHAR *output, ULONG outp return status; }
-static BCRYPT_ALG_HANDLE hash_handle_from_desc( BCryptBufferDesc *desc ) +static struct algorithm *get_hash_alg( BCryptBuffer *buf ) { - ULONG i; - if (!desc) return BCRYPT_SHA1_ALG_HANDLE; - for (i = 0; i < desc->cBuffers; i++) - { - if (desc->pBuffers[i].BufferType == KDF_HASH_ALGORITHM) - { - const WCHAR *str = desc->pBuffers[i].pvBuffer; - if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM )) return BCRYPT_SHA1_ALG_HANDLE; - else if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) return BCRYPT_SHA256_ALG_HANDLE; - else if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) return BCRYPT_SHA384_ALG_HANDLE; - else if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) return BCRYPT_SHA512_ALG_HANDLE; - else - { - FIXME( "hash algorithm %s not supported\n", debugstr_w(str) ); - return NULL; - } - } - else FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType ); - } + const WCHAR *str = buf->pvBuffer; + BCRYPT_ALG_HANDLE handle = NULL;
- return BCRYPT_SHA1_ALG_HANDLE; + if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM )) + handle = BCRYPT_SHA1_ALG_HANDLE; + else if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) + handle = BCRYPT_SHA256_ALG_HANDLE; + else if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) + handle = BCRYPT_SHA384_ALG_HANDLE; + else if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) + handle = BCRYPT_SHA512_ALG_HANDLE; + + if (handle) return get_alg_object( handle ); + FIXME( "hash algorithm %s not supported\n", debugstr_w(str) ); + return NULL; }
static NTSTATUS derive_key_hash( struct secret *secret, BCryptBufferDesc *desc, UCHAR *output, ULONG output_len, ULONG *ret_len ) { struct key_asymmetric_derive_key_params params; - struct algorithm *alg = get_alg_object( hash_handle_from_desc(desc) ); ULONG hash_len, derived_key_len = secret->privkey->u.a.bitlen / 8; UCHAR hash_buf[MAX_HASH_OUTPUT_BYTES]; + struct algorithm *alg = NULL; UCHAR *derived_key; NTSTATUS status; + ULONG i; + + for (i = 0; i < (desc ? desc->cBuffers : 0); i++) + { + if (desc->pBuffers[i].BufferType == KDF_HASH_ALGORITHM) + { + alg = get_hash_alg( desc->pBuffers + i ); + if (!alg) return STATUS_NOT_SUPPORTED; + } + else FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType ); + } + if (!alg) alg = get_alg_object( BCRYPT_SHA1_ALG_HANDLE );
- if (!alg) return STATUS_NOT_SUPPORTED; if (!(derived_key = malloc( derived_key_len ))) return STATUS_NO_MEMORY;
params.privkey = secret->privkey;
From: Piotr Caban piotr@codeweavers.com
--- dlls/bcrypt/bcrypt.spec | 1 + dlls/bcrypt/bcrypt_main.c | 56 ++++++++++++++++++++++++++++++++++---- dlls/bcrypt/tests/bcrypt.c | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 6 deletions(-)
diff --git a/dlls/bcrypt/bcrypt.spec b/dlls/bcrypt/bcrypt.spec index 90ab7b52fef..4f481b0bd30 100644 --- a/dlls/bcrypt/bcrypt.spec +++ b/dlls/bcrypt/bcrypt.spec @@ -35,6 +35,7 @@ @ stdcall BCryptHashData(ptr ptr long long) @ stdcall BCryptImportKey(ptr ptr wstr ptr ptr long ptr long long) @ stdcall BCryptImportKeyPair(ptr ptr wstr ptr ptr long long) +@ stdcall BCryptKeyDerivation(ptr ptr ptr long ptr long) @ stdcall BCryptOpenAlgorithmProvider(ptr wstr wstr long) @ stub BCryptQueryContextConfiguration @ stub BCryptQueryContextFunctionConfiguration diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index f9dc1bcfe02..969049597e4 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -2605,19 +2605,19 @@ static NTSTATUS derive_key_raw( struct secret *secret, UCHAR *output, ULONG outp return status; }
-static struct algorithm *get_hash_alg( BCryptBuffer *buf ) +static struct algorithm *get_hash_alg( BCryptBuffer *buf, BOOL hmac ) { const WCHAR *str = buf->pvBuffer; BCRYPT_ALG_HANDLE handle = NULL;
if (!wcscmp( str, BCRYPT_SHA1_ALGORITHM )) - handle = BCRYPT_SHA1_ALG_HANDLE; + handle = hmac ? BCRYPT_HMAC_SHA1_ALG_HANDLE : BCRYPT_SHA1_ALG_HANDLE; else if (!wcscmp( str, BCRYPT_SHA256_ALGORITHM )) - handle = BCRYPT_SHA256_ALG_HANDLE; + handle = hmac ? BCRYPT_HMAC_SHA256_ALG_HANDLE : BCRYPT_SHA256_ALG_HANDLE; else if (!wcscmp( str, BCRYPT_SHA384_ALGORITHM )) - handle = BCRYPT_SHA384_ALG_HANDLE; + handle = hmac ? BCRYPT_HMAC_SHA384_ALG_HANDLE : BCRYPT_SHA384_ALG_HANDLE; else if (!wcscmp( str, BCRYPT_SHA512_ALGORITHM )) - handle = BCRYPT_SHA512_ALG_HANDLE; + handle = hmac ? BCRYPT_HMAC_SHA512_ALG_HANDLE : BCRYPT_SHA512_ALG_HANDLE;
if (handle) return get_alg_object( handle ); FIXME( "hash algorithm %s not supported\n", debugstr_w(str) ); @@ -2639,7 +2639,7 @@ static NTSTATUS derive_key_hash( struct secret *secret, BCryptBufferDesc *desc, { if (desc->pBuffers[i].BufferType == KDF_HASH_ALGORITHM) { - alg = get_hash_alg( desc->pBuffers + i ); + alg = get_hash_alg( desc->pBuffers + i, FALSE ); if (!alg) return STATUS_NOT_SUPPORTED; } else FIXME( "buffer type %lu not supported\n", desc->pBuffers[i].BufferType ); @@ -2699,6 +2699,50 @@ NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, return STATUS_NOT_SUPPORTED; }
+NTSTATUS WINAPI BCryptKeyDerivation( BCRYPT_KEY_HANDLE handle, BCryptBufferDesc *desc, + UCHAR *output, ULONG output_size, ULONG *ret_len, ULONG flags ) +{ + struct key *key = get_key_object( handle ); + struct algorithm *alg = NULL; + ULONGLONG iter_count = 10000; + ULONG salt_size = 0; + UCHAR *salt = NULL; + NTSTATUS status; + ULONG i; + + TRACE( "%p, %p, %p, %lu, %p, %#lx\n", key, desc, output, output_size, ret_len, flags ); + + if (!key || !desc || !ret_len) return STATUS_INVALID_PARAMETER; + if (key->alg_id != ALG_ID_PBKDF2) + { + FIXME("unsupported key %d\n", key->alg_id); + return STATUS_NOT_IMPLEMENTED; + } + + for (i = 0; i < desc->cBuffers; i++) + { + switch (desc->pBuffers[i].BufferType) + { + case KDF_HASH_ALGORITHM: + alg = get_hash_alg( desc->pBuffers + i, TRUE ); + break; + case KDF_SALT: + salt = desc->pBuffers[i].pvBuffer; + salt_size = desc->pBuffers[i].cbBuffer; + break; + case KDF_ITERATION_COUNT: + if (desc->pBuffers[i].cbBuffer != sizeof(ULONGLONG)) return STATUS_INVALID_PARAMETER; + iter_count = *(ULONGLONG *)desc->pBuffers[i].pvBuffer; + break; + } + } + + status = derive_key_pbkdf2( alg, key->u.s.secret, key->u.s.secret_len, + salt, salt_size, iter_count, output, output_size ); + if (!status) *ret_len = output_size; + return status; +} + BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) { switch (reason) diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 272819d3335..2ca0eb3ab91 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -29,6 +29,8 @@ #include "wine/test.h"
static NTSTATUS (WINAPI *pBCryptHash)(BCRYPT_ALG_HANDLE, UCHAR *, ULONG, UCHAR *, ULONG, UCHAR *, ULONG); +static NTSTATUS (WINAPI *pBCryptKeyDerivation)(BCRYPT_KEY_HANDLE, + BCryptBufferDesc *, UCHAR *, ULONG, ULONG *, ULONG);
static void test_BCryptGenRandom(void) { @@ -4372,11 +4374,46 @@ static void test_RC4(void)
static void test_PBKDF2(void) { + static char salt[] = "cCxuHMEHLibcglJOG88dIw=="; + static ULONGLONG iter_count = 25; + static BCryptBuffer pbkdf2_param_buffers[] = + { + { + sizeof(BCRYPT_SHA1_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA1_ALGORITHM, + }, + { + sizeof(salt) - 1, + KDF_SALT, + salt, + }, + { + sizeof(iter_count), + KDF_ITERATION_COUNT, + (void *)&iter_count, + } + }; + static BCryptBufferDesc pbkdf2_params = + { + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(pbkdf2_param_buffers), + pbkdf2_param_buffers, + }; + static UCHAR pbkdf2_hash[] = + { + 0x18, 0xf2, 0x58, 0x0b, 0x92, 0x6e, 0x6d, 0xfe, + 0x55, 0xbb, 0x62, 0x87, 0x5b, 0x1f, 0x61, 0x83, + 0x4d, 0x16, 0xe4, 0x04, 0xcd, 0xab, 0x43, 0x77, + 0x25, 0x3e, 0x1d, 0xb4, 0x95, 0x5f, 0xf7, 0x01, + }; + BCRYPT_KEY_LENGTHS_STRUCT key_lengths; BCRYPT_ALG_HANDLE alg; BCRYPT_KEY_HANDLE key; NTSTATUS status; ULONG val, size; + static BYTE buf[32];
status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_PBKDF2_ALGORITHM, NULL, 0); if (status == STATUS_NOT_FOUND) @@ -4385,6 +4422,7 @@ static void test_PBKDF2(void) return; } ok(!status, "got %#lx\n", status); + ok(pBCryptKeyDerivation != NULL, "BCryptKeyDerivation not available\n");
val = size = 0; status = BCryptGetProperty(alg, BCRYPT_OBJECT_LENGTH, (UCHAR *)&val, sizeof(val), &size, 0); @@ -4413,6 +4451,23 @@ static void test_PBKDF2(void) ok(!status, "got %#lx\n", status); ok(val == strlen("test") * 8, "got %lu\n", val);
+ status = pBCryptKeyDerivation(key, &pbkdf2_params, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %#lx\n", status); + + buf[0] = buf[1] = 'x'; + status = pBCryptKeyDerivation(key, &pbkdf2_params, buf, 1, &size, 0); + ok(!status, "got %#lx\n", status); + ok(size == 1, "size = %lu\n", size); + ok(buf[0] == pbkdf2_hash[0], "buf[0] = %x\n", buf[0]); + ok(buf[1] == 'x', "buf[1] = %x\n", buf[1]); + + memset(buf, 'x', sizeof(buf)); + status = pBCryptKeyDerivation(key, &pbkdf2_params, buf, sizeof(buf), &size, 0); + ok(!status, "got %#lx\n", status); + ok(size == sizeof(buf), "size = %lu\n", size); + ok(!memcmp(buf, pbkdf2_hash, sizeof(pbkdf2_hash)), + "wrong data (%s)\n", wine_dbgstr_an((char *)buf, size)); + status = BCryptDestroyKey(key); ok(!status, "got %#lx\n", status); status = BCryptCloseAlgorithmProvider(alg, 0); @@ -4430,6 +4485,7 @@ START_TEST(bcrypt) return; } pBCryptHash = (void *)GetProcAddress(module, "BCryptHash"); + pBCryptKeyDerivation = (void *)GetProcAddress(module, "BCryptKeyDerivation");
test_BCryptGenRandom(); test_BCryptGetFipsAlgorithmMode();
On Thu Apr 17 08:28:26 2025 +0000, Hans Leidekker wrote:
Please also add the alternative definition for compilers that don't support wide string literals.
Thanks, I've pushed a fixed version.
This merge request was approved by Hans Leidekker.