[PATCH 1/2] bcrypt: Add support for generating RSA keys.

Hans Leidekker hans at codeweavers.com
Mon Sep 23 08:48:51 CDT 2019


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/bcrypt/bcrypt_internal.h |   4 +-
 dlls/bcrypt/bcrypt_main.c     |  24 ++++++--
 dlls/bcrypt/gnutls.c          | 101 ++++++++++++++++++++++++++++++++--
 dlls/bcrypt/macos.c           |   3 +-
 4 files changed, 118 insertions(+), 14 deletions(-)

diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h
index 943c33d2b2..ee16762593 100644
--- a/dlls/bcrypt/bcrypt_internal.h
+++ b/dlls/bcrypt/bcrypt_internal.h
@@ -158,6 +158,7 @@ struct key_symmetric
 struct key_asymmetric
 {
     gnutls_privkey_t  handle;
+    ULONG             bitlen;     /* ignored for ECC keys */
     UCHAR            *pubkey;
     ULONG             pubkey_len;
 };
@@ -185,6 +186,7 @@ struct key_symmetric
 
 struct key_asymmetric
 {
+    ULONG  bitlen;
     UCHAR *pubkey;
     ULONG  pubkey_len;
 };
@@ -225,7 +227,7 @@ NTSTATUS key_symmetric_set_auth_data( struct key *, UCHAR *, ULONG ) DECLSPEC_HI
 NTSTATUS key_symmetric_encrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
 NTSTATUS key_symmetric_decrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
 NTSTATUS key_symmetric_get_tag( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
-NTSTATUS key_asymmetric_init( struct key *, struct algorithm *, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
+NTSTATUS key_asymmetric_init( struct key *, struct algorithm *, ULONG, const UCHAR *, ULONG ) DECLSPEC_HIDDEN;
 NTSTATUS key_asymmetric_generate( struct key * ) DECLSPEC_HIDDEN;
 NTSTATUS key_asymmetric_verify( struct key *, void *, UCHAR *, ULONG, UCHAR *, ULONG, DWORD ) DECLSPEC_HIDDEN;
 NTSTATUS key_destroy( struct key * ) DECLSPEC_HIDDEN;
diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c
index 79660c1cf1..369e445db0 100644
--- a/dlls/bcrypt/bcrypt_main.c
+++ b/dlls/bcrypt/bcrypt_main.c
@@ -854,6 +854,14 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U
         memcpy( output + sizeof(len), key->u.s.secret, key->u.s.secret_len );
         return STATUS_SUCCESS;
     }
+    else if (!strcmpW( type, BCRYPT_RSAPUBLIC_BLOB ))
+    {
+        *size = key->u.a.pubkey_len;
+        if (output_len < key->u.a.pubkey_len) return STATUS_SUCCESS;
+
+        memcpy( output, key->u.a.pubkey, key->u.a.pubkey_len );
+        return STATUS_SUCCESS;
+    }
     else if (!strcmpW( type, BCRYPT_ECCPUBLIC_BLOB ))
     {
         *size = key->u.a.pubkey_len;
@@ -1059,7 +1067,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
     if (!strcmpW( type, BCRYPT_ECCPUBLIC_BLOB ))
     {
         BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input;
-        DWORD key_size, magic;
+        DWORD key_size, magic, size;
 
         if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER;
 
@@ -1091,7 +1099,9 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
 
         if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
         key->hdr.magic = MAGIC_KEY;
-        if ((status = key_asymmetric_init( key, alg, (BYTE *)ecc_blob, sizeof(*ecc_blob) + ecc_blob->cbKey * 2 )))
+
+        size = sizeof(*ecc_blob) + ecc_blob->cbKey * 2;
+        if ((status = key_asymmetric_init( key, alg, key_size * 8, (BYTE *)ecc_blob, size )))
         {
             heap_free( key );
             return status;
@@ -1125,7 +1135,8 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
 
         if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
         key->hdr.magic = MAGIC_KEY;
-        if ((status = key_asymmetric_init( key, alg, NULL, 0 )))
+
+        if ((status = key_asymmetric_init( key, alg, key_size * 8, NULL, 0 )))
         {
             heap_free( key );
             return status;
@@ -1151,7 +1162,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
         key->hdr.magic = MAGIC_KEY;
 
         size = sizeof(*rsa_blob) + rsa_blob->cbPublicExp + rsa_blob->cbModulus;
-        if ((status = key_asymmetric_init( key, alg, (BYTE *)rsa_blob, size )))
+        if ((status = key_asymmetric_init( key, alg, rsa_blob->BitLength, (BYTE *)rsa_blob, size )))
         {
             heap_free( key );
             return status;
@@ -1189,7 +1200,8 @@ static NTSTATUS key_duplicate( struct key *key_orig, struct key *key_copy )
     return STATUS_NOT_IMPLEMENTED;
 }
 
-NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
+NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, ULONG bitlen, const UCHAR *pubkey,
+                              ULONG pubkey_len )
 {
     ERR( "support for keys not available at build time\n" );
     return STATUS_NOT_IMPLEMENTED;
@@ -1302,7 +1314,7 @@ NTSTATUS WINAPI BCryptGenerateKeyPair( BCRYPT_ALG_HANDLE algorithm, BCRYPT_KEY_H
     if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
     key->hdr.magic = MAGIC_KEY;
 
-    if ((status = key_asymmetric_init( key, alg, NULL, 0 )))
+    if ((status = key_asymmetric_init( key, alg, key_len, NULL, 0 )))
     {
         heap_free( key );
         return status;
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c
index 583a0e47e6..8b2cc8b316 100644
--- a/dlls/bcrypt/gnutls.c
+++ b/dlls/bcrypt/gnutls.c
@@ -81,6 +81,9 @@ static int (*pgnutls_pubkey_import_rsa_raw)(gnutls_pubkey_t key, const gnutls_da
 /* Not present in gnutls version < 3.3.0 */
 static int (*pgnutls_privkey_export_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t *,
                                              gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_t *);
+static int (*pgnutls_privkey_export_rsa_raw)(gnutls_privkey_t, gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_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 void *libgnutls_handle;
@@ -116,6 +119,13 @@ static int compat_gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_c
     return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
 }
 
+static int compat_gnutls_privkey_export_rsa_raw(gnutls_privkey_t key, gnutls_datum_t *m, gnutls_datum_t *e,
+                                                gnutls_datum_t *d, gnutls_datum_t *p, gnutls_datum_t *q,
+                                                gnutls_datum_t *u, gnutls_datum_t *e1, gnutls_datum_t *e2)
+{
+    return GNUTLS_E_UNKNOWN_PK_ALGORITHM;
+}
+
 static int compat_gnutls_privkey_export_ecc_raw(gnutls_privkey_t key, gnutls_ecc_curve_t *curve,
                                                 gnutls_datum_t *x, gnutls_datum_t *y, gnutls_datum_t *k)
 {
@@ -210,6 +220,11 @@ BOOL gnutls_initialize(void)
         WARN("gnutls_pubkey_import_ecc_raw not found\n");
         pgnutls_pubkey_import_ecc_raw = compat_gnutls_pubkey_import_ecc_raw;
     }
+    if (!(pgnutls_privkey_export_rsa_raw = wine_dlsym( libgnutls_handle, "gnutls_privkey_export_rsa_raw", NULL, 0 )))
+    {
+        WARN("gnutls_privkey_export_rsa_raw not found\n");
+        pgnutls_privkey_export_rsa_raw = compat_gnutls_privkey_export_rsa_raw;
+    }
     if (!(pgnutls_privkey_export_ecc_raw = wine_dlsym( libgnutls_handle, "gnutls_privkey_export_ecc_raw", NULL, 0 )))
     {
         WARN("gnutls_privkey_export_ecc_raw not found\n");
@@ -535,6 +550,58 @@ NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
     return STATUS_SUCCESS;
 }
 
+static NTSTATUS export_gnutls_pubkey_rsa( gnutls_privkey_t gnutls_key, ULONG bitlen, UCHAR **pubkey, ULONG *pubkey_len )
+{
+    BCRYPT_RSAKEY_BLOB *rsa_blob;
+    gnutls_datum_t m, e;
+    UCHAR *dst, *src;
+    int ret;
+
+    if ((ret = pgnutls_privkey_export_rsa_raw( gnutls_key, &m, &e, NULL, NULL, NULL, NULL, NULL, NULL )))
+    {
+        pgnutls_perror( ret );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    if (!(rsa_blob = heap_alloc( sizeof(*rsa_blob) + e.size + m.size )))
+    {
+        pgnutls_perror( ret );
+        free( e.data ); free( m.data );
+        return STATUS_NO_MEMORY;
+    }
+
+    dst = (UCHAR *)(rsa_blob + 1);
+    if (e.size == bitlen / 8 + 1 && !e.data[0])
+    {
+        src = e.data + 1;
+        e.size--;
+    }
+    else src = e.data;
+    memcpy( dst, src, e.size );
+
+    dst += e.size;
+    if (m.size == bitlen / 8 + 1 && !m.data[0])
+    {
+        src = m.data + 1;
+        m.size--;
+    }
+    else src = m.data;
+    memcpy( dst, src, m.size );
+
+    rsa_blob->Magic       = BCRYPT_RSAPUBLIC_MAGIC;
+    rsa_blob->BitLength   = bitlen;
+    rsa_blob->cbPublicExp = e.size;
+    rsa_blob->cbModulus   = m.size;
+    rsa_blob->cbPrime1    = 0;
+    rsa_blob->cbPrime2    = 0;
+
+    *pubkey = (UCHAR *)rsa_blob;
+    *pubkey_len = sizeof(*rsa_blob) + e.size + m.size;
+
+    free( e.data ); free( m.data );
+    return STATUS_SUCCESS;
+}
+
 static NTSTATUS export_gnutls_pubkey_ecc( gnutls_privkey_t gnutls_key, UCHAR **pubkey, ULONG *pubkey_len )
 {
     BCRYPT_ECCKEY_BLOB *ecc_blob;
@@ -593,8 +660,8 @@ static NTSTATUS export_gnutls_pubkey_ecc( gnutls_privkey_t gnutls_key, UCHAR **p
 NTSTATUS key_asymmetric_generate( struct key *key )
 {
     gnutls_pk_algorithm_t pk_alg;
-    gnutls_ecc_curve_t curve;
     gnutls_privkey_t handle;
+    unsigned int bitlen;
     NTSTATUS status;
     int ret;
 
@@ -602,9 +669,14 @@ NTSTATUS key_asymmetric_generate( struct key *key )
 
     switch (key->alg_id)
     {
+    case ALG_ID_RSA:
+        pk_alg = GNUTLS_PK_RSA;
+        bitlen = key->u.a.bitlen;
+        break;
+
     case ALG_ID_ECDH_P256:
         pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */
-        curve = GNUTLS_ECC_CURVE_SECP256R1;
+        bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP256R1 );
         break;
 
     default:
@@ -618,14 +690,29 @@ NTSTATUS key_asymmetric_generate( struct key *key )
         return STATUS_INTERNAL_ERROR;
     }
 
-    if ((ret = pgnutls_privkey_generate( handle, pk_alg, GNUTLS_CURVE_TO_BITS(curve), 0 )))
+    if ((ret = pgnutls_privkey_generate( handle, pk_alg, bitlen, 0 )))
     {
         pgnutls_perror( ret );
         pgnutls_privkey_deinit( handle );
         return STATUS_INTERNAL_ERROR;
     }
 
-    if ((status = export_gnutls_pubkey_ecc( handle, &key->u.a.pubkey, &key->u.a.pubkey_len )))
+    switch (pk_alg)
+    {
+    case GNUTLS_PK_RSA:
+        status = export_gnutls_pubkey_rsa( handle, key->u.a.bitlen, &key->u.a.pubkey, &key->u.a.pubkey_len );
+        break;
+
+    case GNUTLS_PK_ECC:
+        status = export_gnutls_pubkey_ecc( handle, &key->u.a.pubkey, &key->u.a.pubkey_len );
+        break;
+
+    default:
+        ERR( "unhandled algorithm %u\n", pk_alg );
+        return STATUS_INTERNAL_ERROR;
+    }
+
+    if (status)
     {
         pgnutls_privkey_deinit( handle );
         return status;
@@ -741,7 +828,8 @@ NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len )
     return STATUS_SUCCESS;
 }
 
-NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
+NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, ULONG bitlen, const UCHAR *pubkey,
+                              ULONG pubkey_len )
 {
     if (!libgnutls_handle) return STATUS_INTERNAL_ERROR;
 
@@ -764,7 +852,8 @@ NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHA
         memcpy( key->u.a.pubkey, pubkey, pubkey_len );
         key->u.a.pubkey_len = pubkey_len;
     }
-    key->alg_id = alg->id;
+    key->alg_id     = alg->id;
+    key->u.a.bitlen = bitlen;
 
     return STATUS_SUCCESS;
 }
diff --git a/dlls/bcrypt/macos.c b/dlls/bcrypt/macos.c
index df79f3e80d..0dd1066130 100644
--- a/dlls/bcrypt/macos.c
+++ b/dlls/bcrypt/macos.c
@@ -192,7 +192,8 @@ NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
     return STATUS_NOT_IMPLEMENTED;
 }
 
-NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, const UCHAR *pubkey, ULONG pubkey_len )
+NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, ULONG bitlen, const UCHAR *pubkey,
+                              ULONG pubkey_len )
 {
     FIXME( "not implemented on Mac\n" );
     return STATUS_NOT_IMPLEMENTED;
-- 
2.20.1




More information about the wine-devel mailing list