bcrypt: Add support for importing and exporting DSS private keys.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Hans Leidekker 2020-06-03 10:50:37 +02:00 committed by Alexandre Julliard
parent 992a0ae7b9
commit 7cf9a75dfa
5 changed files with 213 additions and 0 deletions

View File

@ -32,8 +32,11 @@
#include "windef.h"
#include "winbase.h"
#include "wincrypt.h"
#include "bcrypt.h"
#define MAGIC_DSS2 ('D' | ('S' << 8) | ('S' << 16) | ('2' << 24))
typedef struct
{
ULONG64 len;
@ -176,6 +179,7 @@ struct key_asymmetric
ULONG bitlen; /* ignored for ECC keys */
UCHAR *pubkey;
ULONG pubkey_len;
DSSSEED dss_seed;
};
struct key
@ -250,7 +254,9 @@ NTSTATUS key_asymmetric_sign( struct key *, void *, UCHAR *, ULONG, UCHAR *, ULO
NTSTATUS key_asymmetric_verify( struct key *, void *, UCHAR *, ULONG, UCHAR *, ULONG, DWORD ) DECLSPEC_HIDDEN;
NTSTATUS key_destroy( struct key * ) DECLSPEC_HIDDEN;
BOOL key_is_symmetric( struct key * ) DECLSPEC_HIDDEN;
NTSTATUS key_export_dsa_capi( struct key *, UCHAR *, ULONG, ULONG * ) DECLSPEC_HIDDEN;
NTSTATUS key_export_ecc( struct key *, UCHAR *, ULONG, ULONG * ) DECLSPEC_HIDDEN;
NTSTATUS key_import_dsa_capi( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
NTSTATUS key_import_ecc( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN;
BOOL is_zero_vector( const UCHAR *, ULONG ) DECLSPEC_HIDDEN;

View File

@ -31,6 +31,7 @@
#include "windef.h"
#include "winbase.h"
#include "ntsecapi.h"
#include "wincrypt.h"
#include "bcrypt.h"
#include "bcrypt_internal.h"
@ -948,6 +949,10 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U
{
return key_export_ecc( key, output, output_len, size );
}
else if (!strcmpW( type, LEGACY_DSA_V2_PRIVATE_BLOB ))
{
return key_export_dsa_capi( key, output, output_len, size );
}
FIXME( "unsupported key type %s\n", debugstr_w(type) );
return STATUS_NOT_IMPLEMENTED;
@ -1267,6 +1272,48 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP
*ret_key = key;
return STATUS_SUCCESS;
}
else if (!strcmpW( type, LEGACY_DSA_V2_PRIVATE_BLOB ))
{
BLOBHEADER *hdr = (BLOBHEADER *)input;
DSSPUBKEY *pubkey;
if (input_len < sizeof(*hdr)) return STATUS_INVALID_PARAMETER;
if (hdr->bType != PRIVATEKEYBLOB && hdr->bVersion != 2 && hdr->aiKeyAlg != CALG_DSS_SIGN)
{
FIXME( "blob type %u version %u alg id %u not supported\n", hdr->bType, hdr->bVersion, hdr->aiKeyAlg );
return STATUS_NOT_SUPPORTED;
}
if (alg->id != ALG_ID_DSA)
{
FIXME( "algorithm %u does not support importing blob of type %s\n", alg->id, debugstr_w(type) );
return STATUS_NOT_SUPPORTED;
}
if (input_len < sizeof(*hdr) + sizeof(*pubkey)) return STATUS_INVALID_PARAMETER;
pubkey = (DSSPUBKEY *)(hdr + 1);
if (pubkey->magic != MAGIC_DSS2) return STATUS_NOT_SUPPORTED;
if (input_len < sizeof(*hdr) + sizeof(*pubkey) + (pubkey->bitlen / 8) * 2 + 40 + sizeof(DSSSEED))
return STATUS_INVALID_PARAMETER;
if (!(key = heap_alloc_zero( sizeof(*key) ))) return STATUS_NO_MEMORY;
key->hdr.magic = MAGIC_KEY;
if ((status = key_asymmetric_init( key, alg, pubkey->bitlen, NULL, 0 )))
{
heap_free( key );
return status;
}
if ((status = key_import_dsa_capi( key, input, input_len )))
{
heap_free( key );
return status;
}
*ret_key = key;
return STATUS_SUCCESS;
}
FIXME( "unsupported key type %s\n", debugstr_w(type) );
return STATUS_NOT_SUPPORTED;

View File

@ -33,6 +33,7 @@
#include "windef.h"
#include "winbase.h"
#include "ntsecapi.h"
#include "wincrypt.h"
#include "bcrypt.h"
#include "bcrypt_internal.h"
@ -106,6 +107,7 @@ MAKE_FUNCPTR(gnutls_global_set_log_function);
MAKE_FUNCPTR(gnutls_global_set_log_level);
MAKE_FUNCPTR(gnutls_perror);
MAKE_FUNCPTR(gnutls_privkey_deinit);
MAKE_FUNCPTR(gnutls_privkey_import_dsa_raw);
MAKE_FUNCPTR(gnutls_privkey_init);
MAKE_FUNCPTR(gnutls_privkey_sign_hash);
MAKE_FUNCPTR(gnutls_pubkey_deinit);
@ -220,6 +222,7 @@ BOOL gnutls_initialize(void)
LOAD_FUNCPTR(gnutls_global_set_log_level)
LOAD_FUNCPTR(gnutls_perror)
LOAD_FUNCPTR(gnutls_privkey_deinit);
LOAD_FUNCPTR(gnutls_privkey_import_dsa_raw);
LOAD_FUNCPTR(gnutls_privkey_init);
LOAD_FUNCPTR(gnutls_privkey_sign_hash);
LOAD_FUNCPTR(gnutls_pubkey_deinit);
@ -988,6 +991,115 @@ NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len )
return STATUS_SUCCESS;
}
NTSTATUS key_export_dsa_capi( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len )
{
BLOBHEADER *hdr;
DSSPUBKEY *pubkey;
gnutls_datum_t p, q, g, y, x;
UCHAR *src, *dst;
int ret, size;
if ((ret = pgnutls_privkey_export_dsa_raw( key->u.a.handle, &p, &q, &g, &y, &x )))
{
pgnutls_perror( ret );
return STATUS_INTERNAL_ERROR;
}
if ((q.size != 20 && q.size != 21) || (x.size != 20 && x.size != 21))
{
ERR( "can't export key in this format\n" );
free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data );
return STATUS_NOT_SUPPORTED;
}
size = key->u.a.bitlen / 8;
*ret_len = sizeof(*hdr) + sizeof(*pubkey) + size * 2 + 40 + sizeof(key->u.a.dss_seed);
if (len >= *ret_len && buf)
{
hdr = (BLOBHEADER *)buf;
hdr->bType = PRIVATEKEYBLOB;
hdr->bVersion = 2;
hdr->reserved = 0;
hdr->aiKeyAlg = CALG_DSS_SIGN;
pubkey = (DSSPUBKEY *)(hdr + 1);
pubkey->magic = MAGIC_DSS2;
pubkey->bitlen = key->u.a.bitlen;
dst = (UCHAR *)(pubkey + 1);
if (p.size == size + 1) src = p.data + 1;
else src = p.data;
memcpy( dst, src, size );
dst += size;
if (q.size == 21) src = q.data + 1;
else src = q.data;
memcpy( dst, src, 20 );
dst += 20;
if (g.size == size + 1) src = g.data + 1;
else src = g.data;
memcpy( dst, src, size );
dst += size;
if (x.size == 21) src = x.data + 1;
else src = x.data;
memcpy( dst, src, 20 );
dst += 20;
memcpy( dst, &key->u.a.dss_seed, sizeof(key->u.a.dss_seed) );
}
free( p.data ); free( q.data ); free( g.data ); free( y.data ); free( x.data );
return STATUS_SUCCESS;
}
NTSTATUS key_import_dsa_capi( struct key *key, UCHAR *buf, ULONG len )
{
BLOBHEADER *hdr = (BLOBHEADER *)buf;
DSSPUBKEY *pubkey;
gnutls_privkey_t handle;
gnutls_datum_t p, q, g, y, x;
unsigned char dummy[128];
int ret, size;
if ((ret = pgnutls_privkey_init( &handle )))
{
pgnutls_perror( ret );
return STATUS_INTERNAL_ERROR;
}
hdr = (BLOBHEADER *)buf;
pubkey = (DSSPUBKEY *)(hdr + 1);
size = pubkey->bitlen / 8;
p.data = (unsigned char *)(pubkey + 1);
p.size = size;
q.data = p.data + size;
q.size = 20;
g.data = q.data + 20;
g.size = size;
x.data = g.data + size;
x.size = 20;
WARN( "using dummy public key\n" );
memset( dummy, 1, sizeof(dummy) );
y.data = dummy;
y.size = min( p.size, sizeof(dummy) );
if ((ret = pgnutls_privkey_import_dsa_raw( handle, &p, &q, &g, &y, &x )))
{
pgnutls_perror( ret );
pgnutls_privkey_deinit( handle );
return STATUS_INTERNAL_ERROR;
}
memcpy( &key->u.a.dss_seed, x.data + x.size, sizeof(key->u.a.dss_seed) );
key->u.a.handle = handle;
return STATUS_SUCCESS;
}
NTSTATUS key_asymmetric_init( struct key *key, struct algorithm *alg, ULONG bitlen, const UCHAR *pubkey,
ULONG pubkey_len )
{

View File

@ -2450,6 +2450,27 @@ static UCHAR dsaPublicBlob[] =
0x24,0xce,0x85,0xb9
};
static UCHAR dssKey[] =
{
0x07,0x02,0x00,0x00,0x00,0x22,0x00,0x00,0x44,0x53,0x53,0x32,0x00,0x04,0x00,0x00,0x01,0xd1,0xfc,0x7a,
0x70,0x53,0xb2,0x48,0x70,0x23,0x19,0x1f,0x3c,0xe1,0x26,0x14,0x7e,0x9f,0x0f,0x7f,0x33,0x5e,0x2b,0xf7,
0xca,0x01,0x74,0x8c,0xb4,0xfd,0xf6,0x44,0x95,0x35,0x56,0xaa,0x4d,0x62,0x48,0xe2,0xd1,0xa2,0x7e,0x6e,
0xeb,0xd6,0xcc,0x7c,0xe8,0xfd,0x21,0x9a,0xa2,0xfd,0x7a,0x9d,0x1a,0x38,0x69,0x87,0x39,0x5a,0x91,0xc0,
0x52,0x2b,0x9f,0x2a,0x54,0x78,0x37,0x82,0x9a,0x70,0x57,0xab,0xec,0x93,0x8e,0xac,0x73,0x04,0xe8,0x53,
0x72,0x72,0x32,0xc6,0xcb,0xef,0x47,0x98,0x3c,0x56,0x49,0x62,0xcb,0xbb,0xe7,0x34,0x84,0xa6,0x72,0x3a,
0xbe,0x26,0x46,0x86,0xca,0xcb,0x35,0x62,0x4f,0x19,0x18,0x0b,0xb0,0x78,0xae,0xd5,0x42,0xdf,0x26,0xdb,
0x85,0x63,0x77,0x85,0x01,0x3b,0x32,0xbe,0x5c,0xf8,0x05,0xc8,0xde,0x17,0x7f,0xb9,0x03,0x82,0xfa,0xf1,
0x9e,0x32,0x73,0xfa,0x8d,0xea,0xa3,0x30,0x48,0xe2,0xdf,0x5a,0xcb,0x83,0x3d,0xff,0x56,0xe9,0xc0,0x94,
0xf8,0x6d,0xb3,0xaf,0x4a,0x97,0xb9,0x43,0x0e,0xd4,0x28,0x98,0x57,0x2e,0x3a,0xca,0xde,0x6f,0x45,0x0d,
0xfb,0x58,0xec,0x78,0x34,0x2e,0x46,0x4d,0xfe,0x98,0x02,0xbb,0xef,0x07,0x1a,0x13,0xb6,0xc2,0x2c,0x06,
0xd9,0x0c,0xc4,0xb0,0x4c,0x3a,0xfc,0x01,0x63,0xb5,0x5a,0x5d,0x2d,0x9c,0x47,0x04,0x67,0x51,0xf2,0x52,
0xf5,0x82,0x36,0xeb,0x6e,0x66,0x58,0x4c,0x10,0x2c,0x29,0x72,0x4a,0x6f,0x6b,0x6c,0xe0,0x93,0x31,0x42,
0xf6,0xda,0xfa,0x5b,0x22,0x43,0x9b,0x1a,0x98,0x71,0xe7,0x41,0x74,0xe9,0x12,0xa4,0x1f,0x27,0x0a,0x63,
0x94,0x49,0xd7,0xad,0xa5,0xc4,0x5c,0xc3,0xc9,0x70,0xb3,0x7b,0x16,0xb6,0x1d,0xd4,0x09,0xc4,0x9a,0x46,
0x2d,0x0e,0x75,0x07,0x31,0x7b,0xed,0x45,0xcd,0x99,0x84,0x14,0xf1,0x01,0x00,0x00,0x93,0xd5,0xa3,0xe4,
0x34,0x05,0xeb,0x98,0x3b,0x5f,0x2f,0x11,0xa4,0xa5,0xc4,0xff,0xfb,0x22,0x7c,0x54
};
static void test_DSA(void)
{
BCRYPT_ALG_HANDLE alg;
@ -2513,6 +2534,21 @@ static void test_DSA(void)
ret = pBCryptDestroyKey(key);
ok(!ret, "got %08x\n", ret);
ret = pBCryptImportKeyPair(alg, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, &key, dssKey, sizeof(dssKey), 0);
ok(!ret, "got %08x\n", ret);
size = 0;
ret = pBCryptExportKey(key, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, NULL, 0, &size, 0);
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
ok(size, "size not set\n");
buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
ret = pBCryptExportKey(key, NULL, LEGACY_DSA_V2_PRIVATE_BLOB, buf, size, &size, 0);
ok(ret == STATUS_SUCCESS, "got %08x\n", ret);
ok(size == sizeof(dssKey), "got %u expected %u\n", size, sizeof(dssKey));
ok(!memcmp(dssKey, buf, size), "wrong data\n");
HeapFree(GetProcessHeap(), 0, buf);
ret = pBCryptCloseAlgorithmProvider(alg, 0);
ok(!ret, "got %08x\n", ret);
}

