From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_internal.h | 10 +++ dlls/bcrypt/bcrypt_main.c | 140 +++++++++++++++++++++++++++++----- dlls/bcrypt/gnutls.c | 71 ++++++++++++++++- 3 files changed, 201 insertions(+), 20 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index 505435c6ac2..2f66060ec8e 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -302,6 +302,15 @@ struct key_asymmetric_import_params ULONG len; };
+struct key_asymmetric_derive_key_params +{ + struct key *privkey; + struct key *pubkey; + UCHAR *output; + ULONG output_len; + ULONG *ret_len; +}; + enum key_funcs { unix_process_attach, @@ -321,6 +330,7 @@ enum key_funcs unix_key_asymmetric_destroy, unix_key_asymmetric_export, unix_key_asymmetric_import, + unix_key_asymmetric_derive_key, unix_funcs_count, };
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index b76a36482de..94e91fdc46a 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -17,6 +17,7 @@ * */
+#include <assert.h> #include <stdarg.h> #include <stdlib.h>
@@ -1134,24 +1135,13 @@ NTSTATUS WINAPI BCryptFinishHash( BCRYPT_HASH_HANDLE handle, UCHAR *output, ULON return hash_finalize( hash, output ); }
-NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE handle, UCHAR *secret, ULONG secret_len, - UCHAR *input, ULONG input_len, UCHAR *output, ULONG output_len ) +static NTSTATUS hash_single( struct algorithm *alg, UCHAR *secret, ULONG secret_len, UCHAR *input, ULONG input_len, + UCHAR *output, ULONG output_len ) { - struct algorithm *alg = get_alg_object( handle ); struct hash *hash; NTSTATUS status;
- TRACE( "%p, %p, %lu, %p, %lu, %p, %lu\n", handle, secret, secret_len, input, input_len, output, output_len ); - - if (!alg) return STATUS_INVALID_HANDLE; - if (!output) return STATUS_INVALID_PARAMETER; - if ((status = hash_create( alg, secret, secret_len, 0, &hash ))) return status; - if (output_len != builtin_algorithms[hash->alg_id].hash_length) - { - hash_destroy( hash ); - return STATUS_INVALID_PARAMETER; - } if ((status = hash_update( &hash->inner, hash->alg_id, input, input_len ))) { hash_destroy( hash ); @@ -1162,6 +1152,19 @@ NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE handle, UCHAR *secret, ULONG secre return status; }
+NTSTATUS WINAPI BCryptHash( BCRYPT_ALG_HANDLE handle, UCHAR *secret, ULONG secret_len, UCHAR *input, ULONG input_len, + UCHAR *output, ULONG output_len ) +{ + struct algorithm *alg = get_alg_object( handle ); + + TRACE( "%p, %p, %lu, %p, %lu, %p, %lu\n", handle, secret, secret_len, input, input_len, output, output_len ); + + if (!alg) return STATUS_INVALID_HANDLE; + if (!output || output_len != builtin_algorithms[alg->id].hash_length) return STATUS_INVALID_PARAMETER; + + return hash_single(alg, secret, secret_len, input, input_len, output, output_len ); +} + static NTSTATUS key_asymmetric_create( enum alg_id alg_id, ULONG bitlen, struct key **ret_key ) { struct key *key; @@ -2471,17 +2474,118 @@ NTSTATUS WINAPI BCryptDestroySecret( BCRYPT_SECRET_HANDLE handle ) return STATUS_SUCCESS; }
-NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, BCryptBufferDesc *parameter, - UCHAR *derived, ULONG derived_size, ULONG *result, ULONG flags ) +static void reverse_bytes( UCHAR *buf, ULONG len ) +{ + ULONG i; + for (i = 0; i < len / 2; i++) + { + UCHAR tmp = buf[i]; + buf[i] = buf[len - i - 1]; + buf[len - i - 1] = tmp; + } +} + +static NTSTATUS derive_key_raw( struct secret *secret, UCHAR *output, ULONG output_len, ULONG *ret_len ) +{ + struct key_asymmetric_derive_key_params params; + NTSTATUS status; + + params.privkey = secret->privkey; + params.pubkey = secret->pubkey; + params.output = output; + params.output_len = output_len; + params.ret_len = ret_len; + if (!(status = UNIX_CALL( key_asymmetric_derive_key, ¶ms )) && output) reverse_bytes( output, *ret_len ); + return status; +} + +static BCRYPT_ALG_HANDLE hash_handle_from_desc( BCryptBufferDesc *desc ) +{ + 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 ); + } + + return BCRYPT_SHA1_ALG_HANDLE; +} + +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]; + UCHAR *derived_key; + NTSTATUS status; + + if (!alg) return STATUS_NOT_SUPPORTED; + if (!(derived_key = malloc( derived_key_len ))) return STATUS_NO_MEMORY; + + params.privkey = secret->privkey; + params.pubkey = secret->pubkey; + params.output = derived_key; + params.output_len = derived_key_len; + params.ret_len = ret_len; + if ((status = UNIX_CALL( key_asymmetric_derive_key, ¶ms ))) + { + free( derived_key ); + return status; + } + + hash_len = builtin_algorithms[alg->id].hash_length; + assert( hash_len <= sizeof(hash_buf) ); + if (!(status = hash_single( alg, NULL, 0, derived_key, *params.ret_len, hash_buf, hash_len ))) + { + if (!output) *ret_len = hash_len; + else + { + *ret_len = min( hash_len, output_len ); + memcpy( output, hash_buf, *ret_len ); + } + } + + free( derived_key ); + return status; +} + +NTSTATUS WINAPI BCryptDeriveKey( BCRYPT_SECRET_HANDLE handle, const WCHAR *kdf, BCryptBufferDesc *desc, + UCHAR *output, ULONG output_len, ULONG *ret_len, ULONG flags ) { struct secret *secret = get_secret_object( handle );
- FIXME( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), parameter, derived, derived_size, result, flags ); + TRACE( "%p, %s, %p, %p, %lu, %p, %#lx\n", secret, debugstr_w(kdf), desc, output, output_len, + ret_len, flags );
if (!secret) return STATUS_INVALID_HANDLE; - if (!kdf) return STATUS_INVALID_PARAMETER; + if (!kdf || !ret_len) return STATUS_INVALID_PARAMETER; + + if (!wcscmp(kdf, BCRYPT_KDF_RAW_SECRET)) + { + return derive_key_raw( secret, output, output_len, ret_len ); + } + else if (!wcscmp(kdf, BCRYPT_KDF_HASH)) + { + return derive_key_hash( secret, desc, output, output_len, ret_len ); + }
- return STATUS_INTERNAL_ERROR; + FIXME( "kdf %s not supportedi\n", debugstr_w(kdf) ); + return STATUS_NOT_SUPPORTED; }
BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 5d8c71c78c5..81a6039ad4c 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -144,6 +144,10 @@ static void (*pgnutls_x509_spki_set_rsa_pss_params)(gnutls_x509_spki_t, gnutls_d static int (*pgnutls_pubkey_set_spki)(gnutls_pubkey_t, const gnutls_x509_spki_t, unsigned int); static int (*pgnutls_privkey_set_spki)(gnutls_privkey_t, const gnutls_x509_spki_t, unsigned int);
+/* Not present in gnutls version < 3.8.2 */ +static int (*pgnutls_privkey_derive_secret)(gnutls_privkey_t, gnutls_pubkey_t, const gnutls_datum_t *, + gnutls_datum_t *, unsigned int); + static void *libgnutls_handle; #define MAKE_FUNCPTR(f) static typeof(f) * p##f MAKE_FUNCPTR(gnutls_cipher_decrypt2); @@ -302,6 +306,12 @@ static int compat_gnutls_privkey_set_spki(gnutls_privkey_t key, const gnutls_x50 return GNUTLS_E_UNKNOWN_PK_ALGORITHM; }
+static int compat_gnutls_privkey_derive_secret(gnutls_privkey_t privkey, gnutls_pubkey_t pubkey, const gnutls_datum_t *nonce, + gnutls_datum_t *secret, unsigned int flags) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + static void gnutls_log( int level, const char *msg ) { TRACE( "<%d> %s", level, msg ); @@ -365,6 +375,7 @@ static NTSTATUS gnutls_process_attach( void *args ) LOAD_FUNCPTR_OPT(gnutls_decode_rs_value) LOAD_FUNCPTR_OPT(gnutls_pk_to_sign) LOAD_FUNCPTR_OPT(gnutls_privkey_decrypt_data) + LOAD_FUNCPTR_OPT(gnutls_privkey_derive_secret) LOAD_FUNCPTR_OPT(gnutls_privkey_export_dsa_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_ecc_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_rsa_raw) @@ -2250,6 +2261,30 @@ static NTSTATUS key_asymmetric_encrypt( void *args ) return status; }
+static NTSTATUS key_asymmetric_derive_key( void *args ) +{ + const struct key_asymmetric_derive_key_params *params = args; + gnutls_datum_t s; + int ret; + + if ((ret = pgnutls_privkey_derive_secret( key_data(params->privkey)->a.privkey, + key_data(params->pubkey)->a.pubkey, NULL, &s, 0 ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (!params->output) *params->ret_len = s.size; + else + { + *params->ret_len = min( params->output_len, s.size ); + memcpy( params->output, s.data, *params->ret_len ); + } + + free( s.data ); + return STATUS_SUCCESS; +} + const unixlib_entry_t __wine_unix_call_funcs[] = { gnutls_process_attach, @@ -2268,7 +2303,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] = key_asymmetric_verify, key_asymmetric_destroy, key_asymmetric_export, - key_asymmetric_import + key_asymmetric_import, + key_asymmetric_derive_key, };
C_ASSERT( ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count ); @@ -2733,6 +2769,36 @@ static NTSTATUS wow64_key_asymmetric_import( void *args ) return ret; }
+static NTSTATUS wow64_key_asymmetric_derive_key( void *args ) +{ + struct + { + PTR32 privkey; + PTR32 pubkey; + PTR32 output; + ULONG output_len; + PTR32 ret_len; + } const *params32 = args; + + NTSTATUS ret; + struct key privkey, pubkey; + struct key32 *privkey32 = ULongToPtr( params32->privkey ); + struct key32 *pubkey32 = ULongToPtr( params32->pubkey ); + struct key_asymmetric_derive_key_params params = + { + get_asymmetric_key( privkey32, &privkey ), + get_asymmetric_key( pubkey32, &pubkey ), + ULongToPtr(params32->output), + params32->output_len, + ULongToPtr(params32->ret_len), + }; + + ret = key_asymmetric_derive_key( ¶ms ); + put_asymmetric_key32( &privkey, privkey32 ); + put_asymmetric_key32( &pubkey, pubkey32 ); + return ret; +} + const unixlib_entry_t __wine_unix_call_wow64_funcs[] = { gnutls_process_attach, @@ -2751,7 +2817,8 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = wow64_key_asymmetric_verify, wow64_key_asymmetric_destroy, wow64_key_asymmetric_export, - wow64_key_asymmetric_import + wow64_key_asymmetric_import, + wow64_key_asymmetric_derive_key, };
C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count );