[PATCH 1/5] dssenh: Implement CPAcquireContext and CPReleaseContext.

Hans Leidekker hans at codeweavers.com
Fri Oct 9 05:45:56 CDT 2020


Signed-off-by: Hans Leidekker <hans at 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 8015768f99b..67b0d6a141c 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 5aa885c8b86..32c31a48a8d 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_KEYEXCHANGE, protect_flags );
+        ret->sign_key = read_key( hkey, AT_SIGNATURE, 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 CPGetProvParam( HCRYPTPROV hprov, DWORD param, BYTE *data, DWORD *len, DWORD flags )
diff --git a/dlls/dssenh/tests/dssenh.c b/dlls/dssenh/tests/dssenh.c
index c88fcd14661..d549b985d6f 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);
-- 
2.28.0




More information about the wine-devel mailing list