View File

@ -68,6 +68,12 @@ typedef LONG NTSTATUS;
#define BCRYPT_RSAPRIVATE_BLOB L"RSAPRIVATEBLOB"
#define BCRYPT_DSA_PUBLIC_BLOB L"DSAPUBLICBLOB"
#define BCRYPT_DSA_PRIVATE_BLOB L"DSAPRIVATEBLOB"
#define BCRYPT_PUBLIC_KEY_BLOB L"PUBLICBLOB"
#define BCRYPT_PRIVATE_KEY_BLOB L"PRIVATEBLOB"
#define LEGACY_DSA_PUBLIC_BLOB L"CAPIDSAPUBLICBLOB"
#define LEGACY_DSA_PRIVATE_BLOB L"CAPIDSAPRIVATEBLOB"
#define LEGACY_DSA_V2_PUBLIC_BLOB L"V2CAPIDSAPUBLICBLOB"
#define LEGACY_DSA_V2_PRIVATE_BLOB L"V2CAPIDSAPRIVATEBLOB"
#define MS_PRIMITIVE_PROVIDER L"Microsoft Primitive Provider"
#define MS_PLATFORM_CRYPTO_PROVIDER L"Microsoft Platform Crypto Provider"
@ -133,6 +139,12 @@ static const WCHAR BCRYPT_RSAPUBLIC_BLOB[] = {'R','S','A','P','U','B','L','I','C
static const WCHAR BCRYPT_RSAPRIVATE_BLOB[] = {'R','S','A','P','R','I','V','A','T','E','B','L','O','B',0};
static const WCHAR BCRYPT_DSA_PUBLIC_BLOB[] = {'D','S','A','P','U','B','L','I','C','B','L','O','B',0};
static const WCHAR BCRYPT_DSA_PRIVATE_BLOB[] = {'D','S','A','P','R','I','V','A','T','E','B','L','O','B',0};
static const WCHAR BCRYPT_PUBLIC_KEY_BLOB[] = {'P','U','B','L','I','C','B','L','O','B',0};
static const WCHAR BCRYPT_PRIVATE_KEY_BLOB[] = {'P','R','I','V','A','T','E','B','L','O','B',0};
static const WCHAR LEGACY_DSA_PUBLIC_BLOB[] = {'C','A','P','I','D','S','A','P','U','B','L','I','C','B','L','O','B',0};
static const WCHAR LEGACY_DSA_PRIVATE_BLOB[] = {'C','A','P','I','D','S','A','P','R','I','V','A','T','E','B','L','O','B',0};
static const WCHAR LEGACY_DSA_V2_PUBLIC_BLOB[] = {'V','2','C','A','P','I','D','S','A','P','U','B','L','I','C','B','L','O','B',0};
static const WCHAR LEGACY_DSA_V2_PRIVATE_BLOB[] = {'V','2','C','A','P','I','D','S','A','P','R','I','V','A','T','E','B','L','O','B',0};
static const WCHAR MS_PRIMITIVE_PROVIDER[] = \
{'M','i','c','r','o','s','o','f','t',' ','P','r','i','m','i','t','i','v','e',' ','P','r','o','v','i','d','e','r',0};