Support for generating keys from known DH parameters is not included unfortunately because even the latest stable GnuTLS release doesn't have the necessary support. I have a patch that implements it using _gnutls_dh_generate_key() but that requires a special GnuTLS 3.8.2 build (--enable-fips140-mode). With that patch all included tests pass here.
Paul, can you take a look? I included your tests so please approve this MR if you think it's okay.
-- v2: bcrypt/tests: Add DH tests. bcrypt: Make sure key_asymmetric_derive_key() returns correct size. bcrypt: Add support for generating DH keys from known parameters. bcrypt: Reject DH keys smaller than 512 bits. bcrypt: Make DH blob size validation more strict in key_import_pair(). bcrypt: Add helpers to create a public/private key pair. bcrypt: Allow or disallow some operations based on whether keys are finalized. bcrypt: Add support for retrieving DH parameters. bcrypt: Add support for setting DH parameters.
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_internal.h | 6 +++-- dlls/bcrypt/bcrypt_main.c | 15 +++++++++++++ dlls/bcrypt/gnutls.c | 41 ++++++++++++++++++++++++++++++++--- include/bcrypt.h | 13 +++++++++++ 4 files changed, 70 insertions(+), 5 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index cfbc5d2ac83..a31d170c621 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -187,7 +187,7 @@ struct key { struct object hdr; enum alg_id alg_id; - UINT64 private[2]; /* private data for backend */ + UINT64 private[3]; /* private data for backend */ union { struct key_symmetric s; @@ -294,7 +294,9 @@ struct key_asymmetric_export_params ULONG *ret_len; };
-#define KEY_IMPORT_FLAG_PUBLIC 0x00000001 +#define KEY_IMPORT_FLAG_PUBLIC 0x00000001 +#define KEY_IMPORT_FLAG_DH_PARAMETERS 0x00000002 + struct key_asymmetric_import_params { struct key *key; diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 26472cbfe2b..2f8fd75c06a 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -887,6 +887,21 @@ static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *val key->u.a.bitlen = *(DWORD*)value; return STATUS_SUCCESS; } + else if (!wcscmp( prop, BCRYPT_DH_PARAMETERS )) + { + BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)value; + struct key_asymmetric_import_params params; + + if (key->alg_id != ALG_ID_DH || size < sizeof(*hdr) || hdr->cbLength != size || + hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != key->u.a.bitlen / 8) + return STATUS_INVALID_PARAMETER; + + params.key = key; + params.flags = KEY_IMPORT_FLAG_DH_PARAMETERS; + params.buf = value; + params.len = size; + return UNIX_CALL( key_asymmetric_import, ¶ms ); + }
FIXME( "unsupported key property %s\n", debugstr_w(prop) ); return STATUS_NOT_IMPLEMENTED; diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 68f84a553d2..0d912e7a2d7 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -84,8 +84,9 @@ union key_data gnutls_cipher_hd_t cipher; struct { - gnutls_privkey_t privkey; - gnutls_pubkey_t pubkey; + gnutls_privkey_t privkey; + gnutls_pubkey_t pubkey; + gnutls_dh_params_t dh_params; } a; }; C_ASSERT( sizeof(union key_data) <= sizeof(((struct key *)0)->private) ); @@ -1833,6 +1834,36 @@ static NTSTATUS key_import_dh( struct key *key, UCHAR *buf, ULONG len ) return STATUS_SUCCESS; }
+static NTSTATUS key_import_dh_params( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_DH_PARAMETER_HEADER *dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buf; + gnutls_dh_params_t params; + gnutls_datum_t p, g; + int ret; + + if ((ret = pgnutls_dh_params_init( ¶ms ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + p.data = (unsigned char *)(dh_header + 1); + p.size = dh_header->cbKeyLength; + g.data = p.data + dh_header->cbKeyLength; + g.size = dh_header->cbKeyLength; + + if ((ret = pgnutls_dh_params_import_raw( params, &p, &g ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + + if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params ); + key_data(key)->a.dh_params = params; + return STATUS_SUCCESS; +} + static NTSTATUS key_asymmetric_import( void *args ) { const struct key_asymmetric_import_params *params = args; @@ -1875,9 +1906,12 @@ static NTSTATUS key_asymmetric_import( void *args ) return STATUS_NOT_IMPLEMENTED;
case ALG_ID_DH: + if (flags & KEY_IMPORT_FLAG_DH_PARAMETERS) + return key_import_dh_params( key, params->buf, params->len ); if (flags & KEY_IMPORT_FLAG_PUBLIC) return key_import_dh_public( key, params->buf, params->len ); - return key_import_dh( key, params->buf, params->len ); + ret = key_import_dh( key, params->buf, params->len ); + break;
default: FIXME( "algorithm %u not yet supported\n", key->alg_id ); @@ -2300,6 +2334,7 @@ static NTSTATUS key_asymmetric_destroy( void *args )
if (key_data(key)->a.privkey) pgnutls_privkey_deinit( key_data(key)->a.privkey ); if (key_data(key)->a.pubkey) pgnutls_pubkey_deinit( key_data(key)->a.pubkey ); + if (key_data(key)->a.dh_params) pgnutls_dh_params_deinit( key_data(key)->a.dh_params ); return STATUS_SUCCESS; }
diff --git a/include/bcrypt.h b/include/bcrypt.h index 7f768f61679..462c43a7021 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -118,6 +118,8 @@ typedef LONG NTSTATUS; #define BCRYPT_KDF_TLS_PRF L"TLS_PRF" #define BCRYPT_KDF_SP80056A_CONCAT L"SP800_56A_CONCAT" #define BCRYPT_KDF_RAW_SECRET L"TRUNCATE" + +#define BCRYPT_DH_PARAMETERS L"DHParameters" #else static const WCHAR BCRYPT_ALGORITHM_NAME[] = {'A','l','g','o','r','i','t','h','m','N','a','m','e',0}; static const WCHAR BCRYPT_AUTH_TAG_LENGTH[] = {'A','u','t','h','T','a','g','L','e','n','g','t','h',0}; @@ -198,6 +200,8 @@ static const WCHAR BCRYPT_KDF_HMAC[] = {'H','M','A','C',0}; static const WCHAR BCRYPT_KDF_TLS_PRF[] = {'T','L','S','_','P','R','F',0}; static const WCHAR BCRYPT_KDF_SP80056A_CONCAT[] = {'S','P','8','0','0','_','5','6','A','_','C','O','N','C','A','T',0}; static const WCHAR BCRYPT_KDF_RAW_SECRET[] = {'T','R','U','N','C','A','T','E',0}; + +static const WCHAR BCRYPT_DH_PARAMETERS[] = {'D','H','P','a','r','a','m','e','t','e','r','s',0}; #endif
#define BCRYPT_ECDSA_PUBLIC_P256_MAGIC 0x31534345 @@ -363,6 +367,15 @@ typedef struct _BCRYPT_DH_KEY_BLOB ULONG cbKey; } BCRYPT_DH_KEY_BLOB, *PBCRYPT_DH_KEY_BLOB;
+#define BCRYPT_DH_PARAMETERS_MAGIC 0x4d504844 + +typedef struct _BCRYPT_DH_PARAMETER_HEADER +{ + ULONG cbLength; + ULONG dwMagic; + ULONG cbKeyLength; +} BCRYPT_DH_PARAMETER_HEADER; + #define BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION 1
#define BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG 0x00000001
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_internal.h | 6 +++-- dlls/bcrypt/bcrypt_main.c | 17 +++++++++++++++ dlls/bcrypt/gnutls.c | 41 ++++++++++++++++++++++++++++++++--- 3 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index a31d170c621..a5dbeff4e8e 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -283,8 +283,10 @@ struct key_asymmetric_verify_params unsigned flags; };
-#define KEY_EXPORT_FLAG_PUBLIC 0x00000001 -#define KEY_EXPORT_FLAG_RSA_FULL 0x00000002 +#define KEY_EXPORT_FLAG_PUBLIC 0x00000001 +#define KEY_EXPORT_FLAG_RSA_FULL 0x00000002 +#define KEY_EXPORT_FLAG_DH_PARAMETERS 0x00000004 + struct key_asymmetric_export_params { struct key *key; diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 2f8fd75c06a..ab41867f649 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -917,6 +917,20 @@ static NTSTATUS get_hash_property( const struct hash *hash, const WCHAR *prop, U return status; }
+static NTSTATUS get_dh_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) +{ + struct key_asymmetric_export_params params; + + if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED; + + params.key = (struct key *)key; + params.flags = KEY_EXPORT_FLAG_DH_PARAMETERS; + params.buf = buf; + params.len = size; + params.ret_len = ret_size; + return UNIX_CALL( key_asymmetric_export, ¶ms ); +} + static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHAR *buf, ULONG size, ULONG *ret_size ) { if (!wcscmp( prop, BCRYPT_KEY_STRENGTH )) @@ -940,6 +954,9 @@ static NTSTATUS get_key_property( const struct key *key, const WCHAR *prop, UCHA if (!wcscmp( prop, BCRYPT_AUTH_TAG_LENGTH )) return STATUS_NOT_SUPPORTED; return get_aes_property( key->u.s.mode, prop, buf, size, ret_size );
+ case ALG_ID_DH: + return get_dh_property( key, prop, buf, size, ret_size ); + default: FIXME( "unsupported algorithm %u\n", key->alg_id ); return STATUS_NOT_IMPLEMENTED; diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 0d912e7a2d7..4183aad6cec 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -1651,8 +1651,7 @@ static NTSTATUS key_export_dh( struct key *key, UCHAR *buf, ULONG len, ULONG *re return STATUS_INTERNAL_ERROR; }
- ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, &y, &x, 0 ); - if (ret) + if ((ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, &y, &x, 0 ))) { pgnutls_perror( ret ); pgnutls_dh_params_deinit( params ); @@ -1686,6 +1685,40 @@ static NTSTATUS key_export_dh( struct key *key, UCHAR *buf, ULONG len, ULONG *re return STATUS_SUCCESS; }
+static NTSTATUS key_export_dh_params( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)buf; + unsigned int size = sizeof(*hdr) + key->u.a.bitlen / 8 * 2; + gnutls_datum_t p, g; + NTSTATUS status = STATUS_SUCCESS; + UCHAR *dst; + int ret; + + if (!key_data(key)->a.dh_params) return STATUS_INVALID_PARAMETER; + + if ((ret = pgnutls_dh_params_export_raw( key_data(key)->a.dh_params, &p, &g, NULL ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + *ret_len = size; + if (len < size) status = STATUS_BUFFER_TOO_SMALL; + else if (buf) + { + hdr->cbLength = size; + hdr->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + hdr->cbKeyLength = key->u.a.bitlen / 8; + + dst = (UCHAR *)(hdr + 1); + dst += export_gnutls_datum( dst, hdr->cbKeyLength, &p, 1 ); + dst += export_gnutls_datum( dst, hdr->cbKeyLength, &g, 1 ); + } + + free( p.data ); free( g.data ); + return status; +} + static NTSTATUS key_asymmetric_export( void *args ) { const struct key_asymmetric_export_params *params = args; @@ -1720,7 +1753,9 @@ static NTSTATUS key_asymmetric_export( void *args ) return STATUS_NOT_IMPLEMENTED;
case ALG_ID_DH: - if (flags & KEY_EXPORT_FLAG_PUBLIC) + if (flags & KEY_EXPORT_FLAG_DH_PARAMETERS) + return key_export_dh_params( key, params->buf, params->len, params->ret_len ); + 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 );
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_internal.h | 1 + dlls/bcrypt/bcrypt_main.c | 47 +++++++++++++---------------------- dlls/bcrypt/gnutls.c | 2 ++ 3 files changed, 20 insertions(+), 30 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index a5dbeff4e8e..f6556eb2aaa 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -175,6 +175,7 @@ struct key_symmetric };
#define KEY_FLAG_LEGACY_DSA_V2 0x00000001 +#define KEY_FLAG_FINALIZED 0x00000002
struct key_asymmetric { diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index ab41867f649..f7a22634203 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -892,6 +892,7 @@ static NTSTATUS set_key_property( struct key *key, const WCHAR *prop, UCHAR *val BCRYPT_DH_PARAMETER_HEADER *hdr = (BCRYPT_DH_PARAMETER_HEADER *)value; struct key_asymmetric_import_params params;
+ if (key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; if (key->alg_id != ALG_ID_DH || size < sizeof(*hdr) || hdr->cbLength != size || hdr->dwMagic != BCRYPT_DH_PARAMETERS_MAGIC || hdr->cbKeyLength != key->u.a.bitlen / 8) return STATUS_INVALID_PARAMETER; @@ -922,6 +923,7 @@ static NTSTATUS get_dh_property( const struct key *key, const WCHAR *prop, UCHAR struct key_asymmetric_export_params params;
if (wcscmp( prop, BCRYPT_DH_PARAMETERS )) return STATUS_NOT_SUPPORTED; + if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE;
params.key = (struct key *)key; params.flags = KEY_EXPORT_FLAG_DH_PARAMETERS; @@ -1735,9 +1737,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, BCRYPT_ECCPRIVATE_BLOB )) { @@ -1782,9 +1781,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, BCRYPT_RSAPUBLIC_BLOB )) { @@ -1807,9 +1803,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, BCRYPT_RSAPRIVATE_BLOB ) || !wcscmp( type, BCRYPT_RSAFULLPRIVATE_BLOB )) { @@ -1829,9 +1822,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, LEGACY_RSAPRIVATE_BLOB )) { @@ -1864,9 +1854,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, LEGACY_DSA_V2_PRIVATE_BLOB )) { @@ -1904,9 +1891,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, LEGACY_DSA_V2_PUBLIC_BLOB )) /* not supported on native */ { @@ -1940,9 +1924,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, BCRYPT_DH_PRIVATE_BLOB )) { @@ -1962,9 +1943,6 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } - - *ret_key = key; - return STATUS_SUCCESS; } else if (!wcscmp( type, BCRYPT_DH_PUBLIC_BLOB )) { @@ -1984,13 +1962,19 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP key_destroy( key ); return status; } + } + else + { + FIXME( "unsupported key type %s\n", debugstr_w(type) ); + return STATUS_NOT_SUPPORTED; + }
+ if (!status) + { + key->u.a.flags |= KEY_FLAG_FINALIZED; *ret_key = key; - return STATUS_SUCCESS; } - - FIXME( "unsupported key type %s\n", debugstr_w(type) ); - return STATUS_NOT_SUPPORTED; + return status; }
NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE *ret_handle, @@ -2031,11 +2015,14 @@ NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HAND NTSTATUS WINAPI BCryptFinalizeKeyPair( BCRYPT_KEY_HANDLE handle, ULONG flags ) { struct key *key = get_key_object( handle ); + NTSTATUS ret;
TRACE( "%p, %#lx\n", key, flags );
- if (!key) return STATUS_INVALID_HANDLE; - return UNIX_CALL( key_asymmetric_generate, key ); + if (!key || key->u.a.flags & KEY_FLAG_FINALIZED) return STATUS_INVALID_HANDLE; + + if (!(ret = UNIX_CALL( key_asymmetric_generate, key ))) key->u.a.flags |= KEY_FLAG_FINALIZED; + return ret; }
NTSTATUS WINAPI BCryptImportKey( BCRYPT_ALG_HANDLE handle, BCRYPT_KEY_HANDLE decrypt_key, const WCHAR *type, diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 4183aad6cec..171895e94f5 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -1725,6 +1725,8 @@ static NTSTATUS key_asymmetric_export( void *args ) struct key *key = params->key; unsigned flags = params->flags;
+ if (!(key->u.a.flags & KEY_FLAG_FINALIZED)) return STATUS_INVALID_HANDLE; + switch (key->alg_id) { case ALG_ID_ECDH_P256:
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/gnutls.c | 67 +++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 23 deletions(-)
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 171895e94f5..dbcfd723687 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -1005,6 +1005,48 @@ done: return status; }
+static gnutls_privkey_t create_privkey( gnutls_pk_algorithm_t pk_alg, unsigned int bitlen ) +{ + gnutls_privkey_t privkey; + int ret; + + if ((ret = pgnutls_privkey_init( &privkey ))) + { + pgnutls_perror( ret ); + return NULL; + } + + if ((ret = pgnutls_privkey_generate( privkey, pk_alg, bitlen, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_privkey_deinit( privkey ); + return NULL; + } + + return privkey; +} + +static gnutls_pubkey_t create_pubkey_from_privkey( gnutls_privkey_t privkey ) +{ + gnutls_pubkey_t pubkey; + int ret; + + if ((ret = pgnutls_pubkey_init( &pubkey ))) + { + pgnutls_perror( ret ); + return NULL; + } + + if ((ret = pgnutls_pubkey_import_privkey( pubkey, privkey, 0, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_pubkey_deinit( pubkey ); + return NULL; + } + + return pubkey; +} + static NTSTATUS key_asymmetric_generate( void *args ) { struct key *key = args; @@ -1012,7 +1054,6 @@ static NTSTATUS key_asymmetric_generate( void *args ) gnutls_privkey_t privkey; gnutls_pubkey_t pubkey; unsigned int bitlen; - int ret;
if (!libgnutls_handle) return STATUS_INTERNAL_ERROR; if (key_data(key)->a.privkey) return STATUS_INVALID_HANDLE; @@ -1052,30 +1093,10 @@ static NTSTATUS key_asymmetric_generate( void *args ) return STATUS_NOT_SUPPORTED; }
- if ((ret = pgnutls_privkey_init( &privkey ))) - { - pgnutls_perror( ret ); - return STATUS_INTERNAL_ERROR; - } - if ((ret = pgnutls_pubkey_init( &pubkey ))) - { - pgnutls_perror( ret ); - pgnutls_privkey_deinit( privkey ); - return STATUS_INTERNAL_ERROR; - } - - if ((ret = pgnutls_privkey_generate( privkey, pk_alg, bitlen, 0 ))) - { - pgnutls_perror( ret ); - pgnutls_privkey_deinit( privkey ); - pgnutls_pubkey_deinit( pubkey ); - return STATUS_INTERNAL_ERROR; - } - if ((ret = pgnutls_pubkey_import_privkey( pubkey, privkey, 0, 0 ))) + if (!(privkey = create_privkey( pk_alg, bitlen ))) return STATUS_INTERNAL_ERROR; + if (!(pubkey = create_pubkey_from_privkey( privkey ))) { - pgnutls_perror( ret ); pgnutls_privkey_deinit( privkey ); - pgnutls_pubkey_deinit( pubkey ); return STATUS_INTERNAL_ERROR; }
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index f7a22634203..274bb27d9e8 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1929,7 +1929,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP { BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)input;
- if (input_len < sizeof(*dh_blob)) return STATUS_INVALID_PARAMETER; + if (input_len != sizeof(*dh_blob) + dh_blob->cbKey * 4) return STATUS_INVALID_PARAMETER; if (alg->id != ALG_ID_DH || dh_blob->dwMagic != BCRYPT_DH_PRIVATE_MAGIC) return STATUS_NOT_SUPPORTED;
@@ -1948,7 +1948,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP { BCRYPT_DH_KEY_BLOB *dh_blob = (BCRYPT_DH_KEY_BLOB *)input;
- if (input_len < sizeof(*dh_blob)) return STATUS_INVALID_PARAMETER; + if (input_len != sizeof(*dh_blob) + dh_blob->cbKey * 3) return STATUS_INVALID_PARAMETER; if (alg->id != ALG_ID_DH || dh_blob->dwMagic != BCRYPT_DH_PUBLIC_MAGIC) return STATUS_NOT_SUPPORTED;
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_main.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 274bb27d9e8..5bac53657c1 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -1210,6 +1210,8 @@ static NTSTATUS key_asymmetric_create( enum alg_id alg_id, ULONG bitlen, struct return STATUS_NOT_IMPLEMENTED; }
+ if (alg_id == ALG_ID_DH && bitlen < 512) return STATUS_INVALID_PARAMETER; + if (!(key = calloc( 1, sizeof(*key) ))) return STATUS_NO_MEMORY; key->hdr.magic = MAGIC_KEY; key->alg_id = alg_id;
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/gnutls.c | 70 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-)
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index dbcfd723687..3592c685c56 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -79,6 +79,10 @@ typedef enum typedef struct gnutls_x509_spki_st *gnutls_x509_spki_t; #endif
+#if GUTLS_VERSION_MAJOR < 3 || (GNUTLS_VERSION_MAJOR == 3 && GNUTLS_VERSION_MINOR < 8) +#define GNUTLS_KEYGEN_DH 4 +#endif + union key_data { gnutls_cipher_hd_t cipher; @@ -132,11 +136,14 @@ static int (*pgnutls_privkey_export_rsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *); static int (*pgnutls_privkey_export_dsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *); -static int (*pgnutls_privkey_generate)(gnutls_privkey_t, gnutls_pk_algorithm_t, unsigned int, unsigned int); static int (*pgnutls_privkey_import_rsa_raw)(gnutls_privkey_t, const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *, const gnutls_datum_t *);
+/* Not present in gnutls version < 3.5.0 */ +static int (*pgnutls_privkey_generate2)(gnutls_privkey_t, gnutls_pk_algorithm_t, unsigned int, unsigned int, + const gnutls_keygen_data_st *, unsigned); + /* Not present in gnutls version < 3.6.0 */ static int (*pgnutls_decode_rs_value)(const gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *); static int (*pgnutls_x509_spki_init)(gnutls_x509_spki_t *); @@ -263,12 +270,6 @@ static int compat_gnutls_pubkey_import_dsa_raw(gnutls_pubkey_t key, const gnutls return GNUTLS_E_UNKNOWN_PK_ALGORITHM; }
-static int compat_gnutls_privkey_generate(gnutls_privkey_t key, gnutls_pk_algorithm_t algo, unsigned int bits, - unsigned int flags) -{ - return GNUTLS_E_UNKNOWN_PK_ALGORITHM; -} - static int compat_gnutls_decode_rs_value(const gnutls_datum_t * sig_value, gnutls_datum_t * r, gnutls_datum_t * s) { return GNUTLS_E_INTERNAL_ERROR; @@ -347,6 +348,12 @@ static int compat_gnutls_pubkey_import_dh_raw(gnutls_pubkey_t pubkey, const gnut return GNUTLS_E_UNKNOWN_PK_ALGORITHM; }
+static int compat_gnutls_privkey_generate2(gnutls_privkey_t privkey, gnutls_pk_algorithm_t alg, unsigned int bits, + unsigned int flags, const gnutls_keygen_data_st *data, unsigned data_size) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + static void gnutls_log( int level, const char *msg ) { TRACE( "<%d> %s", level, msg ); @@ -419,7 +426,7 @@ static NTSTATUS gnutls_process_attach( void *args ) LOAD_FUNCPTR_OPT(gnutls_privkey_export_dsa_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_ecc_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_export_rsa_raw) - LOAD_FUNCPTR_OPT(gnutls_privkey_generate) + LOAD_FUNCPTR_OPT(gnutls_privkey_generate2) LOAD_FUNCPTR_OPT(gnutls_privkey_import_dh_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_import_ecc_raw) LOAD_FUNCPTR_OPT(gnutls_privkey_import_rsa_raw) @@ -1005,7 +1012,8 @@ done: return status; }
-static gnutls_privkey_t create_privkey( gnutls_pk_algorithm_t pk_alg, unsigned int bitlen ) +static gnutls_privkey_t create_privkey( gnutls_pk_algorithm_t pk_alg, unsigned int bitlen, + const gnutls_keygen_data_st *data, unsigned int data_size ) { gnutls_privkey_t privkey; int ret; @@ -1016,7 +1024,7 @@ static gnutls_privkey_t create_privkey( gnutls_pk_algorithm_t pk_alg, unsigned i return NULL; }
- if ((ret = pgnutls_privkey_generate( privkey, pk_alg, bitlen, 0 ))) + if ((ret = pgnutls_privkey_generate2( privkey, pk_alg, bitlen, 0, data, data_size ))) { pgnutls_perror( ret ); pgnutls_privkey_deinit( privkey ); @@ -1047,6 +1055,29 @@ static gnutls_pubkey_t create_pubkey_from_privkey( gnutls_privkey_t privkey ) return pubkey; }
+static gnutls_dh_params_t get_dh_params( gnutls_privkey_t privkey ) +{ + gnutls_dh_params_t params; + gnutls_datum_t x; + int ret; + + if ((ret = pgnutls_dh_params_init( ¶ms ))) + { + pgnutls_perror( ret ); + return NULL; + } + + if ((ret = pgnutls_privkey_export_dh_raw( privkey, params, NULL, &x, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return NULL; + } + + free( x.data ); + return params; +} + static NTSTATUS key_asymmetric_generate( void *args ) { struct key *key = args; @@ -1093,7 +1124,24 @@ static NTSTATUS key_asymmetric_generate( void *args ) return STATUS_NOT_SUPPORTED; }
- if (!(privkey = create_privkey( pk_alg, bitlen ))) return STATUS_INTERNAL_ERROR; + if (key->alg_id == ALG_ID_DH && key_data(key)->a.dh_params) + { + gnutls_keygen_data_st data; + + data.type = GNUTLS_KEYGEN_DH; + data.data = (unsigned char *)key_data(key)->a.dh_params; + data.size = 0; + if (!(privkey = create_privkey( pk_alg, bitlen, &data, 1 ))) return STATUS_INTERNAL_ERROR; + } + else if (!(privkey = create_privkey( pk_alg, bitlen, NULL, 0 ))) return STATUS_INTERNAL_ERROR; + + if (key->alg_id == ALG_ID_DH && !key_data(key)->a.dh_params && + !(key_data(key)->a.dh_params = get_dh_params( privkey ))) + { + pgnutls_privkey_deinit( privkey ); + return STATUS_INTERNAL_ERROR; + } + if (!(pubkey = create_pubkey_from_privkey( privkey ))) { pgnutls_privkey_deinit( privkey );
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/gnutls.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 3592c685c56..1e5a81d9366 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -2671,6 +2671,7 @@ static NTSTATUS key_asymmetric_derive_key( void *args ) { const struct key_asymmetric_derive_key_params *params = args; gnutls_datum_t s; + NTSTATUS status = STATUS_SUCCESS; int ret;
if ((ret = pgnutls_privkey_derive_secret( key_data(params->privkey)->a.privkey, @@ -2680,15 +2681,15 @@ static NTSTATUS key_asymmetric_derive_key( void *args ) return STATUS_INTERNAL_ERROR; }
- if (!params->output) *params->ret_len = s.size; - else + *params->ret_len = EXPORT_SIZE( s, params->privkey->u.a.bitlen / 8, 1 ); + if (params->output) { - *params->ret_len = min( params->output_len, s.size ); - memcpy( params->output, s.data, *params->ret_len ); + if (params->output_len < *params->ret_len) status = STATUS_BUFFER_TOO_SMALL; + else export_gnutls_datum( params->output, *params->ret_len, &s, 1 ); }
free( s.data ); - return STATUS_SUCCESS; + return status; }
const unixlib_entry_t __wine_unix_call_funcs[] =
From: Hans Leidekker hans@codeweavers.com
Mostly written by Paul Gofman. --- dlls/bcrypt/tests/bcrypt.c | 326 ++++++++++++++++++++++++++++++++++++- 1 file changed, 323 insertions(+), 3 deletions(-)
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 45e03e5d15b..5262130bbe7 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -3835,11 +3835,113 @@ static void test_DSA(void)
static void test_SecretAgreement(void) { - BCRYPT_SECRET_HANDLE secret; + static BCryptBuffer hash_param_buffers[] = + { + { + sizeof(BCRYPT_SHA256_ALGORITHM), + KDF_HASH_ALGORITHM, + (void *)BCRYPT_SHA256_ALGORITHM, + } + }; + static BCryptBufferDesc hash_params = + { + BCRYPTBUFFER_VERSION, + ARRAY_SIZE(hash_param_buffers), + hash_param_buffers, + }; + + static const ULONG dh_private_key[] = + { + 0xc4caf69c, 0x57b4db27, 0x36f7135f, 0x5ccba686, 0xc37b8819, 0x1d35c9b2, 0xbb07a1cf, 0x0c5d1c1b, + 0xc79acb10, 0x31dfdabb, 0x702e02b9, 0x1efab345, 0x262a8074, 0x5edf7698, 0x9b9dc630, 0x13c34b93, + 0xacbc928b, 0xb79eed8c, 0x7413dce9, 0xa5521280, 0x88d8e695, 0xa310269f, 0xca7c5719, 0xcd0c775b, + 0x9a6e2cf2, 0x9e235c51, 0xf49db62d, 0x28e72424, 0x4a44da5a, 0x3d98268d, 0x8e4d2be3, 0x254e44e6, + + 0x18a67e55, 0x572e13a1, 0x46f81ca8, 0xc331c9b9, 0xf8fe3dd4, 0x8a889e5a, 0x6c0505fd, 0xbd97a121, + 0xed2dbd67, 0xf39efa8e, 0x36f9c287, 0xf6bbfa6c, 0x461e42ad, 0x17dc170e, 0xc002dc2e, 0x4813d9a4, + 0x0b6fabb8, 0x6a9e1860, 0xa8a8cbd9, 0xb7ed6b5d, 0xabb34d23, 0xf2fbe1fd, 0x8670df1e, 0xba7fa4e6, + 0xf7039712, 0x94448f30, 0xe10c812e, 0x3e311976, 0xcfdd72c4, 0xbdbea98f, 0xc9a540d6, 0x89646d57, + + 0x7ab63b33, 0x03a1e9b6, 0x947f7a9b, 0x5ae59eeb, 0x1d12eb05, 0x3f425d92, 0xe028c6ba, 0xbf90ddc9, + 0xb554f55a, 0x7aeb88b6, 0x4a443a5f, 0xbab35111, 0x82c78a0c, 0x298dd482, 0x02937cb1, 0xc94cdc2e, + 0x59b010eb, 0x3bbc0a2b, 0xd845fee0, 0x04c1d0db, 0x0c8c9424, 0x1cafd4b2, 0x9aa7aed9, 0x6a478486, + 0xa8841fd7, 0xbfeff40a, 0x8fd7bcc5, 0x3bb28977, 0x2b9a7955, 0xa55cd2e4, 0x1b6ad657, 0x067cdf21, + + 0x06f36920, 0x63280e1b, 0xf17d930f, 0xa06e74a8, 0x463b3a6f, 0x2a464507, 0x93f8a982, 0x8f620a7d, + 0xeda32d11, 0x9706a6d4, 0x33dce588, 0x75a1c446, 0x048ab567, 0xd735aafa, 0x806f7c1c, 0xdcb9651a, + 0x26acf3b4, 0x45f91cc9, 0x2a0de6fc, 0xf3c03d0c, 0xf5aee0aa, 0x3eeaaf36, 0x18ccee61, 0x83faa783, + 0x4b2b5250, 0xf4ccea22, 0x5ac0714b, 0x3f0b2bc6, 0x481b13ce, 0x12040ea7, 0x66e0bbed, 0x158e1a67, + }; + static const ULONG dh_private_key2[] = + { + 0xffffffff, 0xffffffff, 0xa2da0fc9, 0x34c26821, 0x8b62c6c4, 0xd11cdc80, 0x084e0229, 0x74cc678a, + 0xa6be0b02, 0x229b133b, 0x79084a51, 0xdd04348e, 0xb31995ef, 0x1b433acd, 0x6d0a2b30, 0x37145ff2, + 0x6d35e14f, 0x45c2516d, 0x76b585e4, 0xc67e5e62, 0xe9424cf4, 0x6bed37a6, 0xb65cff0b, 0xedb706f4, + 0xfb6b38ee, 0xa59f895a, 0x11249fae, 0xe61f4b7c, 0x51662849, 0x8153e6ec, 0xffffffff, 0xffffffff, + + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000, + + 0xa0c3c734, 0xc130c92d, 0x5265abf8, 0xff409f17, 0xbcdce187, 0xff64dae3, 0x170560aa, 0xb2423ed8, + 0x9ee5a8b9, 0x92548030, 0x02bba1f9, 0x823e39a4, 0x69c438f5, 0xf91016ac, 0x89bfd166, 0x7f996446, + 0x86224203, 0x15bf689c, 0x619354a4, 0x0c1d3a1f, 0x11bcf3d2, 0x58aae029, 0x41c69824, 0x3fafc179, + 0xa742747c, 0x60658c7a, 0xd3b0bde4, 0x78d3f08b, 0x6cefa061, 0x33752536, 0xe84d4901, 0x48cd73f4, + + 0x8d449700, 0x1f95120e, 0xceb31745, 0x3663177b, 0xbd9bb2d5, 0x9c23c0d9, 0x814d34f8, 0xbc54edb0, + 0xb874659a, 0x3bac8a30, 0xa1f3dd46, 0x1705c900, 0xbc46fefe, 0x7d13875b, 0x3064351a, 0x4bd89a1c, + 0x9e938761, 0x931949db, 0x34490719, 0x84fb08ca, 0xa9dd355a, 0x5b3f5061, 0x2ac96663, 0xc594429e, + 0xbe58395d, 0x2f7d872a, 0x303d37b3, 0xa3a9b606, 0x735a6732, 0xa095bd95, 0x3d55a7c3, 0x00e54635, + }; + static const ULONG dh_peer_key[] = + { + 0xffffffff, 0xffffffff, 0xa2da0fc9, 0x34c26821, 0x8b62c6c4, 0xd11cdc80, 0x084e0229, 0x74cc678a, + 0xa6be0b02, 0x229b133b, 0x79084a51, 0xdd04348e, 0xb31995ef, 0x1b433acd, 0x6d0a2b30, 0x37145ff2, + 0x6d35e14f, 0x45c2516d, 0x76b585e4, 0xc67e5e62, 0xe9424cf4, 0x6bed37a6, 0xb65cff0b, 0xedb706f4, + 0xfb6b38ee, 0xa59f895a, 0x11249fae, 0xe61f4b7c, 0x51662849, 0x8153e6ec, 0xffffffff, 0xffffffff, + + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000, + + 0x3bf7404b, 0x6284fffe, 0x97c0d565, 0xd830c658, 0xcc21bf39, 0xcae45bb6, 0x019df7df, 0xbf4cd293, + 0x6bf1989d, 0x78a81f52, 0xa4ed861c, 0x6bacf493, 0xa3e700d1, 0xd06cc206, 0x411b9727, 0x01e9c9ab, + 0x9b7e6efa, 0xf46bb25d, 0xd1027242, 0x6130787c, 0xa7b87d8b, 0xfee41492, 0x50db6213, 0x321199b6, + 0x7dace53a, 0xe8b1ec51, 0x2181b113, 0x3b33e3c0, 0x5b3a2d67, 0xbd34f0c1, 0x7037c542, 0x4a8d5540, + }; + static const ULONG dh_shared_secret_raw[] = + { + 0x375d89b5, 0x35a9c270, 0xfbc5ba82, 0x09eb3069, 0xd50965b0, 0xace510f7, 0x981e8731, 0x80a76115, + 0xf386d348, 0xca17b8df, 0x0b0e84ec, 0xf81f756e, 0x5030fa20, 0x03113b71, 0x97b7e879, 0x899b5fae, + 0xe6913299, 0x09270076, 0x39bc813a, 0xde3ef070, 0x65ad5b3a, 0x2b7c4ba4, 0x86c98ef9, 0x3236feaf, + 0x3e0253f7, 0x0489d2dd, 0x97669a3d, 0x50242fca, 0x5d4aecb1, 0xcf2d805f, 0x2258afff, 0x750e92cd, + }; + static const ULONG dh_shared_secret_raw2[] = + { + 0x0815f37d, 0x19ee74ab, 0x9f63f123, 0xe1b3f10c, 0xbcc9be83, 0xaddf5b9d, 0x28174e72, 0xf8a33825, + 0xfc74e47d, 0x2c950888, 0xf5b776d9, 0xfc712fef, 0x5b213b32, 0x489a9829, 0xfc0a4d1d, 0x6e641d3b, + 0x3bb2ff57, 0x63500318, 0x081ee54f, 0xf33a2805, 0xb3759e98, 0xa9a64afe, 0x964b8897, 0x04691bbc, + 0x80f4aae1, 0x617405ee, 0xab71724d, 0x6c10c214, 0x6f60b96f, 0xdc777b0b, 0x22f40d4f, 0x8a1c4eb5, + }; + static const ULONG dh_shared_secret_sha1[] = + { + 0x0babba9c, 0x0bdeacbd, 0x04e36574, 0xdd504dcd, 0x0cd88db0, + }; + static const ULONG dh_shared_secret_sha256[] = + { + 0x3213db5b, 0x8cc8250b, 0xc829eaab, 0x00933709, 0x68160aa9, 0xfb9f1e20, 0xf92368e6, 0x2b8e18eb, + }; + static const ULONG length = 1024; + BCRYPT_DH_PARAMETER_HEADER *dh_header; + BCRYPT_DH_KEY_BLOB *dh_key_blob; + BCRYPT_SECRET_HANDLE secret, secret2; BCRYPT_ALG_HANDLE alg; - BCRYPT_KEY_HANDLE key; + BCRYPT_KEY_HANDLE key, key2; + UCHAR buffer[2048]; NTSTATUS status; - ULONG size; + ULONG size, i;
status = BCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDH_P256_ALGORITHM, NULL, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); @@ -3928,6 +4030,224 @@ static void test_SecretAgreement(void) status = BCryptDestroySecret(secret); ok(status == STATUS_SUCCESS, "got %#lx\n", status);
+ key = NULL; + status = BCryptGenerateKeyPair(alg, &key, 256, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08lx\n", status); + + status = BCryptGenerateKeyPair(alg, &key, length, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(key != NULL, "key not set\n"); + + memset(buffer, 0xcc, sizeof(buffer)); + status = BCryptGetProperty(key, BCRYPT_DH_PARAMETERS, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + status = BCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + status = BCryptFinalizeKeyPair(key, 0); + if (status != STATUS_SUCCESS) + { + BCryptDestroyKey(key); + BCryptCloseAlgorithmProvider(alg, 0); + return; + } + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptFinalizeKeyPair(key, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + size = 0xdeadbeef; + status = BCryptGetProperty(key, BCRYPT_DH_PARAMETERS, NULL, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == sizeof(BCRYPT_DH_PARAMETER_HEADER) + length / 8 * 2, "Got unexpected size %lu.\n", size); + + size = 0xdeadbeef; + status = BCryptGetProperty(key, BCRYPT_DH_PARAMETERS, buffer, 28, &size, 0); + ok(status == STATUS_BUFFER_TOO_SMALL, "got %08lx\n", status); + ok(size == sizeof(BCRYPT_DH_PARAMETER_HEADER) + length / 8 * 2, "Got unexpected size %lu.\n", size); + + size = 0xdeadbeef; + status = BCryptGetProperty(key, BCRYPT_DH_PARAMETERS, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == sizeof(BCRYPT_DH_PARAMETER_HEADER) + length / 8 * 2, "Got unexpected size %lu.\n", size); + + dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buffer; + ok(dh_header->cbLength == sizeof(*dh_header) + length / 8 * 2, "Got unexpected length %lu.\n", dh_header->cbLength); + ok(dh_header->cbKeyLength == length / 8, "Got unexpected length %lu.\n", dh_header->cbKeyLength); + ok(dh_header->dwMagic == BCRYPT_DH_PARAMETERS_MAGIC, "Got unexpected magic %#lx.\n", dh_header->dwMagic); + + status = BCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + dh_key_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_key_blob->cbKey = length / 8; + memcpy(dh_key_blob + 1, dh_private_key, sizeof(dh_private_key)); + size = sizeof(buffer); + status = BCryptImportKeyPair(alg, NULL, BCRYPT_DH_PRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08lx\n", status); + size = sizeof(*dh_key_blob) + length / 8 * 4; + status = BCryptImportKeyPair(alg, NULL, BCRYPT_DH_PRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + memset(buffer, 0xcc, sizeof(buffer)); + size = 0xdeadbeef; + status = BCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 3, "Got unexpected size %lu.\n", size); + + size = 0xdeadbeef; + status = BCryptExportKey(key, NULL, BCRYPT_DH_PUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 3, "Got unexpected size %lu.\n", size); + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + ok(dh_key_blob->dwMagic == BCRYPT_DH_PUBLIC_MAGIC, "Got unexpected magic %#lx.\n", dh_key_blob->dwMagic); + ok(dh_key_blob->cbKey == length / 8, "Got unexpected length %lu.\n", dh_key_blob->cbKey); + ok(!memcmp(dh_key_blob + 1, dh_private_key, length / 8 * 3), "Key data does not match.\n"); + + status = BCryptGenerateKeyPair(alg, &key2, length, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + dh_header = (BCRYPT_DH_PARAMETER_HEADER *)buffer; + dh_header->dwMagic = BCRYPT_DH_PARAMETERS_MAGIC; + dh_header->cbLength = sizeof(*dh_header) + length / 8 * 2; + dh_header->cbKeyLength = length / 8; + memcpy(dh_header + 1, dh_private_key, length / 8 * 2); + status = BCryptSetProperty(key2, BCRYPT_DH_PARAMETERS, buffer, dh_header->cbLength, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptFinalizeKeyPair(key2, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptExportKey(key2, NULL, BCRYPT_DH_PUBLIC_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 3, "Got unexpected size %lu.\n", size); + ok(dh_key_blob->dwMagic == BCRYPT_DH_PUBLIC_MAGIC, "Got unexpected dwMagic %#lx.\n", dh_key_blob->dwMagic); + ok(dh_key_blob->cbKey == length / 8, "Got unexpected length %lu.\n", dh_key_blob->cbKey); + todo_wine ok(!memcmp(dh_key_blob + 1, dh_private_key, length / 8 * 2), "DH parameters do not match.\n"); + ok(memcmp((BYTE *)(dh_key_blob + 1) + length / 8 * 2, (BYTE *)dh_private_key + length / 8 * 2, length / 8), + "Random public key data matches.\n"); + + memset(buffer, 0xcc, sizeof(buffer)); + status = BCryptExportKey(key, NULL, BCRYPT_DH_PRIVATE_BLOB, buffer, sizeof(buffer), &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + ok(size == sizeof(BCRYPT_DH_KEY_BLOB) + length / 8 * 4, "Got unexpected size %lu.\n", size); + ok(dh_key_blob->dwMagic == BCRYPT_DH_PRIVATE_MAGIC, "Got unexpected dwMagic %#lx.\n", dh_key_blob->dwMagic); + ok(dh_key_blob->cbKey == length / 8, "Got unexpected length %lu.\n", dh_key_blob->cbKey); + ok(!memcmp(dh_key_blob + 1, dh_private_key, length / 8 * 4), "Private key data does not match.\n"); + + status = BCryptSecretAgreement(NULL, key, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + status = BCryptSecretAgreement(key, NULL, &secret, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + status = BCryptSecretAgreement(key, key, NULL, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08lx\n", status); + + status = BCryptSecretAgreement(key, key, &secret, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptDeriveKey(NULL, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + status = BCryptDeriveKey(key, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_HANDLE, "got %08lx\n", status); + + status = BCryptDeriveKey(secret, NULL, NULL, NULL, 0, &size, 0); + ok(status == STATUS_INVALID_PARAMETER, "got %08lx\n", status); + + size = 0xdeadbeef; + status = BCryptDeriveKey(secret, L"HASH", NULL, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == 20, "Got unexpected size %lu.\n", size); + + size = 0xdeadbeef; + status = BCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == length / 8, "Got unexpected size %lu.\n", size); + + status = BCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == length / 8, "Got unexpected size %lu.\n", size); + ok(!memcmp(buffer, dh_shared_secret_raw, size), "Raw shared secret data does not match.\n"); + + size = sizeof(buffer); + memset(buffer, 0xcc, sizeof(buffer)); + status = BCryptDeriveKey(secret, BCRYPT_KDF_HASH, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == 20, "Got unexpected size %lu.\n", size); + ok(!memcmp(buffer, dh_shared_secret_sha1, sizeof(dh_shared_secret_sha1)), + "sha1 shared secret data does not match.\n"); + + size = sizeof(buffer); + status = BCryptDeriveKey(secret, BCRYPT_KDF_HASH, &hash_params, buffer, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == 32, "Got unexpected size %lu.\n", size); + ok(!memcmp(buffer, dh_shared_secret_sha256, sizeof(dh_shared_secret_sha256)), + "sha256 shared secret data does not match.\n"); + + for (i = size; i < sizeof(buffer); ++i) + if (buffer[i] != 0xcc) break; + ok(i == sizeof(buffer), "Buffer modified at %lu, value %#x.\n", i, buffer[i]); + + status = BCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptSecretAgreement(key, key2, &secret, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptSecretAgreement(key2, key, &secret2, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer + size, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(!memcmp(buffer, buffer + size, size), "Shared secrets do not match.\n"); + + status = BCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptDestroySecret(secret2); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptDestroyKey(key2); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + dh_key_blob->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; + dh_key_blob->cbKey = length / 8; + memcpy(dh_key_blob + 1, dh_private_key2, sizeof(dh_private_key2)); + + size = sizeof(*dh_key_blob) + length / 8 * 4; + status = BCryptImportKeyPair(alg, NULL, BCRYPT_DH_PRIVATE_BLOB, &key, buffer, size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + dh_key_blob = (BCRYPT_DH_KEY_BLOB *)buffer; + dh_key_blob->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; + dh_key_blob->cbKey = length / 8; + memcpy(dh_key_blob + 1, dh_peer_key, sizeof(dh_peer_key)); + + size = sizeof(*dh_key_blob) + length / 8 * 3; + status = BCryptImportKeyPair(alg, NULL, BCRYPT_DH_PUBLIC_BLOB, &key2, buffer, size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptSecretAgreement(key, key2, &secret, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + + status = BCryptDeriveKey(secret, BCRYPT_KDF_RAW_SECRET, NULL, buffer, 128, &size, 0); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + ok(size == length / 8, "Got unexpected size %lu.\n", size); + ok(!memcmp(buffer, dh_shared_secret_raw2, size), "Raw shared secret data does not match.\n"); + + status = BCryptDestroySecret(secret); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptDestroyKey(key); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptDestroyKey(key2); + ok(status == STATUS_SUCCESS, "got %08lx\n", status); + status = BCryptCloseAlgorithmProvider(alg, 0); ok(status == STATUS_SUCCESS, "got %#lx\n", status); }
On Thu Dec 7 20:11:56 2023 +0000, Paul Gofman wrote:
Not sure if this matters too much, but an alternative would be to store gnutls_dh_params_t handle here (performing gnutls_dh_params_import_raw() right during import). The practical difference is that any validation on parameters in gnutls will be performed during import (where it probably belongs).
Right, that's better. I have pushed an update with this change.
On Fri Dec 8 11:23:05 2023 +0000, Hans Leidekker wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/4637/diffs?diff_id=89090&start_sha=49c6f4498583d1a103aabc08a9fb0b172b672ae6#d87270a3ab269adb0c2fec85a202e9681d05761e_1726_1766)
I fixed this as well.
On Fri Dec 8 11:28:16 2023 +0000, Paul Gofman wrote:
I've put a couple of specific suggestions inside the patches. Not related to this specific patchset (rather the already merged implementation), one other thing I came across is that BCryptSecretAgreement is not performing validation (unlike current Proton patches) because actual secret agreement calculation is performed during final secret derivation BCryptDeriveKey(). I am attaching a small addition to test which shows that it actually performed on Windows. The reason for failing validation in this test is that the first key doesn't have a private key in it. There are probably more possible reasons for failures there, like possibly not matching DH parameters between the keys (although I am not sure now if I explicitly tested that case). I understand why it was done the way its done now, that saves some mess around saving the computed secret, checking the state in BCryptDeriveKey(). I don't know if anything depends on that (properly failing BCryptSecretAgreement on bad keys), but I if we want to be accurate here we'd need to compute the agreement in BCryptSecretAgreement(). I checked that all the other tests I have (mostly integrated in this patchset) pass with gnutls 3.8.2 and this patchset plus attached second diff. While I expect the tests to sufficiently cover what games known to use DH need, I picked this to current Proton (with the additional diff) and checked Red Dead Online just in case, that works. The game sets DH parameters before finalizing the key, it is probably unavoidable as at least one peer should do that so the peer keys have matching parameters and the same secret can be established. [test.patch](/uploads/a88dff20bd2587a12aef12d355ea957e/test.patch)
I decided to leave validation in BCryptSecretAgreement() for another day.
On Fri Dec 8 11:29:40 2023 +0000, Paul Gofman wrote:
Another observation, also related to already merged part: key_asymmetric_derive_key() relies on gnutls_datum_t returned from gnutls_privkey_derive_secret() to have correct size. I didn't check with gnutls code if it pads the secret to the expected length on output, but based on all the other gnutls functions which return not a crypt value but generic big number in gnutls_datum_t which may be shorter than expected (without leading zeroes) or instead may have zero prepended to avoid negative value, would it probably be better to use export_gnutls_datum() with specified size like elsewhere?
This is fixed as well, thanks.
key_import_dh() which imports private key doesn't set public key (I actually missed that part yesterday, while it is related to already merged part and not this patchset), that will make the key imported as private do not work as peer key in secret generation. Now key_import_dh() also doesn't set newly added dh_params in key, so export of DH parameters from a key imported as private won't work probably. Similarly, key_import_dh_public() doesn't store DH params handle in the key.
Maybe worth making sure that we always have all the handles properly initialized, and then always use pubkey in key_export_dh_public() without falling back to privkey?
key_import_dh() does set the public key but you are right that dh_params isn't set in key_import_dh/_public(). I'll fix that.