From 7cf9a75dfa77de408261aef689c8f60fee17e46e Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Wed, 3 Jun 2020 10:50:37 +0200 Subject: [PATCH] bcrypt: Add support for importing and exporting DSS private keys. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/bcrypt/bcrypt_internal.h | 6 ++ dlls/bcrypt/bcrypt_main.c | 47 ++++++++++++++ dlls/bcrypt/gnutls.c | 112 ++++++++++++++++++++++++++++++++++ dlls/bcrypt/tests/bcrypt.c | 36 +++++++++++ include/bcrypt.h | 12 ++++ 5 files changed, 213 insertions(+) diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index 2c834bec48d..18343a6c749 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -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; diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 5492c1947e3..fee40ebe8d7 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -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; diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index d4ad327bc47..d447e90a11e 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -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 ) { diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index a67b14f4e4f..e4a99d63048 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -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); } diff --git a/include/bcrypt.h b/include/bcrypt.h index be170a613c4..a099f2f4b0e 100644 --- a/include/bcrypt.h +++ b/include/bcrypt.h @@ -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};