Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 160 additions & 40 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -25818,6 +25818,145 @@ int SendCertificate(WOLFSSL* ssl)


#if !defined(NO_TLS)
/* Returns the certificate_types this server advertises in its
* CertificateRequest. The list is broader than the negotiated cipher suite's
* own signature algorithm so a client may authenticate with a certificate of
* a different type (e.g. an RSA client on an ECDHE-ECDSA suite). */
static int GetServerCertReqCertTypes(const WOLFSSL* ssl, byte* certTypes)
{
int n = 0;
#ifdef HAVE_ECC
if ((ssl->options.cipherSuite0 == ECC_BYTE ||
ssl->options.cipherSuite0 == CHACHA_BYTE) &&
ssl->specs.sig_algo == ecc_dsa_sa_algo) {
certTypes[n++] = ecdsa_sign;
#ifndef NO_RSA
certTypes[n++] = rsa_sign;
#endif
}
else
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
(defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
defined(WOLFSSL_SM4_CCM))
if (ssl->options.cipherSuite0 == SM_BYTE && (0
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
#endif
)) {
certTypes[n++] = ecdsa_sign;
}
else
#endif
#endif /* HAVE_ECC */
{
#ifndef NO_RSA
certTypes[n++] = rsa_sign;
#endif
#ifdef HAVE_ECC
certTypes[n++] = ecdsa_sign;
#endif
}
Comment thread
julek-wolfssl marked this conversation as resolved.
return n;
}

/* Returns the set of sig families covered by the given hash/sig algorithm
* list, as a bitmask of SIG_* values. Uses DecodeSigAlg so the NEW_SA_MAJOR
* encoding (ED25519/ED448/RSA-PSS-PSS/brainpool) is classified correctly. */
static int HashSigAlgoCoverage(const byte* hashSigAlgo, word16 hashSigAlgoSz)
{
int coverage = 0;
word16 j;
byte hashAlgo;
byte sigAlgo;
for (j = 0; (j + 1) < hashSigAlgoSz; j += HELLO_EXT_SIGALGO_SZ) {
DecodeSigAlg(&hashSigAlgo[j], &hashAlgo, &sigAlgo);
(void)hashAlgo;
switch (sigAlgo) {
case rsa_sa_algo:
#ifdef WC_RSA_PSS
case rsa_pss_sa_algo:
case rsa_pss_pss_algo:
#endif
coverage |= SIG_RSA;
break;
#ifdef HAVE_ECC
case ecc_dsa_sa_algo:
#ifdef HAVE_ECC_BRAINPOOL
case ecc_brainpool_sa_algo:
#endif
coverage |= SIG_ECDSA;
break;
#endif
default:
break;
}
}
return coverage;
}

/* Builds the signature_algorithms this server advertises in its
* CertificateRequest. Respects a user-configured suites->hashSigAlgo (e.g.
* via wolfSSL_set1_sigalgs_list) and only broadens the list when one of the
* advertised certificate_types has no matching signature algorithm in the
* configured list. The result is written to the caller's buffer; no SSL
* state is modified. */
static void GetServerCertReqHashSigAlgo(const WOLFSSL* ssl,
byte* hashSigAlgo, word16* hashSigAlgoSz)
{
const Suites* suites = WOLFSSL_SUITES(ssl);
byte certTypes[MAX_CERT_REQ_CERT_TYPE_CNT];
int typeTotal;
int need = 0;
int have;
int j;
word16 localSz = 0;

typeTotal = GetServerCertReqCertTypes(ssl, certTypes);
for (j = 0; j < typeTotal; j++) {
if (certTypes[j] == rsa_sign)
need |= SIG_RSA;
else if (certTypes[j] == ecdsa_sign)
need |= SIG_ECDSA;
}
have = HashSigAlgoCoverage(suites->hashSigAlgo, suites->hashSigAlgoSz);

if ((need & ~have) != 0) {
/* The configured list is missing signature algorithms for at least
* one of the advertised certificate_types. Build a broader list
* locally that covers every advertised type. */
InitSuitesHashSigAlgo(hashSigAlgo, need | have, 1, 0,
ssl->buffers.keySz, &localSz);
*hashSigAlgoSz = localSz;
return;
}

XMEMCPY(hashSigAlgo, suites->hashSigAlgo, suites->hashSigAlgoSz);
*hashSigAlgoSz = suites->hashSigAlgoSz;
}

