From 741f76fc2c9203105ca7f87c0014bb5a9da4d936 Mon Sep 17 00:00:00 2001 From: Derek Lesho Date: Fri, 6 Dec 2019 15:42:48 +0100 Subject: [PATCH] bcrypt: Add support for signing hashes with ECDSA keys. Signed-off-by: Derek Lesho Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/bcrypt/gnutls.c | 147 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 130 insertions(+), 17 deletions(-) diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index a6a07fff19e..28a14e9b851 100644 --- a/dlls/bcrypt/gnutls.c +++ b/dlls/bcrypt/gnutls.c @@ -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,26 +1117,130 @@ 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 && 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; + } } - if (flags != BCRYPT_PAD_PKCS1) + else if (flags == BCRYPT_PAD_PKCS1) { - FIXME( "flags %08x not implemented\n", flags ); - return STATUS_NOT_IMPLEMENTED; + 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; } - if (!pad || !pad->pszAlgId || lstrcmpiW(pad->pszAlgId, BCRYPT_SHA1_ALGORITHM)) + else { - FIXME( "%s padding not implemented\n", debugstr_w(pad ? pad->pszAlgId : NULL) ); + FIXME( "flags %08x not implemented\n", flags ); return STATUS_NOT_IMPLEMENTED; } @@ -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 ) -- 2.11.4.GIT