bcrypt: Add support for importing and exporting ECC private keys.
Signed-off-by: Hans Leidekker <hans@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
5c2ac77ab7
commit
158ce169e4
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue