From 158ce169e4aa60054956dd7dab20ff218d49f8fe Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Wed, 13 Feb 2019 10:21:17 +0100 Subject: [PATCH] bcrypt: Add support for importing and exporting ECC private keys. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/bcrypt/bcrypt_internal.h | 2 + dlls/bcrypt/bcrypt_main.c | 55 ++++++++++++++++ dlls/bcrypt/gnutls.c | 121 +++++++++++++++++++++++++++++++++- dlls/bcrypt/macos.c | 12 ++++ dlls/bcrypt/tests/bcrypt.c | 52 +++++++++++++-- 5 files changed, 237 insertions(+), 5 deletions(-) diff --git a/dlls/bcrypt/bcrypt_internal.h b/dlls/bcrypt/bcrypt_internal.h index b6e3d0b95f9..943c33d2b28 100644 --- a/dlls/bcrypt/bcrypt_internal.h +++ b/dlls/bcrypt/bcrypt_internal.h @@ -230,6 +230,8 @@ 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; BOOL key_is_symmetric( struct key * ) DECLSPEC_HIDDEN; +NTSTATUS key_export_ecc( struct key *, UCHAR *, ULONG, ULONG * ) DECLSPEC_HIDDEN; +NTSTATUS key_import_ecc( struct key *, UCHAR *, ULONG ) DECLSPEC_HIDDEN; BOOL gnutls_initialize(void) DECLSPEC_HIDDEN; void gnutls_uninitialize(void) DECLSPEC_HIDDEN; diff --git a/dlls/bcrypt/bcrypt_main.c b/dlls/bcrypt/bcrypt_main.c index 0d504b8d54f..e511495eed9 100644 --- a/dlls/bcrypt/bcrypt_main.c +++ b/dlls/bcrypt/bcrypt_main.c @@ -821,6 +821,10 @@ static NTSTATUS key_export( struct key *key, const WCHAR *type, UCHAR *output, U memcpy( output, key->u.a.pubkey, key->u.a.pubkey_len ); return STATUS_SUCCESS; } + else if (!strcmpW( type, BCRYPT_ECCPRIVATE_BLOB )) + { + return key_export_ecc( key, output, output_len, size ); + } FIXME( "unsupported key type %s\n", debugstr_w(type) ); return STATUS_NOT_IMPLEMENTED; @@ -1055,6 +1059,45 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP *ret_key = key; return STATUS_SUCCESS; } + else if (!strcmpW( type, BCRYPT_ECCPRIVATE_BLOB )) + { + BCRYPT_ECCKEY_BLOB *ecc_blob = (BCRYPT_ECCKEY_BLOB *)input; + DWORD key_size, magic; + + if (input_len < sizeof(*ecc_blob)) return STATUS_INVALID_PARAMETER; + + switch (alg->id) + { + case ALG_ID_ECDH_P256: + key_size = 32; + magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; + break; + + default: + FIXME( "algorithm %u does not yet support importing blob of type %s\n", alg->id, debugstr_w(type) ); + return STATUS_NOT_SUPPORTED; + } + + if (ecc_blob->dwMagic != magic) return STATUS_NOT_SUPPORTED; + if (ecc_blob->cbKey != key_size || input_len < sizeof(*ecc_blob) + ecc_blob->cbKey * 3) + 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, NULL, 0 ))) + { + heap_free( key ); + return status; + } + if ((status = key_import_ecc( key, input, input_len ))) + { + heap_free( key ); + return status; + } + + *ret_key = key; + return STATUS_SUCCESS; + } else if (!strcmpW( type, BCRYPT_RSAPUBLIC_BLOB )) { BCRYPT_RSAKEY_BLOB *rsa_blob = (BCRYPT_RSAKEY_BLOB *)input; @@ -1163,6 +1206,18 @@ static NTSTATUS key_import_pair( struct algorithm *alg, const WCHAR *type, BCRYP ERR( "support for keys not available at build time\n" ); return STATUS_NOT_IMPLEMENTED; } + +NTSTATUS key_export_ecc( struct key *key, UCHAR *output, ULONG len, ULONG *ret_len ) +{ + ERR( "support for keys not available at build time\n" ); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_import_ecc( struct key *key, UCHAR *input, ULONG len ) +{ + ERR( "support for keys not available at build time\n" ); + return STATUS_NOT_IMPLEMENTED; +} #endif NTSTATUS WINAPI BCryptGenerateSymmetricKey( BCRYPT_ALG_HANDLE algorithm, BCRYPT_KEY_HANDLE *handle, diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index 5a656ccba23..2a7e37ef8a1 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -70,6 +70,8 @@ static int (*pgnutls_cipher_add_auth)(gnutls_cipher_hd_t, const void *, size_t); static gnutls_sign_algorithm_t (*pgnutls_pk_to_sign)(gnutls_pk_algorithm_t, gnutls_digest_algorithm_t); static int (*pgnutls_pubkey_import_ecc_raw)(gnutls_pubkey_t, gnutls_ecc_curve_t, const gnutls_datum_t *, const gnutls_datum_t *); +static int (*pgnutls_privkey_import_ecc_raw)(gnutls_privkey_t, gnutls_ecc_curve_t, const gnutls_datum_t *, + const gnutls_datum_t *, const gnutls_datum_t *); static int (*pgnutls_pubkey_verify_hash2)(gnutls_pubkey_t, gnutls_sign_algorithm_t, unsigned int, const gnutls_datum_t *, const gnutls_datum_t *); @@ -120,6 +122,13 @@ static int compat_gnutls_privkey_export_ecc_raw(gnutls_privkey_t key, gnutls_ecc return GNUTLS_E_UNKNOWN_PK_ALGORITHM; } +static int compat_gnutls_privkey_import_ecc_raw(gnutls_privkey_t key, gnutls_ecc_curve_t curve, + const gnutls_datum_t *x, const gnutls_datum_t *y, + const gnutls_datum_t *k) +{ + return GNUTLS_E_UNKNOWN_PK_ALGORITHM; +} + static gnutls_sign_algorithm_t compat_gnutls_pk_to_sign(gnutls_pk_algorithm_t pk, gnutls_digest_algorithm_t hash) { return GNUTLS_SIGN_UNKNOWN; @@ -206,6 +215,11 @@ BOOL gnutls_initialize(void) WARN("gnutls_privkey_export_ecc_raw not found\n"); pgnutls_privkey_export_ecc_raw = compat_gnutls_privkey_export_ecc_raw; } + if (!(pgnutls_privkey_import_ecc_raw = wine_dlsym( libgnutls_handle, "gnutls_privkey_import_ecc_raw", NULL, 0 ))) + { + WARN("gnutls_privkey_import_ecc_raw not found\n"); + pgnutls_privkey_import_ecc_raw = compat_gnutls_privkey_import_ecc_raw; + } if (!(pgnutls_pk_to_sign = wine_dlsym( libgnutls_handle, "gnutls_pk_to_sign", NULL, 0 ))) { WARN("gnutls_pk_to_sign not found\n"); @@ -614,11 +628,116 @@ NTSTATUS key_asymmetric_generate( struct key *key ) if ((status = export_gnutls_pubkey_ecc( handle, &key->u.a.pubkey, &key->u.a.pubkey_len ))) { pgnutls_privkey_deinit( handle ); - return STATUS_INTERNAL_ERROR; + return status; } key->u.a.handle = handle; + return STATUS_SUCCESS; +} +NTSTATUS key_export_ecc( struct key *key, UCHAR *buf, ULONG len, ULONG *ret_len ) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + gnutls_ecc_curve_t curve; + gnutls_datum_t x, y, d; + DWORD magic, size; + UCHAR *src, *dst; + int ret; + + if ((ret = pgnutls_privkey_export_ecc_raw( key->u.a.handle, &curve, &x, &y, &d ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + switch (curve) + { + case GNUTLS_ECC_CURVE_SECP256R1: + magic = BCRYPT_ECDH_PRIVATE_P256_MAGIC; + size = 32; + break; + + default: + FIXME( "curve %u not supported\n", curve ); + free( x.data ); free( y.data ); free( d.data ); + return STATUS_NOT_IMPLEMENTED; + } + + *ret_len = sizeof(*ecc_blob) + size * 3; + if (len >= *ret_len && buf) + { + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + ecc_blob->dwMagic = magic; + ecc_blob->cbKey = size; + + dst = (UCHAR *)(ecc_blob + 1); + if (x.size == size + 1) src = x.data + 1; + else src = x.data; + memcpy( dst, src, size ); + + dst += size; + if (y.size == size + 1) src = y.data + 1; + else src = y.data; + memcpy( dst, src, size ); + + dst += size; + if (d.size == size + 1) src = d.data + 1; + else src = d.data; + memcpy( dst, src, size ); + } + + free( x.data ); free( y.data ); free( d.data ); + return STATUS_SUCCESS; +} + +NTSTATUS key_import_ecc( struct key *key, UCHAR *buf, ULONG len ) +{ + BCRYPT_ECCKEY_BLOB *ecc_blob; + gnutls_ecc_curve_t curve; + gnutls_privkey_t handle; + gnutls_datum_t x, y, k; + NTSTATUS status; + int ret; + + switch (key->alg_id) + { + case ALG_ID_ECDH_P256: + curve = GNUTLS_ECC_CURVE_SECP256R1; + break; + + default: + FIXME( "algorithm %u not yet supported\n", key->alg_id ); + return STATUS_NOT_IMPLEMENTED; + } + + if ((ret = pgnutls_privkey_init( &handle ))) + { + pgnutls_perror( ret ); + return STATUS_INTERNAL_ERROR; + } + + ecc_blob = (BCRYPT_ECCKEY_BLOB *)buf; + x.data = (unsigned char *)(ecc_blob + 1); + x.size = ecc_blob->cbKey; + y.data = x.data + ecc_blob->cbKey; + y.size = ecc_blob->cbKey; + k.data = y.data + ecc_blob->cbKey; + k.size = ecc_blob->cbKey; + + if ((ret = pgnutls_privkey_import_ecc_raw( handle, curve, &x, &y, &k ))) + { + 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 ))) + { + pgnutls_privkey_deinit( handle ); + return status; + } + + key->u.a.handle = handle; return STATUS_SUCCESS; } diff --git a/dlls/bcrypt/macos.c b/dlls/bcrypt/macos.c index 0e0ed8379d6..cc99637c4b2 100644 --- a/dlls/bcrypt/macos.c +++ b/dlls/bcrypt/macos.c @@ -205,6 +205,18 @@ NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULO return STATUS_NOT_IMPLEMENTED; } +NTSTATUS key_export_ecc( struct key *key, UCHAR *output, ULONG len, ULONG *ret_len ) +{ + FIXME( "not implemented on Mac\n" ); + return STATUS_NOT_IMPLEMENTED; +} + +NTSTATUS key_import_ecc( struct key *key, UCHAR *input, ULONG len ) +{ + FIXME( "not implemented on Mac\n" ); + return STATUS_NOT_IMPLEMENTED; +} + NTSTATUS key_asymmetric_generate( struct key *key ) { FIXME( "not implemented on Mac\n" ); diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index 819c1dc8869..ca7c22d5c40 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -1657,12 +1657,23 @@ static void test_RSA(void) ok(!ret, "pBCryptCloseAlgorithmProvider failed: %08x\n", ret); } +static BYTE eccprivkey[] = +{ + 0x45, 0x43, 0x4b, 0x32, 0x20, 0x00, 0x00, 0x00, 0xfb, 0xbd, 0x3d, 0x20, 0x1b, 0x6d, 0x66, 0xb3, + 0x7c, 0x9f, 0x89, 0xf3, 0xe4, 0x41, 0x16, 0xa5, 0x68, 0x52, 0x77, 0xac, 0xab, 0x55, 0xb2, 0x6c, + 0xb0, 0x23, 0x55, 0xcb, 0x96, 0x14, 0xfd, 0x0b, 0x1c, 0xef, 0xdf, 0x07, 0x6d, 0x31, 0xaf, 0x39, + 0xce, 0x8c, 0x8f, 0x9d, 0x75, 0xd0, 0x7b, 0xea, 0x81, 0xdc, 0x40, 0x21, 0x1f, 0x58, 0x22, 0x5f, + 0x72, 0x55, 0xfc, 0x58, 0x8a, 0xeb, 0x88, 0x5d, 0x02, 0x09, 0x90, 0xd2, 0xe3, 0x36, 0xac, 0xfe, + 0x83, 0x13, 0x6c, 0x88, 0x1a, 0xab, 0x9b, 0xdd, 0xaa, 0x8a, 0xee, 0x69, 0x9a, 0x6a, 0x62, 0x86, + 0x6a, 0x13, 0x69, 0x88, 0xb7, 0xd5, 0xa3, 0xcd +}; + static void test_ECDH(void) { BYTE *buf; BCRYPT_ECCKEY_BLOB *ecckey; BCRYPT_ALG_HANDLE alg; - BCRYPT_KEY_HANDLE key; + BCRYPT_KEY_HANDLE key, privkey, pubkey; NTSTATUS status; ULONG size; @@ -1682,7 +1693,6 @@ static void test_ECDH(void) ok(status == STATUS_SUCCESS, "got %08x\n", status); size = 0; - SetLastError(0xdeadbeef); status = pBCryptExportKey(key, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &size, 0); ok(status == STATUS_SUCCESS, "got %08x\n", status); ok(size, "size not set\n"); @@ -1694,13 +1704,47 @@ static void test_ECDH(void) ok(ecckey->dwMagic == BCRYPT_ECDH_PUBLIC_P256_MAGIC, "got %08x\n", ecckey->dwMagic); ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); ok(size == sizeof(*ecckey) + ecckey->cbKey * 2, "got %u\n", size); - pBCryptDestroyKey(key); - status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &key, buf, size, 0); + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, &pubkey, buf, size, 0); ok(status == STATUS_SUCCESS, "got %08x\n", status); HeapFree(GetProcessHeap(), 0, buf); + size = 0; + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size, "size not set\n"); + + buf = HeapAlloc(GetProcessHeap(), 0, size); + status = pBCryptExportKey(key, NULL, BCRYPT_ECCPRIVATE_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ecckey = (BCRYPT_ECCKEY_BLOB *)buf; + ok(ecckey->dwMagic == BCRYPT_ECDH_PRIVATE_P256_MAGIC, "got %08x\n", ecckey->dwMagic); + ok(ecckey->cbKey == 32, "got %u\n", ecckey->cbKey); + ok(size == sizeof(*ecckey) + ecckey->cbKey * 3, "got %u\n", size); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, buf, size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + HeapFree(GetProcessHeap(), 0, buf); + pBCryptDestroyKey(pubkey); + pBCryptDestroyKey(privkey); pBCryptDestroyKey(key); + + status = pBCryptImportKeyPair(alg, NULL, BCRYPT_ECCPRIVATE_BLOB, &privkey, eccprivkey, sizeof(eccprivkey), 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + + size = 0; + status = pBCryptExportKey(privkey, NULL, BCRYPT_ECCPRIVATE_BLOB, NULL, 0, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size, "size not set\n"); + + buf = HeapAlloc(GetProcessHeap(), 0, size); + status = pBCryptExportKey(privkey, NULL, BCRYPT_ECCPRIVATE_BLOB, buf, size, &size, 0); + ok(status == STATUS_SUCCESS, "got %08x\n", status); + ok(size == sizeof(eccprivkey), "got %u\n", size); + ok(!memcmp(buf, eccprivkey, size), "wrong data\n"); + HeapFree(GetProcessHeap(), 0, buf); + + pBCryptDestroyKey(privkey); pBCryptCloseAlgorithmProvider(alg, 0); }