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.
From: Hans Leidekker hans@codeweavers.com
--- dlls/bcrypt/bcrypt_internal.h | 6 ++++-- dlls/bcrypt/bcrypt_main.c | 15 +++++++++++++++ dlls/bcrypt/gnutls.c | 18 +++++++++++++++++- include/bcrypt.h | 13 +++++++++++++ 4 files changed, 49 insertions(+), 3 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..c74cc8dc606 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -86,6 +86,7 @@ union key_data { gnutls_privkey_t privkey; gnutls_pubkey_t pubkey; + BCRYPT_DH_PARAMETER_HEADER *dh_params; } a; }; C_ASSERT( sizeof(union key_data) <= sizeof(((struct key *)0)->private) ); @@ -1833,6 +1834,17 @@ 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_params; + + if (!(dh_params = malloc( len ))) return STATUS_NO_MEMORY; + memcpy( dh_params, buf, len ); + free( key_data(key)->a.dh_params ); + key_data(key)->a.dh_params = dh_params; + return STATUS_SUCCESS; +} + static NTSTATUS key_asymmetric_import( void *args ) { const struct key_asymmetric_import_params *params = args; @@ -1875,9 +1887,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 +2315,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 ); + free( 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 | 56 +++++++++++++++++++++++++++++++++-- 3 files changed, 74 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 c74cc8dc606..365e5d301ec 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,55 @@ 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, x; + gnutls_dh_params_t params; + NTSTATUS status = STATUS_SUCCESS; + UCHAR *dst; + int ret; + + if ((ret = pgnutls_dh_params_init( ¶ms ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + if ((ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, NULL, &x, 0 ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + return STATUS_INTERNAL_ERROR; + } + free( x.data ); + + if ((ret = pgnutls_dh_params_export_raw( params, &p, &g, NULL ))) + { + pgnutls_perror( ret ); + pgnutls_dh_params_deinit( params ); + 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 ); + pgnutls_dh_params_deinit( params ); + return status; +} + static NTSTATUS key_asymmetric_export( void *args ) { const struct key_asymmetric_export_params *params = args; @@ -1720,7 +1768,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 365e5d301ec..115de04cce9 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -1740,6 +1740,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 115de04cce9..96aa7707b74 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
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); }
This merge request was approved by Hans Leidekker.
[bcrypt_dh.diff](/uploads/316eece85424bca93fa4a20184fb423d/bcrypt_dh.diff)
Here's the patch on top of this MR.
[bcrypt_dh2.diff](/uploads/de2a4c2cf1f9a50773a5c19ed08493f6/bcrypt_dh2.diff)
This patch uses gnutls_privkey_generate2() instead and also passes the tests.
Paul Gofman (@gofman) commented about dlls/bcrypt/gnutls.c:
{ gnutls_privkey_t privkey; gnutls_pubkey_t pubkey;
} a;BCRYPT_DH_PARAMETER_HEADER *dh_params;
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).
Paul Gofman (@gofman) commented about dlls/bcrypt/gnutls.c:
- 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, x;
- gnutls_dh_params_t params;
- NTSTATUS status = STATUS_SUCCESS;
- UCHAR *dst;
- int ret;
- if ((ret = pgnutls_dh_params_init( ¶ms )))
- {
pgnutls_perror( ret );
return STATUS_INTERNAL_ERROR;
- }
- if ((ret = pgnutls_privkey_export_dh_raw( key_data(key)->a.privkey, params, NULL, &x, 0 )))
- {
This assumes that the bcrypt key was generated. But if DH params were just imported and BCryptFinalizeKeyPair() wasn't called this will fail as privkey is not generated yet. We don't have such a test, but I just tested BCryptGetProperty(BCRYPT_DH_PARAMETERS) right after BCryptSetProperty(BCRYPT_DH_PARAMETERS) and before BCryptFinalizeKeyPair() and that succeeds on Windows while fails with the patch.
I guess one way to do it straightforward is to store gnutls_dh_params_t handle in key data as I suggested above and export from it. Then, full key generation without DH parameters set will generate it first, and whatever happened (just setting the parameters or full DH key generation) this will work without extra checks.
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 agreem ent 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)
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?