Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/bcrypt/gnutls.c | 150 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 17 deletions(-)
diff --git a/dlls/bcrypt/gnutls.c b/dlls/bcrypt/gnutls.c index a6a07fff19..fe16e8c435 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,23 @@ static NTSTATUS prepare_gnutls_signature( struct key *key, UCHAR *signature, ULO } }
+gnutls_digest_algorithm_t get_digest_from_id(LPCWSTR alg_id) +{ + if (!lstrcmpiW(alg_id, BCRYPT_SHA1_ALGORITHM)) + return GNUTLS_DIG_SHA1; + if (!lstrcmpiW(alg_id, BCRYPT_SHA256_ALGORITHM)) + return GNUTLS_DIG_SHA256; + if (!lstrcmpiW(alg_id, BCRYPT_SHA384_ALGORITHM)) + return GNUTLS_DIG_SHA384; + if (!lstrcmpiW(alg_id, BCRYPT_SHA512_ALGORITHM)) + return GNUTLS_DIG_SHA512; + if (!lstrcmpiW(alg_id, BCRYPT_MD2_ALGORITHM)) + return GNUTLS_DIG_MD2; + if (!lstrcmpiW(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 +1088,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 +1123,127 @@ NTSTATUS key_asymmetric_verify( struct key *key, void *padding, UCHAR *hash, ULO return (ret < 0) ? STATUS_INVALID_SIGNATURE : STATUS_SUCCESS; }
+NTSTATUS format_gnutls_signature( enum alg_id type, gnutls_datum_t signature, UCHAR *output, + ULONG output_len, ULONG *ret_len) +{ + int err; + unsigned int sig_len; + + switch (type) + { + case ALG_ID_RSA: + case ALG_ID_RSA_SIGN: + { + /* RSA */ + 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: + sig_len = 64; + goto ecc; + case ALG_ID_ECDSA_P384: + sig_len = 96; + ecc: + { + /* ECDSA, format as r||s */ + gnutls_datum_t r,s; + unsigned int pad_size; + + if ((err = pgnutls_decode_rs_value(&signature, &r, &s))) + { + ERR( "failed to get R/S values from signature %u\n", err ); + return 0; + } + + 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 +1260,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 )