/* Returns 1 if algo (2 bytes) is in the server's CertificateRequest
* signature_algorithms list, 0 otherwise. Used to validate the client's
* CertificateVerify against what we actually advertised. */
static int InServerCertReqHashSigAlgo(const WOLFSSL* ssl, const byte* algo)
{
byte list[WOLFSSL_MAX_SIGALGO];
word16 listSz = 0;
word16 j;

GetServerCertReqHashSigAlgo(ssl, list, &listSz);
for (j = 0; (j + 1) < listSz; j += HELLO_EXT_SIGALGO_SZ) {
if (XMEMCMP(&list[j], algo, HELLO_EXT_SIGALGO_SZ) == 0)
return 1;
}
return 0;
}

/* handle generation of certificate_request (13) */
int SendCertificateRequest(WOLFSSL* ssl)
{
Expand All @@ -25829,16 +25968,24 @@ int SendCertificateRequest(WOLFSSL* ssl)
#ifndef WOLFSSL_NO_CA_NAMES
WOLF_STACK_OF(WOLFSSL_X509_NAME)* names;
#endif
const Suites* suites = WOLFSSL_SUITES(ssl);

int typeTotal = 1; /* only 1 for now */
int reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ; /* add auth later */
byte certTypes[MAX_CERT_REQ_CERT_TYPE_CNT];
int typeTotal;
int t;
byte localHashSigAlgo[WOLFSSL_MAX_SIGALGO];
word16 localHashSigAlgoSz = 0;
int reqSz;

WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_SEND);
WOLFSSL_ENTER("SendCertificateRequest");

typeTotal = GetServerCertReqCertTypes(ssl, certTypes);
if (IsAtLeastTLSv1_2(ssl))
GetServerCertReqHashSigAlgo(ssl, localHashSigAlgo, &localHashSigAlgoSz);

reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ; /* add auth later */

if (IsAtLeastTLSv1_2(ssl))
reqSz += LENGTH_SZ + suites->hashSigAlgoSz;
reqSz += LENGTH_SZ + localHashSigAlgoSz;

#ifndef WOLFSSL_NO_CA_NAMES
/* Certificate Authorities */
Expand Down Expand Up @@ -25891,43 +26038,16 @@ int SendCertificateRequest(WOLFSSL* ssl)

/* write to output */
output[i++] = (byte)typeTotal; /* # of types */
#ifdef HAVE_ECC
if ((ssl->options.cipherSuite0 == ECC_BYTE ||
ssl->options.cipherSuite0 == CHACHA_BYTE) &&
ssl->specs.sig_algo == ecc_dsa_sa_algo) {
output[i++] = ecdsa_sign;
}
else
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
(defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
defined(WOLFSSL_SM4_CCM))
if (ssl->options.cipherSuite0 == SM_BYTE && (0
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
#endif
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
#endif
)) {
output[i++] = ecdsa_sign;
}
else
#endif
#endif /* HAVE_ECC */
{
output[i++] = rsa_sign;
}
for (t = 0; t < typeTotal; t++)
output[i++] = certTypes[t];

/* supported hash/sig */
if (IsAtLeastTLSv1_2(ssl)) {
c16toa(suites->hashSigAlgoSz, &output[i]);
c16toa(localHashSigAlgoSz, &output[i]);
i += OPAQUE16_LEN;

XMEMCPY(&output[i], suites->hashSigAlgo, suites->hashSigAlgoSz);
i += suites->hashSigAlgoSz;
XMEMCPY(&output[i], localHashSigAlgo, localHashSigAlgoSz);
i += localHashSigAlgoSz;
}

/* Certificate Authorities */
Expand Down Expand Up @@ -38949,9 +39069,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
ERROR_OUT(BUFFER_ERROR, exit_dcv);
}

