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:
parent
2bca8eac3a
commit
741f76fc2c
|
@ -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 )
|
||||
|
|
Loading…
Reference in New Issue