Commit 81d71ee0 authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

bcrypt: Implement BCryptDeriveKey().

parent cfa655c4
......@@ -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,
};
......
......@@ -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, &params )) && 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, &params )))
{
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 )
......
......@@ -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( &params );
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 );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment