bcrypt: Add support for generating RSA keys.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2019-09-23 15:48:51 +02:00 committed by Alexandre Julliard
parent 7c9139dffb
commit b2bbb6f48f
4 changed files with 118 additions and 14 deletions

View File

@ -158,6 +158,7 @@ struct key_symmetric
struct key_asymmetric struct key_asymmetric
{ {
gnutls_privkey_t handle; gnutls_privkey_t handle;
ULONG bitlen; /* ignored for ECC keys */
UCHAR *pubkey; UCHAR *pubkey;
ULONG pubkey_len; ULONG pubkey_len;
}; };
@ -185,6 +186,7 @@ struct key_symmetric
struct key_asymmetric struct key_asymmetric
{ {
ULONG bitlen;
UCHAR *pubkey; UCHAR *pubkey;
ULONG pubkey_len; 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_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_decrypt( struct key *, const UCHAR *, ULONG, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_symmetric_get_tag( struct key *, 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_generate( struct key * ) DECLSPEC_HIDDEN;
NTSTATUS key_asymmetric_verify( struct key *, void *, UCHAR *, ULONG, UCHAR *, ULONG, DWORD ) DECLSPEC_HIDDEN; NTSTATUS key_asymmetric_verify( struct key *, void *, UCHAR *, ULONG, UCHAR *, ULONG, DWORD ) DECLSPEC_HIDDEN;
NTSTATUS key_destroy( struct key * ) DECLSPEC_HIDDEN; NTSTATUS key_destroy( struct key * ) DECLSPEC_HIDDEN;

View File

@ -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 ); memcpy( output + sizeof(len), key->u.s.secret, key->u.s.secret_len );
return STATUS_SUCCESS; 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 )) else if (!strcmpW( type, BCRYPT_ECCPUBLIC_BLOB ))
{ {
*size = key->u.a.pubkey_len; *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 )) if (!strcmpW( type, BCRYPT_ECCPUBLIC_BLOB ))
{ {
BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input; 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; 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; if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
key->hdr.magic = MAGIC_KEY; 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 ); heap_free( key );
return status; 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; if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
key->hdr.magic = MAGIC_KEY; 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 ); heap_free( key );
return status; return status;
@ -1151,7 +1162,7 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
key->hdr.magic = MAGIC_KEY; key->hdr.magic = MAGIC_KEY;
size = sizeof(*rsa_blob) + rsa_blob->cbPublicExp + rsa_blob->cbModulus; 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 ); heap_free( key );
return status; return status;
@ -1189,7 +1200,8 @@ static NTSTATUS key_duplicate( struct key *key_orig, struct key *key_copy )
return STATUS_NOT_IMPLEMENTED; 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" ); ERR( "support for keys not available at build time\n" );
return STATUS_NOT_IMPLEMENTED; 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; if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
key->hdr.magic = MAGIC_KEY; 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 ); heap_free( key );
return status; return status;

View File

@ -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 */ /* Not present in gnutls version < 3.3.0 */
static int (*pgnutls_privkey_export_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t *, static int (*pgnutls_privkey_export_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t *,
gnutls_datum_t *, gnutls_datum_t *, gnutls_datum_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 int (*pgnutls_privkey_generate)(gnutls_privkey_t, gnutls_pk_algorithm_t, unsigned int, unsigned int);
static void *libgnutls_handle; 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; 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, 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) 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"); WARN("gnutls_pubkey_import_ecc_raw not found\n");
pgnutls_pubkey_import_ecc_raw = compat_gnutls_pubkey_import_ecc_raw; 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 ))) 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"); 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; 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 ) static NTSTATUS export_gnutls_pubkey_ecc( gnutls_privkey_t gnutls_key, UCHAR **pubkey, ULONG *pubkey_len )
{ {
BCRYPT_ECCKEY_BLOB *ecc_blob; 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 ) NTSTATUS key_asymmetric_generate( struct key *key )
{ {
gnutls_pk_algorithm_t pk_alg; gnutls_pk_algorithm_t pk_alg;
gnutls_ecc_curve_t curve;
gnutls_privkey_t handle; gnutls_privkey_t handle;
unsigned int bitlen;
NTSTATUS status; NTSTATUS status;
int ret; int ret;
@ -602,9 +669,14 @@ NTSTATUS key_asymmetric_generate( struct key *key )
switch (key->alg_id) switch (key->alg_id)
{ {
case ALG_ID_RSA:
pk_alg = GNUTLS_PK_RSA;
bitlen = key->u.a.bitlen;
break;
case ALG_ID_ECDH_P256: case ALG_ID_ECDH_P256:
pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */ 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; break;
default: default:
@ -618,14 +690,29 @@ NTSTATUS key_asymmetric_generate( struct key *key )
return STATUS_INTERNAL_ERROR; 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_perror( ret );
pgnutls_privkey_deinit( handle ); pgnutls_privkey_deinit( handle );
return STATUS_INTERNAL_ERROR; 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 ); pgnutls_privkey_deinit( handle );
return status; return status;
@ -741,7 +828,8 @@ NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len )
return STATUS_SUCCESS; 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; 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 ); memcpy( key->u.a.pubkey, pubkey, pubkey_len );
key->u.a.pubkey_len = 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; return STATUS_SUCCESS;
} }

View File

@ -192,7 +192,8 @@ NTSTATUS key_symmetric_get_tag( struct key *key, UCHAR *tag, ULONG len )
return STATUS_NOT_IMPLEMENTED; 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" ); FIXME( "not implemented on Mac\n" );
return STATUS_NOT_IMPLEMENTED; return STATUS_NOT_IMPLEMENTED;