diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 6d266f112bd..25075e1c890 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1346,7 +1346,7 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U return STATUS_SUCCESS; } else if (!wcscmp( type, BCRYPT_DSA_PRIVATE_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB ) || - !wcscmp( type, BCRYPT_ECCPRIVATE_BLOB )) + !wcscmp( type, BCRYPT_ECCPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_DH_PRIVATE_BLOB )) { params.key = key; params.flags = 0; @@ -1365,7 +1365,8 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U return UNIX_CALL( key_asymmetric_export, ¶ms ); } else if (!wcscmp( type, BCRYPT_DSA_PUBLIC_BLOB ) || !wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB ) || - !wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_RSAPUBLIC_BLOB )) + !wcscmp( type, BCRYPT_ECCPUBLIC_BLOB ) || !wcscmp( type, BCRYPT_RSAPUBLIC_BLOB ) || + !wcscmp( type, BCRYPT_DH_PUBLIC_BLOB )) { params.key = key; params.flags = KEY_EXPORT_FLAG_PUBLIC; diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index c448ad68f2b..a5ecae57da9 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -147,6 +147,9 @@ static int (*pgnutls_privkey_set_spki)(gnutls_privkey_t, const gnutls_x509_spki_ /* 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 int (*pgnutls_privkey_export_dh_raw)(gnutls_privkey_t, gnutls_dh_params_t, gnutls_datum_t *, gnutls_datum_t *, + unsigned int); +static int (*pgnutls_pubkey_export_dh_raw)(gnutls_pubkey_t, gnutls_dh_params_t, gnutls_datum_t *, unsigned); static void *libgnutls_handle; #define MAKE_FUNCPTR(f) static typeof(f) * p##f @@ -154,6 +157,9 @@ MAKE_FUNCPTR(gnutls_cipher_decrypt2); MAKE_FUNCPTR(gnutls_cipher_deinit); MAKE_FUNCPTR(gnutls_cipher_encrypt2); MAKE_FUNCPTR(gnutls_cipher_init); +MAKE_FUNCPTR(gnutls_dh_params_deinit); +MAKE_FUNCPTR(gnutls_dh_params_export_raw); +MAKE_FUNCPTR(gnutls_dh_params_init); MAKE_FUNCPTR(gnutls_global_deinit); MAKE_FUNCPTR(gnutls_global_init); MAKE_FUNCPTR(gnutls_global_set_log_function); @@ -312,6 +318,18 @@ static int compat_gnutls_privkey_derive_secret(gnutls_privkey_t privkey, gnutls_ return GNUTLS_E_UNKNOWN_PK_ALGORITHM; } +static int compat_gnutls_privkey_export_dh_raw(gnutls_privkey_t privkey, gnutls_dh_params_t params, gnutls_datum_t *y, + gnutls_datum_t *x, unsigned int flags ) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + +static int compat_gnutls_pubkey_export_dh_raw(gnutls_pubkey_t pubkey, gnutls_dh_params_t params, gnutls_datum_t *y, + unsigned flags) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + static void gnutls_log( int level, const char *msg ) { TRACE( "<%d> %s", level, msg ); @@ -349,6 +367,9 @@ static NTSTATUS gnutls_process_attach( void *args ) LOAD_FUNCPTR(gnutls_cipher_deinit) LOAD_FUNCPTR(gnutls_cipher_encrypt2) LOAD_FUNCPTR(gnutls_cipher_init) + LOAD_FUNCPTR(gnutls_dh_params_deinit) + LOAD_FUNCPTR(gnutls_dh_params_export_raw) + LOAD_FUNCPTR(gnutls_dh_params_init) LOAD_FUNCPTR(gnutls_global_deinit) LOAD_FUNCPTR(gnutls_global_init) LOAD_FUNCPTR(gnutls_global_set_log_function) @@ -376,6 +397,7 @@ static NTSTATUS gnutls_process_attach( void *args ) 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_dh_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_dsa_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_ecc_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_rsa_raw) @@ -384,6 +406,7 @@ static NTSTATUS gnutls_process_attach( void *args ) LOAD_FUNCPTR_OPT(gnutls_privkey_import_rsa_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_set_spki) LOAD_FUNCPTR_OPT(gnutls_pubkey_encrypt_data) + LOAD_FUNCPTR_OPT(gnutls_pubkey_export_dh_raw) LOAD_FUNCPTR_OPT(gnutls_pubkey_export_dsa_raw) LOAD_FUNCPTR_OPT(gnutls_pubkey_export_ecc_raw) LOAD_FUNCPTR_OPT(gnutls_pubkey_export_rsa_raw) @@ -1540,6 +1563,109 @@ static NTSTATUS key_import_dsa_capi_public( struct key *key, UCHAR *buf, ULONG l return STATUS_SUCCESS; } +static NTSTATUS key_export_dh_public( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + ULONG size = key->u.a.bitlen / 8; + gnutls_dh_params_t params; + gnutls_datum_t p, g, y, x = {0}; + UCHAR *dst; + int ret = GNUTLS_E_INVALID_REQUEST; + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.pubkey) + ret = pgnutls_pubkey_export_dh_raw( key_data(key)->a.pubkey, params, &y, 0 ); + else if (key_data(key)->a.privkey) + ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, &y, &x, 0 ); + + if (ret) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL )) < 0) + { + pgnutls_perror( ret ); + free( y.data ); free( x.data ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sizeof(*dh_blob) + EXPORT_SIZE(p, size, 1) + EXPORT_SIZE(g, size, 1) + EXPORT_SIZE(y, size, 1); + if (len >= *ret_len && buf) + { + dst = (UCHAR *)(dh_blob + 1); + dst += export_gnutls_datum( dst, size, &p, 1 ); + dst += export_gnutls_datum( dst, size, &g, 1 ); + dst += export_gnutls_datum( dst, size, &y, 1 ); + + dh_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + dh_blob->cbKey = size; + } + + free( p.data ); free( g.data ); free( y.data ); free( x.data ); + return STATUS_SUCCESS; +} + +static NTSTATUS key_export_dh( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)buf; + gnutls_datum_t p, g, y, x; + gnutls_dh_params_t params; + ULONG size = key->u.a.bitlen / 8; + UCHAR *dst; + int ret; + + if (!key_data(key)->a.privkey) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_dh_params_init( ¶ms )) < 0) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, &y, &x, 0 ); + if (ret) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL )) < 0) + { + pgnutls_perror( ret ); + free( y.data ); free( x.data ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = sizeof(*dh_blob) + EXPORT_SIZE(p, size, 1) + EXPORT_SIZE(g, size, 1) + + EXPORT_SIZE(y, size, 1) + EXPORT_SIZE(x, size, 1); + if (len >= *ret_len && buf) + { + dst = (UCHAR *)(dh_blob + 1); + dst += export_gnutls_datum( dst, size, &p, 1 ); + dst += export_gnutls_datum( dst, size, &g, 1 ); + dst += export_gnutls_datum( dst, size, &y, 1 ); + dst += export_gnutls_datum( dst, size, &x, 1 ); + + dh_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_blob->cbKey = size; + } + + free( p.data ); free( g.data ); free( y.data ); free( x.data ); + pgnutls_dh_params_deinit( params ); + return STATUS_SUCCESS; +} + static NTSTATUS key_asymmetric_export( void *args ) { const struct key_asymmetric_export_params *params = args; @@ -1573,6 +1699,11 @@ static NTSTATUS key_asymmetric_export( void *args ) return key_export_dsa_capi( key, params->buf, params->len, params->ret_len ); return STATUS_NOT_IMPLEMENTED; + case ALG_ID_DH: + if (flags & KEY_EXPORT_FLAG_PUBLIC) + return key_export_dh_public( key, params->buf, params->len, params->ret_len ); + return key_export_dh( key, params->buf, params->len, params->ret_len ); + default: FIXME( "algorithm %u not yet supported\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index e8431ea4a4a..1337f253397 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -3140,7 +3140,10 @@ derive_end: static void test_DH(void) { BCRYPT_KEY_HANDLE key; + BCRYPT_DH_KEY_BLOB *dhkey; NTSTATUS status; + UCHAR *buf; + ULONG size; key = NULL; status = BCryptGenerateKeyPair(BCRYPT_DH_ALG_HANDLE, &key, 512, 0); @@ -3149,7 +3152,26 @@ static void test_DH(void) status = BCryptFinalizeKeyPair(key, 0); todo_wine ok(status == STATUS_SUCCESS, "got %#lx\n", status); + if (status != STATUS_SUCCESS) + { + BCryptDestroyKey(key); + return; + } + size = 0; + status = BCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); + ok(size, "size not set\n"); + + buf = malloc(size); + status = BCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %#lx\n", status); + dhkey = (BCRYPT_DH_KEY_BLOB *)buf; + ok(dhkey->dwMagic == BCRYPT_DH_PUBLIC_MAGIC, "got %#lx\n", dhkey->dwMagic); + ok(dhkey->cbKey == 64, "got %lu\n", dhkey->cbKey); + ok(size == sizeof(*dhkey) + dhkey->cbKey * 3, "got %lu\n", size); + + free(buf); BCryptDestroyKey(key); }