Signed-off-by: Hans Leidekker hans@codeweavers.com --- dlls/dssenh/Makefile.in | 1 + dlls/dssenh/main.c | 256 ++++++++++++++++++++++++++++++++++++- dlls/dssenh/tests/dssenh.c | 42 ++++++ 3 files changed, 297 insertions(+), 2 deletions(-)
diff --git a/dlls/dssenh/Makefile.in b/dlls/dssenh/Makefile.in index 8015768f99..67b0d6a141 100644 --- a/dlls/dssenh/Makefile.in +++ b/dlls/dssenh/Makefile.in @@ -1,4 +1,5 @@ MODULE = dssenh.dll +IMPORTS = bcrypt crypt32 advapi32
EXTRADLLFLAGS = -mno-cygwin
diff --git a/dlls/dssenh/main.c b/dlls/dssenh/main.c index 3d5e8d97d6..ea082b29e3 100644 --- a/dlls/dssenh/main.c +++ b/dlls/dssenh/main.c @@ -22,23 +22,275 @@ #include "windef.h" #include "winbase.h" #include "wincrypt.h" +#include "winreg.h" +#include "bcrypt.h" #include "objbase.h" #include "rpcproxy.h"
#include "wine/debug.h" +#include "wine/heap.h"
static HINSTANCE instance;
WINE_DEFAULT_DEBUG_CHANNEL(dssenh);
+#define MAGIC_KEY (('K' << 24) | ('E' << 16) | ('Y' << 8) | '0') +struct key +{ + DWORD magic; + DWORD algid; + DWORD flags; + BCRYPT_ALG_HANDLE alg_handle; + BCRYPT_KEY_HANDLE handle; +}; + +#define MAGIC_CONTAINER (('C' << 24) | ('O' << 16) | ('N' << 8) | 'T') +struct container +{ + DWORD magic; + DWORD flags; + struct key *exch_key; + struct key *sign_key; + char name[MAX_PATH]; +}; + +static const char dss_path_fmt[] = "Software\Wine\Crypto\DSS\%s"; + +static BOOL create_container_regkey( struct container *container, REGSAM sam, HKEY *hkey ) +{ + char path[sizeof(dss_path_fmt) + MAX_PATH]; + HKEY rootkey; + + sprintf( path, dss_path_fmt, container->name ); + + if (container->flags & CRYPT_MACHINE_KEYSET) + rootkey = HKEY_LOCAL_MACHINE; + else + rootkey = HKEY_CURRENT_USER; + + /* @@ Wine registry key: HKLM\Software\Wine\Crypto\DSS */ + /* @@ Wine registry key: HKCU\Software\Wine\Crypto\DSS */ + return !RegCreateKeyExA( rootkey, path, 0, NULL, REG_OPTION_NON_VOLATILE, sam, NULL, hkey, NULL ); +} + +static struct container *create_key_container( const char *name, DWORD flags ) +{ + struct container *ret; + + if (!(ret = heap_alloc_zero( sizeof(*ret) ))) return NULL; + ret->magic = MAGIC_CONTAINER; + ret->flags = flags; + if (name) strcpy( ret->name, name ); + + if (!(flags & CRYPT_VERIFYCONTEXT)) + { + HKEY hkey; + if (create_container_regkey( ret, KEY_WRITE, &hkey )) RegCloseKey( hkey ); + } + return ret; +} + +static BOOL open_container_regkey( const char *name, DWORD flags, REGSAM access, HKEY *hkey ) +{ + char path[sizeof(dss_path_fmt) + MAX_PATH]; + HKEY rootkey; + + sprintf( path, dss_path_fmt, name ); + + if (flags & CRYPT_MACHINE_KEYSET) + rootkey = HKEY_LOCAL_MACHINE; + else + rootkey = HKEY_CURRENT_USER; + + /* @@ Wine registry key: HKLM\Software\Wine\Crypto\DSS */ + /* @@ Wine registry key: HKCU\Software\Wine\Crypto\DSS */ + return !RegOpenKeyExA( rootkey, path, 0, access, hkey ); +} + +static const WCHAR *map_keyspec_to_keypair_name( DWORD keyspec ) +{ + const WCHAR *name; + + switch (keyspec) + { + case AT_KEYEXCHANGE: + name = L"KeyExchangeKeyPair"; + break; + case AT_SIGNATURE: + name = L"SignatureKeyPair"; + break; + default: + ERR( "invalid key spec %u\n", keyspec ); + return NULL; + } + return name; +} + +static struct key *create_key( ALG_ID algid, DWORD flags ) +{ + struct key *ret; + const WCHAR *alg; + + switch (algid) + { + case AT_SIGNATURE: + case CALG_DSS_SIGN: + alg = BCRYPT_DSA_ALGORITHM; + break; + + default: + FIXME( "unhandled algorithm %08x\n", algid ); + return NULL; + } + + if (!(ret = heap_alloc_zero( sizeof(*ret) ))) return NULL; + + ret->magic = MAGIC_KEY; + ret->algid = algid; + ret->flags = flags; + if (BCryptOpenAlgorithmProvider( &ret->alg_handle, alg, MS_PRIMITIVE_PROVIDER, 0 )) + { + heap_free( ret ); + return NULL; + } + return ret; +} + +static void destroy_key( struct key *key ) +{ + if (!key) return; + BCryptDestroyKey( key->handle ); + BCryptCloseAlgorithmProvider( key->alg_handle, 0 ); + key->magic = 0; + heap_free( key ); +} + +static struct key *import_key( DWORD keyspec, BYTE *data, DWORD len ) +{ + struct key *ret; + + if (!(ret = create_key( keyspec, 0 ))) return NULL; + + if (BCryptImportKeyPair( ret->alg_handle, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, &ret->handle, data, len, 0 )) + { + WARN( "failed to import key\n" ); + destroy_key( ret ); + return NULL; + } + return ret; +} + +static struct key *read_key( HKEY hkey, DWORD keyspec, DWORD flags ) +{ + const WCHAR *value; + DWORD type, len; + BYTE *data; + DATA_BLOB blob_in, blob_out; + struct key *ret = NULL; + + if (!(value = map_keyspec_to_keypair_name( keyspec ))) return NULL; + if (RegQueryValueExW( hkey, value, 0, &type, NULL, &len )) return NULL; + if (!(data = heap_alloc( len ))) return NULL; + + if (!RegQueryValueExW( hkey, value, 0, &type, data, &len )) + { + blob_in.pbData = data; + blob_in.cbData = len; + if (CryptUnprotectData( &blob_in, NULL, NULL, NULL, NULL, flags, &blob_out )) + { + ret = import_key( keyspec, blob_out.pbData, blob_out.cbData ); + LocalFree( blob_out.pbData ); + } + } + + heap_free( data ); + return ret; +} + +static void destroy_container( struct container *container ) +{ + if (!container) return; + destroy_key( container->exch_key ); + destroy_key( container->sign_key ); + container->magic = 0; + heap_free( container ); +} + +static struct container *read_key_container( const char *name, DWORD flags ) +{ + DWORD protect_flags = (flags & CRYPT_MACHINE_KEYSET) ? CRYPTPROTECT_LOCAL_MACHINE : 0; + struct container *ret; + HKEY hkey; + + if (!open_container_regkey( name, flags, KEY_READ, &hkey )) return NULL; + + if ((ret = create_key_container( name, flags ))) + { + ret->exch_key = read_key( hkey, AT_SIGNATURE, protect_flags ); + ret->sign_key = read_key( hkey, AT_KEYEXCHANGE, protect_flags ); + } + + RegCloseKey( hkey ); + return ret; +} + BOOL WINAPI CPAcquireContext( HCRYPTPROV *ret_prov, LPSTR container, DWORD flags, PVTableProvStruc vtable ) { - return FALSE; + struct container *ret; + char name[MAX_PATH]; + + TRACE( "%p, %s, %08x, %p\n", ret_prov, debugstr_a(container), flags, vtable ); + + if (container && *container) + { + if (lstrlenA( container ) >= sizeof(name)) return FALSE; + lstrcpyA( name, container ); + } + else + { + DWORD len = sizeof(name); + if (!GetUserNameA( name, &len )) return FALSE; + } + + switch (flags) + { + case 0: + ret = read_key_container( name, flags ); + break; + + case CRYPT_NEWKEYSET: + if ((ret = read_key_container( name, flags ))) + { + heap_free( ret ); + SetLastError( NTE_EXISTS ); + return FALSE; + } + ret = create_key_container( name, flags ); + break; + + case CRYPT_VERIFYCONTEXT: + ret = create_key_container( "", flags ); + break; + + default: + FIXME( "unsupported flags %08x\n", flags ); + return FALSE; + } + + if (!ret) return FALSE; + *ret_prov = (HCRYPTPROV)ret; + return TRUE; }
BOOL WINAPI CPReleaseContext( HCRYPTPROV hprov, DWORD flags ) { - return FALSE; + struct container *container = (struct container *)hprov; + + TRACE( "%p, %08x\n", (void *)hprov, flags ); + + if (container->magic != MAGIC_CONTAINER) return FALSE; + destroy_container( container ); + return TRUE; }
BOOL WINAPI CPGenKey( HCRYPTPROV hprov, ALG_ID algid, DWORD flags, HCRYPTKEY *ret_key ) diff --git a/dlls/dssenh/tests/dssenh.c b/dlls/dssenh/tests/dssenh.c index c88fcd1466..d549b985d6 100644 --- a/dlls/dssenh/tests/dssenh.c +++ b/dlls/dssenh/tests/dssenh.c @@ -323,6 +323,7 @@ static void test_keylength_array(HCRYPTPROV hProv,const struct keylength_test *t static void test_keylength(void) { HCRYPTPROV hProv = 0; + HCRYPTKEY key; BOOL result;
/* acquire base dss provider */ @@ -335,6 +336,17 @@ static void test_keylength(void) } ok(result, "Expected no errors.\n");
+ result = CryptGenKey(hProv, AT_SIGNATURE, 0, &key); + todo_wine ok(result, "Expected no errors.\n"); + if (!result) + { + skip("skipping key length tests\n"); + return; + } + + result = CryptDestroyKey(key); + ok(result, "Expected no errors.\n"); + /* perform keylength tests */ test_keylength_array(hProv, baseDSS_keylength, ARRAY_SIZE(baseDSS_keylength));
@@ -434,6 +446,11 @@ static void test_hash(const struct hash_test *tests, int testLen)
/* test algid hash */ result = CryptCreateHash(hProv, tests[i].algid, 0, 0, &hHash); + if (!result) + { + skip("skipping hash tests\n"); + return; + } ok(result, "Expected creation of a hash.\n");
result = CryptHashData(hHash, data, dataLen, 0); @@ -588,6 +605,11 @@ static void test_data_encryption(const struct encrypt_test *tests, int testLen)
SetLastError(0xdeadbeef); result = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash); + if (!result) + { + skip("skipping encryption tests\n"); + return; + } ok(result, "Expected creation of a MD5 hash for key derivation.\n");
result = CryptHashData(hHash, (BYTE *)dataToHash, sizeof(dataToHash), 0); @@ -673,6 +695,11 @@ static void test_cipher_modes(const struct ciphermode_test *tests, int testLen)
SetLastError(0xdeadbeef); result = CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash); + if (!result) + { + skip("skipping ciper modes tests\n"); + return; + } ok(result, "Expected creation of a MD5 hash for key derivation.\n");
result = CryptHashData(hHash, (BYTE *)dataToHash, sizeof(dataToHash), 0); @@ -831,6 +858,11 @@ static void test_signhash_array(HCRYPTPROV hProv, const struct signature_test *t
/* Get a private key of array specified ALG_ID */ result = CryptImportKey(hProv, tests[i].privateKey, tests[i].keyLen, 0, 0, &privKey); + if (!result) + { + skip("skipping sign tests\n"); + return; + } ok(result, "Failed to imported key, got %x\n", GetLastError());
/* Create hash object and add data for signature 1 */ @@ -1084,6 +1116,11 @@ static void test_keyExchange_baseDSS(HCRYPTPROV hProv, const struct keyExchange_
/* Generate key exchange keys for user1 and user2 */ result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey1); + if (!result) + { + skip("skipping key exchange tests\n"); + return; + } ok(!result && GetLastError() == NTE_BAD_ALGID, "Expected NTE_BAD_ALGID, got %x\n", GetLastError());
@@ -1233,6 +1270,11 @@ static void test_keyExchange_dssDH(HCRYPTPROV hProv, const struct keyExchange_te
/* Generate key exchange keys for user1 and user2 */ result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey1); + if (!result) + { + skip("skipping key exchange tests\n"); + return; + } ok(result, "Failed to generate a key for user1, got %x\n", GetLastError());
result = CryptGenKey(hProv, tests[i].algid, 512 << 16 | CRYPT_PREGEN, &privKey2);
Hans Leidekker hans@codeweavers.com wrote:
+static struct container *read_key_container( const char *name, DWORD flags ) +{
- DWORD protect_flags = (flags & CRYPT_MACHINE_KEYSET) ? CRYPTPROTECT_LOCAL_MACHINE : 0;
- struct container *ret;
- HKEY hkey;
- if (!open_container_regkey( name, flags, KEY_READ, &hkey )) return NULL;
- if ((ret = create_key_container( name, flags )))
- {
ret->exch_key = read_key( hkey, AT_SIGNATURE, protect_flags );
ret->sign_key = read_key( hkey, AT_KEYEXCHANGE, protect_flags );
- }
Seems to be a typo.
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=80032
Your paranoid android.
=== debiant (32 bit report) ===
dssenh: dssenh.c:57: Test failed: Expected NTE_PROV_TYPE_NO_MATCH, got 80090019
=== debiant (32 bit French report) ===
dssenh: dssenh.c:57: Test failed: Expected NTE_PROV_TYPE_NO_MATCH, got 80090019
=== debiant (32 bit Japanese:Japan report) ===
dssenh: dssenh.c:57: Test failed: Expected NTE_PROV_TYPE_NO_MATCH, got 80090019
=== debiant (32 bit Chinese:China report) ===
dssenh: dssenh.c:57: Test failed: Expected NTE_PROV_TYPE_NO_MATCH, got 80090019
=== debiant (32 bit WoW report) ===
dssenh: dssenh.c:57: Test failed: Expected NTE_PROV_TYPE_NO_MATCH, got 80090019
=== debiant (64 bit WoW report) ===
dssenh: dssenh.c:57: Test failed: Expected NTE_PROV_TYPE_NO_MATCH, got 80090019
Hans Leidekker hans@codeweavers.com writes:
Signed-off-by: Hans Leidekker hans@codeweavers.com
dlls/dssenh/Makefile.in | 1 + dlls/dssenh/main.c | 256 ++++++++++++++++++++++++++++++++++++- dlls/dssenh/tests/dssenh.c | 42 ++++++ 3 files changed, 297 insertions(+), 2 deletions(-)
This breaks the tests:
tools/runtest -q -P wine -T . -M crypt32.dll -p dlls/crypt32/tests/crypt32_test.exe main && touch dlls/crypt32/tests/main.ok wine: Call from 7B010217 to unimplemented function dssenh.dll.CPGetProvParam, aborting wine: Unimplemented function dssenh.dll.CPGetProvParam called at address 7B010217 (thread 0024), starting debugger... Unhandled exception: unimplemented function dssenh.dll.CPGetProvParam called in 32-bit code (0x7b010217). Register dump: CS:0023 SS:002b DS:002b ES:002b FS:0063 GS:006b EIP:7b010217 ESP:0074fb24 EBP:0074fb88 EFLAGS:00000246( - -- I Z- -P- ) EAX:0074fb30 EBX:675c4000 ECX:00000004 EDX:0074fba8 ESI:0074fc10 EDI:0074fc0c Stack dump: 0x0074fb24: 0000003d 00000000 00000000 80000100 0x0074fb34: 00000001 00000000 7b010217 00000002 0x0074fb44: 675c4000 675c4058 6174e6c2 00ac8460 0x0074fb54: 00000000 f0000000 00ac7e20 00ac8058 0x0074fb64: 0074fbc4 0074fb90 00000001 0074fbb4 0x0074fb74: 0074fb94 0074fba4 f7c8119e 00ac7d50 Backtrace: =>0 0x7b010217 format_exception_msg+0x347(ptr=<is not available>, buffer=<is not available>, size=<is not available>) [Z:\home\julliard\wine\wine\dlls\kernelbase\debug.c:316] in kernelbase (0x0074fb88) 1 0x675c2e00 __wine_spec_unimplemented_stub+0x3f() [Z:\home\julliard\wine\wine\dlls\winecrt0\stub.c:32] in dssenh (0x0074fbb8) 2 0x675c10e0 hkey+0x675c10df() in dssenh (0x0074fc48) 3 0x7e9fa7ba in crypt32 (+0x3a7b9) (0x0074fc88) 4 0x004373a9 func_main+0xd88() [Z:\home\julliard\wine\wine\dlls\crypt32\tests\main.c:338] in crypt32_test (0x0074fdf8) 5 0x00463792 main+0x271(argv=<is not available>) [Z:\home\julliard\wine\wine\include\wine\test.h:569] in crypt32_test (0x0074fed8) 6 0x00462a56 mainCRTStartup+0x75() [Z:\home\julliard\wine\wine\dlls\msvcrt\crt_main.c:62] in crypt32_test (0x0074ff28) 7 0x7b62fe9e BaseThreadInitThunk+0xd(unknown=<is not available>, entry=<is not available>) [Z:\home\julliard\wine\wine\dlls\kernel32\thread.c:60] in kernel32 (0x0074ff48) 8 0x7bc55e07 RtlSleepConditionVariableSRW+0x196(lock=<is not available>, timeout=<is not available>, flags=<is not available>) [Z:\home\julliard\wine\wine\dlls\ntdll\sync.c:556] in ntdll (0x0074ff5c) 9 0x7bc56030 call_thread_func+0xaf(arg=0x7ffde000) [Z:\home\julliard\wine\wine\dlls\ntdll\thread.c:134] in ntdll (0x0074ffec) 0x7b010217 format_exception_msg+0x347 [Z:\home\julliard\wine\wine\dlls\kernelbase\debug.c:316] in kernelbase: movl 0xfffffffc(%ebp),%ebx 316 }