Skip to content

Commit f3ab345

Browse files
authored
Merge pull request #10553 from julek-wolfssl/tls-12-mutual-auth
Allow RSA client certs on ECDHE-ECDSA mutual auth
2 parents e513172 + a3bc7c9 commit f3ab345

6 files changed

Lines changed: 307 additions & 71 deletions

File tree

src/internal.c

Lines changed: 168 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25888,6 +25888,153 @@ int SendCertificate(WOLFSSL* ssl)
2588825888

2588925889

2589025890
#if !defined(NO_TLS)
25891+
/* Returns the certificate_types this server advertises in its
25892+
* CertificateRequest. The list is broader than the negotiated cipher suite's
25893+
* own signature algorithm so a client may authenticate with a certificate of
25894+
* a different type (e.g. an RSA client on an ECDHE-ECDSA suite). */
25895+
WC_MAYBE_UNUSED static int GetServerCertReqCertTypes(const WOLFSSL* ssl,
25896+
byte* certTypes)
25897+
{
25898+
int n = 0;
25899+
(void)ssl;
25900+
(void)certTypes;
25901+
#ifdef HAVE_ECC
25902+
if ((ssl->options.cipherSuite0 == ECC_BYTE ||
25903+
ssl->options.cipherSuite0 == CHACHA_BYTE) &&
25904+
ssl->specs.sig_algo == ecc_dsa_sa_algo) {
25905+
certTypes[n++] = ecdsa_sign;
25906+
#ifndef NO_RSA
25907+
certTypes[n++] = rsa_sign;
25908+
#endif
25909+
}
25910+
else
25911+
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
25912+
(defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
25913+
defined(WOLFSSL_SM4_CCM))
25914+
if (ssl->options.cipherSuite0 == SM_BYTE && (0
25915+
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
25916+
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
25917+
#endif
25918+
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
25919+
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
25920+
#endif
25921+
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
25922+
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
25923+
#endif
25924+
)) {
25925+
certTypes[n++] = ecdsa_sign;
25926+
}
25927+
else
25928+
#endif
25929+
#endif /* HAVE_ECC */
25930+
{
25931+
#ifndef NO_RSA
25932+
certTypes[n++] = rsa_sign;
25933+
#endif
25934+
#ifdef HAVE_ECC
25935+
certTypes[n++] = ecdsa_sign;
25936+
#endif
25937+
}
25938+
return n;
25939+
}
25940+
25941+
/* Returns the set of sig families covered by the given hash/sig algorithm
25942+
* list, as a bitmask of SIG_* values. Uses DecodeSigAlg so the NEW_SA_MAJOR
25943+
* encoding (ED25519/ED448/RSA-PSS-PSS/brainpool) is classified correctly. */
25944+
WC_MAYBE_UNUSED static int HashSigAlgoCoverage(const byte* hashSigAlgo,
25945+
word16 hashSigAlgoSz)
25946+
{
25947+
int coverage = 0;
25948+
word16 j;
25949+
byte hashAlgo;
25950+
byte sigAlgo;
25951+
for (j = 0; (j + 1) < hashSigAlgoSz; j += HELLO_EXT_SIGALGO_SZ) {
25952+
DecodeSigAlg(&hashSigAlgo[j], &hashAlgo, &sigAlgo);
25953+
(void)hashAlgo;
25954+
switch (sigAlgo) {
25955+
case rsa_sa_algo:
25956+
#ifdef WC_RSA_PSS
25957+
case rsa_pss_sa_algo:
25958+
case rsa_pss_pss_algo:
25959+
#endif
25960+
coverage |= SIG_RSA;
25961+
break;
25962+
#ifdef HAVE_ECC
25963+
case ecc_dsa_sa_algo:
25964+
#ifdef HAVE_ECC_BRAINPOOL
25965+
case ecc_brainpool_sa_algo:
25966+
#endif
25967+
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
25968+
case sm2_sa_algo:
25969+
#endif
25970+
coverage |= SIG_ECDSA;
25971+
break;
25972+
#endif
25973+
default:
25974+
break;
25975+
}
25976+
}
25977+
return coverage;
25978+
}
25979+
25980+
/* Builds the signature_algorithms this server advertises in its
25981+
* CertificateRequest. Respects a user-configured suites->hashSigAlgo (e.g.
25982+
* via wolfSSL_set1_sigalgs_list) and only broadens the list when one of the
25983+
* advertised certificate_types has no matching signature algorithm in the
25984+
* configured list. The result is written to the caller's buffer; no SSL
25985+
* state is modified. */
25986+
WC_MAYBE_UNUSED static void GetServerCertReqHashSigAlgo(const WOLFSSL* ssl,
25987+
byte* hashSigAlgo, word16* hashSigAlgoSz)
25988+
{
25989+
const Suites* suites = WOLFSSL_SUITES(ssl);
25990+
byte certTypes[MAX_CERT_REQ_CERT_TYPE_CNT];
25991+
int typeTotal;
25992+
int need = 0;
25993+
int have;
25994+
int j;
25995+
word16 localSz = 0;
25996+
25997+
typeTotal = GetServerCertReqCertTypes(ssl, certTypes);
25998+
for (j = 0; j < typeTotal; j++) {
25999+
if (certTypes[j] == rsa_sign)
26000+
need |= SIG_RSA;
26001+
else if (certTypes[j] == ecdsa_sign)
26002+
need |= SIG_ECDSA;
26003+
}
26004+
have = HashSigAlgoCoverage(suites->hashSigAlgo, suites->hashSigAlgoSz);
26005+
26006+
if ((need & ~have) != 0) {
26007+
/* The configured list is missing signature algorithms for at least
26008+
* one of the advertised certificate_types. Build a broader list
26009+
* locally that covers every advertised type. */
26010+
InitSuitesHashSigAlgo(hashSigAlgo, need | have, 1, 0,
26011+
ssl->buffers.keySz, &localSz);
26012+
*hashSigAlgoSz = localSz;
26013+
return;
26014+
}
26015+
26016+
XMEMCPY(hashSigAlgo, suites->hashSigAlgo, suites->hashSigAlgoSz);
26017+
*hashSigAlgoSz = suites->hashSigAlgoSz;
26018+
}
26019+
26020+
/* Returns 1 if algo (2 bytes) is in the server's CertificateRequest
26021+
* signature_algorithms list, 0 otherwise. Used to validate the client's
26022+
* CertificateVerify against what we actually advertised. */
26023+
WC_MAYBE_UNUSED static int InServerCertReqHashSigAlgo(const WOLFSSL* ssl,
26024+
const byte* algo)
26025+
{
26026+
byte list[WOLFSSL_MAX_SIGALGO];
26027+
word16 listSz = 0;
26028+
word16 j;
26029+
26030+
GetServerCertReqHashSigAlgo(ssl, list, &listSz);
26031+
for (j = 0; (j + 1) < listSz; j += HELLO_EXT_SIGALGO_SZ) {
26032+
if (XMEMCMP(&list[j], algo, HELLO_EXT_SIGALGO_SZ) == 0)
26033+
return 1;
26034+
}
26035+
return 0;
26036+
}
26037+
2589126038
/* handle generation of certificate_request (13) */
2589226039
int SendCertificateRequest(WOLFSSL* ssl)
2589326040
{
@@ -25899,16 +26046,24 @@ int SendCertificateRequest(WOLFSSL* ssl)
2589926046
#ifndef WOLFSSL_NO_CA_NAMES
2590026047
WOLF_STACK_OF(WOLFSSL_X509_NAME)* names;
2590126048
#endif
25902-
const Suites* suites = WOLFSSL_SUITES(ssl);
25903-
25904-
int typeTotal = 1; /* only 1 for now */
25905-
int reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ; /* add auth later */
26049+
byte certTypes[MAX_CERT_REQ_CERT_TYPE_CNT];
26050+
int typeTotal;
26051+
int t;
26052+
byte localHashSigAlgo[WOLFSSL_MAX_SIGALGO];
26053+
word16 localHashSigAlgoSz = 0;
26054+
int reqSz;
2590626055

2590726056
WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_SEND);
2590826057
WOLFSSL_ENTER("SendCertificateRequest");
2590926058

