@@ -938,6 +938,73 @@ int test_tls12_ec_point_formats_no_uncompressed(void)
938938 return EXPECT_RESULT ();
939939}
940940
941+ /* RFC 7507: a server that supports a version higher than the one offered by the
942+ * client MUST abort with a fatal inappropriate_fallback alert when the client
943+ * includes TLS_FALLBACK_SCSV (0x5600) in its cipher list. This enforcement is
944+ * always compiled in, so it must hold in the default build.
945+ *
946+ * The ClientHello below offers TLS 1.2 with TLS_FALLBACK_SCSV present, against a
947+ * downgrade-capable server whose maximum is TLS 1.3 - i.e. a genuine downgrade.
948+ * The server must reject it with VERSION_ERROR and send inappropriate_fallback.
949+ */
950+ int test_tls_fallback_scsv (void )
951+ {
952+ EXPECT_DECLS ;
953+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
954+ !defined(WOLFSSL_NO_TLS12 ) && defined(WOLFSSL_TLS13 )
955+ const byte clientHello [] = {
956+ /* record header: handshake, TLS 1.2, length 47 */
957+ 0x16 , 0x03 , 0x03 , 0x00 , 0x2f ,
958+ /* handshake header: ClientHello, length 43 */
959+ 0x01 , 0x00 , 0x00 , 0x2b ,
960+ /* client version: TLS 1.2 (lower than the server's TLS 1.3 max) */
961+ 0x03 , 0x03 ,
962+ /* random: 32 bytes */
963+ 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
964+ 0x08 , 0x09 , 0x0a , 0x0b , 0x0c , 0x0d , 0x0e , 0x0f ,
965+ 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 ,
966+ 0x18 , 0x19 , 0x1a , 0x1b , 0x1c , 0x1d , 0x1e , 0x1f ,
967+ /* session id length: 0 */
968+ 0x00 ,
969+ /* cipher suites length: 4 */
970+ 0x00 , 0x04 ,
971+ /* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
972+ 0x56 , 0x00 , 0x00 , 0x2f ,
973+ /* compression methods: 1 entry, null */
974+ 0x01 , 0x00 ,
975+ };
976+ /* Expected fatal inappropriate_fallback (86) alert on the wire. */
977+ const byte fallbackAlert [] = {
978+ 0x15 , /* alert content type */
979+ 0x03 , 0x03 , /* version: TLS 1.2 */
980+ 0x00 , 0x02 , /* length: 2 */
981+ 0x02 , /* level: fatal */
982+ 0x56 , /* description: inappropriate_fallback (86) */
983+ };
984+ WOLFSSL_CTX * ctx_s = NULL ;
985+ WOLFSSL * ssl_s = NULL ;
986+ struct test_memio_ctx test_ctx ;
987+
988+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
989+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 ,
990+ (const char * )clientHello , sizeof (clientHello )), 0 );
991+ ExpectIntEQ (test_memio_setup (& test_ctx , NULL , & ctx_s , NULL , & ssl_s ,
992+ NULL , wolfSSLv23_server_method ), 0 );
993+ ExpectIntEQ (wolfSSL_accept (ssl_s ), WOLFSSL_FATAL_ERROR );
994+ ExpectIntEQ (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
995+ WC_NO_ERR_TRACE (VERSION_ERROR ));
996+ /* The first record on the wire must be the inappropriate_fallback alert.
997+ * The accept state machine then also queues a generic protocol_version
998+ * alert for the VERSION_ERROR, which RFC 7507 permits. */
999+ ExpectIntGE (test_ctx .c_len , (int )sizeof (fallbackAlert ));
1000+ ExpectBufEQ (test_ctx .c_buff , fallbackAlert , sizeof (fallbackAlert ));
1001+
1002+ wolfSSL_free (ssl_s );
1003+ wolfSSL_CTX_free (ctx_s );
1004+ #endif
1005+ return EXPECT_RESULT ();
1006+ }
1007+
9411008/* RFC 8422 Section 5.1.2 ties the missing-uncompressed-format abort to the
9421009 * server actually negotiating an ECC cipher suite. A client that omits the
9431010 * uncompressed point format but negotiates a NON-ECC suite (here DHE_RSA) must
@@ -988,6 +1055,201 @@ int test_tls12_ec_point_formats_no_uncompressed_non_ecc(void)
9881055 return EXPECT_RESULT ();
9891056}
9901057
1058+ /* DTLS counterpart of test_tls_fallback_scsv, exercising the DTLS branch of the
1059+ * fallback-SCSV check. DTLS version minors decrease as the version increases
1060+ * (DTLS 1.0=0xff, 1.2=0xfd, 1.3=0xfc), so the downgrade comparison is inverted.
1061+ *
1062+ * The ClientHello offers DTLS 1.2 with TLS_FALLBACK_SCSV present, against a
1063+ * downgrade-capable server whose maximum is DTLS 1.3 - i.e. a genuine
1064+ * downgrade. dtlsStateful is forced on so DoClientHello skips the stateless
1065+ * cookie exchange (HelloVerifyRequest) and reaches the cipher-suite parsing.
1066+ */
1067+ int test_dtls_fallback_scsv (void )
1068+ {
1069+ EXPECT_DECLS ;
1070+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && \
1071+ defined(WOLFSSL_DTLS ) && defined(WOLFSSL_DTLS13 )
1072+ const byte clientHello [] = {
1073+ /* DTLS record header: handshake, DTLS 1.2, epoch 0, seq 0, length 56 */
1074+ 0x16 , 0xfe , 0xfd , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
1075+ 0x00 , 0x00 , 0x00 , 0x00 , 0x38 ,
1076+ /* DTLS handshake header: ClientHello, length 44, msg_seq 0,
1077+ * fragment_offset 0, fragment_length 44 */
1078+ 0x01 , 0x00 , 0x00 , 0x2c , 0x00 , 0x00 , 0x00 , 0x00 ,
1079+ 0x00 , 0x00 , 0x00 , 0x2c ,
1080+ /* client version: DTLS 1.2 (lower than the server's DTLS 1.3 max) */
1081+ 0xfe , 0xfd ,
1082+ /* random: 32 bytes */
1083+ 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
1084+ 0x08 , 0x09 , 0x0a , 0x0b , 0x0c , 0x0d , 0x0e , 0x0f ,
1085+ 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 ,
1086+ 0x18 , 0x19 , 0x1a , 0x1b , 0x1c , 0x1d , 0x1e , 0x1f ,
1087+ /* session id length: 0 */
1088+ 0x00 ,
1089+ /* cookie length: 0 */
1090+ 0x00 ,
1091+ /* cipher suites length: 4 */
1092+ 0x00 , 0x04 ,
1093+ /* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
1094+ 0x56 , 0x00 , 0x00 , 0x2f ,
1095+ /* compression methods: 1 entry, null */
1096+ 0x01 , 0x00 ,
1097+ };
1098+ WOLFSSL_CTX * ctx_s = NULL ;
1099+ WOLFSSL * ssl_s = NULL ;
1100+ struct test_memio_ctx test_ctx ;
1101+
1102+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
1103+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 ,
1104+ (const char * )clientHello , sizeof (clientHello )), 0 );
1105+ ExpectIntEQ (test_memio_setup (& test_ctx , NULL , & ctx_s , NULL , & ssl_s ,
1106+ NULL , wolfDTLS_server_method ), 0 );
1107+ /* Skip the stateless cookie exchange so the suites are parsed directly. */
1108+ if (ssl_s != NULL )
1109+ ssl_s -> options .dtlsStateful = 1 ;
1110+ ExpectIntEQ (wolfSSL_accept (ssl_s ), WOLFSSL_FATAL_ERROR );
1111+ ExpectIntEQ (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
1112+ WC_NO_ERR_TRACE (VERSION_ERROR ));
1113+ /* The first record on the wire must be a fatal inappropriate_fallback alert.
1114+ * Check by offset to stay robust against the DTLS epoch/sequence bytes. */
1115+ ExpectIntGE (test_ctx .c_len , 15 );
1116+ ExpectIntEQ ((byte )test_ctx .c_buff [0 ], 0x15 ); /* alert content type */
1117+ ExpectIntEQ ((byte )test_ctx .c_buff [1 ], 0xfe ); /* DTLS 1.2 */
1118+ ExpectIntEQ ((byte )test_ctx .c_buff [2 ], 0xfd );
1119+ ExpectIntEQ ((byte )test_ctx .c_buff [11 ], 0x00 ); /* record length: 2 */
1120+ ExpectIntEQ ((byte )test_ctx .c_buff [12 ], 0x02 );
1121+ ExpectIntEQ ((byte )test_ctx .c_buff [13 ], 0x02 ); /* level: fatal */
1122+ ExpectIntEQ ((byte )test_ctx .c_buff [14 ], 0x56 ); /* inappropriate_fallback */
1123+
1124+ wolfSSL_free (ssl_s );
1125+ wolfSSL_CTX_free (ctx_s );
1126+ #endif
1127+ return EXPECT_RESULT ();
1128+ }
1129+
1130+ /* Negative control for the fallback-SCSV check: a ClientHello that carries
1131+ * TLS_FALLBACK_SCSV but offers the server's highest supported version is NOT a
1132+ * downgrade, so RFC 7507 requires the handshake to proceed (no
1133+ * inappropriate_fallback). Guards against over-aggressive enforcement now that
1134+ * the check is always compiled in.
1135+ */
1136+ int test_tls_fallback_scsv_no_downgrade (void )
1137+ {
1138+ EXPECT_DECLS ;
1139+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && !defined(WOLFSSL_NO_TLS12 )
1140+ /* TLS 1.2 ClientHello with TLS_FALLBACK_SCSV against a TLS 1.2 server, i.e.
1141+ * the client offers the server's maximum version - not a downgrade. */
1142+ const byte clientHello [] = {
1143+ /* record header: handshake, TLS 1.2, length 47 */
1144+ 0x16 , 0x03 , 0x03 , 0x00 , 0x2f ,
1145+ /* handshake header: ClientHello, length 43 */
1146+ 0x01 , 0x00 , 0x00 , 0x2b ,
1147+ /* client version: TLS 1.2 (== server max) */
1148+ 0x03 , 0x03 ,
1149+ /* random: 32 bytes */
1150+ 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
1151+ 0x08 , 0x09 , 0x0a , 0x0b , 0x0c , 0x0d , 0x0e , 0x0f ,
1152+ 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 ,
1153+ 0x18 , 0x19 , 0x1a , 0x1b , 0x1c , 0x1d , 0x1e , 0x1f ,
1154+ /* session id length: 0 */
1155+ 0x00 ,
1156+ /* cipher suites length: 4 */
1157+ 0x00 , 0x04 ,
1158+ /* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
1159+ 0x56 , 0x00 , 0x00 , 0x2f ,
1160+ /* compression methods: 1 entry, null */
1161+ 0x01 , 0x00 ,
1162+ };
1163+ WOLFSSL_CTX * ctx_s = NULL ;
1164+ WOLFSSL * ssl_s = NULL ;
1165+ struct test_memio_ctx test_ctx ;
1166+
1167+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
1168+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 ,
1169+ (const char * )clientHello , sizeof (clientHello )), 0 );
1170+ ExpectIntEQ (test_memio_setup (& test_ctx , NULL , & ctx_s , NULL , & ssl_s ,
1171+ NULL , wolfTLSv1_2_server_method ), 0 );
1172+ /* The handshake must not be rejected for the fallback reason: no
1173+ * VERSION_ERROR and no inappropriate_fallback alert on the wire. Whatever
1174+ * happens afterwards (suite match or not) is not under test here. */
1175+ (void )wolfSSL_accept (ssl_s );
1176+ ExpectIntNE (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
1177+ WC_NO_ERR_TRACE (VERSION_ERROR ));
1178+ ExpectFalse (test_ctx .c_len >= 7 && (byte )test_ctx .c_buff [0 ] == 0x15 &&
1179+ (byte )test_ctx .c_buff [6 ] == 0x56 /* inappropriate_fallback */ );
1180+
1181+ wolfSSL_free (ssl_s );
1182+ wolfSSL_CTX_free (ctx_s );
1183+ #endif
1184+ return EXPECT_RESULT ();
1185+ }
1186+
1187+ /* Negative control for runtime max version handling: the fallback-SCSV ceiling must honor a
1188+ * runtime-disabled version, not the compiled method version. A TLS 1.3 capable
1189+ * method (wolfSSLv23_server_method) has SSL_OP_NO_TLSv1_3 applied at runtime, so
1190+ * its effective maximum is TLS 1.2. A client that legitimately offers TLS 1.2
1191+ * with TLS_FALLBACK_SCSV is therefore NOT downgrading and must not be rejected
1192+ * with inappropriate_fallback (RFC 7507). Before the fix the check compared
1193+ * against ssl->ctx->method->version.minor (still TLS 1.3), wrongly aborting.
1194+ */
1195+ int test_tls_fallback_scsv_no_downgrade_runtime_max (void )
1196+ {
1197+ EXPECT_DECLS ;
1198+ #if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES ) && defined(WOLFSSL_TLS13 ) && \
1199+ !defined(WOLFSSL_NO_TLS12 )
1200+ /* TLS 1.2 ClientHello with TLS_FALLBACK_SCSV. The server's compiled max is
1201+ * TLS 1.3 but TLS 1.3 is disabled at runtime, so TLS 1.2 == effective max -
1202+ * not a downgrade. */
1203+ const byte clientHello [] = {
1204+ /* record header: handshake, TLS 1.2, length 47 */
1205+ 0x16 , 0x03 , 0x03 , 0x00 , 0x2f ,
1206+ /* handshake header: ClientHello, length 43 */
1207+ 0x01 , 0x00 , 0x00 , 0x2b ,
1208+ /* client version: TLS 1.2 (== server effective max) */
1209+ 0x03 , 0x03 ,
1210+ /* random: 32 bytes */
1211+ 0x00 , 0x01 , 0x02 , 0x03 , 0x04 , 0x05 , 0x06 , 0x07 ,
1212+ 0x08 , 0x09 , 0x0a , 0x0b , 0x0c , 0x0d , 0x0e , 0x0f ,
1213+ 0x10 , 0x11 , 0x12 , 0x13 , 0x14 , 0x15 , 0x16 , 0x17 ,
1214+ 0x18 , 0x19 , 0x1a , 0x1b , 0x1c , 0x1d , 0x1e , 0x1f ,
1215+ /* session id length: 0 */
1216+ 0x00 ,
1217+ /* cipher suites length: 4 */
1218+ 0x00 , 0x04 ,
1219+ /* TLS_FALLBACK_SCSV, TLS_RSA_WITH_AES_128_CBC_SHA */
1220+ 0x56 , 0x00 , 0x00 , 0x2f ,
1221+ /* compression methods: 1 entry, null */
1222+ 0x01 , 0x00 ,
1223+ };
1224+ WOLFSSL_CTX * ctx_s = NULL ;
1225+ WOLFSSL * ssl_s = NULL ;
1226+ struct test_memio_ctx test_ctx ;
1227+
1228+ XMEMSET (& test_ctx , 0 , sizeof (test_ctx ));
1229+ ExpectIntEQ (test_memio_inject_message (& test_ctx , 0 ,
1230+ (const char * )clientHello , sizeof (clientHello )), 0 );
1231+ ExpectIntEQ (test_memio_setup (& test_ctx , NULL , & ctx_s , NULL , & ssl_s ,
1232+ NULL , wolfSSLv23_server_method ), 0 );
1233+ /* Lower the effective max to TLS 1.2 at runtime; this updates
1234+ * ssl->version.minor but leaves ssl->ctx->method->version.minor at TLS 1.3,
1235+ * which is exactly the condition that triggered the false positive. */
1236+ if (ssl_s != NULL )
1237+ ExpectIntNE (wolfSSL_set_options (ssl_s , WOLFSSL_OP_NO_TLSv1_3 ), 0 );
1238+ /* The handshake must not be rejected for the fallback reason: no
1239+ * VERSION_ERROR and no inappropriate_fallback alert on the wire. Whatever
1240+ * happens afterwards (suite match or not) is not under test here. */
1241+ (void )wolfSSL_accept (ssl_s );
1242+ ExpectIntNE (wolfSSL_get_error (ssl_s , WOLFSSL_FATAL_ERROR ),
1243+ WC_NO_ERR_TRACE (VERSION_ERROR ));
1244+ ExpectFalse (test_ctx .c_len >= 7 && (byte )test_ctx .c_buff [0 ] == 0x15 &&
1245+ (byte )test_ctx .c_buff [6 ] == 0x56 /* inappropriate_fallback */ );
1246+
1247+ wolfSSL_free (ssl_s );
1248+ wolfSSL_CTX_free (ctx_s );
1249+ #endif
1250+ return EXPECT_RESULT ();
1251+ }
1252+
9911253/* Test that set_curves_list correctly resolves ECC curve names that fall
9921254 * through the kNistCurves table and reach the wc_ecc_get_curve_idx_from_name
9931255 * fallback path. The kNistCurves lookup uses a case-sensitive XSTRNCMP, so
0 commit comments