Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -33224,7 +33224,7 @@ static int MatchSigAlgo(WOLFSSL* ssl, int hashAlgo, int sigAlgo)
#if defined(HAVE_ECC) && \
((defined(WOLFSSL_TLS13) && !defined(NO_CERTS)) || \
defined(USE_ECDSA_KEYSZ_HASH_ALGO))
static int CmpEccStrength(int hashAlgo, int curveSz)
int CmpEccStrength(int hashAlgo, int curveSz)
{
int dgstSz = GetMacDigestSize((byte)hashAlgo);
if (dgstSz <= 0)
Expand Down
5 changes: 3 additions & 2 deletions src/tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -3321,9 +3321,11 @@ static int TLSX_MFL_Parse(WOLFSSL* ssl, const byte* input, word16 length,
return BUFFER_ERROR;

#ifdef WOLFSSL_OLD_UNSUPPORTED_EXTENSION
(void) isRequest;
/* Legacy lenient handling applies to TLS 1.2 and earlier only. */
if (!isRequest && IsAtLeastTLSv1_3(ssl->version)) {
#else
if (!isRequest) {
#endif
TLSX* extension;

if (TLSX_CheckUnsupportedExtension(ssl, TLSX_MAX_FRAGMENT_LENGTH))
Expand All @@ -3344,7 +3346,6 @@ static int TLSX_MFL_Parse(WOLFSSL* ssl, const byte* input, word16 length,
return UNKNOWN_MAX_FRAG_LEN_E;
}
}
#endif

switch (*input) {
case WOLFSSL_MFL_2_8 : ssl->max_fragment = 256; break;
Expand Down
56 changes: 54 additions & 2 deletions src/tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -12420,6 +12420,45 @@ static void FreeDcv13Args(WOLFSSL* ssl, void* pArgs)
(void)ssl;
}

#ifdef HAVE_ECC
/* An ECDSA SignatureScheme names a curve as well as a hash, so the peer key
* has to be on the curve the announced scheme names (RFC 8446 4.4.3).
*
* returns 1 when the peer key's group agrees with the announced hash. */
static int EccPeerCurveMatchesSigAlgo(WOLFSSL* ssl, byte hashAlgo,
byte sigAlgo)
{
ecc_key* key = ssl->peerEccDsaKey;

if ((key == NULL) || (key->dp == NULL))
return 0;

/* A curve with a scheme of its own has to be used with it, or a same
* sized curve would satisfy any of them. */
#ifdef HAVE_ECC_BRAINPOOL
if ((key->dp->oidSum == ECC_BRAINPOOLP256R1_OID) ||
(key->dp->oidSum == ECC_BRAINPOOLP384R1_OID) ||
(key->dp->oidSum == ECC_BRAINPOOLP512R1_OID)) {
if (sigAlgo != ecc_brainpool_sa_algo)
return 0;
}
else
#endif
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
if (key->dp->oidSum == ECC_SM2P256V1_OID) {
if (sigAlgo != sm2_sa_algo)
return 0;
}
else
#endif
if (sigAlgo != ecc_dsa_sa_algo) {
return 0;
}

return CmpEccStrength(hashAlgo, key->dp->size) == 0;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't possible as RFC 8446 defines no ECDSA codepoint for secp256k1 at all, so any TLS 1.3 use of it
must borrow secp256r1's. secp256k1 has roughly equivalent security as secp256r1, so this is not a security issue.

}
#endif /* HAVE_ECC */

#ifdef WOLFSSL_DUAL_ALG_CERTS
#ifndef NO_RSA
/* ssl->peerCert->sapkiDer is the alternative public key. Hopefully it is a
Expand Down Expand Up @@ -12826,16 +12865,25 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
#endif
#ifdef HAVE_ECC
if (ssl->options.peerSigAlgo == ecc_dsa_sa_algo) {
byte curveHash = 0, curveSig = 0;
WOLFSSL_MSG("Peer sent ECC sig");
/* DecodeTls13SigAlg folds the brainpool schemes onto
* ecc_dsa_sa_algo; DecodeSigAlg keeps them apart. */
DecodeSigAlg(input + args->begin, &curveHash, &curveSig);
validSigAlgo = (ssl->peerEccDsaKey != NULL) &&
ssl->peerEccDsaKeyPresent;
ssl->peerEccDsaKeyPresent &&
EccPeerCurveMatchesSigAlgo(ssl, curveHash,
curveSig);
}
#endif
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
if (ssl->options.peerSigAlgo == sm2_sa_algo) {
WOLFSSL_MSG("Peer sent SM2 sig");
validSigAlgo = (ssl->peerEccDsaKey != NULL) &&
ssl->peerEccDsaKeyPresent;
ssl->peerEccDsaKeyPresent &&
EccPeerCurveMatchesSigAlgo(ssl,
ssl->options.peerHashAlgo,
ssl->options.peerSigAlgo);
}
#endif
#ifdef HAVE_FALCON
Expand Down Expand Up @@ -13260,6 +13308,10 @@ static int DoTls13CertificateVerify(WOLFSSL* ssl, byte* input,
if ((args->altSigAlgo == ecc_dsa_sa_algo) &&
(ssl->peerEccDsaKeyPresent)) {
WOLFSSL_MSG("Doing ECC peer cert alt verify");
if (!EccPeerCurveMatchesSigAlgo(ssl,
ssl->options.peerHashAlgo, ecc_dsa_sa_algo)) {
ERROR_OUT(SIG_VERIFY_E, exit_dcv);
}
ret = EccVerify(ssl, sig, args->altSignatureSz,
args->altSigData, args->altSigDataSz,
ssl->peerEccDsaKey,
Expand Down
211 changes: 211 additions & 0 deletions tests/api/test_tls13.c
Original file line number Diff line number Diff line change
Expand Up @@ -13979,3 +13979,214 @@ int test_tls13_psk_key_zeroized(void)
#endif
return EXPECT_RESULT();
}

/* A TLS 1.3 ECDSA SignatureScheme names a curve as well as a hash
* (RFC 8446 4.4.3), so the receiver must check the peer key's curve against
* the announced scheme. Both directions are covered: the server's
* CertificateVerify and, under mutual TLS, the client's. */
int test_tls13_ecdsa_scheme_curve_binding(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \
defined(HAVE_ECC) && !defined(NO_ECC256) && !defined(NO_SHA256) && \
(defined(HAVE_ALL_CURVES) || defined(HAVE_ECC521)) && \
defined(WOLFSSL_SHA512) && defined(OPENSSL_EXTRA) && \
defined(WOLFSSL_PEM_TO_DER) && \
!defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER) && \
!defined(NO_CERTS) && !defined(NO_FILESYSTEM)
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
struct test_memio_ctx test_ctx;
const char* p521Cert = "./certs/p521/server-p521.pem";
const char* p521Key = "./certs/p521/server-p521-priv.pem";
const char* p521Ca = "./certs/p521/ca-p521.pem";
#ifdef HAVE_ECC_BRAINPOOL
const char* bpCert = "./certs/ecc/server-bp256r1-cert.pem";
const char* bpKey = "./certs/ecc/bp256r1-key.pem";
#endif

/* Sanity: the P-256 server certificate authenticates under the scheme that
* names its own curve, ecdsa_secp256r1_sha256. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, eccCertFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "ECDSA+SHA256"),
WOLFSSL_SUCCESS);
ExpectIntEQ(ssl_c->suites->hashSigAlgoSz, 2);
ExpectIntEQ(ssl_c->suites->hashSigAlgo[0], sha256_mac);
ExpectIntEQ(ssl_c->suites->hashSigAlgo[1], ecc_dsa_sa_algo);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_c->options.peerSigAlgo, ecc_dsa_sa_algo);
ExpectIntEQ(ssl_c->options.peerHashAlgo, sha256_mac);
ExpectIntEQ(ssl_c->options.peerAuthGood, 1);

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

/* Sanity: a P-521 certificate authenticates under ecdsa_secp521r1_sha512,
* the only scheme this client offers. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, p521Ca, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, p521Cert,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, p521Key,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "ECDSA+SHA512"),
WOLFSSL_SUCCESS);
ExpectIntEQ(ssl_c->suites->hashSigAlgoSz, 2);
ExpectIntEQ(ssl_c->suites->hashSigAlgo[0], sha512_mac);
ExpectIntEQ(ssl_c->suites->hashSigAlgo[1], ecc_dsa_sa_algo);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_c->options.peerSigAlgo, ecc_dsa_sa_algo);
ExpectIntEQ(ssl_c->options.peerHashAlgo, sha512_mac);
ExpectIntEQ(ssl_c->options.peerAuthGood, 1);

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

/* Control: the same client policy against the P-256 certificate. The
* server finds no scheme it can use for that key, so the handshake never
* reaches CertificateVerify. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, eccCertFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "ECDSA+SHA512"),
WOLFSSL_SUCCESS);
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(MATCH_SUITE_ERROR));

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

/* Server CertificateVerify. Overriding the key size the sender pairs its
* digest against makes it announce ecdsa_secp521r1_sha512 over a P-256
* key. The certificate, the chain and the signature are all internally
* consistent, so only the curve check can reject it. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, caEccCertFile, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, eccCertFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, eccKeyFile, CERT_FILETYPE),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "ECDSA+SHA512"),
WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS())
ssl_s->buffers.keySz = 66;
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_c->error, WC_NO_ERR_TRACE(SIG_VERIFY_E));
ExpectIntEQ(ssl_c->options.peerAuthGood, 0);

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

/* Client CertificateVerify under mutual TLS. The server holds a P-521
* certificate and restricts its CertificateRequest to
* ecdsa_secp521r1_sha512; the client answers with a P-256 certificate
* under that label. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, p521Ca, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, p521Cert,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, p521Key,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_s, cliEccCertFile, NULL),
WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS()) {
wolfSSL_set_verify(ssl_s,
WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_s, "ECDSA+SHA512"),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_c, cliEccCertFile,
CERT_FILETYPE), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_c, cliEccKeyFile,
CERT_FILETYPE), WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS())
ssl_c->buffers.keySz = 66;
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_s->error, WC_NO_ERR_TRACE(SIG_VERIFY_E));
ExpectIntEQ(ssl_s->options.peerAuthGood, 0);

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

#ifdef HAVE_ECC_BRAINPOOL
/* Same-size curves are not interchangeable: brainpoolP256r1 has schemes of
* its own (RFC 8734), so it must not be accepted under
* ecdsa_secp256r1_sha256. Overriding the curve the sender reports for its
* own key is what makes it use the plain ECDSA scheme. The digest length
* matches the group here, so only the curve check can reject it. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, bpCert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, bpCert,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, bpKey,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_set1_sigalgs_list(ssl_c, "ECDSA+SHA256"),
WOLFSSL_SUCCESS);
if (EXPECT_SUCCESS())
ssl_s->pkCurveOID = ECC_SECP256R1_OID;
ExpectIntNE(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_c->error, WC_NO_ERR_TRACE(SIG_VERIFY_E));
ExpectIntEQ(ssl_c->options.peerAuthGood, 0);

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;

/* Control: the same certificate under its own scheme is accepted. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(wolfSSL_CTX_load_verify_locations(ctx_c, bpCert, NULL),
WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_certificate_file(ssl_s, bpCert,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(wolfSSL_use_PrivateKey_file(ssl_s, bpKey,
WOLFSSL_FILETYPE_PEM), WOLFSSL_SUCCESS);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
ExpectIntEQ(ssl_c->options.peerAuthGood, 1);

wolfSSL_free(ssl_c); ssl_c = NULL;
wolfSSL_free(ssl_s); ssl_s = NULL;
wolfSSL_CTX_free(ctx_c); ctx_c = NULL;
wolfSSL_CTX_free(ctx_s); ctx_s = NULL;
#endif /* HAVE_ECC_BRAINPOOL */
#endif
return EXPECT_RESULT();
}
11 changes: 11 additions & 0 deletions tests/api/test_tls13.c.rej
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--- tests/api/test_tls13.c
+++ tests/api/test_tls13.c
@@ -13685,7 +13685,7 @@ static void test_tls13_cks_free_cb(void* ptr)
int test_tls13_cks_hrr_reparse(void)
{
EXPECT_DECLS;
-#ifdef TEST_TLS13_CKS_REPARSE
+#if defined(TEST_TLS13_CKS_REPARSE) && defined(USE_WOLFSSL_MEMORY)
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
4 changes: 3 additions & 1 deletion tests/api/test_tls13.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ int test_tls13_cryptocb_async(void);
int test_tls13_ticket_psk_modes(void);
int test_tls13_psk_mode_mismatch_falls_back(void);
int test_tls13_ticket_psk_modes_uses_policy(void);
int test_tls13_ecdsa_scheme_curve_binding(void);
int test_tls13_send_session_ticket_psk_modes(void);
int test_tls13_new_session_ticket_ext_framing(void);
int test_tls13_new_session_ticket_keeps_ems(void);
Expand Down Expand Up @@ -302,6 +303,7 @@ int test_tls13_psk_key_zeroized(void);
TEST_DECL_GROUP("tls13", test_tls13_hs_secret_zeroized_psk_ke), \
TEST_DECL_GROUP("tls13", test_tls13_hs_secret_zeroized_sha384), \
TEST_DECL_GROUP("tls13", test_tls13_early_secret_zeroized), \
TEST_DECL_GROUP("tls13", test_tls13_psk_key_zeroized)
TEST_DECL_GROUP("tls13", test_tls13_psk_key_zeroized), \
TEST_DECL_GROUP("tls13", test_tls13_ecdsa_scheme_curve_binding)

#endif /* WOLFCRYPT_TEST_TLS13_H */
Loading
Loading