26059+
typeTotal = GetServerCertReqCertTypes(ssl, certTypes);
26060+
if (IsAtLeastTLSv1_2(ssl))
26061+
GetServerCertReqHashSigAlgo(ssl, localHashSigAlgo, &localHashSigAlgoSz);
26062+
26063+
reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ; /* add auth later */
26064+
2591026065
if (IsAtLeastTLSv1_2(ssl))
25911-
reqSz += LENGTH_SZ + suites->hashSigAlgoSz;
26066+
reqSz += LENGTH_SZ + localHashSigAlgoSz;
2591226067

2591326068
#ifndef WOLFSSL_NO_CA_NAMES
2591426069
/* Certificate Authorities */
@@ -25961,43 +26116,16 @@ int SendCertificateRequest(WOLFSSL* ssl)
2596126116

2596226117
/* write to output */
2596326118
output[i++] = (byte)typeTotal; /* # of types */
25964-
#ifdef HAVE_ECC
25965-
if ((ssl->options.cipherSuite0 == ECC_BYTE ||
25966-
ssl->options.cipherSuite0 == CHACHA_BYTE) &&
25967-
ssl->specs.sig_algo == ecc_dsa_sa_algo) {
25968-
output[i++] = ecdsa_sign;
25969-
}
25970-
else
25971-
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
25972-
(defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
25973-
defined(WOLFSSL_SM4_CCM))
25974-
if (ssl->options.cipherSuite0 == SM_BYTE && (0
25975-
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
25976-
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
25977-
#endif
25978-
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
25979-
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
25980-
#endif
25981-
#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
25982-
|| ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
25983-
#endif
25984-
)) {
25985-
output[i++] = ecdsa_sign;
25986-
}
25987-
else
25988-
#endif
25989-
#endif /* HAVE_ECC */
25990-
{
25991-
output[i++] = rsa_sign;
25992-
}
26119+
for (t = 0; t < typeTotal; t++)
26120+
output[i++] = certTypes[t];
2599326121

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