/* Check if hashSigAlgo in CertificateVerify is supported
* in our ssl->suites or ssl->ctx->suites. */
if (!SupportedHashSigAlgo(ssl, &input[args->idx])) {
/* Check the algorithm in CertificateVerify against the
* list we actually advertised in our CertificateRequest. */
if (!InServerCertReqHashSigAlgo(ssl, &input[args->idx])) {
WOLFSSL_MSG("Signature algorithm was not in "
"CertificateRequest");
ERROR_OUT(INVALID_PARAMETER, exit_dcv);
Expand Down
34 changes: 21 additions & 13 deletions tests/api.c
Original file line number Diff line number Diff line change
Expand Up @@ -35648,8 +35648,9 @@ static int test_dtls_seq_num_downgrade(void)
}

/**
* Make sure we don't send RSA Signature Hash Algorithms in the
* CertificateRequest when we don't have any such ciphers set.
* Make sure the CertificateRequest advertises ECDSA signature hash algorithms
* for an ECDHE-ECDSA server, and also includes RSA algorithms so that RSA
* clients can authenticate (the certificate_type advertised covers both).
* @return EXPECT_RESULT()
*/
static int test_certreq_sighash_algos(void)
Expand Down Expand Up @@ -35710,17 +35711,24 @@ static int test_certreq_sighash_algos(void)
idx += OPAQUE16_LEN;
maxIdx = idx + (int)len;
for (; idx < maxIdx && EXPECT_SUCCESS(); idx += OPAQUE16_LEN) {
if (test_ctx.c_buff[idx+1] == ED25519_SA_MINOR ||
test_ctx.c_buff[idx+1] == ED448_SA_MINOR ||
test_ctx.c_buff[idx+1] ==
ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR ||
test_ctx.c_buff[idx+1] ==
ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR ||
test_ctx.c_buff[idx+1] ==
ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR)
ExpectIntEQ(test_ctx.c_buff[idx], NEW_SA_MAJOR);
else
ExpectIntEQ(test_ctx.c_buff[idx+1], ecc_dsa_sa_algo);
byte first = test_ctx.c_buff[idx];
byte second = test_ctx.c_buff[idx+1];
if (second == ED25519_SA_MINOR ||
second == ED448_SA_MINOR ||
second == ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR ||
second == ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR ||
second == ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR) {
ExpectIntEQ(first, NEW_SA_MAJOR);
}
else {
/* ECDHE-ECDSA suites advertise ECDSA so the negotiated
* cipher can be used, and also RSA / RSA-PSS so RSA
* clients can authenticate via mutual auth. Note that
* RSA-PSS is encoded with sigAlgo first then mac. */
ExpectTrue(second == ecc_dsa_sa_algo ||
second == rsa_sa_algo ||
first == rsa_pss_sa_algo);
}
}
break;
}
Expand Down
108 changes: 108 additions & 0 deletions tests/api/test_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -1219,3 +1219,111 @@ int test_wolfSSL_alert_desc_string(void)
#endif
return EXPECT_RESULT();
}

/* TLS 1.2 mutual auth: an ECDHE-ECDSA server (ECDSA certificate) accepting an
* RSA client certificate. */
int test_tls12_ecdhe_ecdsa_rsa_client_cert(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12) \
&& defined(HAVE_ECC) && !defined(NO_RSA) && !defined(NO_SHA256) \
&& defined(HAVE_AESGCM) && defined(KEEP_PEER_CERT) \
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \
&& !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
struct test_memio_ctx test_ctx;
WOLFSSL_X509* peer = NULL;
const char* cipher = "ECDHE-ECDSA-AES128-GCM-SHA256";

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);

/* Server: ECDSA certificate (=> ECDHE-ECDSA suite), require client
* authentication, and trust the (self-signed) RSA client certificate. */
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, eccCertFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliCertFile, NULL),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cipher), WOLFSSL_SUCCESS);

/* Client: RSA certificate/key, and trust the ECC CA that signed the
* server's ECDSA certificate. */
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliCertFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliKeyFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cipher), WOLFSSL_SUCCESS);

/* Mutual authentication completes and the server obtains the client's
* RSA certificate even though the negotiated suite is ECDHE-ECDSA. */
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c), cipher);
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
wolfSSL_X509_free(peer);

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}

/* TLS 1.2 mutual auth: an ECDHE-RSA server (RSA certificate) accepting an
* ECDSA client certificate. */
int test_tls12_ecdhe_rsa_ecdsa_client_cert(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(WOLFSSL_NO_TLS12) \
&& defined(HAVE_ECC) && !defined(NO_RSA) && !defined(NO_SHA256) \
&& defined(HAVE_AESGCM) && defined(KEEP_PEER_CERT) \
&& !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) \
&& !defined(NO_FILESYSTEM) && !defined(NO_CERTS)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
struct test_memio_ctx test_ctx;
WOLFSSL_X509* peer = NULL;
const char* cipher = "ECDHE-RSA-AES128-GCM-SHA256";

XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_2_client_method, wolfTLSv1_2_server_method), 0);

/* Server: default RSA certificate (=> ECDHE-RSA), require client
* authentication, and trust the (self-signed) ECDSA client certificate. */
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliEccCertFile, NULL),
WOLFSSL_SUCCESS);
wolfSSL_set_verify(ssl_s, WOLFSSL_VERIFY_PEER |
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_s, cipher), WOLFSSL_SUCCESS);

/* Client: ECDSA certificate/key. The default client CTX already trusts
* the RSA CA that signed the server's certificate. */
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliEccCertFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliEccKeyFile,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set_cipher_list(ssl_c, cipher), WOLFSSL_SUCCESS);

/* Mutual authentication completes and the server obtains the client's
* ECDSA certificate even though the negotiated suite is ECDHE-RSA. */
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectStrEQ(wolfSSL_get_cipher_name(ssl_c), cipher);
ExpectNotNull(peer = wolfSSL_get_peer_certificate(ssl_s));
wolfSSL_X509_free(peer);

wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
Loading
Loading