bcrypt: Add support for signing hashes with ECDSA keys.

Signed-off-by: Derek Lesho <dlesho@codeweavers.com>
Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Derek Lesho 2019-12-06 15:42:48 +01:00 committed by Alexandre Julliard
parent 2bca8eac3a
commit 741f76fc2c
1 changed files with 131 additions and 18 deletions

View File

@ -92,6 +92,7 @@ MAKE_FUNCPTR(gnutls_cipher_decrypt2);
MAKE_FUNCPTR(gnutls_cipher_deinit);
MAKE_FUNCPTR(gnutls_cipher_encrypt2);
MAKE_FUNCPTR(gnutls_cipher_init);
MAKE_FUNCPTR(gnutls_decode_rs_value);
MAKE_FUNCPTR(gnutls_global_deinit);
MAKE_FUNCPTR(gnutls_global_init);
MAKE_FUNCPTR(gnutls_global_set_log_function);
@ -189,6 +190,7 @@ BOOL gnutls_initialize(void)
LOAD_FUNCPTR(gnutls_cipher_deinit)
LOAD_FUNCPTR(gnutls_cipher_encrypt2)
LOAD_FUNCPTR(gnutls_cipher_init)
LOAD_FUNCPTR(gnutls_decode_rs_value)
LOAD_FUNCPTR(gnutls_global_deinit)
LOAD_FUNCPTR(gnutls_global_init)
LOAD_FUNCPTR(gnutls_global_set_log_function)
@ -711,6 +713,7 @@ NTSTATUS key_asymmetric_generate( struct key *key )
break;
case ALG_ID_ECDH_P256:
case ALG_ID_ECDSA_P256:
pk_alg = GNUTLS_PK_ECC; /* compatible with ECDSA and ECDH */
bitlen = GNUTLS_CURVE_TO_BITS( GNUTLS_ECC_CURVE_SECP256R1 );
break;
@ -1029,6 +1032,17 @@ static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULO
}
}
static gnutls_digest_algorithm_t get_digest_from_id( const WCHAR *alg_id )
{
if (!strcmpW( alg_id, BCRYPT_SHA1_ALGORITHM )) return GNUTLS_DIG_SHA1;
if (!strcmpW( alg_id, BCRYPT_SHA256_ALGORITHM )) return GNUTLS_DIG_SHA256;
if (!strcmpW( alg_id, BCRYPT_SHA384_ALGORITHM )) return GNUTLS_DIG_SHA384;
if (!strcmpW( alg_id, BCRYPT_SHA512_ALGORITHM )) return GNUTLS_DIG_SHA512;
if (!strcmpW( alg_id, BCRYPT_MD2_ALGORITHM )) return GNUTLS_DIG_MD2;
if (!strcmpW( alg_id, BCRYPT_MD5_ALGORITHM )) return GNUTLS_DIG_MD5;
return -1;
}
NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULONG hash_len, UCHAR *signature,
ULONG signature_len, DWORD flags )
{
@ -1068,11 +1082,7 @@ NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULO
if (!(flags & BCRYPT_PAD_PKCS1) || !info) return STATUS_INVALID_PARAMETER;
if (!info->pszAlgId) return STATUS_INVALID_SIGNATURE;
if (!strcmpW( info->pszAlgId, BCRYPT_SHA1_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA1;
else if (!strcmpW( info->pszAlgId, BCRYPT_SHA256_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA256;
else if (!strcmpW( info->pszAlgId, BCRYPT_SHA384_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA384;
else if (!strcmpW( info->pszAlgId, BCRYPT_SHA512_ALGORITHM )) hash_alg = GNUTLS_DIG_SHA512;
else
if ((hash_alg = get_digest_from_id(info->pszAlgId)) == -1)
{
FIXME( "hash algorithm %s not supported\n", debugstr_w(info->pszAlgId) );
return STATUS_NOT_SUPPORTED;
@ -1107,28 +1117,132 @@ NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULO
return (ret < 0) ? STATUS_INVALID_SIGNATURE : STATUS_SUCCESS;
}
static unsigned int get_signature_length( enum alg_id id )
{
switch (id)
{
case ALG_ID_ECDSA_P256: return 64;
case ALG_ID_ECDSA_P384: return 96;
default:
FIXME( "unhandled algorithm %u\n", id );
return 0;
}
}
NTSTATUS format_gnutls_signature( enum alg_id type, gnutls_datum_t signature, UCHAR *output,
ULONG output_len, ULONG *ret_len )
{
switch (type)
{
case ALG_ID_RSA:
case ALG_ID_RSA_SIGN:
{
if (output_len < signature.size) return STATUS_BUFFER_TOO_SMALL;
memcpy( output, signature.data, signature.size );
*ret_len = signature.size;
return STATUS_SUCCESS;
}
case ALG_ID_ECDSA_P256:
case ALG_ID_ECDSA_P384:
{
int err;
unsigned int pad_size, sig_len = get_signature_length( type );
gnutls_datum_t r, s; /* format as r||s */
if ((err = pgnutls_decode_rs_value( &signature, &r, &s )))
{
pgnutls_perror( err );
return STATUS_INTERNAL_ERROR;
}
if (output_len < sig_len) return STATUS_BUFFER_TOO_SMALL;
/* remove prepended zero byte */
if (r.size % 2)
{
r.size--;
r.data += 1;
}
if (s.size % 2)
{
s.size--;
s.data += 1;
}
if (r.size != s.size || r.size + s.size > sig_len)
{
ERR( "we didn't get a correct signature\n" );
return STATUS_INTERNAL_ERROR;
}
pad_size = (sig_len / 2) - s.size;
memset( output, 0, sig_len );
memcpy( output + pad_size, r.data, r.size );
memcpy( output + (sig_len / 2) + pad_size, s.data, s.size );
*ret_len = sig_len;
return STATUS_SUCCESS;
}
default:
return STATUS_INTERNAL_ERROR;
}
}
NTSTATUS key_asymmetric_sign( struct key *key, void *padding, UCHAR *input, ULONG input_len, UCHAR *output,
ULONG output_len, ULONG *ret_len, ULONG flags )
{
BCRYPT_PKCS1_PADDING_INFO *pad = padding;
gnutls_datum_t hash, signature;
gnutls_digest_algorithm_t hash_alg;
NTSTATUS status;
int ret;
if (key->alg_id != ALG_ID_RSA && key->alg_id != ALG_ID_RSA_SIGN)
if (key->alg_id == ALG_ID_ECDSA_P256 || key->alg_id == ALG_ID_ECDSA_P384)
{
FIXME( "algorithm %u not supported\n", key->alg_id );
return STATUS_NOT_IMPLEMENTED;
/* With ECDSA, we find the digest algorithm from the hash length, and verify it */
switch (input_len)
{
case 20: hash_alg = GNUTLS_DIG_SHA1; break;
case 32: hash_alg = GNUTLS_DIG_SHA256; break;
case 48: hash_alg = GNUTLS_DIG_SHA384; break;
case 64: hash_alg = GNUTLS_DIG_SHA512; break;
default:
FIXME( "hash size %u not yet supported\n", input_len );
return STATUS_INVALID_PARAMETER;
}
if (flags != BCRYPT_PAD_PKCS1)
if (flags == BCRYPT_PAD_PKCS1 && pad && pad->pszAlgId && get_digest_from_id( pad->pszAlgId ) != hash_alg)
{
WARN( "incorrect hashing algorithm %s, expected %u\n", debugstr_w(pad->pszAlgId), hash_alg );
return STATUS_INVALID_PARAMETER;
}
}
else if (flags == BCRYPT_PAD_PKCS1)
{
if (!pad || !pad->pszAlgId)
{
WARN( "padding info not found\n" );
return STATUS_INVALID_PARAMETER;
}
if ((hash_alg = get_digest_from_id( pad->pszAlgId )) == -1)
{
FIXME( "hash algorithm %s not recognized\n", debugstr_w(pad->pszAlgId) );
return STATUS_NOT_SUPPORTED;
}
}
else if (!flags)
{
WARN( "invalid flags %08x\n", flags );
return STATUS_INVALID_PARAMETER;
}
else
{
FIXME( "flags %08x not implemented\n", flags );
return STATUS_NOT_IMPLEMENTED;
}
if (!pad || !pad->pszAlgId || lstrcmpiW(pad->pszAlgId, BCRYPT_SHA1_ALGORITHM))
{
FIXME( "%s padding not implemented\n", debugstr_w(pad ? pad->pszAlgId : NULL) );
return STATUS_NOT_IMPLEMENTED;
}
if (!input)
{
@ -1143,17 +1257,16 @@ NTSTATUS key_asymmetric_sign( struct key *key, void *padding, UCHAR *input, ULON
signature.data = NULL;
signature.size = 0;
if ((ret = pgnutls_privkey_sign_hash( key->u.a.handle, GNUTLS_DIG_SHA1, 0, &hash, &signature )))
if ((ret = pgnutls_privkey_sign_hash( key->u.a.handle, hash_alg, 0, &hash, &signature )))
{
pgnutls_perror( ret );
return STATUS_INTERNAL_ERROR;
}
if (output_len >= signature.size) memcpy( output, signature.data, signature.size );
*ret_len = signature.size;
status = format_gnutls_signature( key->alg_id, signature, output, output_len, ret_len );
free( signature.data );
return STATUS_SUCCESS;
return status;
}
NTSTATUS key_destroy( struct key *key )