25999-
XMEMCPY(&output[i], suites->hashSigAlgo, suites->hashSigAlgoSz);
26000-
i += suites->hashSigAlgoSz;
26127+
XMEMCPY(&output[i], localHashSigAlgo, localHashSigAlgoSz);
26128+
i += localHashSigAlgoSz;
2600126129
}
2600226130

2600326131
/* Certificate Authorities */
@@ -39067,9 +39195,9 @@ static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
3906739195
ERROR_OUT(BUFFER_ERROR, exit_dcv);
3906839196
}
3906939197

39070-
/* Check if hashSigAlgo in CertificateVerify is supported
39071-
* in our ssl->suites or ssl->ctx->suites. */
39072-
if (!SupportedHashSigAlgo(ssl, &input[args->idx])) {
39198+
/* Check the algorithm in CertificateVerify against the
39199+
* list we actually advertised in our CertificateRequest. */
39200+
if (!InServerCertReqHashSigAlgo(ssl, &input[args->idx])) {
3907339201
WOLFSSL_MSG("Signature algorithm was not in "
3907439202
"CertificateRequest");
3907539203
ERROR_OUT(INVALID_PARAMETER, exit_dcv);

tests/api.c

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31600,8 +31600,9 @@ static int test_session_ticket_hs_update(void)
3160031600

3160131601

3160231602
/**
31603-
* Make sure we don't send RSA Signature Hash Algorithms in the
31604-
* CertificateRequest when we don't have any such ciphers set.
31603+
* Make sure the CertificateRequest advertises ECDSA signature hash algorithms
31604+
* for an ECDHE-ECDSA server, and also includes RSA algorithms so that RSA
31605+
* clients can authenticate (the certificate_type advertised covers both).
3160531606
* @return EXPECT_RESULT()
3160631607
*/
3160731608
static int test_certreq_sighash_algos(void)
@@ -31662,17 +31663,24 @@ static int test_certreq_sighash_algos(void)
3166231663
idx += OPAQUE16_LEN;
3166331664
maxIdx = idx + (int)len;
3166431665
for (; idx < maxIdx && EXPECT_SUCCESS(); idx += OPAQUE16_LEN) {
31665-
if (test_ctx.c_buff[idx+1] == ED25519_SA_MINOR ||
31666-
test_ctx.c_buff[idx+1] == ED448_SA_MINOR ||
31667-
test_ctx.c_buff[idx+1] ==
31668-
ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR ||
31669-
test_ctx.c_buff[idx+1] ==
31670-
ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR ||
31671-
test_ctx.c_buff[idx+1] ==
31672-
ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR)
31673-
ExpectIntEQ(test_ctx.c_buff[idx], NEW_SA_MAJOR);
31674-
else
31675-
ExpectIntEQ(test_ctx.c_buff[idx+1], ecc_dsa_sa_algo);
31666+
byte first = test_ctx.c_buff[idx];
31667+
byte second = test_ctx.c_buff[idx+1];
31668+
if (second == ED25519_SA_MINOR ||
31669+
second == ED448_SA_MINOR ||
31670+
second == ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR ||
31671+
second == ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR ||
31672+
second == ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR) {
31673+
ExpectIntEQ(first, NEW_SA_MAJOR);
31674+
}
31675+
else {
31676+
/* ECDHE-ECDSA suites advertise ECDSA so the negotiated
31677+
* cipher can be used, and also RSA / RSA-PSS so RSA
31678+
* clients can authenticate via mutual auth. Note that
31679+
* RSA-PSS is encoded with sigAlgo first then mac. */
31680+
ExpectTrue(second == ecc_dsa_sa_algo ||
31681+
second == rsa_sa_algo ||
31682+
first == rsa_pss_sa_algo);
31683+
}
3167631684
}
3167731685
break;
3167831686
}

0 commit comments

Comments
 (0)