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 )
Signed-off-by: Derek Lesho dlesho@codeweavers.com --- dlls/bcrypt/tests/bcrypt.c | 47 +++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-)
diff --git a/dlls/bcrypt/tests/bcrypt.c b/dlls/bcrypt/tests/bcrypt.c index d125e0c89d..8e495438df 100644 --- a/dlls/bcrypt/tests/bcrypt.c +++ b/dlls/bcrypt/tests/bcrypt.c @@ -2048,7 +2048,9 @@ static BYTE rsapublic[] = static void test_BCryptSignHash(void) { static UCHAR hash[] = - {0x7e,0xe3,0x74,0xe7,0xc5,0x0b,0x6b,0x70,0xdb,0xab,0x32,0x6d,0x1d,0x51,0xd6,0x74,0x79,0x8e,0x5b,0x4b}; + {0x04,0xD6,0xAC,0x44,0x02,0xA9,0x2A,0x36,0x78,0x0B,0xC7,0x79,0xC8,0xEC,0x71,0x34,0x49,0xFB,0x92,0x2F}; + static UCHAR hash_sha256[] = + {0x25,0x2F,0x10,0xC8,0x36,0x10,0xEB,0xCA,0x1A,0x05,0x9C,0x0B,0xAE,0x82,0x55,0xEB,0xA2,0xF9,0x5B,0xE4,0xD1,0xD7,0xBC,0xFA,0x89,0xD7,0x24,0x8A,0x82,0xD9,0xF1,0x11}; BCRYPT_PKCS1_PADDING_INFO pad; BCRYPT_ALG_HANDLE alg; BCRYPT_KEY_HANDLE key; @@ -2056,6 +2058,8 @@ static void test_BCryptSignHash(void) NTSTATUS ret; ULONG len;
+ /* RSA */ + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_RSA_ALGORITHM, NULL, 0); if (ret) { @@ -2087,6 +2091,14 @@ static void test_BCryptSignHash(void)
len = 0; memset(sig, 0, sizeof(sig)); + + /* inference of padding info on RSA not supported */ + ret = pBCryptSignHash(key, NULL, hash, sizeof(hash), sig, sizeof(sig), &len, 0); + ok(ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); + + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, 0, &len, BCRYPT_PAD_PKCS1); + ok(ret == STATUS_BUFFER_TOO_SMALL, "got %08x\n", ret); + ret = pBCryptSignHash(key, &pad, hash, sizeof(hash), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); ok(!ret, "got %08x\n", ret); ok(len == 64, "got %u\n", len); @@ -2099,6 +2111,39 @@ static void test_BCryptSignHash(void)
ret = pBCryptCloseAlgorithmProvider(alg, 0); ok(!ret, "got %08x\n", ret); + + /* ECDSA */ + + ret = pBCryptOpenAlgorithmProvider(&alg, BCRYPT_ECDSA_P256_ALGORITHM, NULL, 0); + if (ret) + { + win_skip("failed to open ECDSA provider: %08x\n", ret); + return; + } + + ret = pBCryptGenerateKeyPair(alg, &key, 256, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + + ret = pBCryptFinalizeKeyPair(key, 0); + ok(ret == STATUS_SUCCESS, "got %08x\n", ret); + + memset(sig, 0, sizeof(sig)); + len = 0; + + /* automatically detects padding info */ + ret = pBCryptSignHash(key, NULL, hash, sizeof(hash), sig, sizeof(sig), &len, 0); + ok (!ret, "got %08x\n", ret); + ok (len == 64, "got %u\n", len); + + /* mismatch info (SHA-1 != SHA-256) */ + ret = pBCryptSignHash(key, &pad, hash_sha256, sizeof(hash_sha256), sig, sizeof(sig), &len, BCRYPT_PAD_PKCS1); + ok (ret == STATUS_INVALID_PARAMETER, "got %08x\n", ret); + + ret = pBCryptDestroyKey(key); + ok(!ret, "got %08x\n", ret); + + ret = pBCryptCloseAlgorithmProvider(alg, 0); + ok(!ret, "got %08x\n", ret); }
static void test_BCryptEnumAlgorithms(void)