summaryrefslogtreecommitdiffstats
path: root/tests/tls13
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tests/tls13-cert-key-exchange.c185
-rw-r--r--tests/tls13-cipher-neg.c171
-rw-r--r--tests/tls13-compat-mode.c140
-rw-r--r--tests/tls13-early-data-neg.c479
-rw-r--r--tests/tls13-early-data-neg2.c378
-rw-r--r--tests/tls13-early-data.c856
-rw-r--r--tests/tls13-early-start.c346
-rw-r--r--tests/tls13-rehandshake-cert.c202
-rw-r--r--tests/tls13-server-kx-neg.c309
-rw-r--r--tests/tls13-without-timeout-func.c145
-rw-r--r--tests/tls13/anti_replay.c138
-rw-r--r--tests/tls13/change_cipher_spec.c360
-rw-r--r--tests/tls13/compress-cert-cli.c246
-rw-r--r--tests/tls13/compress-cert-neg.c275
-rw-r--r--tests/tls13/compress-cert-neg2.c216
-rw-r--r--tests/tls13/compress-cert.c286
-rw-r--r--tests/tls13/cookie.c265
-rw-r--r--tests/tls13/ext-parse.h229
-rw-r--r--tests/tls13/hello_retry_request.c255
-rw-r--r--tests/tls13/hello_retry_request_resume.c318
-rw-r--r--tests/tls13/key_limits.c341
-rw-r--r--tests/tls13/key_share.c233
-rw-r--r--tests/tls13/key_update.c276
-rw-r--r--tests/tls13/key_update_multiple.c232
-rw-r--r--tests/tls13/multi-ocsp.c212
-rw-r--r--tests/tls13/no-auto-send-ticket.c315
-rw-r--r--tests/tls13/no-psk-exts.c259
-rw-r--r--tests/tls13/ocsp-client.c223
-rw-r--r--tests/tls13/post-handshake-with-cert-auto.c366
-rw-r--r--tests/tls13/post-handshake-with-cert-pkcs11.c465
-rw-r--r--tests/tls13/post-handshake-with-cert-ticket.c385
-rw-r--r--tests/tls13/post-handshake-with-cert.c384
-rw-r--r--tests/tls13/post-handshake-with-psk.c367
-rw-r--r--tests/tls13/post-handshake-without-cert.c262
-rw-r--r--tests/tls13/prf-early.c471
-rw-r--r--tests/tls13/prf.c376
-rw-r--r--tests/tls13/psk-dumbfw.c337
-rw-r--r--tests/tls13/psk-ext.c205
-rw-r--r--tests/tls13/psk-ke-modes.c170
-rw-r--r--tests/tls13/rnd-check-rollback-val.c305
-rw-r--r--tests/tls13/rnd-rollback-detection.c237
-rw-r--r--tests/tls13/supported_versions.c353
-rw-r--r--tests/tls13/tls12-no-tls13-exts.c242
43 files changed, 12815 insertions, 0 deletions
diff --git a/tests/tls13-cert-key-exchange.c b/tests/tls13-cert-key-exchange.c
new file mode 100644
index 0000000..0eae61c
--- /dev/null
+++ b/tests/tls13-cert-key-exchange.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests the various certificate key exchange methods supported
+ * in gnutls */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "common-cert-key-exchange.h"
+#include "cert-common.h"
+
+void doit(void)
+{
+ global_init();
+
+ server_priority = "NORMAL:+ANON-DH:+ANON-ECDH:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+CURVE-X25519:+SIGN-EDDSA-ED25519";
+ try_x509("TLS 1.3 with ffdhe2048 rsa no-cli-cert / anon on server", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+
+ /** X.509 tests **/
+ server_priority = "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+CURVE-X25519:+SIGN-EDDSA-ED25519";
+
+ /* TLS 1.3 no client cert */
+ try_x509("TLS 1.3 with ffdhe2048 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.3 with ffdhe3072 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE3072", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.3 with ffdhe4096 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE4096", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.3 with secp256r1 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.3 with secp384r1 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP384R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.3 with secp521r1 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP521R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.3 with x25519 rsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+
+ try_with_key_ks("TLS 1.3 with secp256r1 ecdsa no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0, GNUTLS_GROUP_SECP256R1, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+
+ /* Test RSA-PSS cert/key combo issues */
+ try_with_key_ks("TLS 1.3 with x25519 with rsa-pss-sha256 key no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_rsa_pss2_cert, &server_ca3_rsa_pss2_key, NULL, NULL, 0, GNUTLS_GROUP_X25519, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+ try_with_key_ks("TLS 1.3 with x25519 with rsa-pss-sha256 key and 1 sig no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:-SIGN-ALL:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_rsa_pss2_cert, &server_ca3_rsa_pss2_key, NULL, NULL, 0, GNUTLS_GROUP_X25519, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+ try_with_key_ks("TLS 1.3 with x25519 with rsa-pss-sha256 key and rsa-pss-sha384 first sig no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:-SIGN-ALL:+SIGN-RSA-PSS-SHA384:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_rsa_pss2_cert, &server_ca3_rsa_pss2_key, NULL, NULL, 0, GNUTLS_GROUP_X25519, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+ try_with_key_ks("TLS 1.3 with x25519 with rsa-pss-sha256 key and rsa-pss-sha512 first sig no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:-SIGN-ALL:+SIGN-RSA-PSS-SHA512:+SIGN-RSA-PSS-SHA384:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_rsa_pss2_cert, &server_ca3_rsa_pss2_key, NULL, NULL, 0, GNUTLS_GROUP_X25519, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+
+ try_with_key_ks("TLS 1.3 with x25519 rsa-pss/rsa-pss no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:-SIGN-ALL:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, NULL, NULL, 0, GNUTLS_GROUP_X25519, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+ try_with_key_ks("TLS 1.3 with x25519 ed25519 no-cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-ECDSA:-CURVE-ALL:+CURVE-X25519:-SIGN-ALL:+SIGN-EDDSA-ED25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_EDDSA_ED25519, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_eddsa_cert, &server_ca3_eddsa_key, NULL, NULL, 0, GNUTLS_GROUP_X25519, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+
+ /* client authentication */
+ try_with_key("TLS 1.3 with rsa-pss cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_RSA_PSS_SHA256,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_rsa_pss_cert, &cli_ca3_rsa_pss_key, USE_CERT, GNUTLS_CRT_X509, GNUTLS_CRT_X509);
+ try_with_key("TLS 1.3 with rsa cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, USE_CERT, GNUTLS_CRT_X509, GNUTLS_CRT_X509);
+ try_with_key("TLS 1.3 with ecdsa cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256, GNUTLS_SIGN_ECDSA_SECP256R1_SHA256,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, USE_CERT, GNUTLS_CRT_X509, GNUTLS_CRT_X509);
+ try_with_key("TLS 1.3 with x25519 ed25519 cli-cert (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA:-CURVE-ALL:+CURVE-X25519:-SIGN-ALL:+SIGN-EDDSA-ED25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_EDDSA_ED25519, GNUTLS_SIGN_EDDSA_ED25519,
+ &server_ca3_eddsa_cert, &server_ca3_eddsa_key, &server_ca3_eddsa_cert, &server_ca3_eddsa_key, USE_CERT, GNUTLS_CRT_X509, GNUTLS_CRT_X509);
+
+ /* TLS 1.3 mis-matching groups */
+ /* Our policy is to send a key share for the first of each type of groups, so make sure
+ * the server doesn't support them */
+ server_priority = "NORMAL:-GROUP-ALL:-VERS-TLS-ALL:+VERS-TLS1.3:+GROUP-FFDHE3072:+GROUP-SECP521R1",
+
+ try_x509_ks("TLS 1.3 with default key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-FFDHE3072", GNUTLS_KX_DHE_RSA, GNUTLS_GROUP_FFDHE3072);
+ try_x509_ks("TLS 1.3 with ffdhe2048 key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-FFDHE3072", GNUTLS_KX_DHE_RSA, GNUTLS_GROUP_FFDHE3072);
+ try_x509_ks("TLS 1.3 with ffdhe4096 key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE4096:+GROUP-FFDHE3072", GNUTLS_KX_DHE_RSA, GNUTLS_GROUP_FFDHE3072);
+ try_x509_ks("TLS 1.3 with secp256r1 key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-SECP521R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_GROUP_SECP521R1);
+ try_x509_ks("TLS 1.3 with secp384r1 key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP384R1:+GROUP-SECP521R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_GROUP_SECP521R1);
+ try_x509_ks("TLS 1.3 with secp521r1 key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP521R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_GROUP_SECP521R1);
+ try_x509_ks("TLS 1.3 with x25519 -> ffdhe3072 key share (ctype X.509)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-FFDHE3072", GNUTLS_KX_DHE_RSA, GNUTLS_GROUP_FFDHE3072);
+
+ /* TLS 1.2 fallback */
+ server_priority = "NORMAL:-VERS-ALL:+VERS-TLS1.2:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+CURVE-X25519:+SIGN-EDDSA-ED25519",
+
+ try_with_key_ks("TLS 1.2 fallback with x25519 ed25519 no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:-KX-ALL:+ECDHE-ECDSA:-CURVE-ALL:+CURVE-X25519:-SIGN-ALL:+SIGN-EDDSA-ED25519", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_EDDSA_ED25519, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_eddsa_cert, &server_ca3_eddsa_key, NULL, NULL, 0, 0, GNUTLS_CRT_X509, GNUTLS_CRT_UNKNOWN);
+ try_x509("TLS 1.2 fallback with secp521r1 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:-GROUP-ALL:+GROUP-SECP521R1", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_x509("TLS 1.2 fallback with ffdhe2048 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:-KX-ALL:+DHE-RSA:-GROUP-ALL:+GROUP-FFDHE2048", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+
+ /** Raw public-key tests **/
+ server_priority = "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+CURVE-X25519:+SIGN-EDDSA-ED25519:+CTYPE-ALL";
+
+ try_rawpk("TLS 1.3 with ffdhe2048 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+CTYPE-ALL", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_rawpk("TLS 1.3 with ffdhe3072 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE3072:+CTYPE-ALL", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_rawpk("TLS 1.3 with ffdhe4096 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE4096:+CTYPE-ALL", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_rawpk("TLS 1.3 with secp256r1 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+CTYPE-ALL", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_rawpk("TLS 1.3 with secp384r1 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP384R1:+CTYPE-ALL", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_rawpk("TLS 1.3 with secp521r1 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP521R1:+CTYPE-ALL", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ try_rawpk("TLS 1.3 with x25519 rsa no-cli-cert (ctype Raw PK)", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+CTYPE-ALL", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+
+
+ /** Illegal setups **/
+ server_priority = "NORMAL:-VERS-ALL:+VERS-TLS1.3";
+ try_with_key_fail("TLS 1.3 with rsa cert and only RSA-PSS sig algos in client",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-SIGN-ALL:+SIGN-RSA-PSS-SHA256:+SIGN-RSA-PSS-SHA384:+SIGN-RSA-PSS-SHA512",
+ GNUTLS_E_NO_CIPHER_SUITES, GNUTLS_E_AGAIN,
+ &server_ca3_localhost_cert, &server_ca3_key, NULL, NULL);
+
+ try_with_key_fail("TLS 1.3 with x25519 with rsa-pss cert and RSAE signatures",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256:+SIGN-RSA-PSS-RSAE-SHA384",
+ GNUTLS_E_NO_CIPHER_SUITES, GNUTLS_E_AGAIN,
+ &server_ca3_rsa_pss2_cert, &server_ca3_rsa_pss2_key, NULL, NULL);
+
+ server_priority = NULL;
+ try_with_key_fail("TLS 1.3 with rsa cert and only RSA-PSS sig algos",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-SIGN-ALL:+SIGN-RSA-PSS-SHA256:+SIGN-RSA-PSS-SHA384:+SIGN-RSA-PSS-SHA512",
+ GNUTLS_E_NO_CIPHER_SUITES, GNUTLS_E_AGAIN,
+ &server_ca3_localhost_cert, &server_ca3_key, NULL, NULL);
+
+ try_with_key_fail("TLS 1.3 with rsa-pss cert and rsa cli cert with only RSA-PSS sig algos",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-SIGN-ALL:+SIGN-RSA-PSS-SHA256:+SIGN-RSA-PSS-SHA384:+SIGN-RSA-PSS-SHA512",
+ GNUTLS_E_CERTIFICATE_REQUIRED, GNUTLS_E_SUCCESS,
+ &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, &cli_ca3_cert, &cli_ca3_key);
+
+ try_with_key_fail("TLS 1.3 with rsa encryption cert",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3",
+ GNUTLS_E_NO_CIPHER_SUITES, GNUTLS_E_AGAIN,
+ &server_ca3_localhost_rsa_decrypt_cert, &server_ca3_key, NULL, NULL);
+
+ try_with_key_fail("TLS 1.3 and TLS 1.2 with rsa encryption cert",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2",
+ GNUTLS_E_SUCCESS, GNUTLS_E_SUCCESS,
+ &server_ca3_localhost_rsa_decrypt_cert, &server_ca3_key, NULL, NULL);
+
+ try_with_key_fail("TLS 1.3 with (forced) rsa encryption cert - client should detect",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:%DEBUG_ALLOW_KEY_USAGE_VIOLATIONS",
+ GNUTLS_E_AGAIN, GNUTLS_E_KEY_USAGE_VIOLATION,
+ &server_ca3_localhost_rsa_decrypt_cert, &server_ca3_key, NULL, NULL);
+
+ try_with_key_fail("TLS 1.3 with client rsa encryption cert",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3",
+ GNUTLS_E_AGAIN, GNUTLS_E_INSUFFICIENT_CREDENTIALS,
+ &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, &server_ca3_localhost_rsa_decrypt_cert, &server_ca3_key);
+
+ try_with_key_fail("TLS 1.3 with (forced) client rsa encryption cert - server should detect",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:%DEBUG_ALLOW_KEY_USAGE_VIOLATIONS",
+ GNUTLS_E_KEY_USAGE_VIOLATION, GNUTLS_E_SUCCESS,
+ &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, &server_ca3_localhost_rsa_decrypt_cert, &server_ca3_key);
+
+ try_with_rawpk_key_fail("rawpk TLS 1.3 with rsa encryption cert",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:+CTYPE-RAWPK",
+ GNUTLS_E_NO_CIPHER_SUITES, GNUTLS_E_AGAIN,
+ &rawpk_public_key1, &rawpk_private_key1, GNUTLS_KEY_KEY_ENCIPHERMENT, NULL, NULL, 0);
+
+ try_with_rawpk_key_fail("rawpk TLS 1.3 and TLS 1.2 with rsa encryption cert",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+CTYPE-RAWPK",
+ GNUTLS_E_SUCCESS, GNUTLS_E_SUCCESS,
+ &rawpk_public_key1, &rawpk_private_key1, GNUTLS_KEY_KEY_ENCIPHERMENT, NULL, NULL, 0);
+
+ try_with_rawpk_key_fail("rawpk TLS 1.3 with client rsa encryption cert",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:+CTYPE-RAWPK",
+ GNUTLS_E_AGAIN, GNUTLS_E_INSUFFICIENT_CREDENTIALS,
+ &rawpk_public_key2, &rawpk_private_key2, 0, &rawpk_public_key1, &rawpk_private_key1, GNUTLS_KEY_KEY_ENCIPHERMENT);
+
+ /* we do not test TLS 1.3 with (forced) rsa encryption cert - client should detect, because
+ * there is no way under raw public keys for the client or server to know the intended type. */
+
+ gnutls_global_deinit();
+}
diff --git a/tests/tls13-cipher-neg.c b/tests/tls13-cipher-neg.c
new file mode 100644
index 0000000..16a8883
--- /dev/null
+++ b/tests/tls13-cipher-neg.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests the ciphersuite negotiation for various key exchange
+ * methods and options under TLS1.3. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "cert-common.h"
+#include "eagain-common.h"
+
+#include "cipher-neg-common.c"
+
+/* We remove the ECDHE and DHE key exchanges as they impose additional
+ * rules in the sorting of groups.
+ */
+#define SPRIO "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+#define CPRIO "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+
+test_case_st tests[] = {
+ {
+ .name = "server TLS 1.3: NULL (server - exp fallback)",
+ .not_on_fips = 1,
+ .cipher = GNUTLS_CIPHER_NULL,
+ .server_prio = SPRIO":+VERS-TLS1.2:-CIPHER-ALL:+NULL:+CIPHER-ALL:%SERVER_PRECEDENCE:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-ALL",
+ .client_prio = CPRIO":+VERS-TLS1.2:+NULL:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP384R1:+GROUP-SECP521R1:+GROUP-SECP256R1",
+ .desc = "(TLS1.2)-(ECDHE-SECP256R1)-(ECDSA-SHA256)-(NULL)-(SHA1)",
+ },
+ {
+ .name = "client TLS 1.3: NULL (client)",
+ .not_on_fips = 1,
+ .cipher = GNUTLS_CIPHER_NULL,
+ .server_prio = SPRIO":+VERS-TLS1.2:+NULL:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP384R1:+GROUP-SECP521R1:+GROUP-SECP256R1",
+ .client_prio = CPRIO":-CIPHER-ALL:+NULL:+CIPHER-ALL:+VERS-TLS1.2:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-ALL",
+ .desc = "(TLS1.2)-(ECDHE-SECP256R1)-(ECDSA-SHA256)-(NULL)-(SHA1)",
+ },
+ {
+ .name = "server TLS 1.3: AES-128-GCM with SECP256R1 (server)",
+ .cipher = GNUTLS_CIPHER_AES_128_GCM,
+ .group = GNUTLS_GROUP_SECP256R1,
+ .server_prio = SPRIO":-CIPHER-ALL:+AES-128-GCM:+CIPHER-ALL:%SERVER_PRECEDENCE:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-ALL",
+ .client_prio = CPRIO":+AES-128-GCM:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP384R1:+GROUP-SECP521R1:+GROUP-SECP256R1",
+ .desc = "(TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)",
+ },
+ {
+ .name = "both TLS 1.3: AES-128-GCM with X25519 (server)",
+ .cipher = GNUTLS_CIPHER_AES_128_GCM,
+ .group = GNUTLS_GROUP_X25519,
+ .server_prio = SPRIO":-CIPHER-ALL:+AES-128-GCM:+CIPHER-ALL:%SERVER_PRECEDENCE:-GROUP-ALL:+GROUP-X25519:+GROUP-ALL",
+ .client_prio = CPRIO":+AES-128-GCM:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP384R1:+GROUP-SECP521R1:+GROUP-SECP256R1:+GROUP-ALL",
+ .desc = "(TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)",
+ },
+ {
+ .name = "client TLS 1.3: AES-128-GCM with SECP256R1 (client)",
+ .cipher = GNUTLS_CIPHER_AES_128_GCM,
+ .group = GNUTLS_GROUP_SECP256R1,
+ .server_prio = SPRIO":+AES-128-GCM:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP384R1:+GROUP-SECP521R1:+GROUP-SECP256R1",
+ .client_prio = CPRIO":-CIPHER-ALL:+AES-128-GCM:+CIPHER-ALL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-ALL",
+ .desc = "(TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)",
+ },
+ {
+ .name = "both TLS 1.3: AES-128-GCM with X25519 (client)",
+ .cipher = GNUTLS_CIPHER_AES_128_GCM,
+ .group = GNUTLS_GROUP_X25519,
+ .server_prio = SPRIO":+AES-128-GCM:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP384R1:+GROUP-SECP521R1:+GROUP-SECP256R1:+GROUP-ALL",
+ .client_prio = CPRIO":-CIPHER-ALL:+AES-128-GCM:+CIPHER-ALL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+GROUP-ALL",
+ .desc = "(TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM)",
+ },
+ {
+ .name = "server TLS 1.3: AES-128-CCM and FFDHE2048 (server)",
+ .cipher = GNUTLS_CIPHER_AES_128_CCM,
+ .group = GNUTLS_GROUP_FFDHE2048,
+ .server_prio = SPRIO":-CIPHER-ALL:+AES-128-CCM:+CIPHER-ALL:%SERVER_PRECEDENCE:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-ALL",
+ .client_prio = CPRIO":+AES-128-CCM",
+ .desc = "(TLS1.3)-(DHE-FFDHE2048)-(RSA-PSS-RSAE-SHA256)-(AES-128-CCM)",
+ },
+ {
+ .name = "both TLS 1.3: AES-128-CCM and FFDHE 2048 (server)",
+ .cipher = GNUTLS_CIPHER_AES_128_CCM,
+ .group = GNUTLS_GROUP_FFDHE2048,
+ .server_prio = SPRIO":-CIPHER-ALL:+AES-128-CCM:+CIPHER-ALL:%SERVER_PRECEDENCE:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-ALL",
+ .client_prio = CPRIO":+AES-128-CCM:+VERS-TLS1.3",
+ .desc = "(TLS1.3)-(DHE-FFDHE2048)-(RSA-PSS-RSAE-SHA256)-(AES-128-CCM)",
+ },
+ {
+ .name = "client TLS 1.3: AES-128-CCM and FFDHE 2048 (client)",
+ .cipher = GNUTLS_CIPHER_AES_128_CCM,
+ .group = GNUTLS_GROUP_FFDHE2048,
+ .server_prio = SPRIO":+AES-128-CCM",
+ .client_prio = CPRIO":-CIPHER-ALL:+AES-128-CCM:+CIPHER-ALL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-ALL",
+ .desc = "(TLS1.3)-(DHE-FFDHE2048)-(RSA-PSS-RSAE-SHA256)-(AES-128-CCM)",
+ },
+ {
+ .name = "both TLS 1.3: AES-128-CCM and FFDHE 2048 (client)",
+ .cipher = GNUTLS_CIPHER_AES_128_CCM,
+ .group = GNUTLS_GROUP_FFDHE2048,
+ .server_prio = SPRIO":+AES-128-CCM:+VERS-TLS1.3",
+ .client_prio = CPRIO":-CIPHER-ALL:+AES-128-CCM:+CIPHER-ALL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-ALL",
+ .desc = "(TLS1.3)-(DHE-FFDHE2048)-(RSA-PSS-RSAE-SHA256)-(AES-128-CCM)",
+ },
+ {
+ .name = "server TLS 1.3: CHACHA20-POLY (server)",
+ .cipher = GNUTLS_CIPHER_CHACHA20_POLY1305,
+ .not_on_fips = 1,
+ .server_prio = SPRIO":-CIPHER-ALL:+CHACHA20-POLY1305:+CIPHER-ALL:%SERVER_PRECEDENCE",
+ .client_prio = CPRIO":+CHACHA20-POLY1305",
+ .desc = "(TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(CHACHA20-POLY1305)",
+ },
+ {
+ .name = "both TLS 1.3: CHACHA20-POLY (server)",
+ .cipher = GNUTLS_CIPHER_CHACHA20_POLY1305,
+ .not_on_fips = 1,
+ .server_prio = SPRIO":-CIPHER-ALL:+CHACHA20-POLY1305:+CIPHER-ALL:%SERVER_PRECEDENCE",
+ .client_prio = CPRIO":+CHACHA20-POLY1305:+VERS-TLS1.3",
+ .desc = "(TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(CHACHA20-POLY1305)",
+ },
+ {
+ .name = "client TLS 1.3: CHACHA20-POLY (client)",
+ .cipher = GNUTLS_CIPHER_CHACHA20_POLY1305,
+ .not_on_fips = 1,
+ .server_prio = SPRIO":+CHACHA20-POLY1305",
+ .client_prio = CPRIO":-CIPHER-ALL:+CHACHA20-POLY1305:+CIPHER-ALL",
+ .desc = "(TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(CHACHA20-POLY1305)",
+ },
+ {
+ .name = "both TLS 1.3: CHACHA20-POLY (client)",
+ .cipher = GNUTLS_CIPHER_CHACHA20_POLY1305,
+ .not_on_fips = 1,
+ .server_prio = SPRIO":+CHACHA20-POLY1305",
+ .client_prio = CPRIO":-CIPHER-ALL:+CHACHA20-POLY1305:+CIPHER-ALL",
+ .desc = "(TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(CHACHA20-POLY1305)",
+ }
+};
+
+void doit(void)
+{
+ unsigned i;
+ global_init();
+
+ for (i=0;i<sizeof(tests)/sizeof(tests[0]);i++) {
+ try(&tests[i]);
+ }
+
+ gnutls_global_deinit();
+}
diff --git a/tests/tls13-compat-mode.c b/tests/tls13-compat-mode.c
new file mode 100644
index 0000000..e8f9980
--- /dev/null
+++ b/tests/tls13-compat-mode.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GnuTLS. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include "cert-common.h"
+#include "eagain-common.h"
+#include "utils.h"
+
+/* This tests TLS 1.3 middlebox compatibility mode. */
+
+#define COMPAT_PRIO "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+#define NO_COMPAT_PRIO COMPAT_PRIO ":%DISABLE_TLS13_COMPAT_MODE"
+
+#define HANDSHAKE_SESSION_ID_POS 34
+
+struct data {
+ bool compat;
+};
+
+static int
+handshake_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming,
+ const gnutls_datum_t *msg)
+{
+ unsigned pos;
+ struct data *data;
+ uint8_t s;
+
+ assert(htype == GNUTLS_HANDSHAKE_CLIENT_HELLO);
+ assert(msg->size >= HANDSHAKE_SESSION_ID_POS);
+
+ data = gnutls_session_get_ptr(session);
+
+ pos = HANDSHAKE_SESSION_ID_POS;
+ if (pos + 1 > msg->size)
+ fail("error\n");
+ s = msg->data[pos];
+
+ if (data->compat && s == 0) {
+ fail("empty session ID while compat mode is enabled\n");
+ } else if (!data->compat && s > 0) {
+ fail("non-empty session ID while compat mode is disabled\n");
+ }
+
+ return 0;
+}
+
+static void
+test(const char *name, bool client_compat, bool server_compat)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ struct data data;
+ int cret = GNUTLS_E_AGAIN;
+
+ success("%s\n", name);
+
+ /* Init server */
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0);
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+ assert(gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, serverx509cred) >= 0);
+ assert(gnutls_priority_set_direct(server,
+ server_compat ?
+ COMPAT_PRIO : NO_COMPAT_PRIO,
+ NULL) >= 0);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0);
+ assert(gnutls_init(&client, GNUTLS_CLIENT) >= 0);
+ assert(gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred) >= 0);
+
+ assert(gnutls_priority_set_direct(client,
+ client_compat ?
+ COMPAT_PRIO : NO_COMPAT_PRIO,
+ NULL) >= 0);
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+ data.compat = client_compat;
+ gnutls_session_set_ptr(client, &data);
+ gnutls_handshake_set_hook_function(client, GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ GNUTLS_HOOK_POST,
+ handshake_callback);
+
+ HANDSHAKE(client, server);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+}
+
+void doit(void)
+{
+ test("client compat, server compat", true, true);
+ test("client compat, server non-compat", true, false);
+ test("client non-compat, server compat", false, true);
+ test("client non-compat, server non-compat", false, false);
+}
diff --git a/tests/tls13-early-data-neg.c b/tests/tls13-early-data-neg.c
new file mode 100644
index 0000000..075c21f
--- /dev/null
+++ b/tests/tls13-early-data-neg.c
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2012-2018 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main(void)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "virt-time.h"
+
+/* This program tests the robustness of record sending with padding.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+#define SESSIONS 3
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+#define EARLY_MSG "Hello TLS, it's early"
+#define PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+
+static const
+gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+
+static int gnutls_rnd_works;
+
+int __attribute__ ((visibility ("protected")))
+gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len)
+{
+ gnutls_rnd_works = 1;
+
+ memset(data, 0xff, len);
+
+ /* Flip the first byte to avoid infinite loop in the RSA
+ * blinding code of Nettle */
+ if (len > 0)
+ memset(data, 0x0, 1);
+ return 0;
+}
+
+gnutls_datum_t client_hello_msg = {NULL, 0};
+
+static int handshake_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ assert(client_hello_msg.data == NULL);
+
+ client_hello_msg.data = gnutls_malloc(msg->size+9);
+ assert(client_hello_msg.data != NULL);
+ client_hello_msg.size = msg->size+9;
+ memcpy(client_hello_msg.data+9, msg->data, msg->size);
+ /* reconstruct record header */
+ client_hello_msg.data[0] = 22;
+ client_hello_msg.data[1] = 3;
+ client_hello_msg.data[2] = 3;
+ client_hello_msg.data[3] = (msg->size+4) >> 8;
+ client_hello_msg.data[4] = (msg->size+4);
+
+ client_hello_msg.data[5] = GNUTLS_HANDSHAKE_CLIENT_HELLO;
+ client_hello_msg.data[6] = 0;
+ client_hello_msg.data[7] = msg->size >> 8;
+ client_hello_msg.data[8] = msg->size;
+
+ return 0;
+}
+
+static void client(int sds[])
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ int t;
+ gnutls_datum_t session_data = {NULL, 0};
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ /* Generate the same ob_ticket_age value, which affects the
+ * binder calculation.
+ */
+ virt_time_init();
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ for (t = 0; t < SESSIONS-1; t++) {
+ int sd = sds[t];
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
+ assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, sd);
+
+ if (t > 0) {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ assert(gnutls_record_send_early_data(session, EARLY_MSG, sizeof(EARLY_MSG)) >= 0);
+ assert(gnutls_handshake_set_random(session, &hrnd) >= 0);
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ GNUTLS_HOOK_POST,
+ handshake_callback);
+ }
+
+ /* Perform the TLS handshake
+ */
+ gnutls_handshake_set_timeout(session, get_timeout());
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed\n");
+ gnutls_perror(ret);
+ exit(1);
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (t == 0) {
+ /* get the session data size */
+ ret =
+ gnutls_session_get_data2(session,
+ &session_data);
+ if (ret < 0)
+ fail("client: Getting resume data failed\n");
+ }
+
+ if (t > 0) {
+ if (!gnutls_session_is_resumed(session)) {
+ fail("client: session_is_resumed error (%d)\n", t);
+ }
+ }
+
+ gnutls_record_send(session, MSG, strlen(MSG));
+
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret == GNUTLS_E_AGAIN);
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+
+ gnutls_deinit(session);
+ }
+
+ assert(client_hello_msg.data != NULL);
+
+ ret = send(sds[SESSIONS-1], client_hello_msg.data, client_hello_msg.size, 0);
+ assert(ret == (int)client_hello_msg.size);
+
+ end:
+ gnutls_free(client_hello_msg.data);
+ gnutls_free(session_data.data);
+ gnutls_certificate_free_credentials(x509_cred);
+}
+
+
+static pid_t child;
+
+#define MAX_CLIENT_HELLO_RECORDED 10
+
+struct storage_st {
+ gnutls_datum_t entries[MAX_CLIENT_HELLO_RECORDED];
+ size_t num_entries;
+};
+
+static int
+storage_add(void *ptr, time_t expires, const gnutls_datum_t *key, const gnutls_datum_t *value)
+{
+ struct storage_st *storage = ptr;
+ gnutls_datum_t *datum;
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++) {
+ if (key->size == storage->entries[i].size &&
+ memcmp(storage->entries[i].data, key->data, key->size) == 0) {
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+ }
+
+ /* If the maximum number of ClientHello exceeded, reject early
+ * data until next time.
+ */
+ if (storage->num_entries == MAX_CLIENT_HELLO_RECORDED)
+ return GNUTLS_E_DB_ERROR;
+
+ datum = &storage->entries[storage->num_entries];
+ datum->data = gnutls_malloc(key->size);
+ if (!datum->data)
+ return GNUTLS_E_MEMORY_ERROR;
+ memcpy(datum->data, key->data, key->size);
+ datum->size = key->size;
+
+ storage->num_entries++;
+
+ return 0;
+}
+
+static void
+storage_clear(struct storage_st *storage)
+{
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++)
+ gnutls_free(storage->entries[i].data);
+ storage->num_entries = 0;
+}
+
+static void server(int sds[])
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_datum_t session_ticket_key = { NULL, 0 };
+ struct storage_st storage;
+ gnutls_anti_replay_t anti_replay;
+ int t;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+ memset(&storage, 0, sizeof(storage));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_session_ticket_key_generate(&session_ticket_key);
+
+ ret = gnutls_anti_replay_init(&anti_replay);
+ if (ret < 0)
+ fail("server: failed to initialize anti-replay\n");
+ gnutls_anti_replay_set_add_function(anti_replay, storage_add);
+ gnutls_anti_replay_set_ptr(anti_replay, &storage);
+
+ for (t = 0; t < SESSIONS; t++) {
+ int sd = sds[t];
+
+ success("=== session %d ===\n", t);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_ENABLE_EARLY_DATA)>=0);
+
+ assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
+
+ gnutls_anti_replay_enable(session, anti_replay);
+
+ gnutls_transport_set_int(session, sd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (t == SESSIONS-1) {
+ /* duplicate data expected */
+ if (ret < 0 && !(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA)) {
+ success("we detected the duplicate data!\n");
+ close(sd);
+ gnutls_deinit(session);
+ goto cleanup;
+ } else {
+ fail("server: duplicate early data was not detected (%d)\n", t);
+ }
+ }
+
+ if (ret < 0) {
+ close(sd);
+ gnutls_deinit(session);
+ fail("server[%d]: Handshake has failed (%s)\n\n",
+ t, gnutls_strerror(ret));
+ return;
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (t > 0) {
+ if (!gnutls_session_is_resumed(session)) {
+ fail("server: session_is_resumed error (%d)\n", t);
+ }
+
+ /* as we reuse the same ticket twice, expect
+ * early data only on the first resumption */
+ if (t == 1) {
+ if (gnutls_rnd_works) {
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA)) {
+ fail("server: early data is not received (%d)\n", t);
+ }
+ } else {
+ success("server: gnutls_rnd() could not be overridden, skip checking replay (%d)\n", t);
+ }
+
+ ret = gnutls_record_recv_early_data(session, buffer, sizeof(buffer));
+ if (ret < 0) {
+ fail("server: failed to retrieve early data: %s\n",
+ gnutls_strerror(ret));
+ }
+
+ if (ret != sizeof(EARLY_MSG) || memcmp(buffer, EARLY_MSG, ret))
+ fail("server: early data mismatch\n");
+ } else {
+ if (gnutls_rnd_works) {
+ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA) {
+ fail("server: early data is not rejected (%d)\n", t);
+ }
+ } else {
+ success("server: gnutls_rnd() could not be overridden, skip checking replay (%d)\n", t);
+ }
+ }
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ kill(child, SIGTERM);
+ fail("server: Received corrupted data(%d). Closing...\n", ret);
+ break;
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ }
+ }
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+
+ gnutls_deinit(session);
+ }
+
+ cleanup:
+ gnutls_anti_replay_deinit(anti_replay);
+
+ storage_clear(&storage);
+
+ gnutls_free(session_ticket_key.data);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ if (debug)
+ success("server: finished\n");
+}
+
+void doit(void)
+{
+ int client_sds[SESSIONS], server_sds[SESSIONS];
+ int i, status = 0;
+ int ret;
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ for (i = 0; i < SESSIONS; i++) {
+ int sockets[2];
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ server_sds[i] = sockets[0];
+ client_sds[i] = sockets[1];
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ for (i = 0; i < SESSIONS; i++)
+ close(client_sds[i]);
+ server(server_sds);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ for (i = 0; i < SESSIONS; i++)
+ close(server_sds[i]);
+ client(client_sds);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13-early-data-neg2.c b/tests/tls13-early-data-neg2.c
new file mode 100644
index 0000000..37b6c3e
--- /dev/null
+++ b/tests/tls13-early-data-neg2.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2012-2018 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main(void)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "virt-time.h"
+
+/* This program checks that early data is refused upon resumption failure.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+#define SESSIONS 2
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+#define EARLY_MSG "Hello TLS, it's early"
+#define PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3"
+
+static void client(int sds[])
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ int t;
+ gnutls_datum_t session_data = {NULL, 0};
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ /* Generate the same ob_ticket_age value, which affects the
+ * binder calculation.
+ */
+ virt_time_init();
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ for (t = 0; t < SESSIONS; t++) {
+ int sd = sds[t];
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
+ assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, sd);
+
+ if (t > 0) {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ assert(gnutls_record_send_early_data(session, EARLY_MSG, sizeof(EARLY_MSG)) >= 0);
+ }
+
+ /* Perform the TLS handshake
+ */
+ gnutls_handshake_set_timeout(session, get_timeout());
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n",
+ gnutls_strerror(ret));
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (t == 0) {
+ /* get the session data size */
+ ret =
+ gnutls_session_get_data2(session,
+ &session_data);
+ if (ret < 0)
+ fail("client: Getting resume data failed\n");
+ }
+
+ if (gnutls_session_is_resumed(session)) {
+ fail("client: Session unexpectedly resumed (%d)\n", t);
+ }
+
+ gnutls_record_send(session, MSG, strlen(MSG));
+
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret == GNUTLS_E_AGAIN);
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+
+ gnutls_deinit(session);
+ }
+
+ end:
+ gnutls_free(session_data.data);
+ gnutls_certificate_free_credentials(x509_cred);
+}
+
+
+static pid_t child;
+
+#define MAX_CLIENT_HELLO_RECORDED 10
+
+struct storage_st {
+ gnutls_datum_t entries[MAX_CLIENT_HELLO_RECORDED];
+ size_t num_entries;
+};
+
+static int
+storage_add(void *ptr, time_t expires, const gnutls_datum_t *key, const gnutls_datum_t *value)
+{
+ struct storage_st *storage = ptr;
+ gnutls_datum_t *datum;
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++) {
+ if (key->size == storage->entries[i].size &&
+ memcmp(storage->entries[i].data, key->data, key->size) == 0) {
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+ }
+
+ /* If the maximum number of ClientHello exceeded, reject early
+ * data until next time.
+ */
+ if (storage->num_entries == MAX_CLIENT_HELLO_RECORDED)
+ return GNUTLS_E_DB_ERROR;
+
+ datum = &storage->entries[storage->num_entries];
+ datum->data = gnutls_malloc(key->size);
+ if (!datum->data)
+ return GNUTLS_E_MEMORY_ERROR;
+ memcpy(datum->data, key->data, key->size);
+ datum->size = key->size;
+
+ storage->num_entries++;
+
+ return 0;
+}
+
+static void
+storage_clear(struct storage_st *storage)
+{
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++)
+ gnutls_free(storage->entries[i].data);
+ storage->num_entries = 0;
+}
+
+static void server(int sds[])
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_datum_t session_ticket_key = { NULL, 0 };
+ struct storage_st storage;
+ gnutls_anti_replay_t anti_replay;
+ int t;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+ memset(&storage, 0, sizeof(storage));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ ret = gnutls_anti_replay_init(&anti_replay);
+ if (ret < 0)
+ fail("server: failed to initialize anti-replay\n");
+ gnutls_anti_replay_set_add_function(anti_replay, storage_add);
+ gnutls_anti_replay_set_ptr(anti_replay, &storage);
+
+ for (t = 0; t < SESSIONS; t++) {
+ int sd = sds[t];
+
+ success("=== session %d ===\n", t);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_ENABLE_EARLY_DATA)>=0);
+
+ assert(gnutls_priority_set_direct(session, PRIORITY, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ /* Intentionally overwrite the previous key to cause resumption
+ * failure. */
+ gnutls_session_ticket_key_generate(&session_ticket_key);
+
+ gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
+
+ gnutls_anti_replay_enable(session, anti_replay);
+
+ gnutls_transport_set_int(session, sd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ gnutls_deinit(session);
+ fail("server[%d]: Handshake has failed (%s)\n\n",
+ t, gnutls_strerror(ret));
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (gnutls_session_is_resumed(session)) {
+ fail("server: Session unexpectedly resumed (%d)\n", t);
+ }
+
+ if (gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA) {
+ fail("server: Unexpected early data received (%d)\n", t);
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ kill(child, SIGTERM);
+ fail("server: Received corrupted data(%d). Closing...\n", ret);
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ }
+ }
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+
+ gnutls_deinit(session);
+
+ gnutls_free(session_ticket_key.data);
+ }
+
+ gnutls_anti_replay_deinit(anti_replay);
+
+ storage_clear(&storage);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ if (debug)
+ success("server: finished\n");
+}
+
+void doit(void)
+{
+ int client_sds[SESSIONS], server_sds[SESSIONS];
+ int i, status = 0;
+ int ret;
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ for (i = 0; i < SESSIONS; i++) {
+ int sockets[2];
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ server_sds[i] = sockets[0];
+ client_sds[i] = sockets[1];
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ for (i = 0; i < SESSIONS; i++)
+ close(client_sds[i]);
+ server(server_sds);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ for (i = 0; i < SESSIONS; i++)
+ close(server_sds[i]);
+ client(client_sds);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13-early-data.c b/tests/tls13-early-data.c
new file mode 100644
index 0000000..381ecbd
--- /dev/null
+++ b/tests/tls13-early-data.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2012-2018 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main(void)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "virt-time.h"
+#define MIN(x,y) (((x)<(y))?(x):(y))
+
+#define TRACE_CLIENT 1
+#define TRACE_SERVER 2
+
+/* To reproduce the entries in {client,server}-secrets.h, set this to
+ * either TRACE_CLIENT or TRACE_SERVER.
+ */
+#define TRACE 0
+
+/* This program tests the robustness of record sending with padding.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+/* A very basic TLS client.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+#define EARLY_MSG "Hello TLS, it's early"
+
+extern unsigned int _gnutls_global_version;
+
+/* This test makes connection 3 times with different ciphersuites:
+ * first with TLS_AES_128_GCM_SHA256, then
+ * TLS_CHACHA20_POLY1305_SHA256 two times. The reason for doing this
+ * is to check that the early data is encrypted with the ciphersuite
+ * selected during the initial handshake, not the resuming handshakes.
+ */
+#define SESSIONS 3
+#define TLS13_AES_128_GCM "NONE:+VERS-TLS1.3:+AES-128-GCM:+AEAD:+SIGN-RSA-PSS-RSAE-SHA384:+GROUP-SECP256R1"
+#define TLS13_CHACHA20_POLY1305 "NONE:+VERS-TLS1.3:+CHACHA20-POLY1305:+AEAD:+SIGN-RSA-PSS-RSAE-SHA384:+GROUP-SECP256R1"
+
+static const
+gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+static const
+gnutls_datum_t hsrnd = {(void*)"\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+
+static int gnutls_rnd_works;
+
+int __attribute__ ((visibility ("protected")))
+gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len)
+{
+ gnutls_rnd_works = 1;
+
+ memset(data, 0xff, len);
+
+ /* Flip the first byte to avoid infinite loop in the RSA
+ * blinding code of Nettle */
+ if (len > 0)
+ memset(data, 0x0, 1);
+ return 0;
+}
+
+#define MAX_SECRET_SIZE 64
+#define MAX_SECRET_COUNT 10
+
+struct secret {
+ gnutls_record_encryption_level_t level;
+ size_t secret_size;
+ const uint8_t *secret_read;
+ const uint8_t *secret_write;
+ uint8_t secret_read_buf[MAX_SECRET_SIZE];
+ uint8_t secret_write_buf[MAX_SECRET_SIZE];
+};
+
+#include "client-secrets.h"
+#include "server-secrets.h"
+
+struct secrets_expected {
+ const struct secret *secrets;
+ size_t count;
+};
+
+#define SIZEOF(array) (sizeof(array) / sizeof(array[0]))
+
+static const struct secrets_expected client_normal[SESSIONS] = {
+ { client_normal_0, SIZEOF(client_normal_0) },
+ { client_normal_1, SIZEOF(client_normal_1) },
+ { client_normal_2, SIZEOF(client_normal_2) },
+};
+
+static const struct secrets_expected client_small[SESSIONS] = {
+ { client_small_0, SIZEOF(client_small_0) },
+ { client_small_1, SIZEOF(client_small_1) },
+ { client_small_2, SIZEOF(client_small_2) },
+};
+
+static const struct secrets_expected client_empty[SESSIONS] = {
+ { client_empty_0, SIZEOF(client_empty_0) },
+ { client_empty_1, SIZEOF(client_empty_1) },
+ { client_empty_2, SIZEOF(client_empty_2) },
+};
+
+static const struct secrets_expected client_explicit[SESSIONS] = {
+ { client_explicit_0, SIZEOF(client_explicit_0) },
+ { client_explicit_1, SIZEOF(client_explicit_1) },
+ { client_explicit_2, SIZEOF(client_explicit_2) },
+};
+
+static const struct secrets_expected server_normal[SESSIONS] = {
+ { server_normal_0, SIZEOF(server_normal_0) },
+ { server_normal_1, SIZEOF(server_normal_1) },
+ { server_normal_2, SIZEOF(server_normal_2) },
+};
+
+static const struct secrets_expected server_small[SESSIONS] = {
+ { server_small_0, SIZEOF(server_small_0) },
+ { server_small_1, SIZEOF(server_small_1) },
+ { server_small_2, SIZEOF(server_small_2) },
+};
+
+static const struct secrets_expected server_empty[SESSIONS] = {
+ { server_empty_0, SIZEOF(server_empty_0) },
+ { server_empty_1, SIZEOF(server_empty_1) },
+ { server_empty_2, SIZEOF(server_empty_2) },
+};
+
+static const struct secrets_expected server_explicit[SESSIONS] = {
+ { server_explicit_0, SIZEOF(server_explicit_0) },
+ { server_explicit_1, SIZEOF(server_explicit_1) },
+ { server_explicit_2, SIZEOF(server_explicit_2) },
+};
+
+struct fixture {
+ const char *name;
+ unsigned int cflags;
+ unsigned int sflags;
+ gnutls_datum_t early_data;
+ size_t max_early_data_size;
+ bool expect_early_data;
+ const struct secrets_expected *client_secrets;
+ const struct secrets_expected *server_secrets;
+};
+
+static const struct fixture fixtures[] = {
+ {
+ .name = "normal",
+ .cflags = 0,
+ .sflags = 0,
+ .early_data = { (uint8_t *)EARLY_MSG, sizeof(EARLY_MSG) },
+ .max_early_data_size = MAX_BUF,
+ .expect_early_data = true,
+ .client_secrets = client_normal,
+ .server_secrets = server_normal,
+ },
+ {
+ .name = "small",
+ .cflags = 0,
+ .sflags = 0,
+ .early_data = { (uint8_t *)EARLY_MSG, sizeof(EARLY_MSG) },
+ .max_early_data_size = 10,
+ .expect_early_data = true,
+ .client_secrets = client_small,
+ .server_secrets = server_small,
+ },
+ {
+ .name = "empty",
+ .cflags = 0,
+ .sflags = 0,
+ .early_data = { NULL, 0 },
+ .max_early_data_size = MAX_BUF,
+ .expect_early_data = false,
+ .client_secrets = client_empty,
+ .server_secrets = server_empty,
+ },
+ {
+ .name = "explicit",
+ .cflags = GNUTLS_ENABLE_EARLY_DATA,
+ .sflags = 0,
+ .early_data = { NULL, 0 },
+ .max_early_data_size = MAX_BUF,
+ .expect_early_data = false,
+ .client_secrets = client_explicit,
+ .server_secrets = server_explicit,
+ },
+};
+
+#if TRACE
+static void
+print_secret(FILE *out, struct secret *secret)
+{
+ const char *level;
+
+ switch (secret->level) {
+ case GNUTLS_ENCRYPTION_LEVEL_INITIAL:
+ level = "GNUTLS_ENCRYPTION_LEVEL_INITIAL";
+ break;
+ case GNUTLS_ENCRYPTION_LEVEL_EARLY:
+ level = "GNUTLS_ENCRYPTION_LEVEL_EARLY";
+ break;
+ case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE:
+ level = "GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE";
+ break;
+ case GNUTLS_ENCRYPTION_LEVEL_APPLICATION:
+ level = "GNUTLS_ENCRYPTION_LEVEL_APPLICATION";
+ break;
+ }
+
+ fprintf(out, "\t\t%s,\n\t\t%zu,\n", level, secret->secret_size);
+ if (secret->secret_read) {
+ size_t i;
+
+ fputs("\t\t(const uint8_t *)\"", out);
+ for (i = 0; i < secret->secret_size; i++) {
+ fprintf(out, "\\x%.2x", secret->secret_read[i]);
+ }
+ fputs("\",\n", out);
+ } else {
+ fputs("\t\tNULL,\n", out);
+ }
+ if (secret->secret_write) {
+ size_t i;
+
+ fputs("\t\t(const uint8_t *)\"", out);
+ for (i = 0; i < secret->secret_size; i++) {
+ fprintf(out, "\\x%.2x", secret->secret_write[i]);
+ }
+ fputs("\",\n", out);
+ } else {
+ fputs("\t\tNULL,\n", out);
+ }
+}
+
+static void
+print_secrets(FILE *out, const char *side, const char *name, int t,
+ struct secret *secrets, size_t count)
+{
+ size_t i;
+
+ fprintf(out, "static const struct secret %s_%s_%d[] = {\n",
+ side, name, t);
+ for (i = 0; i < count; i++) {
+ fputs("\t{\n", out);
+ print_secret(out, &secrets[i]);
+ fputs("\t},\n", out);
+ }
+ fputs("};\n\n", out);
+}
+#endif
+
+static void
+check_secrets(const struct secret *secrets, size_t count,
+ const struct secrets_expected *expected)
+{
+ size_t i;
+
+ if (count != expected->count) {
+ fail("unexpected number of secrets: %zu != %zu\n",
+ count, expected->count);
+ }
+
+ for (i = 0; i < count; i++) {
+ if (secrets[i].level != expected->secrets[i].level) {
+ fail("unexpected secret level: %d != %d\n",
+ secrets[i].level, expected->secrets[i].level);
+ }
+ if (secrets[i].secret_size != expected->secrets[i].secret_size) {
+ fail("unexpected secret size: %zu != %zu\n",
+ secrets[i].secret_size, expected->secrets[i].secret_size);
+ }
+ if ((secrets[i].secret_read == NULL) !=
+ (expected->secrets[i].secret_read == NULL)) {
+ fail("unexpected secret for read: %p != %p\n",
+ secrets[i].secret_read, expected->secrets[i].secret_read);
+ }
+ if (expected->secrets[i].secret_read &&
+ memcmp(secrets[i].secret_read,
+ expected->secrets[i].secret_read,
+ secrets[i].secret_size) != 0) {
+ fail("unexpected secret for read\n");
+ }
+ if ((secrets[i].secret_write == NULL) !=
+ (expected->secrets[i].secret_write == NULL)) {
+ fail("unexpected secret for write: %p != %p\n",
+ secrets[i].secret_write, expected->secrets[i].secret_write);
+ }
+ if (expected->secrets[i].secret_write &&
+ memcmp(secrets[i].secret_write,
+ expected->secrets[i].secret_write,
+ secrets[i].secret_size) != 0) {
+ fail("unexpected secret for write\n");
+ }
+ }
+}
+
+struct callback_data {
+ int t;
+ size_t secret_callback_called;
+ struct secret secrets[MAX_SECRET_COUNT];
+};
+
+static int
+secret_callback(gnutls_session_t session,
+ gnutls_record_encryption_level_t level,
+ const void *secret_read,
+ const void *secret_write,
+ size_t secret_size)
+{
+ struct callback_data *data = gnutls_session_get_ptr(session);
+ struct secret *secret = &data->secrets[data->secret_callback_called];
+
+ if (data->t == 0) {
+ if (level == GNUTLS_ENCRYPTION_LEVEL_EARLY) {
+ fail("early secret is set on initial connection\n");
+ }
+ } else {
+ if (level == GNUTLS_ENCRYPTION_LEVEL_EARLY) {
+ gnutls_cipher_algorithm_t cipher_algo;
+ gnutls_digest_algorithm_t digest_algo;
+
+ cipher_algo = gnutls_early_cipher_get(session);
+ if (cipher_algo != GNUTLS_CIPHER_AES_128_GCM) {
+ fail("unexpected cipher used for early data: %s != %s\n",
+ gnutls_cipher_get_name(cipher_algo),
+ gnutls_cipher_get_name(GNUTLS_CIPHER_AES_128_GCM));
+ }
+
+ digest_algo = gnutls_early_prf_hash_get(session);
+ if (digest_algo != GNUTLS_DIG_SHA256) {
+ fail("unexpected PRF hash used for early data: %s != %s\n",
+ gnutls_digest_get_name(digest_algo),
+ gnutls_digest_get_name(GNUTLS_DIG_SHA256));
+ }
+ }
+ }
+
+ if (secret_size > MAX_SECRET_SIZE) {
+ fail("secret is too long\n");
+ }
+
+ secret->secret_size = secret_size;
+ secret->level = level;
+ if (secret_read) {
+ memcpy(secret->secret_read_buf, secret_read, secret_size);
+ secret->secret_read = secret->secret_read_buf;
+ }
+ if (secret_write) {
+ memcpy(secret->secret_write_buf, secret_write, secret_size);
+ secret->secret_write = secret->secret_write_buf;
+ }
+
+ data->secret_callback_called++;
+ if (data->secret_callback_called > MAX_SECRET_COUNT) {
+ fail("secret func called too many times");
+ }
+
+ return 0;
+}
+
+static void
+client(int sds[], const struct fixture *fixture)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ int t;
+ gnutls_datum_t session_data = {NULL, 0};
+
+ global_init();
+
+ /* date --date='TZ="UTC" 2021-04-29' +%s */
+ virt_time_init_at(1619654400);
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ for (t = 0; t < SESSIONS; t++) {
+ int sd = sds[t];
+ struct callback_data callback_data;
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT|fixture->cflags)>=0);
+ assert(gnutls_priority_set_direct(session, t == 0 ? TLS13_AES_128_GCM : TLS13_CHACHA20_POLY1305, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, sd);
+ assert(gnutls_handshake_set_random(session, &hrnd) >= 0);
+
+ memset(&callback_data, 0, sizeof(callback_data));
+ callback_data.t = t;
+ gnutls_session_set_ptr(session, &callback_data);
+ gnutls_handshake_set_secret_function(session, secret_callback);
+
+ if (t > 0) {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ /* The server should have advertised the same maximum. */
+ if (gnutls_record_get_max_early_data_size(session) !=
+ fixture->max_early_data_size)
+ fail("client: max_early_data_size mismatch %d != %d\n",
+ (int) gnutls_record_get_max_early_data_size(session),
+ (int) fixture->max_early_data_size);
+ assert(gnutls_record_send_early_data(session,
+ fixture->early_data.data,
+ MIN(fixture->early_data.size,
+ fixture->max_early_data_size)) >= 0);
+ }
+
+ /* Perform the TLS handshake
+ */
+ gnutls_handshake_set_timeout(session, get_timeout());
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n",
+ gnutls_strerror(ret));
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (!gnutls_rnd_works) {
+ success("client: gnutls_rnd() could not be overridden\n");
+ } else {
+#if TRACE == TRACE_CLIENT
+ print_secrets(stderr, "client", fixture->name, t,
+ callback_data.secrets,
+ callback_data.secret_callback_called);
+#endif
+ check_secrets(callback_data.secrets,
+ callback_data.secret_callback_called,
+ &fixture->client_secrets[t]);
+ }
+
+ ret = gnutls_cipher_get(session);
+ if ((t == 0 && ret != GNUTLS_CIPHER_AES_128_GCM) ||
+ (t > 0 && ret != GNUTLS_CIPHER_CHACHA20_POLY1305)) {
+ fail("negotiated unexpected cipher: %s\n",
+ gnutls_cipher_get_name(ret));
+ }
+
+ if (t == 0) {
+ /* get the session data size */
+ ret =
+ gnutls_session_get_data2(session,
+ &session_data);
+ if (ret < 0)
+ fail("client: Getting resume data failed\n");
+ }
+
+ if (t > 0) {
+ if (!gnutls_session_is_resumed(session)) {
+ fail("client: session_is_resumed error (%d)\n", t);
+ }
+ }
+
+ gnutls_record_send(session, MSG, strlen(MSG));
+
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret == GNUTLS_E_AGAIN);
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+
+ gnutls_deinit(session);
+ }
+
+ end:
+ gnutls_free(session_data.data);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+
+static pid_t child;
+
+#define MAX_CLIENT_HELLO_RECORDED 10
+
+struct storage_st {
+ gnutls_datum_t entries[MAX_CLIENT_HELLO_RECORDED];
+ size_t num_entries;
+};
+
+static int
+storage_add(void *ptr, time_t expires, const gnutls_datum_t *key, const gnutls_datum_t *value)
+{
+ struct storage_st *storage = ptr;
+ gnutls_datum_t *datum;
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++) {
+ if (key->size == storage->entries[i].size &&
+ memcmp(storage->entries[i].data, key->data, key->size) == 0) {
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+ }
+
+ /* If the maximum number of ClientHello exceeded, reject early
+ * data until next time.
+ */
+ if (storage->num_entries == MAX_CLIENT_HELLO_RECORDED)
+ return GNUTLS_E_DB_ERROR;
+
+ datum = &storage->entries[storage->num_entries];
+ datum->data = gnutls_malloc(key->size);
+ if (!datum->data)
+ return GNUTLS_E_MEMORY_ERROR;
+ memcpy(datum->data, key->data, key->size);
+ datum->size = key->size;
+
+ storage->num_entries++;
+
+ return 0;
+}
+
+static void
+storage_clear(struct storage_st *storage)
+{
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++)
+ gnutls_free(storage->entries[i].data);
+ storage->num_entries = 0;
+}
+
+static void
+server(int sds[], const struct fixture *fixture)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_datum_t session_ticket_key = { NULL, 0 };
+ struct storage_st storage;
+ gnutls_anti_replay_t anti_replay;
+ int t;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ /* date --date='TZ="UTC" 2021-04-29' +%s */
+ virt_time_init_at(1619654400);
+
+ memset(buffer, 0, sizeof(buffer));
+ memset(&storage, 0, sizeof(storage));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_session_ticket_key_generate(&session_ticket_key);
+
+ ret = gnutls_anti_replay_init(&anti_replay);
+ if (ret < 0)
+ fail("server: failed to initialize anti-replay\n");
+
+ gnutls_anti_replay_set_add_function(anti_replay, storage_add);
+ gnutls_anti_replay_set_ptr(anti_replay, &storage);
+
+ for (t = 0; t < SESSIONS; t++) {
+ int sd = sds[t];
+ struct callback_data callback_data;
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_ENABLE_EARLY_DATA)>=0);
+
+ assert(gnutls_priority_set_direct(session, t == 0 ? TLS13_AES_128_GCM : TLS13_CHACHA20_POLY1305, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
+
+ gnutls_anti_replay_enable(session, anti_replay);
+
+ /* on the replay connection, early data is skipped
+ * until max_early_data_size without decryption
+ */
+ if (t < 2)
+ (void) gnutls_record_set_max_early_data_size(session, fixture->max_early_data_size);
+
+ assert(gnutls_handshake_set_random(session, &hsrnd) >= 0);
+ gnutls_transport_set_int(session, sd);
+
+ memset(&callback_data, 0, sizeof(callback_data));
+ callback_data.t = t;
+ gnutls_session_set_ptr(session, &callback_data);
+ gnutls_handshake_set_secret_function(session, secret_callback);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ close(sd);
+ gnutls_deinit(session);
+ fail("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (t > 0) {
+ if (!gnutls_session_is_resumed(session)) {
+ fail("server: session_is_resumed error (%d)\n", t);
+ }
+ }
+
+ if (!gnutls_rnd_works) {
+ success("server: gnutls_rnd() could not be overridden\n");
+ goto skip_early_data;
+ }
+
+ ret = gnutls_cipher_get(session);
+ if ((t == 0 && ret != GNUTLS_CIPHER_AES_128_GCM) ||
+ (t > 0 && ret != GNUTLS_CIPHER_CHACHA20_POLY1305)) {
+ fail("negotiated unexpected cipher: %s\n",
+ gnutls_cipher_get_name(ret));
+ }
+
+#if TRACE == TRACE_SERVER
+ print_secrets(stderr, "server", fixture->name, t,
+ callback_data.secrets,
+ callback_data.secret_callback_called);
+#endif
+ check_secrets(callback_data.secrets,
+ callback_data.secret_callback_called,
+ &fixture->server_secrets[t]);
+
+ /* as we reuse the same ticket twice, expect
+ * early data only on the first resumption */
+ if (t == 1) {
+ if (fixture->expect_early_data &&
+ !(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA)) {
+ fail("server: early data is not received (%d)\n",
+ t);
+ }
+
+ ret = gnutls_record_recv_early_data(session, buffer, sizeof(buffer));
+ if (ret < 0) {
+ if (fixture->early_data.size == 0 ||
+ fixture->max_early_data_size == 0) {
+ if (ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ fail("server: unexpected error code when retrieving empty early data: %s\n",
+ gnutls_strerror(ret));
+ }
+ } else {
+ fail("server: failed to retrieve early data: %s\n",
+ gnutls_strerror(ret));
+ }
+ } else {
+ if (fixture->early_data.size == 0 ||
+ fixture->max_early_data_size == 0) {
+ fail("server: unexpected early data received: %d\n",
+ ret);
+ } else if ((size_t) ret != MIN(fixture->early_data.size,
+ fixture->max_early_data_size) ||
+ memcmp(buffer, fixture->early_data.data, ret)) {
+ fail("server: early data mismatch\n");
+ }
+ }
+ } else if (t == 2) {
+ if (fixture->expect_early_data &&
+ gnutls_session_get_flags(session) & GNUTLS_SFLAGS_EARLY_DATA) {
+ fail("server: early data is not rejected (%d)\n", t);
+ }
+ }
+
+ skip_early_data:
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ kill(child, SIGTERM);
+ fail("server: Error: %s\n", gnutls_strerror(ret));
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ }
+ }
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+
+ gnutls_deinit(session);
+ }
+
+ gnutls_anti_replay_deinit(anti_replay);
+
+ storage_clear(&storage);
+
+ gnutls_free(session_ticket_key.data);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void
+start(const struct fixture *fixture)
+{
+ int client_sds[SESSIONS], server_sds[SESSIONS];
+ int i;
+ int ret;
+
+ _gnutls_global_version = 0x030607;
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ for (i = 0; i < SESSIONS; i++) {
+ int sockets[2];
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ server_sds[i] = sockets[0];
+ client_sds[i] = sockets[1];
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ }
+
+ if (child) {
+ /* parent */
+ for (i = 0; i < SESSIONS; i++)
+ close(client_sds[i]);
+ server(server_sds, fixture);
+ kill(child, SIGTERM);
+ } else {
+ for (i = 0; i < SESSIONS; i++)
+ close(server_sds[i]);
+ client(client_sds, fixture);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ size_t i;
+
+ /* TLS_CHACHA20_POLY1305_SHA256 is needed for this test */
+ if (gnutls_fips140_mode_enabled()) {
+ exit(77);
+ }
+
+ for (i = 0; i < SIZEOF(fixtures); i++) {
+ start(&fixtures[i]);
+ }
+
+ if (!gnutls_rnd_works) {
+ exit(77);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13-early-start.c b/tests/tls13-early-start.c
new file mode 100644
index 0000000..3c79dba
--- /dev/null
+++ b/tests/tls13-early-start.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests support for early start in TLS1.3 handshake */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "eagain-common.h"
+#include <assert.h>
+
+#define USE_CERT 1
+#define ASK_CERT 2
+
+const char *side;
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+#define try_ok(name, client_prio) \
+ try_with_key(name, client_prio, \
+ &server_ca3_localhost_cert, &server_ca3_key, NULL, NULL, 0)
+
+#define MSG "hello there ppl"
+
+static
+void try_with_key_fail(const char *name, const char *client_prio,
+ const gnutls_datum_t *serv_cert,
+ const gnutls_datum_t *serv_key,
+ const gnutls_datum_t *cli_cert,
+ const gnutls_datum_t *cli_key,
+ unsigned init_flags)
+{
+ int ret;
+ char buffer[256];
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ int cret = GNUTLS_E_AGAIN, version;
+ const char *err;
+
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ reset_buffers();
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+
+ ret = gnutls_certificate_set_x509_key_mem(serverx509cred,
+ serv_cert, serv_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ fail("Could not set key/cert: %s\n", gnutls_strerror(ret));
+
+ assert(gnutls_init(&server, GNUTLS_SERVER|init_flags)>=0);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ assert(gnutls_priority_set_direct(server, client_prio, NULL) >= 0);
+
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ if (cli_cert) {
+ gnutls_certificate_set_x509_key_mem(clientx509cred,
+ cli_cert, cli_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUIRE);
+ }
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ ret = gnutls_priority_set_direct(client, client_prio, &err);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_INVALID_REQUEST)
+ fprintf(stderr, "Error in %s\n", err);
+ exit(1);
+ }
+
+ success("negotiating %s\n", name);
+ HANDSHAKE(client, server);
+
+ assert(!(gnutls_session_get_flags(server) & GNUTLS_SFLAGS_EARLY_START));
+ assert(!(gnutls_session_get_flags(client) & GNUTLS_SFLAGS_EARLY_START));
+
+ version = gnutls_protocol_get_version(client);
+ assert(version == GNUTLS_TLS1_3);
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(server, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(client, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("client: Peer has closed the TLS connection\n");
+ exit(1);
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(client, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(server, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("server: Peer has closed the TLS connection\n");
+ } else if (ret < 0) {
+ fail("server: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+}
+
+static
+void try_with_key_ks(const char *name, const char *client_prio,
+ const gnutls_datum_t *serv_cert,
+ const gnutls_datum_t *serv_key,
+ const gnutls_datum_t *client_cert,
+ const gnutls_datum_t *client_key,
+ unsigned cert_flags,
+ unsigned init_flags)
+{
+ int ret;
+ char buffer[256];
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ int cret = GNUTLS_E_AGAIN, version;
+ const char *err;
+
+ /* General init. */
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ reset_buffers();
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+
+ ret = gnutls_certificate_set_x509_key_mem(serverx509cred,
+ serv_cert, serv_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fail("Could not set key/cert: %s\n", gnutls_strerror(ret));
+ }
+
+ assert(gnutls_init(&server, GNUTLS_SERVER|init_flags)>=0);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+
+ assert(gnutls_priority_set_direct(server,
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3",
+ NULL)>=0);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ if (cert_flags == USE_CERT) {
+ gnutls_certificate_set_x509_key_mem(clientx509cred,
+ client_cert, client_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUIRE);
+ } else if (cert_flags == ASK_CERT) {
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUEST);
+ }
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT);
+ if (ret < 0)
+ exit(1);
+
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ ret = gnutls_priority_set_direct(client, client_prio, &err);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_INVALID_REQUEST)
+ fprintf(stderr, "Error in %s\n", err);
+ exit(1);
+ }
+ success("negotiating %s\n", name);
+ HANDSHAKE(client, server);
+
+ assert(gnutls_session_get_flags(server) & GNUTLS_SFLAGS_EARLY_START);
+ assert(!(gnutls_session_get_flags(client) & GNUTLS_SFLAGS_EARLY_START));
+
+ version = gnutls_protocol_get_version(client);
+ assert(version == GNUTLS_TLS1_3);
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(server, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(client, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("client: Peer has closed the TLS connection\n");
+ exit(1);
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ memset(buffer, 0, sizeof(buffer));
+ assert(gnutls_record_send(client, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(server, buffer, sizeof(buffer));
+ if (ret == 0) {
+ fail("server: Peer has closed the TLS connection\n");
+ } else if (ret < 0) {
+ fail("server: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (ret != strlen(MSG) || memcmp(MSG, buffer, ret) != 0) {
+ fail("client: Error in data received. Expected %d, got %d\n", (int)strlen(MSG), ret);
+ exit(1);
+ }
+
+ gnutls_bye(client, GNUTLS_SHUT_RDWR);
+ gnutls_bye(server, GNUTLS_SHUT_RDWR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+}
+
+static
+void try_with_key(const char *name, const char *client_prio,
+ const gnutls_datum_t *serv_cert,
+ const gnutls_datum_t *serv_key,
+ const gnutls_datum_t *cli_cert,
+ const gnutls_datum_t *cli_key,
+ unsigned cert_flags)
+{
+ return try_with_key_ks(name, client_prio,
+ serv_cert, serv_key, cli_cert, cli_key, cert_flags, GNUTLS_ENABLE_EARLY_START);
+}
+
+#include "cert-common.h"
+
+void doit(void)
+{
+ /* TLS 1.3 no client cert: early start expected */
+ try_ok("TLS 1.3 with ffdhe2048 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048");
+ try_ok("TLS 1.3 with secp256r1 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1");
+ try_ok("TLS 1.3 with x25519 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519");
+
+ try_with_key_ks("TLS 1.3 with secp256r1 ecdsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0, GNUTLS_ENABLE_EARLY_START);
+
+ /* client authentication: no early start possible */
+ try_with_key_fail("TLS 1.3 with rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_rsa_pss_cert, &cli_ca3_rsa_pss_key, GNUTLS_ENABLE_EARLY_START);
+ try_with_key_fail("TLS 1.3 with rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, GNUTLS_ENABLE_EARLY_START);
+ try_with_key_fail("TLS 1.3 with ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, GNUTLS_ENABLE_EARLY_START);
+
+ /* TLS 1.3 no client cert: no early start flag specified */
+ try_with_key_fail("TLS 1.3 with rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:+ECDHE-RSA",
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0);
+}
diff --git a/tests/tls13-rehandshake-cert.c b/tests/tls13-rehandshake-cert.c
new file mode 100644
index 0000000..9a2c889
--- /dev/null
+++ b/tests/tls13-rehandshake-cert.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2008-2012 Free Software Foundation, Inc.
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "eagain-common.h"
+#include "cert-common.h"
+
+/* This program tests server initiated rehandshake under TLS 1.3.
+ * Although rehandshake doesn't happen under TLS1.3 this tests
+ * whether the old APIs would still work.
+ */
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static
+void server_initiated_handshake(void)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ unsigned char buffer[64];
+ int cret = GNUTLS_E_AGAIN;
+ size_t transferred = 0;
+
+ success("testing server initiated re-handshake\n");
+
+ /* General init. */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(2);
+
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_init(&server, GNUTLS_SERVER);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+ gnutls_priority_set_direct(server, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3", NULL);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ gnutls_certificate_allocate_credentials(&clientx509cred);
+ gnutls_init(&client, GNUTLS_CLIENT);
+ gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ gnutls_priority_set_direct(client, "NORMAL:+VERS-TLS1.3", NULL);
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE(client, server);
+
+ if (gnutls_protocol_get_version(client) != GNUTLS_TLS1_3)
+ fail("TLS1.3 was not negotiated\n");
+
+ sret = gnutls_rehandshake(server);
+ if (debug) {
+ tls_log_func(0, "gnutls_rehandshake (server)...\n");
+ tls_log_func(0, gnutls_strerror(sret));
+ tls_log_func(0, "\n");
+ }
+
+ {
+ ssize_t n;
+ char b[1];
+ n = gnutls_record_recv(client, b, 1);
+ /* in TLS1.2 we get REHANDSHAKE error, here nothing */
+ if (n != GNUTLS_E_AGAIN) {
+ fail("error msg: %s\n", gnutls_strerror(n));
+ }
+ }
+
+ TRANSFER(client, server, "xxxx", 4, buffer, sizeof(buffer));
+
+ gnutls_bye(client, GNUTLS_SHUT_RDWR);
+ gnutls_bye(server, GNUTLS_SHUT_RDWR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+
+ reset_buffers();
+}
+
+static
+void client_initiated_handshake(void)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ unsigned char buffer[64];
+ int cret = GNUTLS_E_AGAIN;
+ size_t transferred = 0;
+
+ success("testing client initiated re-handshake\n");
+
+ /* General init. */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(2);
+
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_init(&server, GNUTLS_SERVER);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+ gnutls_priority_set_direct(server, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3", NULL);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ gnutls_certificate_allocate_credentials(&clientx509cred);
+ gnutls_init(&client, GNUTLS_CLIENT);
+ gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ gnutls_priority_set_direct(client, "NORMAL:+VERS-TLS1.3", NULL);
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE(client, server);
+
+ if (gnutls_protocol_get_version(client) != GNUTLS_TLS1_3)
+ fail("TLS1.3 was not negotiated\n");
+
+ HANDSHAKE(client, server);
+
+ TRANSFER(client, server, "xxxx", 4, buffer, sizeof(buffer));
+
+ gnutls_bye(client, GNUTLS_SHUT_RDWR);
+ gnutls_bye(server, GNUTLS_SHUT_RDWR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+
+ reset_buffers();
+}
+
+void doit(void)
+{
+ server_initiated_handshake();
+ client_initiated_handshake();
+}
diff --git a/tests/tls13-server-kx-neg.c b/tests/tls13-server-kx-neg.c
new file mode 100644
index 0000000..a4cca3f
--- /dev/null
+++ b/tests/tls13-server-kx-neg.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests the negotiation for various key exchange
+ * methods and options which are considered legacy in TLS1.3. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "cert-common.h"
+#include "eagain-common.h"
+
+#include "server-kx-neg-common.c"
+
+#define PVERSION "-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2"
+
+test_case_st tests[] = {
+ {
+ .name = "TLS 1.3 DHE-PSK without cred",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS,
+ .server_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_3,
+ },
+ {
+ .name = "TLS 1.3 DHE-PSK with cred but no DH params",
+ .client_ret = 0,
+ .server_ret = 0,
+ .have_psk_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_3,
+ },
+ {
+ .name = "TLS 1.3 DHE-PSK with cred and DH params (level)",
+ .client_ret = 0,
+ .server_ret = 0,
+ .have_psk_cred = 1,
+ .have_psk_dh_params = 1,
+ .server_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_3,
+ },
+ {
+ .name = "TLS 1.3 DHE-PSK with cred and DH params (explicit)",
+ .client_ret = 0,
+ .server_ret = 0,
+ .have_psk_cred = 1,
+ .have_psk_exp_dh_params = 1,
+ .server_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+DHE-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_3,
+ },
+ {
+ .name = "TLS 1.3 ECDHE-PSK with cred but no common curve",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_NO_COMMON_KEY_SHARE,
+ .have_psk_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+ECDHE-PSK:-CURVE-ALL:+CURVE-SECP256R1:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+ECDHE-PSK:-CURVE-ALL:+CURVE-SECP384R1:"PVERSION,
+ .exp_version = GNUTLS_TLS1_3,
+ },
+ {
+ .name = "TLS 1.3 ECDHE-PSK with cred and common curve",
+ .client_ret = 0,
+ .server_ret = 0,
+ .have_psk_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+ECDHE-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+ECDHE-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_3,
+ },
+ {
+ .name = "TLS 1.3 RSA-PSK without cert cred",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS,
+ .have_psk_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 RSA-PSK without psk cred",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_NO_CIPHER_SUITES,
+ .have_psk_cred = 0,
+ .have_cert_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 RSA-PSK with cred but invalid cert",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_NO_CIPHER_SUITES,
+ .have_psk_cred = 1,
+ .have_cert_cred = 1,
+ .have_rsa_sign_cert = 1,
+ .have_ecc_sign_cert = 1,
+ .server_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 RSA-PSK with cred",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_psk_cred = 1,
+ .have_cert_cred = 1,
+ .have_rsa_decrypt_cert = 1,
+ .server_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 RSA-PSK with cred and multiple certs",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_psk_cred = 1,
+ .have_cert_cred = 1,
+ .have_rsa_sign_cert = 1,
+ .have_ecc_sign_cert = 1,
+ .have_rsa_decrypt_cert = 1,
+ .server_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+RSA-PSK:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP-RSA without cert cred",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS,
+ .have_srp_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP-RSA without srp cred",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_NO_CIPHER_SUITES,
+ .have_srp_cred = 0,
+ .have_cert_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP-RSA with cred but invalid cert",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_NO_CIPHER_SUITES,
+ .have_srp_cred = 1,
+ .have_cert_cred = 1,
+ .have_rsa_decrypt_cert = 1,
+ .have_ecc_sign_cert = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP-RSA with cred",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_srp_cred = 1,
+ .have_cert_cred = 1,
+ .have_rsa_sign_cert = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP-RSA with cred and multiple certs",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_srp_cred = 1,
+ .have_cert_cred = 1,
+ .have_rsa_sign_cert = 1,
+ .have_ecc_sign_cert = 1,
+ .have_rsa_decrypt_cert = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP-RSA:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP without srp cred",
+ .client_ret = GNUTLS_E_AGAIN,
+ .server_ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS,
+ .have_srp_cred = 0,
+ .have_cert_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 SRP with cred",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_srp_cred = 1,
+ .server_prio = "NORMAL:-KX-ALL:+SRP:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+SRP:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+#ifdef ENABLE_GOST
+ {
+ .name = "TLS 1.3 server, TLS 1.2 client VKO-GOST-12 with cred and GOST-256 cert",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_cert_cred = 1,
+ .have_gost12_256_cert = 1,
+ .not_on_fips = 1,
+ .server_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:" "-VERS-ALL:+VERS-TLS1.2",
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 server, TLS 1.2 client VKO-GOST-12 with cred and GOST-512 cert",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_cert_cred = 1,
+ .have_gost12_512_cert = 1,
+ .not_on_fips = 1,
+ .server_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:" "-VERS-ALL:+VERS-TLS1.2",
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.2 server TLS 1.3 client VKO-GOST-12 with cred and GOST-256 cert",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_cert_cred = 1,
+ .have_gost12_256_cert = 1,
+ .not_on_fips = 1,
+ .server_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:" "-VERS-ALL:+VERS-TLS1.2",
+ .client_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.2 server TLS 1.3 client with cred and GOST-512 cert",
+ .server_ret = 0,
+ .client_ret = 0,
+ .have_cert_cred = 1,
+ .have_gost12_512_cert = 1,
+ .not_on_fips = 1,
+ .server_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:" "-VERS-ALL:+VERS-TLS1.2",
+ .client_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ /* Ideally for the next two test cases we should fallback to TLS 1.2 + GOST
+ * but this is unsuppored for now */
+ {
+ .name = "TLS 1.3 server and client VKO-GOST-12 with cred and GOST-256 cert",
+ .server_ret = GNUTLS_E_NO_CIPHER_SUITES,
+ .client_ret = GNUTLS_E_AGAIN,
+ .have_cert_cred = 1,
+ .have_gost12_256_cert = 1,
+ .not_on_fips = 1,
+ .server_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+ {
+ .name = "TLS 1.3 server and client VKO-GOST-12 with cred and GOST-512 cert",
+ .server_ret = GNUTLS_E_NO_CIPHER_SUITES,
+ .client_ret = GNUTLS_E_AGAIN,
+ .have_cert_cred = 1,
+ .have_gost12_512_cert = 1,
+ .not_on_fips = 1,
+ .server_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .client_prio = "NORMAL:-KX-ALL:+VKO-GOST-12:+GROUP-GOST-ALL:+CIPHER-GOST-ALL:+MAC-GOST-ALL:+SIGN-GOST-ALL:"PVERSION,
+ .exp_version = GNUTLS_TLS1_2,
+ },
+#endif
+};
+
+void doit(void)
+{
+ unsigned i;
+ global_init();
+
+ for (i=0;i<sizeof(tests)/sizeof(tests[0]);i++) {
+ try(&tests[i]);
+ }
+
+ gnutls_global_deinit();
+}
diff --git a/tests/tls13-without-timeout-func.c b/tests/tls13-without-timeout-func.c
new file mode 100644
index 0000000..8235835
--- /dev/null
+++ b/tests/tls13-without-timeout-func.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include "utils.h"
+#include "eagain-common.h"
+#include "cert-common.h"
+
+/* This tests TLS1.3 and gnutls_session_get_data2() when no
+ * callback with gnutls_transport_set_pull_timeout_function()
+ * is set */
+
+const char *side;
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static time_t mytime(time_t * t)
+{
+ time_t then = 1461671166;
+
+ if (t)
+ *t = then;
+
+ return then;
+}
+
+static ssize_t
+server_pull_fail(gnutls_transport_ptr_t tr, void *data, size_t len)
+{
+ fail("unexpected call to pull callback detected\n");
+ return -1;
+}
+
+void doit(void)
+{
+ int ret;
+ /* Server stuff. */
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t server;
+ int sret = GNUTLS_E_AGAIN;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t client;
+ int cret = GNUTLS_E_AGAIN;
+ gnutls_datum_t data;
+ char buf[128];
+
+ /* General init. */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ gnutls_global_set_time_function(mytime);
+
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+ assert(gnutls_set_default_priority(server)>=0);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred)>=0);
+
+ assert(gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM)>=0);
+
+ assert(gnutls_init(&client, GNUTLS_CLIENT)>=0);
+
+ assert(gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred)>=0);
+
+ assert(gnutls_priority_set_direct(client, "NORMAL:-VERS-ALL:+VERS-TLS1.3", NULL)>=0);
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE(client, server);
+
+ ret = gnutls_record_recv(client, buf, sizeof(buf));
+ if (ret < 0 && ret != GNUTLS_E_AGAIN) {
+ fail("unexpected error: %s\n", gnutls_strerror(ret));
+ }
+
+ gnutls_transport_set_pull_function(server, server_pull_fail);
+
+ ret = gnutls_session_get_data2(client, &data);
+ if (ret != 0) {
+ fail("unexpected error: %s\n", gnutls_strerror(ret));
+ }
+ gnutls_free(data.data);
+ gnutls_transport_set_pull_function(server, server_pull);
+
+ ret = gnutls_record_recv(client, buf, sizeof(buf));
+ if (ret < 0 && ret != GNUTLS_E_AGAIN) {
+ fail("unexpected error: %s\n", gnutls_strerror(ret));
+ }
+
+ gnutls_bye(client, GNUTLS_SHUT_RDWR);
+ gnutls_bye(server, GNUTLS_SHUT_RDWR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+}
diff --git a/tests/tls13/anti_replay.c b/tests/tls13/anti_replay.c
new file mode 100644
index 0000000..506c115
--- /dev/null
+++ b/tests/tls13/anti_replay.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "virt-time.h"
+#include "../../lib/tls13/anti_replay.h"
+#include "../../lib/system.h"
+
+/* utils.h must be loaded after gnutls_int.h, as it redefines some
+ * macros from gnulib */
+#include "utils.h"
+
+#define MAX_CLIENT_HELLO_RECORDED 10
+
+struct storage_st {
+ gnutls_datum_t entries[MAX_CLIENT_HELLO_RECORDED];
+ size_t num_entries;
+};
+
+static int
+storage_add(void *ptr, time_t expires, const gnutls_datum_t *key, const gnutls_datum_t *value)
+{
+ struct storage_st *storage = ptr;
+ gnutls_datum_t *datum;
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++) {
+ if (key->size == storage->entries[i].size &&
+ memcmp(storage->entries[i].data, key->data, key->size) == 0) {
+ return GNUTLS_E_DB_ENTRY_EXISTS;
+ }
+ }
+
+ /* If the maximum number of ClientHello exceeded, reject early
+ * data until next time.
+ */
+ if (storage->num_entries == MAX_CLIENT_HELLO_RECORDED)
+ return GNUTLS_E_DB_ERROR;
+
+ datum = &storage->entries[storage->num_entries];
+ datum->data = gnutls_malloc(key->size);
+ if (!datum->data)
+ return GNUTLS_E_MEMORY_ERROR;
+ memcpy(datum->data, key->data, key->size);
+ datum->size = key->size;
+
+ storage->num_entries++;
+
+ return 0;
+}
+
+static void
+storage_clear(struct storage_st *storage)
+{
+ size_t i;
+
+ for (i = 0; i < storage->num_entries; i++)
+ gnutls_free(storage->entries[i].data);
+ storage->num_entries = 0;
+}
+
+void doit(void)
+{
+ gnutls_anti_replay_t anti_replay;
+ gnutls_datum_t key = { (unsigned char *) "\xFF\xFF\xFF\xFF", 4 };
+ struct timespec creation_time;
+ struct storage_st storage;
+ int ret;
+
+ virt_time_init();
+ memset(&storage, 0, sizeof(storage));
+
+ /* server_ticket_age < client_ticket_age */
+ ret = gnutls_anti_replay_init(&anti_replay);
+ assert(ret == 0);
+ gnutls_anti_replay_set_window(anti_replay, 10000);
+ gnutls_anti_replay_set_add_function(anti_replay, storage_add);
+ gnutls_anti_replay_set_ptr(anti_replay, &storage);
+ mygettime(&creation_time);
+ ret = _gnutls_anti_replay_check(anti_replay, 10000, &creation_time, &key);
+ if (ret != GNUTLS_E_ILLEGAL_PARAMETER)
+ fail("error is not returned, while server_ticket_age < client_ticket_age\n");
+ gnutls_anti_replay_deinit(anti_replay);
+ storage_clear(&storage);
+
+ /* server_ticket_age - client_ticket_age > window */
+ ret = gnutls_anti_replay_init(&anti_replay);
+ assert(ret == 0);
+ gnutls_anti_replay_set_add_function(anti_replay, storage_add);
+ gnutls_anti_replay_set_ptr(anti_replay, &storage);
+ gnutls_anti_replay_set_window(anti_replay, 10000);
+ mygettime(&creation_time);
+ virt_sec_sleep(30);
+ ret = _gnutls_anti_replay_check(anti_replay, 10000, &creation_time, &key);
+ if (ret != GNUTLS_E_EARLY_DATA_REJECTED)
+ fail("early data is NOT rejected, while freshness check fails\n");
+ gnutls_anti_replay_deinit(anti_replay);
+ storage_clear(&storage);
+
+ /* server_ticket_age - client_ticket_age < window */
+ ret = gnutls_anti_replay_init(&anti_replay);
+ assert(ret == 0);
+ gnutls_anti_replay_set_add_function(anti_replay, storage_add);
+ gnutls_anti_replay_set_ptr(anti_replay, &storage);
+ gnutls_anti_replay_set_window(anti_replay, 10000);
+ mygettime(&creation_time);
+ virt_sec_sleep(15);
+ ret = _gnutls_anti_replay_check(anti_replay, 10000, &creation_time, &key);
+ if (ret != 0)
+ fail("early data is rejected, while freshness check succeeds\n");
+ ret = _gnutls_anti_replay_check(anti_replay, 10000, &creation_time, &key);
+ if (ret != GNUTLS_E_EARLY_DATA_REJECTED)
+ fail("early data is NOT rejected for a duplicate key\n");
+ gnutls_anti_replay_deinit(anti_replay);
+ storage_clear(&storage);
+}
diff --git a/tests/tls13/change_cipher_spec.c b/tests/tls13/change_cipher_spec.c
new file mode 100644
index 0000000..81baa76
--- /dev/null
+++ b/tests/tls13/change_cipher_spec.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the ChangeCipherSpec message
+ * is ignored during handshake.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static unsigned client_sent_ccs = 0;
+static unsigned server_sent_ccs = 0;
+
+static int cli_hsk_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg);
+
+static void client(int fd, unsigned ccs_check)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ char buf[64];
+
+ global_init();
+ client_sent_ccs = 0;
+ server_sent_ccs = 0;
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH);
+
+ gnutls_session_set_ptr(session, &ccs_check);
+ gnutls_handshake_set_timeout(session, get_timeout());
+ if (ccs_check) {
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_PRE,
+ cli_hsk_callback);
+ }
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+ success("client handshake completed\n");
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0)
+ fail("client: recv did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ /* send change cipher spec, this should fail in the server */
+ do {
+ ret = send(fd, "\x14\x03\x03\x00\x01\x01", 6, 0);
+ } while(ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ if (ccs_check) {
+ if (client_sent_ccs != 1) {
+ fail("client: did not sent CCS\n");
+ }
+ }
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static int cli_hsk_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ unsigned *p;
+ unsigned ccs_check;
+ static unsigned hello_received = 0;
+
+ p = gnutls_session_get_ptr(session);
+ ccs_check = *p;
+
+ assert(ccs_check != 0);
+ assert(post == GNUTLS_HOOK_PRE);
+
+ if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && !incoming) {
+ hello_received = 1;
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC,
+ GNUTLS_HOOK_PRE,
+ cli_hsk_callback);
+ }
+
+ if (htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC && !incoming && hello_received) {
+ client_sent_ccs++;
+ assert(msg->size == 1 && msg->data[0] == 0x01);
+ }
+
+
+ return 0;
+}
+
+static int hsk_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ int ret;
+ int fd;
+ unsigned *p;
+ unsigned ccs_check;
+
+ p = gnutls_session_get_ptr(session);
+ ccs_check = *p;
+
+ assert(post == GNUTLS_HOOK_PRE);
+
+ if (!ccs_check) {
+ if (!incoming || htype == GNUTLS_HANDSHAKE_CLIENT_HELLO ||
+ htype == GNUTLS_HANDSHAKE_FINISHED)
+ return 0;
+
+ fd = gnutls_transport_get_int(session);
+
+ /* send change cipher spec */
+ do {
+ ret = send(fd, "\x14\x03\x03\x00\x01\x01", 6, 0);
+ } while(ret == -1 && (errno == EINTR || errno == EAGAIN));
+ } else { /* checking whether server received it */
+ if (htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC && !incoming) {
+ server_sent_ccs++;
+ assert(msg->size == 1 && msg->data[0] == 0x01);
+ }
+ }
+ return 0;
+}
+
+static void server(int fd, unsigned ccs_check)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ char buf[64];
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ client_sent_ccs = 0;
+ server_sent_ccs = 0;
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ if (ccs_check)
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC,
+ GNUTLS_HOOK_PRE,
+ hsk_callback);
+ else
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_PRE,
+ hsk_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL) >= 0);
+ gnutls_session_set_ptr(session, &ccs_check);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ success("server handshake completed\n");
+
+ gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_record_send(session, "\x00", 1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0)
+ fail("server: gnutls_record_send did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ /* receive CCS and fail */
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != GNUTLS_E_UNEXPECTED_PACKET)
+ fail("server: incorrect alert sent: %d != %d\n",
+ ret, GNUTLS_E_UNEXPECTED_PACKET);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ if (ccs_check) {
+ if (server_sent_ccs != 1) {
+ fail("server: did not sent CCS\n");
+ }
+ }
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+static
+void start(unsigned ccs_check)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], ccs_check);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1], ccs_check);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ start(0);
+ start(1);
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/compress-cert-cli.c b/tests/tls13/compress-cert-cli.c
new file mode 100644
index 0000000..f4e66bf
--- /dev/null
+++ b/tests/tls13/compress-cert-cli.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <assert.h>
+#include "cert-common.h"
+
+#include "utils.h"
+#include "eagain-common.h"
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+struct handshake_cb_data_st {
+ bool is_server;
+ bool found_compress_certificate;
+ bool found_compressed_certificate;
+ bool found_certificate;
+};
+
+static int ext_callback(void *ctx, unsigned tls_id, const unsigned char *data, unsigned size)
+{
+ struct handshake_cb_data_st *cb_data = ctx;
+ if (tls_id == 27) { /* compress_certificate */
+ cb_data->found_compress_certificate = 1;
+ }
+ return 0;
+}
+
+#define SKIP8(pos, total) { \
+ uint8_t _s; \
+ if (pos+1 > total) fail("error\n"); \
+ _s = msg->data[pos]; \
+ if ((size_t)(pos+1+_s) > total) fail("error\n"); \
+ pos += 1+_s; \
+ }
+
+static int
+handshake_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming,
+ const gnutls_datum_t *msg)
+{
+ struct handshake_cb_data_st *data = gnutls_session_get_ptr(session);
+ unsigned pos = 0;
+ gnutls_datum_t mmsg;
+ int ret;
+
+ if ((data->is_server && incoming) ||
+ (!data->is_server && !incoming)) {
+ return 0;
+ }
+
+ switch (htype) {
+ case GNUTLS_HANDSHAKE_CERTIFICATE_REQUEST:
+ SKIP8(pos, msg->size);
+
+ mmsg.data = &msg->data[pos];
+ mmsg.size = msg->size - pos;
+ ret = gnutls_ext_raw_parse(data, ext_callback, &mmsg, 0);
+ assert(ret >= 0);
+ break;
+ case GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT:
+ data->found_compressed_certificate = true;
+ break;
+ case GNUTLS_HANDSHAKE_CERTIFICATE_PKT:
+ data->found_certificate = true;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void run(void)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t scred;
+ gnutls_session_t server;
+ gnutls_compression_method_t smethods[] = {
+ GNUTLS_COMP_ZSTD, GNUTLS_COMP_BROTLI, GNUTLS_COMP_ZLIB
+ };
+ struct handshake_cb_data_st sdata = { 0, false, false, false };
+ int sret;
+ /* Client stuff. */
+ gnutls_certificate_credentials_t ccred;
+ gnutls_session_t client;
+ gnutls_compression_method_t cmethods[] = {
+ GNUTLS_COMP_ZLIB, GNUTLS_COMP_BROTLI
+ };
+ struct handshake_cb_data_st cdata = { 0, false, false, false };
+ int cret;
+ /* Need to enable anonymous KX specifically. */
+ int ret;
+
+ /* General init. */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(9);
+
+ /* Init server */
+ assert(gnutls_certificate_allocate_credentials(&scred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(scred,
+ &server_ca3_localhost_cert,
+ &server_ca3_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+ assert(gnutls_certificate_set_x509_trust_mem(scred,
+ &ca3_cert,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+ gnutls_certificate_server_set_request(server, GNUTLS_CERT_REQUEST);
+ ret =
+ gnutls_priority_set_direct(server,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ NULL);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_compress_certificate_set_methods(server, smethods, sizeof(smethods)/sizeof(*smethods));
+ if (ret < 0) {
+ fail("server: setting compression method failed (%s)\n",
+ gnutls_strerror(ret));
+ }
+ sdata.is_server = true;
+ gnutls_session_set_ptr(server, &sdata);
+ gnutls_handshake_set_hook_function(server, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_POST,
+ handshake_callback);
+
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ assert(gnutls_certificate_allocate_credentials(&ccred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem
+ (ccred, &cli_ca3_cert_chain, &cli_ca3_key, GNUTLS_X509_FMT_PEM) >= 0);
+ assert(gnutls_certificate_set_x509_trust_mem
+ (ccred, &ca3_cert, GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&client, GNUTLS_CLIENT);
+ ret =
+ gnutls_priority_set_direct(client,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ NULL);
+ assert(ret >= 0);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_compress_certificate_set_methods(client, cmethods, sizeof(cmethods)/sizeof(*cmethods));
+ if (ret < 0) {
+ fail("client: setting compression method failed (%s)\n",
+ gnutls_strerror(ret));
+ }
+ cdata.is_server = false;
+ gnutls_session_set_ptr(client, &cdata);
+ gnutls_handshake_set_hook_function(client, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_POST,
+ handshake_callback);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+
+ HANDSHAKE(client, server);
+ if (debug)
+ success("Handshake established\n");
+
+ if (!sdata.found_compress_certificate) {
+ fail("server: compress_certificate extension not sent\n");
+ }
+ if (!sdata.found_compressed_certificate) {
+ fail("server: CompressedCertificate not sent\n");
+ }
+ if (sdata.found_certificate) {
+ fail("server: Certificate sent\n");
+ }
+ if (!cdata.found_compress_certificate) {
+ fail("client: compress_certificate extension not received\n");
+ }
+ if (!cdata.found_compressed_certificate) {
+ fail("client: CompressedCertificate not received\n");
+ }
+ if (cdata.found_certificate) {
+ fail("client: Certificate not received\n");
+ }
+
+ gnutls_bye(client, GNUTLS_SHUT_WR);
+ gnutls_bye(server, GNUTLS_SHUT_WR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(scred);
+ gnutls_certificate_free_credentials(ccred);
+
+ gnutls_global_deinit();
+ reset_buffers();
+}
+
+void doit(void)
+{
+#if !defined(HAVE_LIBZ) || !defined(HAVE_LIBBROTLI) || !defined(HAVE_LIBZSTD)
+ exit(77);
+#endif
+ run();
+}
diff --git a/tests/tls13/compress-cert-neg.c b/tests/tls13/compress-cert-neg.c
new file mode 100644
index 0000000..5364e88
--- /dev/null
+++ b/tests/tls13/compress-cert-neg.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32) || !defined(HAVE_LIBZ) || \
+ !defined(HAVE_LIBBROTLI) || !defined(HAVE_LIBZSTD)
+
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the compress_certificate extensions is disabled
+ * when client and server have incompatible compression methods set */
+
+#define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3"
+#define CHECK(X) assert((X)>=0)
+
+static pid_t child;
+int client_bad;
+int server_bad;
+
+static void terminate(void)
+{
+ int status = 0;
+
+ if (child) {
+ kill(child, SIGTERM);
+ wait(&status);
+ }
+ exit(1);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static int client_callback(gnutls_session_t session, unsigned htype,
+ unsigned post, unsigned incoming, const gnutls_datum_t *msg)
+{
+ client_bad = 1;
+ return 0;
+}
+
+static int server_callback(gnutls_session_t session, unsigned htype,
+ unsigned post, unsigned incoming, const gnutls_datum_t *msg)
+{
+ server_bad = 1;
+ return 0;
+}
+
+static void client(int fd)
+{
+ int ret;
+ unsigned status;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_compression_method_t method;
+ gnutls_compression_method_t methods[] = { GNUTLS_COMP_BROTLI, GNUTLS_COMP_ZSTD };
+ size_t methods_len = sizeof(methods) / sizeof(gnutls_compression_method_t);
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ CHECK(gnutls_certificate_allocate_credentials(&x509_cred));
+ CHECK(gnutls_certificate_set_x509_trust_mem(x509_cred, &ca3_cert, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert_chain,
+ &cli_ca3_key, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_init(&session, GNUTLS_CLIENT));
+ CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred));
+ CHECK(gnutls_priority_set_direct(session, PRIO, NULL));
+
+ ret = gnutls_compress_certificate_set_methods(session, methods, methods_len);
+ if (ret < 0) {
+ fail("client: setting compression method failed (%s)\n\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT,
+ GNUTLS_HOOK_PRE, client_callback);
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n", strerror(ret));
+ goto cleanup;
+ }
+ if (debug)
+ success("client: Handshake was completed\n");
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ method = gnutls_compress_certificate_get_selected_method(session);
+ if (method != GNUTLS_COMP_UNKNOWN)
+ fail("client: compression method should should not be set\n");
+
+ if (client_bad)
+ fail("client: certificate should not be compressed\n");
+
+ ret = gnutls_certificate_verify_peers2(session, &status);
+ if (ret < 0)
+ fail("client: could not verify server certificate: %s\n", gnutls_strerror(ret));
+ if (status)
+ fail("client: certificate verification failed\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ if (debug)
+ success("client: finished\n");
+
+cleanup:
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+}
+
+static void server(int fd)
+{
+ int ret;
+ unsigned status;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_compression_method_t method;
+ gnutls_compression_method_t methods[] = { GNUTLS_COMP_ZLIB };
+ size_t methods_len = sizeof(methods) / sizeof(gnutls_compression_method_t);
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ CHECK(gnutls_certificate_allocate_credentials(&x509_cred));
+ CHECK(gnutls_certificate_set_x509_trust_mem(x509_cred, &ca3_cert, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_certificate_set_x509_key_mem(x509_cred, &server_ca3_localhost_cert_chain,
+ &server_ca3_key, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_init(&session, GNUTLS_SERVER));
+ CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred));
+ CHECK(gnutls_priority_set_direct(session, PRIO, NULL));
+
+ ret = gnutls_compress_certificate_set_methods(session, methods, methods_len);
+ if (ret < 0) {
+ fail("server: setting compression method failed (%s)\n\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT,
+ GNUTLS_HOOK_PRE, server_callback);
+ gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUEST);
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ fail("server: Handshake has failed (%s)\n\n", gnutls_strerror(ret));
+ goto cleanup;
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+ if (debug)
+ success("server: TLS version is: %s\n", gnutls_protocol_get_name(
+ gnutls_protocol_get_version(session)));
+
+ method = gnutls_compress_certificate_get_selected_method(session);
+ if (method != GNUTLS_COMP_UNKNOWN)
+ fail("server: compression method should not be set\n");
+
+ if (server_bad)
+ fail("server: certificate should not be compressed\n");
+
+ ret = gnutls_certificate_verify_peers2(session, &status);
+ if (ret < 0)
+ fail("server: could not verify client certificate: %s\n", gnutls_strerror(ret));
+ if (status)
+ fail("server: certificate verification failed\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ if (debug)
+ success("server: finished\n");
+
+cleanup:
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ int status = 0;
+
+ server(fd[0]);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/compress-cert-neg2.c b/tests/tls13/compress-cert-neg2.c
new file mode 100644
index 0000000..b083e38
--- /dev/null
+++ b/tests/tls13/compress-cert-neg2.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32) || !defined(HAVE_LIBZ)
+
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the compress_certificate extension correctly fails
+ * in the case of compression/decompression failure */
+
+#define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3"
+#define CHECK(X) assert((X)>=0)
+
+static pid_t child;
+
+static void terminate(void)
+{
+ int status = 0;
+
+ if (child) {
+ kill(child, SIGTERM);
+ wait(&status);
+ }
+ exit(1);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static int client_callback(gnutls_session_t session, unsigned htype,
+ unsigned post, unsigned incoming, const gnutls_datum_t *msg)
+{
+ /* change compression method to BROTLI */
+ msg->data[1] = 0x02;
+ return 0;
+}
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_compression_method_t methods[] = { GNUTLS_COMP_ZLIB };
+ size_t methods_len = sizeof(methods) / sizeof(gnutls_compression_method_t);
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ CHECK(gnutls_certificate_allocate_credentials(&x509_cred));
+ CHECK(gnutls_certificate_set_x509_trust_mem(x509_cred, &ca3_cert, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert_chain,
+ &cli_ca3_key, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_init(&session, GNUTLS_CLIENT));
+ CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred));
+ CHECK(gnutls_priority_set_direct(session, PRIO, NULL));
+
+ ret = gnutls_compress_certificate_set_methods(session, methods, methods_len);
+ if (ret < 0) {
+ fail("client: setting compression method failed (%s)\n\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT,
+ GNUTLS_HOOK_PRE, client_callback);
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret >= 0)
+ fail("client: handshake should have failed\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+}
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_compression_method_t method;
+ gnutls_compression_method_t methods[] = { GNUTLS_COMP_ZLIB };
+ size_t methods_len = sizeof(methods) / sizeof(gnutls_compression_method_t);
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ CHECK(gnutls_certificate_allocate_credentials(&x509_cred));
+ CHECK(gnutls_certificate_set_x509_trust_mem(x509_cred, &ca3_cert, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_certificate_set_x509_key_mem(x509_cred, &server_ca3_localhost_cert_chain,
+ &server_ca3_key, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_init(&session, GNUTLS_SERVER));
+ CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred));
+ CHECK(gnutls_priority_set_direct(session, PRIO, NULL));
+
+ ret = gnutls_compress_certificate_set_methods(session, methods, methods_len);
+ if (ret < 0) {
+ fail("server: setting compression method failed (%s)\n\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret >= 0)
+ fail("server: handshake should have failed\n");
+
+ if (gnutls_alert_get(session) != GNUTLS_A_BAD_CERTIFICATE)
+ fail("server: didn't receive BAD CERTIFICATE alert\n");
+
+ method = gnutls_compress_certificate_get_selected_method(session);
+ if (method != GNUTLS_COMP_ZLIB)
+ fail("server: compression method should be set to ZLIB\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ int status = 0;
+
+ server(fd[0]);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/compress-cert.c b/tests/tls13/compress-cert.c
new file mode 100644
index 0000000..6b867ca
--- /dev/null
+++ b/tests/tls13/compress-cert.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2022 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32) || !defined(HAVE_LIBZ) || \
+ !defined(HAVE_LIBBROTLI) || !defined(HAVE_LIBZSTD)
+
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the compress_certificate extensions works as expected */
+
+#define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3"
+#define CHECK(X) assert((X)>=0)
+
+static pid_t child;
+int client_ok;
+int server_ok;
+
+static void terminate(void)
+{
+ int status = 0;
+
+ if (child) {
+ kill(child, SIGTERM);
+ wait(&status);
+ }
+ exit(1);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static int client_callback(gnutls_session_t session, unsigned htype,
+ unsigned post, unsigned incoming, const gnutls_datum_t *msg)
+{
+ if (incoming == 0)
+ return 0;
+
+ /* check ZLIB number */
+ if (msg->data[0] == 0x00 && msg->data[1] == 0x01)
+ client_ok = 1;
+
+ return 0;
+}
+
+static int server_callback(gnutls_session_t session, unsigned htype,
+ unsigned post, unsigned incoming, const gnutls_datum_t *msg)
+{
+ if (incoming == 0)
+ return 0;
+
+ /* check BROTLI number */
+ if (msg->data[0] == 0x00 && msg->data[1] == 0x02)
+ server_ok = 1;
+
+ return 0;
+}
+
+static void client(int fd)
+{
+ int ret;
+ unsigned status;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_compression_method_t method;
+ gnutls_compression_method_t methods[] = { GNUTLS_COMP_ZLIB, GNUTLS_COMP_BROTLI };
+ size_t methods_len = sizeof(methods) / sizeof(gnutls_compression_method_t);
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ CHECK(gnutls_certificate_allocate_credentials(&x509_cred));
+ CHECK(gnutls_certificate_set_x509_trust_mem(x509_cred, &ca3_cert, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert_chain,
+ &cli_ca3_key, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_init(&session, GNUTLS_CLIENT));
+ CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred));
+ CHECK(gnutls_priority_set_direct(session, PRIO, NULL));
+
+ ret = gnutls_compress_certificate_set_methods(session, methods, methods_len);
+ if (ret < 0) {
+ fail("client: setting compression method failed (%s)\n\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT,
+ GNUTLS_HOOK_PRE, client_callback);
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n", strerror(ret));
+ goto cleanup;
+ }
+ if (debug)
+ success("client: Handshake was completed\n");
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ method = gnutls_compress_certificate_get_selected_method(session);
+ if (method != GNUTLS_COMP_BROTLI)
+ fail("client: compression method should be set to BROTLI\n");
+
+ if (!client_ok)
+ fail("client: didn't receive cert compressed with ZLIB\n");
+
+ ret = gnutls_certificate_verify_peers2(session, &status);
+ if (ret < 0)
+ fail("client: could not verify server certificate: %s\n", gnutls_strerror(ret));
+ if (status)
+ fail("client: certificate verification failed\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ if (debug)
+ success("client: finished\n");
+
+cleanup:
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+}
+
+static void server(int fd)
+{
+ int ret;
+ unsigned status;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_compression_method_t method;
+ gnutls_compression_method_t methods[] = { GNUTLS_COMP_ZSTD, GNUTLS_COMP_BROTLI, GNUTLS_COMP_ZLIB };
+ size_t methods_len = sizeof(methods) / sizeof(gnutls_compression_method_t);
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ CHECK(gnutls_certificate_allocate_credentials(&x509_cred));
+ CHECK(gnutls_certificate_set_x509_trust_mem(x509_cred, &ca3_cert, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_certificate_set_x509_key_mem(x509_cred, &server_ca3_localhost_cert_chain,
+ &server_ca3_key, GNUTLS_X509_FMT_PEM));
+ CHECK(gnutls_init(&session, GNUTLS_SERVER));
+ CHECK(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred));
+ CHECK(gnutls_priority_set_direct(session, PRIO, NULL));
+
+ ret = gnutls_compress_certificate_set_methods(session, methods, methods_len);
+ if (ret < 0) {
+ fail("server: setting compression method failed (%s)\n\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_COMPRESSED_CERTIFICATE_PKT,
+ GNUTLS_HOOK_PRE, server_callback);
+ gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUEST);
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ fail("server: Handshake has failed (%s)\n\n", gnutls_strerror(ret));
+ goto cleanup;
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+ if (debug)
+ success("server: TLS version is: %s\n", gnutls_protocol_get_name(
+ gnutls_protocol_get_version(session)));
+
+ method = gnutls_compress_certificate_get_selected_method(session);
+ if (method != GNUTLS_COMP_ZLIB)
+ fail("server: compression method should be set to ZLIB\n");
+
+ if (!server_ok)
+ fail("server: didn't receive cert compressed with BROTLI\n");
+
+ ret = gnutls_certificate_verify_peers2(session, &status);
+ if (ret < 0)
+ fail("server: could not verify client certificate: %s\n", gnutls_strerror(ret));
+ if (status)
+ fail("server: certificate verification failed\n");
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ if (debug)
+ success("server: finished\n");
+
+cleanup:
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ int status = 0;
+
+ server(fd[0]);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/cookie.c b/tests/tls13/cookie.c
new file mode 100644
index 0000000..dde00af
--- /dev/null
+++ b/tests/tls13/cookie.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Thierry Quemerais, Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+/* This program tests whether a cookie sent by the server is repeated
+ * by the gnutls client. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+/* socketpair isn't supported on Win32. */
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if !defined(_WIN32)
+#include <sys/wait.h>
+#include <signal.h>
+#endif
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <assert.h>
+
+#include "utils.h"
+#include "cert-common.h"
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static int TLSEXT_TYPE_server_sent = 0;
+static int TLSEXT_TYPE_server_received = 0;
+
+static const unsigned char ext_data[] =
+{
+ 0x00,
+ 0x03,
+ 0xFE,
+ 0xED,
+ 0xFF
+};
+
+static int ext_recv_server_cookie(gnutls_session_t session, const unsigned char *buf, size_t buflen)
+{
+ if (buflen != sizeof(ext_data))
+ fail("ext_recv_server_params: Invalid input buffer length\n");
+
+ if (memcmp(buf, ext_data, sizeof(ext_data)) != 0)
+ fail("ext_recv_server_params: Invalid input buffer data\n");
+
+ TLSEXT_TYPE_server_received = 1;
+
+ return 0; //Success
+}
+
+static int ext_send_server_cookie(gnutls_session_t session, gnutls_buffer_t extdata)
+{
+ if (gnutls_ext_get_current_msg(session) == GNUTLS_EXT_FLAG_HRR) {
+ TLSEXT_TYPE_server_sent = 1;
+
+ gnutls_buffer_append_data(extdata, ext_data, sizeof(ext_data));
+ return sizeof(ext_data);
+ }
+ return 0;
+}
+
+static void client(int sd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t clientx509cred;
+
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(4711);
+
+ side = "client";
+
+ gnutls_certificate_allocate_credentials(&clientx509cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ assert(gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3",
+ NULL)>=0);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+
+ gnutls_transport_set_int(session, sd);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ /* Perform the TLS handshake
+ */
+ ret = gnutls_handshake(session);
+
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n", gnutls_strerror(ret));
+ goto end;
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+end:
+ close(sd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+}
+
+static void server(int sd)
+{
+ gnutls_certificate_credentials_t serverx509cred;
+ int ret;
+ gnutls_session_t session;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(4711);
+
+ side = "server";
+
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+
+ /* force a hello retry request by disabling all the groups that are
+ * enabled by default. */
+ assert(gnutls_priority_set_direct(session,
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:"
+ "-GROUP-SECP256R1:-GROUP-X25519:-GROUP-FFDHE2048",
+ NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ ret = gnutls_session_ext_register(session, "cookie_server", 44, GNUTLS_EXT_TLS, ext_recv_server_cookie, ext_send_server_cookie,
+ NULL, NULL, NULL,
+ GNUTLS_EXT_FLAG_CLIENT_HELLO|GNUTLS_EXT_FLAG_HRR|GNUTLS_EXT_FLAG_OVERRIDE_INTERNAL|GNUTLS_EXT_FLAG_IGNORE_CLIENT_REQUEST);
+ if (ret != 0)
+ fail("server: cannot register: %s", gnutls_strerror(ret));
+
+ gnutls_transport_set_int(session, sd);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ fail("server: Handshake has failed: %s\n\n",
+ gnutls_strerror(ret));
+ goto end;
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (TLSEXT_TYPE_server_sent != 1)
+ fail("server: extension not properly sent\n");
+
+ if (TLSEXT_TYPE_server_received != 1)
+ fail("server: extension not properly received\n");
+
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+end:
+ close(sd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+void doit(void)
+{
+ pid_t child;
+ int sockets[2];
+ int err;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (err == -1) {
+ perror("socketpair");
+ fail("socketpair failed\n");
+ return;
+ }
+
+ TLSEXT_TYPE_server_sent = 0;
+ TLSEXT_TYPE_server_received = 0;
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ return;
+ }
+
+ if (child) {
+ int status = 0;
+ /* parent */
+ close(sockets[1]);
+ server(sockets[0]);
+ wait(&status);
+ } else {
+ close(sockets[0]);
+ client(sockets[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/ext-parse.h b/tests/tls13/ext-parse.h
new file mode 100644
index 0000000..9a22de5
--- /dev/null
+++ b/tests/tls13/ext-parse.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#include "utils.h"
+
+#define TLS_EXT_SUPPORTED_VERSIONS 43
+#define TLS_EXT_POST_HANDSHAKE 49
+
+#define SKIP16(pos, _total) { \
+ uint16_t _s; \
+ if ((size_t)pos+2 > (size_t)_total) fail("error0: at %d total: %d\n", pos+2, _total); \
+ _s = (msg->data[pos] << 8) | msg->data[pos+1]; \
+ if ((size_t)(pos+2+_s) > (size_t)_total) fail("error1: at %d field: %d, total: %d\n", pos+2, (int)_s, _total); \
+ pos += 2+_s; \
+ }
+
+#define SKIP8(pos, _total) { \
+ uint8_t _s; \
+ if ((size_t)pos+1 > (size_t)_total) fail("error\n"); \
+ _s = msg->data[pos]; \
+ if ((size_t)(pos+1+_s) > (size_t)_total) fail("error\n"); \
+ pos += 1+_s; \
+ }
+
+typedef void (*ext_parse_func)(void *priv, gnutls_datum_t *extdata);
+
+#define HANDSHAKE_SESSION_ID_POS 34
+
+#if defined __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+/* Returns 0 if the extension was not found, 1 otherwise.
+ */
+static unsigned find_client_extension(const gnutls_datum_t *msg, unsigned extnr, void *priv, ext_parse_func cb)
+{
+ unsigned pos;
+
+ if (msg->size < HANDSHAKE_SESSION_ID_POS)
+ fail("invalid client hello\n");
+
+ /* we expect the legacy version to be present */
+ /* ProtocolVersion legacy_version = 0x0303 */
+ if (msg->data[0] != 0x03) {
+ fail("ProtocolVersion contains %d.%d\n", (int)msg->data[0], (int)msg->data[1]);
+ }
+
+ pos = HANDSHAKE_SESSION_ID_POS;
+ /* legacy_session_id */
+ SKIP8(pos, msg->size);
+
+ /* CipherSuites */
+ SKIP16(pos, msg->size);
+
+ /* legacy_compression_methods */
+ SKIP8(pos, msg->size);
+
+ pos += 2;
+
+ while (pos < msg->size) {
+ uint16_t type;
+
+ if (pos+4 > msg->size)
+ fail("invalid client hello\n");
+
+ type = (msg->data[pos] << 8) | msg->data[pos+1];
+ pos+=2;
+
+ if (debug)
+ success("Found client extension %d\n", (int)type);
+
+ if (type != extnr) {
+ SKIP16(pos, msg->size);
+ } else { /* found */
+ ssize_t size = (msg->data[pos] << 8) | msg->data[pos+1];
+ gnutls_datum_t data;
+
+ pos+=2;
+ if (pos + size > msg->size) {
+ fail("error in extension length (pos: %d, ext: %d, total: %d)\n", pos, (int)size, msg->size);
+ }
+ data.data = &msg->data[pos];
+ data.size = size;
+ if (cb)
+ cb(priv, &data);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static unsigned is_client_extension_last(const gnutls_datum_t *msg, unsigned extnr)
+{
+ unsigned pos, found = 0;
+
+ if (msg->size < HANDSHAKE_SESSION_ID_POS)
+ fail("invalid client hello\n");
+
+ /* we expect the legacy version to be present */
+ /* ProtocolVersion legacy_version = 0x0303 */
+ if (msg->data[0] != 0x03) {
+ fail("ProtocolVersion contains %d.%d\n", (int)msg->data[0], (int)msg->data[1]);
+ }
+
+ pos = HANDSHAKE_SESSION_ID_POS;
+ /* legacy_session_id */
+ SKIP8(pos, msg->size);
+
+ /* CipherSuites */
+ SKIP16(pos, msg->size);
+
+ /* legacy_compression_methods */
+ SKIP8(pos, msg->size);
+
+ pos += 2;
+
+ while (pos < msg->size) {
+ uint16_t type;
+
+ if (pos+4 > msg->size)
+ fail("invalid client hello\n");
+
+ type = (msg->data[pos] << 8) | msg->data[pos+1];
+ pos+=2;
+
+ if (debug)
+ success("Found client extension %d\n", (int)type);
+
+ if (type != extnr) {
+ if (found) {
+ success("found extension %d after %d\n", type, extnr);
+ return 0;
+ }
+ SKIP16(pos, msg->size);
+ } else { /* found */
+ found = 1;
+ SKIP16(pos, msg->size);
+ }
+ }
+
+ if (found)
+ return 1;
+ return 0;
+}
+
+#define TLS_RANDOM_SIZE 32
+
+static unsigned find_server_extension(const gnutls_datum_t *msg, unsigned extnr, void *priv, ext_parse_func cb)
+{
+ unsigned pos = 0;
+
+ success("server hello of %d bytes\n", msg->size);
+ /* we expect the legacy version to be present */
+ /* ProtocolVersion legacy_version = 0x0303 */
+ if (msg->data[0] != 0x03 || msg->data[1] != 0x03) {
+ fail("ProtocolVersion contains %d.%d\n", (int)msg->data[0], (int)msg->data[1]);
+ }
+
+ if (msg->data[1] >= 0x04) {
+ success("assuming TLS 1.3 or better hello format (seen %d.%d)\n", (int)msg->data[0], (int)msg->data[1]);
+ }
+
+ pos += 2+TLS_RANDOM_SIZE;
+
+ /* legacy_session_id */
+ SKIP8(pos, msg->size);
+
+ /* CipherSuite */
+ pos += 2;
+
+ /* legacy_compression_methods */
+ SKIP8(pos, msg->size);
+
+ pos += 2;
+
+ while (pos < msg->size) {
+ uint16_t type;
+
+ if (pos+4 > msg->size)
+ fail("invalid server hello\n");
+
+ type = (msg->data[pos] << 8) | msg->data[pos+1];
+ pos+=2;
+
+ success("Found server extension %d\n", (int)type);
+
+ if (type != extnr) {
+ SKIP16(pos, msg->size);
+ } else { /* found */
+ ssize_t size = (msg->data[pos] << 8) | msg->data[pos+1];
+ gnutls_datum_t data;
+
+ pos+=2;
+ if (pos + size < msg->size) {
+ fail("error in server extension length (pos: %d, total: %d)\n", pos, msg->size);
+ }
+ data.data = &msg->data[pos];
+ data.size = size;
+ if (cb)
+ cb(priv, &data);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#if defined __clang__ || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+# pragma GCC diagnostic pop
+#endif
diff --git a/tests/tls13/hello_retry_request.c b/tests/tls13/hello_retry_request.c
new file mode 100644
index 0000000..dd4506b
--- /dev/null
+++ b/tests/tls13/hello_retry_request.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <assert.h>
+#include <signal.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "tls13/ext-parse.h"
+
+/* This program tests whether the version in Hello Retry Request message
+ * is the expected */
+
+const char *testname = "";
+
+#define myfail(fmt, ...) \
+ fail("%s: "fmt, testname, ##__VA_ARGS__)
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define HANDSHAKE_SESSION_ID_POS 34
+
+struct ctx_st {
+ unsigned hrr_seen;
+ unsigned hello_counter;
+ uint8_t session_id[32];
+ size_t session_id_len;
+};
+
+static int hello_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ struct ctx_st *ctx = gnutls_session_get_ptr(session);
+ assert(ctx != NULL);
+
+ if (htype == GNUTLS_HANDSHAKE_HELLO_RETRY_REQUEST)
+ ctx->hrr_seen = 1;
+
+ if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && post == GNUTLS_HOOK_POST) {
+ size_t session_id_len;
+ uint8_t *session_id;
+
+ assert(msg->size > HANDSHAKE_SESSION_ID_POS + 1);
+ session_id_len = msg->data[HANDSHAKE_SESSION_ID_POS];
+ session_id = &msg->data[HANDSHAKE_SESSION_ID_POS + 1];
+
+ if (ctx->hello_counter > 0) {
+ assert(msg->size > 4);
+ if (msg->data[0] != 0x03 || msg->data[1] != 0x03) {
+ fail("version is %d.%d expected 3,3\n", (int)msg->data[0], (int)msg->data[1]);
+ }
+
+ if (session_id_len != ctx->session_id_len ||
+ memcmp(session_id, ctx->session_id, session_id_len) != 0) {
+ fail("different legacy_session_id is sent after HRR\n");
+ }
+ }
+
+ ctx->session_id_len = session_id_len;
+ memcpy(ctx->session_id, session_id, session_id_len);
+
+ ctx->hello_counter++;
+ }
+
+ return 0;
+}
+
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ struct ctx_st ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_KEY_SHARE_TOP)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_session_set_ptr(session, &ctx);
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-X25519", NULL);
+ if (ret < 0)
+ myfail("cannot set TLS 1.3 priorities\n");
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hello_callback);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ assert(ctx.hrr_seen != 0);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+}
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ /* server only supports x25519, client advertises secp256r1 */
+ assert(gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0)
+ myfail("handshake error: %s\n", gnutls_strerror(ret));
+
+ if (gnutls_group_get(session) != GNUTLS_GROUP_X25519)
+ myfail("group doesn't match the expected: %s\n", gnutls_group_get_name(gnutls_group_get(session)));
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ client(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ server(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/hello_retry_request_resume.c b/tests/tls13/hello_retry_request_resume.c
new file mode 100644
index 0000000..f75ea53
--- /dev/null
+++ b/tests/tls13/hello_retry_request_resume.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2017-2020 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "../lib/handshake-defs.h"
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the certificate seen in Post Handshake Auth
+ * is found in a resumed session under TLS 1.3.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static int ticket_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ gnutls_datum *d;
+ int ret;
+
+ assert(htype == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+
+ d = gnutls_session_get_ptr(session);
+
+ if (post == GNUTLS_HOOK_POST) {
+ if (d->data)
+ gnutls_free(d->data);
+ ret = gnutls_session_get_data2(session, d);
+ assert(ret >= 0);
+ assert(d->size > 4);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ unsigned try = 0;
+ gnutls_datum_t session_data = {NULL, 0};
+ gnutls_certificate_credentials_t x509_cred;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ retry:
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-X25519", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (try == 0) {
+ gnutls_session_set_ptr(session, &session_data);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
+ GNUTLS_HOOK_BOTH,
+ ticket_callback);
+ } else {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0) {
+ fail("error in recv: %s\n", gnutls_strerror(ret));
+ }
+
+ gnutls_deinit(session);
+
+ if (try == 0) {
+ try++;
+ goto retry;
+ }
+
+ gnutls_free(session_data.data);
+ close(fd);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+#define HANDSHAKE_SESSION_ID_POS 34
+
+static int client_hello_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming,
+ const gnutls_datum_t *msg)
+{
+ gnutls_datum *d;
+
+ assert(post == GNUTLS_HOOK_POST);
+ assert(msg->size >= HANDSHAKE_SESSION_ID_POS + 1);
+
+ d = gnutls_session_get_ptr(session);
+ d->size = msg->data[HANDSHAKE_SESSION_ID_POS];
+ d->data = gnutls_malloc(d->size);
+ memcpy(d->data, &msg->data[HANDSHAKE_SESSION_ID_POS], d->size);
+
+ return 0;
+}
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ unsigned try = 0;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_datum_t skey;
+ gnutls_datum_t session_id = {NULL, 0};
+ gnutls_datum_t retry_session_id = {NULL, 0};
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ retry:
+ assert(gnutls_init(&session, GNUTLS_SERVER)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ /* server only supports x25519, client advertises secp256r1 */
+ assert(gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ if (try == 0) {
+ gnutls_session_set_ptr(session, &session_id);
+ } else {
+ gnutls_session_set_ptr(session, &retry_session_id);
+ }
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ GNUTLS_HOOK_POST,
+ client_hello_callback);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (try > 0) {
+ assert(gnutls_session_is_resumed(session) != 0);
+
+ /* Check that the same (non-empty) session ID is used in both
+ * initial and resumption handshakes. This assumes
+ * TLS13_APPENDIX_D4 is set to 1 in lib/handshake-defs.h. Once
+ * it's turned off, both session IDs should be empty. */
+ if (session_id.size == 0 ||
+ session_id.size != retry_session_id.size ||
+ memcmp(session_id.data, retry_session_id.data, session_id.size)) {
+ fail("session ids are different after resumption: %u, %u\n",
+ session_id.size, retry_session_id.size);
+ }
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_deinit(session);
+
+ if (try == 0) {
+ try++;
+ goto retry;
+ }
+
+ gnutls_free(skey.data);
+ close(fd);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_free(session_id.data);
+ gnutls_free(retry_session_id.data);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/key_limits.c b/tests/tls13/key_limits.c
new file mode 100644
index 0000000..e2e533a
--- /dev/null
+++ b/tests/tls13/key_limits.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <assert.h>
+#include <signal.h>
+
+#include "utils.h"
+#include "cert-common.h"
+
+static void terminate(void);
+
+/* This program tests whether re-key occurs at the expected
+ * time.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+
+static void client(int fd, const char *prio, unsigned expect_update)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ unsigned char seq[8];
+ unsigned update_happened = 0;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ ret = gnutls_priority_set_direct(session, prio, NULL);
+ if (ret < 0) {
+ fail("error in priority '%s': %s\n", prio, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed\n");
+ gnutls_perror(ret);
+ exit(1);
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ /* make sure we are not blocked forever */
+ gnutls_record_set_timeout(session, 10000);
+
+ assert(gnutls_record_get_state(session, 1, NULL, NULL, NULL, seq) >= 0);
+ assert(gnutls_record_set_state(session, 1, (void*)"\x00\x00\x00\x00\x00\xff\xff\xfa") >= 0);
+
+ do {
+ do {
+ ret = gnutls_record_recv_seq(session, buffer, MAX_BUF, seq);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+
+ if (memcmp(seq, "\x00\x00\x00\x00\x00\x00\x00\x01", 8) == 0) {
+ update_happened = 1;
+ }
+ } while (ret > 0);
+
+ if (ret == 0 || ret == GNUTLS_E_TIMEDOUT) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ if (ret != 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (expect_update && update_happened == 0) {
+ fail("no update occurred!\n");
+ exit(1);
+ } else if (!expect_update && update_happened) {
+ fail("update occurred unexpectedly!\n");
+ exit(1);
+ } else {
+ if (debug)
+ success("detected update!\n");
+ }
+}
+
+
+/* These are global */
+pid_t child;
+
+static void terminate(void)
+{
+ assert(child);
+ kill(child, SIGTERM);
+ exit(1);
+}
+
+static void server(int fd, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned i;
+ unsigned char seq[8];
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ ret = gnutls_priority_set_direct(session, prio, NULL);
+ if (ret < 0) {
+ fail("error in priority '%s': %s\n", prio, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ close(fd);
+ gnutls_deinit(session);
+ fail("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (debug)
+ success("server: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ assert(gnutls_record_get_state(session, 0, NULL, NULL, NULL, seq) >= 0);
+ assert(gnutls_record_set_state(session, 0, (void*)"\x00\x00\x00\x00\x00\xff\xff\xfa") >= 0);
+
+ memset(buffer, 1, sizeof(buffer));
+ for (i = 0; i<32; i++) {
+ usleep(10000); /* some systems like FreeBSD have their buffers full during this send */
+ do {
+ ret =
+ gnutls_record_send(session, buffer,
+ sizeof(buffer));
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0) {
+ fail("Error sending %d byte packet: %s\n", (int)sizeof(buffer),
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ if (ret != sizeof(buffer)) {
+ fail("Error sending %d byte packet: sent: %d\n", (int)sizeof(buffer),
+ ret);
+ terminate();
+ }
+ }
+
+
+ /* wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void start(const char *name, const char *prio, unsigned exp_update)
+{
+ int fd[2];
+ int ret, status = 0;
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ success("trying: %s\n", name);
+ close(fd[0]);
+ server(fd[1], prio);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[1]);
+ client(fd[0], prio, exp_update);
+ exit(0);
+ }
+}
+
+#define AES_GCM "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM"
+#define CHACHA_POLY1305 "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+CHACHA20-POLY1305"
+
+static void ch_handler(int sig)
+{
+ return;
+}
+
+void doit(void)
+{
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ start("aes-gcm", AES_GCM, 1);
+ if (!gnutls_fips140_mode_enabled()) {
+ start("chacha20", CHACHA_POLY1305, 0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/key_share.c b/tests/tls13/key_share.c
new file mode 100644
index 0000000..fa785a5
--- /dev/null
+++ b/tests/tls13/key_share.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "tls13/ext-parse.h"
+#include "eagain-common.h"
+
+/* This program tests the Key Share behavior in Client Hello,
+ * and whether the flags to gnutls_init for key share are followed.
+ */
+
+const char *testname = "";
+
+#define myfail(fmt, ...) \
+ fail("%s: "fmt, testname, ##__VA_ARGS__)
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+unsigned int tls_id_to_group[] = {
+ [23] = GNUTLS_GROUP_SECP256R1,
+ [24] = GNUTLS_GROUP_SECP384R1,
+ [29] = GNUTLS_GROUP_X25519,
+ [30] = GNUTLS_GROUP_X448,
+ [0x100] = GNUTLS_GROUP_FFDHE2048,
+ [0x101] = GNUTLS_GROUP_FFDHE3072
+};
+
+
+#define TLS_EXT_KEY_SHARE 51
+
+typedef struct ctx_st {
+ gnutls_group_t group;
+ unsigned ngroups;
+} ctx_st;
+
+static
+void check_ks_contents(void *priv, gnutls_datum_t *msg)
+{
+ ctx_st *ctx;
+ int len;
+ gnutls_session_t session = priv;
+ int pos;
+ unsigned total = 0, id;
+ unsigned found = 0;
+
+ ctx = gnutls_session_get_ptr(session);
+
+ len = (msg->data[0] << 8) | msg->data[1];
+ if (len+2 != (int)msg->size)
+ myfail("mismatch in length (%d vs %d)!\n", len, (int)msg->size);
+
+ pos = 2;
+
+ while((unsigned)pos < msg->size) {
+ id = (msg->data[pos] << 8) | msg->data[pos+1];
+ pos += 2;
+ len -= 2;
+
+ if (debug)
+ success("found group: %u\n", id);
+ if (id < sizeof(tls_id_to_group)/sizeof(tls_id_to_group[0])) {
+ if (tls_id_to_group[id] == ctx->group)
+ found = 1;
+ }
+ total++;
+
+ SKIP16(pos, msg->size);
+ }
+
+ if (total != ctx->ngroups) {
+ myfail("found %d groups, expected %d\n", total, ctx->ngroups);
+ }
+
+ if (found == 0) {
+ myfail("did not find group %s\n", gnutls_group_get_name(ctx->group));
+ }
+}
+
+static int client_hello_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_client_extension(msg, TLS_EXT_KEY_SHARE, session, check_ks_contents) == 0)
+ fail("Could not find key share extension!\n");
+ }
+
+ return 0;
+}
+
+static void start(const char *name, const char *prio, unsigned flag,
+ gnutls_group_t group, unsigned ngroups)
+{
+ int sret, cret;
+ gnutls_certificate_credentials_t scred, ccred;
+ gnutls_session_t server, client;
+ ctx_st ctx;
+
+ testname = name;
+ success("== test %s ==\n", testname);
+
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(9);
+
+ /* Init server */
+ assert(gnutls_certificate_allocate_credentials(&scred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(scred,
+ &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&server, GNUTLS_SERVER);
+
+ gnutls_handshake_set_hook_function(server, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ client_hello_callback);
+ ctx.group = group;
+ ctx.ngroups = ngroups;
+ gnutls_session_set_ptr(server, &ctx);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(server, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ gnutls_certificate_allocate_credentials(&ccred);
+ assert(gnutls_certificate_set_x509_trust_mem
+ (ccred, &ca3_cert, GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&client, GNUTLS_CLIENT|flag);
+
+ cret = gnutls_priority_set_direct(client, prio, NULL);
+ if (cret < 0)
+ myfail("cannot set TLS 1.3 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE(client, server);
+ if (debug)
+ success("Handshake established\n");
+
+ if (gnutls_group_get(server) != group)
+ myfail("group doesn't match the expected: %s\n", gnutls_group_get_name(gnutls_group_get(server)));
+
+ gnutls_bye(client, GNUTLS_SHUT_WR);
+ gnutls_bye(server, GNUTLS_SHUT_WR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(scred);
+ gnutls_certificate_free_credentials(ccred);
+
+ gnutls_global_deinit();
+ reset_buffers();
+}
+
+void doit(void)
+{
+ start("single group: default secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3", GNUTLS_KEY_SHARE_TOP, GNUTLS_GROUP_SECP256R1, 1);
+ start("single group: secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP, GNUTLS_GROUP_SECP256R1, 1);
+ start("single group: x25519", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP, GNUTLS_GROUP_X25519, 1);
+
+ /* unfortunately we strictly follow the rfc7919 RFC and we prioritize groups
+ * based on ciphersuite listing as well. To prioritize the FFDHE groups we need
+ * to prioritize the non-EC ciphersuites first. */
+ start("single group: ffdhe2048", "NORMAL:-KX-ALL:+DHE-RSA:+ECDHE-RSA:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE3072", GNUTLS_KEY_SHARE_TOP, GNUTLS_GROUP_FFDHE2048, 1);
+
+ start("two groups: default secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3", GNUTLS_KEY_SHARE_TOP2, GNUTLS_GROUP_SECP256R1, 2);
+ start("two groups: secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP2, GNUTLS_GROUP_SECP256R1, 2);
+ start("two groups: x25519", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP2, GNUTLS_GROUP_X25519, 2);
+ start("two groups: x448", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X448:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP2, GNUTLS_GROUP_X448, 2);
+ start("two groups: ffdhe2048", "NORMAL:-KX-ALL:+DHE-RSA:+ECDHE-RSA:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE3072", GNUTLS_KEY_SHARE_TOP2, GNUTLS_GROUP_FFDHE2048, 2);
+
+ start("three groups: default secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3", GNUTLS_KEY_SHARE_TOP3, GNUTLS_GROUP_SECP256R1, 3);
+ start("three groups: secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP3, GNUTLS_GROUP_SECP256R1, 3);
+ start("three groups: x25519", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP3, GNUTLS_GROUP_X25519, 3);
+ start("three groups: x448", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X448:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-FFDHE2048", GNUTLS_KEY_SHARE_TOP3, GNUTLS_GROUP_X448, 3);
+ start("three groups: ffdhe2048", "NORMAL:-KX-ALL:+DHE-RSA:+ECDHE-RSA:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE3072", GNUTLS_KEY_SHARE_TOP3, GNUTLS_GROUP_FFDHE2048, 3);
+
+ /* test default behavior */
+ start("default groups(2): default secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3", 0, GNUTLS_GROUP_SECP256R1, 2);
+ start("default groups(2): secp256r1", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE2048", 0, GNUTLS_GROUP_SECP256R1, 2);
+ start("default groups(2): x25519", "NORMAL:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-X25519:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-FFDHE2048", 0, GNUTLS_GROUP_X25519, 2);
+ start("default groups(2): ffdhe2048", "NORMAL:-KX-ALL:+DHE-RSA:+ECDHE-RSA:-VERS-ALL:+VERS-TLS1.3:-GROUP-ALL:+GROUP-FFDHE2048:+GROUP-SECP256R1:+GROUP-SECP384R1:+GROUP-X25519:+GROUP-FFDHE3072", 0, GNUTLS_GROUP_FFDHE2048, 2);
+}
diff --git a/tests/tls13/key_update.c b/tests/tls13/key_update.c
new file mode 100644
index 0000000..e9fae86
--- /dev/null
+++ b/tests/tls13/key_update.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <assert.h>
+#include "cert-common.h"
+
+#include "utils.h"
+#define RANDOMIZE
+#include "eagain-common.h"
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
+
+static unsigned key_update_msg_inc = 0;
+static unsigned key_update_msg_out = 0;
+
+static int hsk_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ assert(post == GNUTLS_HOOK_PRE);
+
+ assert(msg->size == 1);
+
+ if (htype == GNUTLS_HANDSHAKE_KEY_UPDATE) {
+ if (incoming)
+ key_update_msg_inc++;
+ else
+ key_update_msg_out++;
+ }
+
+ return 0;
+}
+
+static void run(const char *name, unsigned test)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t ccred;
+ gnutls_certificate_credentials_t scred;
+ gnutls_session_t server;
+ int sret, cret;
+ /* Client stuff. */
+ gnutls_session_t client;
+ /* Need to enable anonymous KX specifically. */
+ char buffer[MAX_BUF + 1];
+ int ret, transferred = 0;
+
+ /* General init. */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(9);
+
+ /* Init server */
+ assert(gnutls_certificate_allocate_credentials(&scred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(scred,
+ &server_ca3_localhost_cert,
+ &server_ca3_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+ ret =
+ gnutls_priority_set_direct(server,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ NULL);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ assert(gnutls_certificate_allocate_credentials(&ccred) >= 0);
+ assert(gnutls_certificate_set_x509_trust_mem
+ (ccred, &ca3_cert, GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&client, GNUTLS_CLIENT);
+ ret =
+ gnutls_priority_set_direct(client,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ NULL);
+ assert(ret >= 0);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+
+ HANDSHAKE(client, server);
+ if (debug)
+ success("Handshake established\n");
+
+ switch (test) {
+ case 0:
+ case 1:
+ success("%s: updating client's key\n", name);
+ do {
+ ret = gnutls_session_key_update(client, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ /* server receives the client key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+ if (test != 0)
+ break;
+ sec_sleep(2);
+ FALLTHROUGH;
+ case 2:
+ success("%s: updating server's key\n", name);
+
+ do {
+ ret = gnutls_session_key_update(server, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ /* client receives the key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+ if (test != 0)
+ break;
+ sec_sleep(2);
+ FALLTHROUGH;
+ case 3:
+ success("%s: updating client's key and asking server\n", name);
+ do {
+ ret = gnutls_session_key_update(client, GNUTLS_KU_PEER);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ /* server receives the client key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+ if (test != 0)
+ break;
+ sec_sleep(2);
+ FALLTHROUGH;
+ case 4:
+ success("%s: updating server's key and asking client\n", name);
+ do {
+ ret = gnutls_session_key_update(server, GNUTLS_KU_PEER);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ sec_sleep(2);
+ break;
+ case 5:
+ success("%s: client cork\n", name);
+ gnutls_record_cork(client);
+
+ /* server sends key update */
+ do {
+ ret = gnutls_session_key_update(server, GNUTLS_KU_PEER);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ /* client has data in the corked buffer */
+ do {
+ ret = gnutls_record_send(client, MSG, strlen(MSG));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("cannot send: %s\n", gnutls_strerror(ret));
+
+ /* client receives key update */
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ /* client uncorks and sends key update */
+ do {
+ ret = gnutls_record_uncork(client, GNUTLS_RECORD_WAIT);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("cannot send: %s\n", gnutls_strerror(ret));
+
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ sec_sleep(2);
+ break;
+ case 6:
+ key_update_msg_inc = 0;
+ key_update_msg_out = 0;
+
+ success("%s: callbacks are called\n", name);
+
+ gnutls_handshake_set_hook_function(client, -1, GNUTLS_HOOK_PRE, hsk_callback);
+ gnutls_handshake_set_hook_function(server, -1, GNUTLS_HOOK_PRE, hsk_callback);
+
+ do {
+ ret = gnutls_session_key_update(client, GNUTLS_KU_PEER);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ /* server receives the client key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ assert(key_update_msg_inc == 2);
+ assert(key_update_msg_out == 2);
+ break;
+ }
+
+
+ gnutls_bye(client, GNUTLS_SHUT_WR);
+ gnutls_bye(server, GNUTLS_SHUT_WR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(scred);
+ gnutls_certificate_free_credentials(ccred);
+
+ gnutls_global_deinit();
+ reset_buffers();
+}
+
+void doit(void)
+{
+ run("single", 1);
+ run("single", 2);
+ run("single", 3);
+ run("single", 4);
+ run("single", 5);
+ run("single", 6);
+ run("all", 0); /* all one after each other */
+}
diff --git a/tests/tls13/key_update_multiple.c b/tests/tls13/key_update_multiple.c
new file mode 100644
index 0000000..8b2c2db
--- /dev/null
+++ b/tests/tls13/key_update_multiple.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2017-2019 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <assert.h>
+#include "cert-common.h"
+
+#include "utils.h"
+#include "virt-time.h"
+#define RANDOMIZE
+#include "eagain-common.h"
+
+const char *side = "";
+
+/* This program tests whether multiple key update messages are handled
+ * properly with rate-limit. */
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
+
+/* These must match the definitions in lib/tls13/key_update.c. */
+#define KEY_UPDATES_WINDOW 1000
+#define KEY_UPDATES_PER_WINDOW 8
+
+static unsigned key_update_msg_inc = 0;
+static unsigned key_update_msg_out = 0;
+
+static int hsk_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ assert(post == GNUTLS_HOOK_PRE);
+
+ assert(msg->size == 1);
+
+ if (htype == GNUTLS_HANDSHAKE_KEY_UPDATE) {
+ if (incoming)
+ key_update_msg_inc++;
+ else
+ key_update_msg_out++;
+ }
+
+ return 0;
+}
+
+static void run(const char *name, bool exceed_limit)
+{
+ /* Server stuff. */
+ gnutls_certificate_credentials_t ccred;
+ gnutls_certificate_credentials_t scred;
+ gnutls_session_t server;
+ int sret, cret;
+ /* Client stuff. */
+ gnutls_session_t client;
+ /* Need to enable anonymous KX specifically. */
+ char buffer[MAX_BUF + 1];
+ int ret, transferred = 0;
+ size_t i;
+
+ success("%s\n", name);
+
+ /* General init. */
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(9);
+
+ /* Init server */
+ assert(gnutls_certificate_allocate_credentials(&scred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(scred,
+ &server_ca3_localhost_cert,
+ &server_ca3_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ assert(gnutls_init(&server, GNUTLS_SERVER) >= 0);
+ ret =
+ gnutls_priority_set_direct(server,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ NULL);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ assert(gnutls_certificate_allocate_credentials(&ccred) >= 0);
+ assert(gnutls_certificate_set_x509_trust_mem
+ (ccred, &ca3_cert, GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&client, GNUTLS_CLIENT);
+ ret =
+ gnutls_priority_set_direct(client,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ NULL);
+ assert(ret >= 0);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+
+ HANDSHAKE(client, server);
+ if (debug)
+ success("Handshake established\n");
+
+ key_update_msg_inc = 0;
+ key_update_msg_out = 0;
+
+ gnutls_handshake_set_hook_function(client, -1, GNUTLS_HOOK_PRE, hsk_callback);
+
+ /* schedule multiple key updates */
+ for (i = 0; i < KEY_UPDATES_PER_WINDOW; i++) {
+ do {
+ ret = gnutls_session_key_update(client, 1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+ }
+
+ /* server receives the client key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ if (key_update_msg_out != KEY_UPDATES_PER_WINDOW)
+ fail("unexpected number of key updates are sent: %d\n",
+ key_update_msg_out);
+ else {
+ if (debug)
+ success("successfully sent %d key updates\n",
+ KEY_UPDATES_PER_WINDOW);
+ }
+ if (key_update_msg_inc != 1)
+ fail("unexpected number of key updates received: %d\n",
+ key_update_msg_inc);
+ else {
+ if (debug)
+ success("successfully received 1 key update\n");
+ }
+
+ if (exceed_limit) {
+ /* excessive key update in the same time window should
+ * be rejected by the peer */
+ do {
+ ret = gnutls_session_key_update(client, 1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ /* server receives the client key update and sends data */
+ ret = record_send_loop(client, MSG, strlen(MSG), 0);
+ assert(ret == strlen(MSG));
+ ret = gnutls_record_recv(server, buffer, MAX_BUF);
+ if (ret != GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS)
+ fail("server didn't reject excessive number of key updates\n");
+ else {
+ if (debug)
+ success("server rejected excessive number of key updates\n");
+ }
+ } else {
+ virt_sec_sleep(KEY_UPDATES_WINDOW / 1000 + 1);
+
+ /* the time window should be rolled over now */
+ do {
+ ret = gnutls_session_key_update(client, 1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("error in key update: %s\n", gnutls_strerror(ret));
+
+ /* server receives the client key update and sends data */
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+ }
+
+ gnutls_bye(client, GNUTLS_SHUT_WR);
+ gnutls_bye(server, GNUTLS_SHUT_WR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(scred);
+ gnutls_certificate_free_credentials(ccred);
+
+ gnutls_global_deinit();
+ reset_buffers();
+}
+
+void doit(void)
+{
+ virt_time_init();
+
+ run("not exceeding limit", 0);
+ run("exceeding limit", 1);
+}
diff --git a/tests/tls13/multi-ocsp.c b/tests/tls13/multi-ocsp.c
new file mode 100644
index 0000000..e7a52e1
--- /dev/null
+++ b/tests/tls13/multi-ocsp.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <assert.h>
+
+#ifdef ENABLE_OCSP
+
+#include "ocsp-common.h"
+#include "cert-common.h"
+#include "utils.h"
+
+/* Tests whether we can send and receive multiple OCSP responses
+ * one for each certificate in a chain under TLS 1.3.
+ */
+
+static time_t mytime(time_t * t)
+{
+ time_t then = 1469186559;
+ if (t)
+ *t = then;
+
+ return then;
+}
+
+static const gnutls_datum_t ocsp_resp_localhost[] = {
+ { (void*)_ocsp_ca3_localhost_unknown, sizeof(_ocsp_ca3_localhost_unknown) },
+ { NULL, 0}};
+
+static const gnutls_datum_t ocsp_resp_localhost6[] = {
+ { (void*)_ocsp_ca3_localhost6_unknown, sizeof(_ocsp_ca3_localhost6_unknown) },
+ { (void*)_ocsp_subca3_unknown, sizeof(_ocsp_subca3_unknown) }};
+
+typedef struct ctx_st {
+ const char *name;
+ const gnutls_datum_t *ocsp;
+ unsigned nocsp;
+} ctx_st;
+
+static ctx_st test_localhost = {"single response", ocsp_resp_localhost, 1};
+static ctx_st test_localhost6 = {"two responses", ocsp_resp_localhost6, 2};
+
+#define myfail(fmt, ...) \
+ fail("%s: "fmt, test->name, ##__VA_ARGS__)
+
+static void check_response(gnutls_session_t session, void *priv)
+{
+ int ret;
+ gnutls_datum_t resp;
+ ctx_st *test = priv;
+ unsigned i;
+
+ assert(test != NULL);
+
+ for (i=0;;i++) {
+ ret = gnutls_ocsp_status_request_get2(session, i, &resp);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (ret < 0) {
+ if (test->ocsp[i].size == 0)
+ return;
+ myfail("no response was received\n");
+ }
+
+ if (test->ocsp[i].size == 0) {
+ myfail("not expected response, but received one\n");
+ }
+
+ if (resp.size != test->ocsp[i].size) {
+ myfail("did not receive the expected response size for %d\n", i);
+ }
+
+ if (memcmp(resp.data, test->ocsp[i].data, resp.size) != 0) {
+ myfail("did not receive the expected response for %d\n", i);
+ }
+ }
+
+ if (i != test->nocsp) {
+ myfail("The number of OCSP responses received (%d) does not match the expected (%d)\n", i, test->nocsp);
+ }
+
+}
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+void doit(void)
+{
+ int ret;
+ gnutls_certificate_credentials_t xcred;
+ gnutls_certificate_credentials_t clicred;
+ const char *certfile1;
+ const char *certfile2;
+ char certname1[TMPNAME_SIZE];
+ char certname2[TMPNAME_SIZE];
+ FILE *fp;
+ unsigned index1, index2; /* indexes of certs */
+
+ global_init();
+ gnutls_global_set_time_function(mytime);
+
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(4711);
+
+ assert(gnutls_certificate_allocate_credentials(&xcred) >= 0);
+ assert(gnutls_certificate_allocate_credentials(&clicred) >= 0);
+
+ gnutls_certificate_set_flags(xcred, GNUTLS_CERTIFICATE_API_V2);
+
+ /* set cert with localhost name */
+ certfile1 = get_tmpname(certname1);
+
+ fp = fopen(certfile1, "wb");
+ if (fp == NULL)
+ fail("error in fopen\n");
+ assert(fwrite(server_localhost_ca3_cert_chain_pem, 1, strlen(server_localhost_ca3_cert_chain_pem), fp)>0);
+ assert(fwrite(server_ca3_key_pem, 1, strlen((char*)server_ca3_key_pem), fp)>0);
+ fclose(fp);
+
+ ret = gnutls_certificate_set_x509_key_file2(xcred, certfile1, certfile1,
+ GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (ret < 0)
+ fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+ index1 = ret;
+
+ /* set cert with localhost6 name */
+ certfile2 = get_tmpname(certname2);
+
+ fp = fopen(certfile2, "wb");
+ if (fp == NULL)
+ fail("error in fopen\n");
+ assert(fwrite(server_localhost6_ca3_cert_chain_pem, 1, strlen(server_localhost6_ca3_cert_chain_pem), fp)>0);
+ assert(fwrite(server_ca3_key_pem, 1, strlen((char*)server_ca3_key_pem), fp)>0);
+ fclose(fp);
+
+ ret = gnutls_certificate_set_x509_key_file2(xcred, certfile2, certfile2,
+ GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (ret < 0)
+ fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+ index2 = ret;
+
+
+ /* set OCSP response1 */
+ ret = gnutls_certificate_set_ocsp_status_request_mem(xcred, &test_localhost.ocsp[0], index1, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+ /* set OCSP response2 */
+ ret = gnutls_certificate_set_ocsp_status_request_mem(xcred, &test_localhost6.ocsp[0], index2, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+ ret = gnutls_certificate_set_ocsp_status_request_mem(xcred, &test_localhost6.ocsp[1], index2, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+ /* make sure that our invalid OCSP responses are not considered in verification
+ */
+ gnutls_certificate_set_verify_flags(clicred, GNUTLS_VERIFY_DISABLE_CRL_CHECKS);
+ if (gnutls_certificate_get_verify_flags(clicred) != GNUTLS_VERIFY_DISABLE_CRL_CHECKS)
+ fail("error in gnutls_certificate_set_verify_flags\n");
+
+ ret = gnutls_certificate_set_x509_trust_mem(clicred, &ca3_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fail("error in setting trust cert: %s\n", gnutls_strerror(ret));
+ }
+
+ test_cli_serv(xcred, clicred, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3", "localhost", &test_localhost, check_response, NULL);
+ test_cli_serv(xcred, clicred, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3", "localhost6", &test_localhost6, check_response, NULL);
+
+ gnutls_certificate_free_credentials(xcred);
+ gnutls_certificate_free_credentials(clicred);
+ gnutls_global_deinit();
+ remove(certfile1);
+ remove(certfile2);
+}
+
+#else
+void doit(void)
+{
+ exit(77);
+}
+#endif
diff --git a/tests/tls13/no-auto-send-ticket.c b/tests/tls13/no-auto-send-ticket.c
new file mode 100644
index 0000000..64facf7
--- /dev/null
+++ b/tests/tls13/no-auto-send-ticket.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2017-2020 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "../lib/handshake-defs.h"
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the certificate seen in Post Handshake Auth
+ * is found in a resumed session under TLS 1.3.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static unsigned tickets_seen = 0;
+static int ticket_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ gnutls_datum *d;
+ int ret;
+
+ assert(htype == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+
+ d = gnutls_session_get_ptr(session);
+
+ if (post == GNUTLS_HOOK_POST) {
+ tickets_seen++;
+ if (d->data)
+ gnutls_free(d->data);
+ ret = gnutls_session_get_data2(session, d);
+ assert(ret >= 0);
+ assert(d->size > 4);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static void client(int fd, unsigned flags, unsigned tickets)
+{
+ int ret;
+ gnutls_session_t session;
+ unsigned try = 0;
+ gnutls_datum_t session_data = {NULL, 0};
+ gnutls_certificate_credentials_t x509_cred;
+
+ global_init();
+ tickets_seen = 0;
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ retry:
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|flags)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (try == 0) {
+ gnutls_session_set_ptr(session, &session_data);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
+ GNUTLS_HOOK_BOTH,
+ ticket_callback);
+ } else {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0) {
+ fail("error in recv: %s\n", gnutls_strerror(ret));
+ }
+
+ if (tickets_seen != tickets)
+ fail("unexpected number of tickets received: %u != %u",
+ tickets_seen, tickets);
+
+ gnutls_deinit(session);
+
+ if (tickets > 0 && try == 0) {
+ try++;
+ goto retry;
+ }
+
+ close(fd);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_free(session_data.data);
+
+ gnutls_global_deinit();
+}
+
+static void server(int fd, unsigned flags,
+ unsigned tickets_sent, unsigned tickets_expected)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_datum_t skey;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|flags)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (tickets_sent > 0) {
+ do {
+ ret = gnutls_session_ticket_send(session, tickets_sent, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_deinit(session);
+
+ if (tickets_expected > 0) {
+ /* resume session
+ */
+ assert(gnutls_init(&session, GNUTLS_SERVER|flags)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, get_timeout());
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ assert(gnutls_session_is_resumed(session) != 0);
+
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(session);
+ }
+
+ gnutls_free(skey.data);
+ close(fd);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+static void start(const char *name,
+ unsigned flags,
+ unsigned tickets_sent,
+ unsigned tickets_expected)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ success("testing: %s\n", name);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], flags, tickets_sent, tickets_expected);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1], flags, tickets_expected);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ start("auto send ticket 0", 0, 0, TLS13_TICKETS_TO_SEND);
+ start("auto send ticket 1", 0, 1, TLS13_TICKETS_TO_SEND + 1);
+ start("no auto send ticket 0", GNUTLS_NO_AUTO_SEND_TICKET, 0, 0);
+ start("no auto send ticket 1", GNUTLS_NO_AUTO_SEND_TICKET, 1, 1);
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/no-psk-exts.c b/tests/tls13/no-psk-exts.c
new file mode 100644
index 0000000..f11d15e
--- /dev/null
+++ b/tests/tls13/no-psk-exts.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+/* This program tests whether a connection without the PSK priority
+ * options, will contain PSK extensions */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_psk_client_credentials_t psk_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_psk_allocate_client_credentials(&psk_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_NO_TICKETS);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_credentials_set(session, GNUTLS_CRD_PSK, psk_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ /* try if gnutls_reauth() would fail as expected */
+ ret = gnutls_reauth(session, 0);
+ if (ret != GNUTLS_E_INVALID_REQUEST)
+ fail("server: gnutls_reauth did not fail as expected: %s", gnutls_strerror(ret));
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_psk_free_client_credentials(psk_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_PSK 41
+#define TLS_EXT_PSK_KE 45
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_PSK_KE, NULL, NULL)) {
+ fail("PSK KE extension seen on server (illegal)!\n");
+ }
+ if (find_server_extension(msg, TLS_EXT_PSK, NULL, NULL)) {
+ fail("PSK extension seen on server (illegal)!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_PSK, NULL, NULL))
+ fail("PSK extension seen in client hello with no PSK!\n");
+
+ if (find_client_extension(msg, TLS_EXT_PSK_KE, NULL, NULL))
+ fail("PSK KE extension seen in client hello with no PSK!\n");
+
+ return 0;
+}
+
+static void server(int fd)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/ocsp-client.c b/tests/tls13/ocsp-client.c
new file mode 100644
index 0000000..a98dfdc
--- /dev/null
+++ b/tests/tls13/ocsp-client.c
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2016-2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <assert.h>
+
+#ifdef ENABLE_OCSP
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* Tests whether we can send and receive multiple OCSP responses
+ * one for each certificate in a chain under TLS 1.3, but unrelated
+ * to these certificate (using the GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK
+ * flag).
+ */
+
+static time_t mytime(time_t * t)
+{
+ time_t then = 1469186559;
+ if (t)
+ *t = then;
+
+ return then;
+}
+
+#define RESP1 "\x30\x82\x06\x8C\x0A\x01\x00\xA0\x82\x06\x85\x30\x82\x06\x81\x06\x09\x2B\x06\x01\x05\x05\x07\x30\x01\x01\x04\x82\x06\x72\x30\x82\x06\x6E\x30\x82\x01\x07\xA1\x69\x30\x67\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1F\x30\x1D\x06\x03\x55\x04\x0B\x13\x16\x4F\x43\x53\x50\x20\x53\x69\x67\x6E\x69\x6E\x67\x20\x41\x75\x74\x68\x6F\x72\x69\x74\x79\x31\x1C\x30\x1A\x06\x03\x55\x04\x03\x13\x13\x6F\x63\x73\x70\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x18\x0F\x32\x30\x31\x31\x30\x39\x32\x37\x30\x39\x35\x34\x32\x38\x5A\x30\x64\x30\x62\x30\x3A\x30\x09\x06\x05\x2B\x0E\x03\x02\x1A\x05\x00\x04\x14\x13\x9D\xA0\x9E\xF4\x32\xAB\x8F\xE2\x89\x56\x67\xFA\xD0\xD4\xE3\x35\x86\x71\xB9\x04\x14\x5D\xA7\xDD\x70\x06\x51\x32\x7E\xE7\xB6\x6D\xB3\xB5\xE5\xE0\x60\xEA\x2E\x4D\xEF\x02\x01\x1D\x80\x00\x18\x0F\x32\x30\x31\x31\x30\x39\x32\x37\x30\x39\x35\x34\x32\x38\x5A\xA0\x11\x18\x0F\x32\x30\x31\x31\x30\x39\x32\x37\x30\x39\x35\x39\x32\x38\x5A\xA1\x23\x30\x21\x30\x1F\x06\x09\x2B\x06\x01\x05\x05\x07\x30\x01\x02\x04\x12\x04\x10\x16\x89\x7D\x91\x3A\xB5\x25\xA4\x45\xFE\xC9\xFD\xC2\xE5\x08\xA4\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x4E\xAD\x6B\x2B\xF7\xF2\xBF\xA9\x23\x1E\x3A\x0B\x06\xDB\x55\x53\x2B\x64\x54\x11\x32\xBF\x60\xF7\x4F\xE0\x8E\x9B\xA0\xA2\x4C\x79\xC3\x2A\xE0\x43\xF7\x40\x1A\xDC\xB9\xB4\x25\xEF\x48\x01\x97\x8C\xF5\x1E\xDB\xD1\x30\x37\x73\x69\xD6\xA7\x7A\x2D\x8E\xDE\x5C\xAA\xEA\x39\xB9\x52\xAA\x25\x1E\x74\x7D\xF9\x78\x95\x8A\x92\x1F\x98\x21\xF4\x60\x7F\xD3\x28\xEE\x47\x9C\xBF\xE2\x5D\xF6\x3F\x68\x0A\xD6\xFF\x08\xC1\xDC\x95\x1E\x29\xD7\x3E\x85\xD5\x65\xA4\x4B\xC0\xAF\xC3\x78\xAB\x06\x98\x88\x19\x8A\x64\xA6\x83\x91\x87\x13\xDB\x17\xCC\x46\xBD\xAB\x4E\xC7\x16\xD1\xF8\x35\xFD\x27\xC8\xF6\x6B\xEB\x37\xB8\x08\x6F\xE2\x6F\xB4\x7E\xD5\x68\xDB\x7F\x5D\x5E\x36\x38\xF2\x77\x59\x13\xE7\x3E\x4D\x67\x5F\xDB\xA2\xF5\x5D\x7C\xBF\xBD\xB5\x37\x33\x51\x36\x63\xF8\x21\x1E\xFC\x73\x8F\x32\x69\xBB\x97\xA7\xBD\xF1\xB6\xE0\x40\x09\x68\xEA\xD5\x93\xB8\xBB\x39\x8D\xA8\x16\x1B\xBF\x04\x7A\xBC\x18\x43\x01\xE9\x3C\x19\x5C\x4D\x4B\x98\xD8\x23\x37\x39\xA4\xC4\xDD\xED\x9C\xEC\x37\xAB\x66\x44\x9B\xE7\x5B\x5D\x32\xA2\xDB\xA6\x0B\x3B\x8C\xE1\xF5\xDB\xCB\x7D\x58\xA0\x82\x04\x4B\x30\x82\x04\x47\x30\x82\x04\x43\x30\x82\x03\x2B\xA0\x03\x02\x01\x02\x02\x01\x1E\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B\x05\x00\x30\x45\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1B\x30\x19\x06\x03\x55\x04\x03\x13\x12\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x20\x52\x6F\x6F\x74\x20\x43\x41\x30\x1E\x17\x0D\x30\x39\x31\x31\x32\x34\x31\x32\x35\x31\x35\x33\x5A\x17\x0D\x31\x34\x31\x31\x32\x33\x31\x32\x35\x31\x35\x33\x5A\x30\x67\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1F\x30\x1D\x06\x03\x55\x04\x0B\x13\x16\x4F\x43\x53\x50\x20\x53\x69\x67\x6E\x69\x6E\x67\x20\x41\x75\x74\x68\x6F\x72\x69\x74\x79\x31\x1C\x30\x1A\x06\x03\x55\x04\x03\x13\x13\x6F\x63\x73\x70\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x30\x82\x01\x22\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00\x03\x82\x01\x0F\x00\x30\x82\x01\x0A\x02\x82\x01\x01\x00\xBC\x05\x3E\x4B\xBE\xC6\xB1\x33\x48\x0E\xC3\xD4\x0C\xEF\x83\x0B\xBD\xBC\x57\x5F\x14\xEF\xF5\x6D\x0B\xFF\xFA\x01\x9C\xFA\x21\x6D\x5C\xAE\x79\x29\x74\xFE\xBD\xAB\x70\x87\x98\x6B\x48\x35\x79\xE3\xE0\xC1\x14\x41\x1F\x0A\xF7\xE7\xA3\xA6\xDA\x6B\xFF\xCD\x74\xE9\x95\x00\x38\xAA\xD6\x3A\x60\xC6\x64\xA1\xE6\x02\x39\x58\x4E\xFD\xF2\x78\x08\x63\xB6\xD7\x7A\x96\x79\x62\x18\x39\xEE\x27\x8D\x3B\xA2\x3D\x48\x88\xDB\x43\xD6\x6A\x77\x20\x6A\x27\x39\x50\xE0\x02\x50\x19\xF2\x7A\xCF\x78\x23\x99\x01\xD4\xE5\xB1\xD1\x31\xE6\x6B\x84\xAF\xD0\x77\x41\x46\x85\xB0\x3B\xE6\x6A\x00\x0F\x3B\x7E\x95\x7F\x59\xA8\x22\xE8\x49\x49\x05\xC8\xCB\x6C\xEE\x47\xA7\x2D\xC9\x74\x5B\xEB\x8C\xD5\x99\xC2\xE2\x70\xDB\xEA\x87\x43\x84\x0E\x4F\x83\x1C\xA6\xEB\x1F\x22\x38\x17\x69\x9B\x72\x12\x95\x48\x71\xB2\x7B\x92\x73\x52\xAB\xE3\x1A\xA5\xD3\xF4\x44\x14\xBA\xC3\x35\xDA\x91\x6C\x7D\xB4\xC2\x00\x07\xD8\x0A\x51\xF1\x0D\x4C\xD9\x7A\xD1\x99\xE6\xA8\x8D\x0A\x80\xA8\x91\xDD\x8A\xA2\x6B\xF6\xDB\xB0\x3E\xC9\x71\xA9\xE0\x39\xC3\xA3\x58\x0D\x87\xD0\xB2\xA7\x9C\xB7\x69\x02\x03\x01\x00\x01\xA3\x82\x01\x1A\x30\x82\x01\x16\x30\x09\x06\x03\x55\x1D\x13\x04\x02\x30\x00\x30\x0B\x06\x03\x55\x1D\x0F\x04\x04\x03\x02\x03\xA8\x30\x1D\x06\x03\x55\x1D\x0E\x04\x16\x04\x14\x34\x91\x6E\x91\x32\xBF\x35\x25\x43\xCC\x28\x74\xEF\x82\xC2\x57\x92\x79\x13\x73\x30\x6D\x06\x03\x55\x1D\x23\x04\x66\x30\x64\x80\x14\x5D\xA7\xDD\x70\x06\x51\x32\x7E\xE7\xB6\x6D\xB3\xB5\xE5\xE0\x60\xEA\x2E\x4D\xEF\xA1\x49\xA4\x47\x30\x45\x31\x0B\x30\x09\x06\x03\x55\x04\x06\x13\x02\x43\x48\x31\x19\x30\x17\x06\x03\x55\x04\x0A\x13\x10\x4C\x69\x6E\x75\x78\x20\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x31\x1B\x30\x19\x06\x03\x55\x04\x03\x13\x12\x73\x74\x72\x6F\x6E\x67\x53\x77\x61\x6E\x20\x52\x6F\x6F\x74\x20\x43\x41\x82\x01\x00\x30\x1E\x06\x03\x55\x1D\x11\x04\x17\x30\x15\x82\x13\x6F\x63\x73\x70\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x30\x13\x06\x03\x55\x1D\x25\x04\x0C\x30\x0A\x06\x08\x2B\x06\x01\x05\x05\x07\x03\x09\x30\x39\x06\x03\x55\x1D\x1F\x04\x32\x30\x30\x30\x2E\xA0\x2C\xA0\x2A\x86\x28\x68\x74\x74\x70\x3A\x2F\x2F\x63\x72\x6C\x2E\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x6F\x72\x67\x2F\x73\x74\x72\x6F\x6E\x67\x73\x77\x61\x6E\x2E\x63\x72\x6C\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0B\x05\x00\x03\x82\x01\x01\x00\x6D\x78\xD7\x66\x90\xA6\xEB\xDD\xB5\x09\x48\xA4\xDA\x27\xFA\xAC\xB1\xBC\x8F\x8C\xBE\xCC\x8C\x09\xA2\x40\x0D\x6C\x4A\xAE\x72\x22\x1E\xC8\xAF\x6D\xF1\x12\xAF\xD7\x40\x51\x79\xD4\xDD\xB2\x0C\xDB\x97\x84\xB6\x24\xD5\xF5\xA8\xBB\xC0\x4B\xF9\x7F\x71\xF7\xB0\x65\x42\x4A\x7D\xFE\x76\x7E\x05\xD2\x46\xB8\x7D\xB3\x39\x4C\x5C\xB1\xFA\xB9\xEE\x3B\x70\x33\x39\x57\x1A\xB9\x95\x51\x33\x00\x25\x1B\x4C\xAA\xB4\xA7\x55\xAF\x63\x6D\x6F\x88\x17\x6A\x7F\xB0\x97\xDE\x49\x14\x6A\x27\x6A\xB0\x42\x80\xD6\xA6\x9B\xEF\x04\x5E\x11\x7D\xD5\x8E\x54\x20\xA2\x76\xD4\x66\x58\xAC\x9C\x12\xD3\xF5\xCA\x54\x98\xCA\x21\xEC\xC1\x55\xA1\x2F\x68\x0B\x5D\x04\x50\xD2\x5E\x70\x25\xD8\x13\xD9\x44\x51\x0E\x8A\x42\x08\x18\x84\xE6\x61\xCE\x5A\x7D\x7B\x81\x35\x90\xC3\xD4\x9D\x19\xB6\x37\xEE\x8F\x63\x5C\xDA\xD8\xF0\x64\x60\x39\xEB\x9B\x1C\x54\x66\x75\x76\xB5\x0A\x58\xB9\x3F\x91\xE1\x21\x9C\xA0\x50\x15\x97\xB6\x7E\x41\xBC\xD0\xC4\x21\x4C\xF5\xD7\xF0\x13\xF8\x77\xE9\x74\xC4\x8A\x0E\x20\x17\x32\xAE\x38\xC2\xA5\xA8\x62\x85\x17\xB1\xA2\xD3\x22\x9F\x95\xB7\xA3\x4C"
+
+static gnutls_datum_t ocsp_resp1 =
+ { (unsigned char *) RESP1, sizeof(RESP1) - 1 };
+
+#define RESP3 "\x30\x82\x01\xd3\x0a\x01\x00\xa0\x82\x01\xcc\x30\x82\x01\xc8\x06\x09\x2b\x06\x01\x05\x05\x07\x30\x01\x01\x04\x82\x01\xb9\x30\x82\x01\xb5\x30\x81\x9e\xa2\x16\x04\x14\x50\xea\x73\x89\xdb\x29\xfb\x10\x8f\x9e\xe5\x01\x20\xd4\xde\x79\x99\x48\x83\xf7\x18\x0f\x32\x30\x31\x34\x30\x39\x30\x34\x30\x35\x34\x39\x30\x30\x5a\x30\x73\x30\x71\x30\x49\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14\xed\x48\xad\xdd\xcb\x7b\x00\xe2\x0e\x84\x2a\xa9\xb4\x09\xf1\xac\x30\x34\xcf\x96\x04\x14\x50\xea\x73\x89\xdb\x29\xfb\x10\x8f\x9e\xe5\x01\x20\xd4\xde\x79\x99\x48\x83\xf7\x02\x10\x02\x01\x48\x91\x5d\xfd\x5e\xb6\xe0\x02\x90\xa9\x67\xb0\xe4\x64\x80\x00\x18\x0f\x32\x30\x31\x34\x30\x39\x30\x34\x30\x35\x34\x39\x30\x30\x5a\xa0\x11\x18\x0f\x32\x30\x31\x34\x30\x39\x31\x31\x30\x36\x30\x34\x30\x30\x5a\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\x6e\x5e\x5e\x81\xff\x3f\x4d\xc7\x53\xc7\x1b\xf3\xd3\x1d\xdc\x9a\xc7\xce\x77\x2c\x67\x56\x13\x98\x91\x02\x01\x76\xdc\x48\xb2\x1f\x9b\x17\xea\xbf\x2c\x0a\xf5\x1d\x98\x90\x3c\x5f\x55\xc2\xff\x4b\x9a\xbc\xa6\x83\x9e\xab\x2b\xeb\x9d\x01\xea\x3b\x5f\xbe\x03\x29\x70\x63\x2a\xa4\x1d\xa8\xab\x69\xb2\x64\xba\x5d\x73\x91\x5c\x92\xf3\x69\xd4\xc9\x39\x9c\x7c\x7d\xa2\x47\x92\xc2\x56\xfe\xa1\x0d\x4a\x69\xff\xda\x48\xc5\x5e\xd8\xab\x39\x88\x6a\x06\xfa\x07\x57\xd6\x48\xb5\xce\xc9\x5f\xa5\x96\xfe\x37\x18\x5e\x7f\x35\x51\xc1\x9e\x79\x5a\x26\xba\x67\x67\x38\x2a\x80\x75\x42\x99\x68\x3e\xec\x2f\x7e\x2d\xa1\xa6\xbe\x9f\x01\x51\x22\x88\x3a\xc9\x9c\xed\x51\xef\x21\x66\x7e\xa9\xd0\x3f\x13\x9c\xbb\xd2\x94\x14\x6f\x4b\xd9\xc4\xf5\x2c\xf5\x7d\x07\x68\xf3\x51\xac\xda\xc2\x09\x66\xa9\x3d\xed\xad\x02\x4d\x9c\x11\x29\x1a\x54\xfb\x1e\x7e\x36\xf4\xbb\x0d\x08\x8c\x6a\x42\x08\x10\x29\x08\x7c\x56\x0b\x18\x47\xff\x87\x11\xfd\xb2\xfb\xc9\x22\x7f\xe3\x1f\x7b\xf9\x98\xaa\x3a\x32\xb6\x2f\x02\xba\xb6\xc1\xdc\xc3\x5d\xb5\x4b\xae\x5d\x29\x6a\x31\xde\xcd"
+static gnutls_datum_t ocsp_resp2 =
+ { (unsigned char *) RESP3, sizeof(RESP3) - 1 };
+
+
+static void check_response(gnutls_session_t session, void *priv)
+{
+ int ret;
+ gnutls_datum_t resp;
+ gnutls_datum_t *ocsp = priv;
+ unsigned i;
+
+ assert(ocsp != NULL);
+
+ for (i=0;;i++) {
+ ret = gnutls_ocsp_status_request_get2(session, i, &resp);
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (ret < 0) {
+ if (ocsp->size == 0)
+ return;
+ fail("no response was received: %s\n", gnutls_strerror(ret));
+ }
+
+ if (ocsp->size == 0) {
+ fail("not expected response, but received one\n");
+ }
+
+ if (resp.size != ocsp->size) {
+ fail("did not receive the expected response size for %d\n", i);
+ }
+
+ if (memcmp(resp.data, ocsp->data, resp.size) != 0) {
+ fail("did not receive the expected response for %d\n", i);
+ }
+ }
+
+ if (i != 1) {
+ fail("The number of OCSP responses received (%d) does not match the expected (%d)\n", i, 1);
+ }
+}
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+void doit(void)
+{
+ int ret;
+ gnutls_certificate_credentials_t xcred;
+ gnutls_certificate_credentials_t clicred;
+ const char *certfile1;
+ const char *certfile2;
+ const char *certfile3;
+ char certname1[TMPNAME_SIZE];
+ char certname2[TMPNAME_SIZE];
+ char certname3[TMPNAME_SIZE];
+ FILE *fp;
+ unsigned index1, index2; /* indexes of certs */
+
+ global_init();
+ gnutls_global_set_time_function(mytime);
+
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(4711);
+
+ assert(gnutls_certificate_allocate_credentials(&xcred) >= 0);
+ assert(gnutls_certificate_allocate_credentials(&clicred) >= 0);
+
+ gnutls_certificate_set_flags(clicred, GNUTLS_CERTIFICATE_API_V2);
+
+ certfile1 = get_tmpname(certname1);
+
+ /* set cert with localhost name */
+ fp = fopen(certfile1, "wb");
+ if (fp == NULL)
+ fail("error in fopen\n");
+ assert(fwrite(server_localhost_ca3_cert_chain_pem, 1, strlen(server_localhost_ca3_cert_chain_pem), fp)>0);
+ assert(fwrite(server_ca3_key_pem, 1, strlen((char*)server_ca3_key_pem), fp)>0);
+ fclose(fp);
+
+ ret = gnutls_certificate_set_x509_key_file2(xcred, certfile1, certfile1,
+ GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (ret < 0)
+ fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+
+ /* load client certificates */
+ certfile2 = get_tmpname(certname2);
+
+ fp = fopen(certfile2, "wb");
+ if (fp == NULL)
+ fail("error in fopen\n");
+ assert(fwrite(cli_ca3_cert_pem, 1, strlen(cli_ca3_cert_pem), fp)>0);
+ assert(fwrite(cli_ca3_key_pem, 1, strlen(cli_ca3_key_pem), fp)>0);
+ fclose(fp);
+ ret = gnutls_certificate_set_x509_key_file2(clicred, certfile2, certfile2,
+ GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (ret < 0)
+ fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+ index1 = ret;
+
+
+ certfile3 = get_tmpname(certname3);
+ fp = fopen(certfile3, "wb");
+ if (fp == NULL)
+ fail("error in fopen\n");
+ assert(fwrite(cert_pem, 1, strlen((char*)cert_pem), fp)>0);
+ assert(fwrite(key_pem, 1, strlen((char*)key_pem), fp)>0);
+ fclose(fp);
+
+ ret = gnutls_certificate_set_x509_key_file2(clicred, certfile3, certfile3,
+ GNUTLS_X509_FMT_PEM, NULL, 0);
+ if (ret < 0)
+ fail("set_x509_key_file failed: %s\n", gnutls_strerror(ret));
+ index2 = ret;
+
+
+ gnutls_certificate_set_flags(clicred, GNUTLS_CERTIFICATE_SKIP_OCSP_RESPONSE_CHECK);
+ /* set OCSP response1 */
+ ret = gnutls_certificate_set_ocsp_status_request_mem(clicred, &ocsp_resp2, index2, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+ /* set OCSP response2 */
+ ret = gnutls_certificate_set_ocsp_status_request_mem(clicred, &ocsp_resp1, index1, GNUTLS_X509_FMT_DER);
+ if (ret < 0)
+ fail("ocsp file set failed: %s\n", gnutls_strerror(ret));
+
+ /* make sure that our invalid OCSP responses are not considered in verification
+ */
+ gnutls_certificate_set_verify_flags(clicred, GNUTLS_VERIFY_DISABLE_CRL_CHECKS);
+ if (gnutls_certificate_get_verify_flags(clicred) != GNUTLS_VERIFY_DISABLE_CRL_CHECKS)
+ fail("error in gnutls_certificate_set_verify_flags\n");
+
+ ret = gnutls_certificate_set_x509_trust_mem(clicred, &ca3_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fail("error in setting trust cert: %s\n", gnutls_strerror(ret));
+ }
+
+ ret = gnutls_certificate_set_x509_trust_mem(xcred, &subca3_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fail("error in setting trust cert: %s\n", gnutls_strerror(ret));
+ }
+
+ _test_cli_serv(xcred, clicred, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3",
+ "localhost",
+ &ocsp_resp1, NULL, check_response,
+ 0, 1, 0, 0);
+
+ gnutls_certificate_free_credentials(xcred);
+ gnutls_certificate_free_credentials(clicred);
+ gnutls_global_deinit();
+ remove(certfile1);
+ remove(certfile2);
+ remove(certfile3);
+}
+
+#else
+void doit(void)
+{
+ exit(77);
+}
+#endif
diff --git a/tests/tls13/post-handshake-with-cert-auto.c b/tests/tls13/post-handshake-with-cert-auto.c
new file mode 100644
index 0000000..8d3c4e8
--- /dev/null
+++ b/tests/tls13/post-handshake-with-cert-auto.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+#define MAX_AUTHS 4
+
+/* This program tests whether the Post Handshake Auth extension is
+ * present in the client hello, and whether it is missing from server
+ * hello. In addition it contains basic functionality test for
+ * post handshake authentication.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+#define MAX_APP_DATA 3
+
+static void client(int fd, unsigned send_cert, unsigned max_auths)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ char buf[64];
+ unsigned i;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH|GNUTLS_AUTO_REAUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (send_cert) {
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (debug)
+ success("client handshake completed\n");
+
+ gnutls_record_set_timeout(session, 20 * 1000);
+
+ for (i=0;i<max_auths;i++) {
+ if (debug)
+ success("waiting for auth nr %d\n", i);
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0)
+ fail("client: gnutls_record_recv did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+ assert(ret == 0);
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_WR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_POST_HANDSHAKE 49
+
+static void parse_ext(void *priv, gnutls_datum_t *msg)
+{
+ if (msg->size != 0) {
+ fail("error in extension length: %d\n", (int)msg->size);
+ }
+}
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Post handshake extension seen in server hello!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, parse_ext))
+ client_hello_ok = 1;
+ else
+ fail("Post handshake extension NOT seen in client hello!\n");
+
+ return 0;
+}
+
+static void server(int fd, int err, int type, unsigned max_auths)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned i, retries;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH)) {
+ fail("server: session flags did not contain GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH\n");
+ }
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ if (debug)
+ success("server handshake completed\n");
+
+ gnutls_certificate_server_set_request(session, type);
+
+ /* i = 0 */
+ /* ask peer for re-authentication */
+ retries = 0;
+ do {
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == GNUTLS_E_GOT_APPLICATION_DATA) {
+ int ret2;
+ do {
+ ret2 = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret2 == GNUTLS_E_AGAIN || ret2 == GNUTLS_E_INTERRUPTED);
+
+ if (ret2 < 0)
+ fail("error receiving app data: %s\n", gnutls_strerror(ret2));
+
+ /* sender memsets the message with the retry attempt */
+ assert((uint8_t)buffer[0] == retries);
+ assert(retries < MAX_APP_DATA);
+ }
+
+ retries++;
+ } while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+
+ for (i=1;i<max_auths;i++) {
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static
+void start(const char *name, int err, int type, unsigned max_auths, unsigned send_cert)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+ int status = 0;
+
+ success("testing %s\n", name);
+
+ client_hello_ok = 0;
+ server_hello_ok = 0;
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], err, type, max_auths);
+ kill(child, SIGTERM);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], send_cert, max_auths);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ start("multi-reauth", 0, GNUTLS_CERT_REQUIRE, MAX_AUTHS, 1);
+ start("reauth-require with no-cert", GNUTLS_E_CERTIFICATE_REQUIRED, GNUTLS_CERT_REQUIRE, 1, 0);
+ start("reauth-request with no-cert", 0, GNUTLS_CERT_REQUEST, 1, 0);
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/post-handshake-with-cert-pkcs11.c b/tests/tls13/post-handshake-with-cert-pkcs11.c
new file mode 100644
index 0000000..f588250
--- /dev/null
+++ b/tests/tls13/post-handshake-with-cert-pkcs11.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2017-2019 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "pkcs11/softhsm.h"
+#include "utils.h"
+
+/* This program tests whether the Post Handshake Auth extension is
+ * present in the client hello, and whether it is missing from server
+ * hello. In addition it contains basic functionality test for
+ * post handshake authentication.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+
+#define P11LIB "libpkcs11mock2.so"
+
+#define PIN "1234"
+
+#define CONFIG_NAME "softhsm-post-handshake-with-cert-pkcs11"
+#define CONFIG CONFIG_NAME".config"
+
+static
+int pin_func(void *userdata, int attempt, const char *url, const char *label,
+ unsigned flags, char *pin, size_t pin_max)
+{
+ if (attempt == 0) {
+ strcpy(pin, PIN);
+ return 0;
+ }
+ return -1;
+}
+
+static void client(int fd, int err)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ gnutls_x509_crt_t crt;
+ gnutls_x509_privkey_t key;
+ gnutls_datum_t tmp;
+ const char *lib;
+ char buffer[MAX_BUF + 1];
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ /* point to SoftHSM token that libpkcs11mock2.so internally uses */
+ setenv(SOFTHSM_ENV, CONFIG, 1);
+
+ gnutls_pkcs11_set_pin_function(pin_func, NULL);
+
+ lib = getenv("P11MOCKLIB2");
+ if (lib == NULL)
+ lib = P11LIB;
+
+ ret = gnutls_pkcs11_init(GNUTLS_PKCS11_FLAG_MANUAL, NULL);
+ if (ret != 0) {
+ fail("%d: %s\n", ret, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret = gnutls_pkcs11_add_provider(lib, NULL);
+ if (ret != 0) {
+ fail("%d: %s\n", ret, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret = gnutls_x509_crt_init(&crt);
+ if (ret < 0) {
+ fprintf(stderr,
+ "gnutls_x509_crt_init: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret = gnutls_x509_crt_import(crt, &cli_ca3_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fprintf(stderr,
+ "gnutls_x509_crt_import: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ if (debug) {
+ gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &tmp);
+
+ printf("\tCertificate: %.*s\n", tmp.size, tmp.data);
+ gnutls_free(tmp.data);
+ }
+
+ ret = gnutls_x509_privkey_init(&key);
+ if (ret < 0) {
+ fprintf(stderr,
+ "gnutls_x509_privkey_init: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret = gnutls_x509_privkey_import(key, &cli_ca3_key, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ fprintf(stderr,
+ "gnutls_x509_privkey_import: %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+
+ /* initialize softhsm token */
+ ret = gnutls_pkcs11_token_init(SOFTHSM_URL, PIN, "test");
+ if (ret < 0) {
+ fail("gnutls_pkcs11_token_init: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret =
+ gnutls_pkcs11_token_set_pin(SOFTHSM_URL, NULL, PIN,
+ GNUTLS_PIN_USER);
+ if (ret < 0) {
+ fail("gnutls_pkcs11_token_set_pin: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret = gnutls_pkcs11_copy_x509_crt(SOFTHSM_URL, crt, "cert",
+ GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE |
+ GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
+ if (ret < 0) {
+ fail("gnutls_pkcs11_copy_x509_crt: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+
+ ret =
+ gnutls_pkcs11_copy_x509_privkey(SOFTHSM_URL, key, "cert",
+ GNUTLS_KEY_DIGITAL_SIGNATURE |
+ GNUTLS_KEY_KEY_ENCIPHERMENT,
+ GNUTLS_PKCS11_OBJ_FLAG_MARK_PRIVATE
+ |
+ GNUTLS_PKCS11_OBJ_FLAG_MARK_SENSITIVE
+ | GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
+ if (ret < 0) {
+ fail("gnutls_pkcs11_copy_x509_privkey: %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+
+ gnutls_x509_crt_deinit(crt);
+ gnutls_x509_privkey_deinit(key);
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH|GNUTLS_AUTO_REAUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:-SIGN-RSA-SHA256", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ assert(gnutls_certificate_set_x509_key_file(x509_cred,
+ SOFTHSM_URL
+ ";object=cert;object-type=cert",
+ SOFTHSM_URL
+ ";object=cert;object-type=private;pin-value="
+ PIN,
+ GNUTLS_X509_FMT_DER)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (debug)
+ success("client handshake completed\n");
+
+ gnutls_record_set_timeout(session, 20 * 1000);
+
+ if (debug)
+ success("waiting for auth\n");
+
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (err) {
+ if (ret != err)
+ fail("client: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret < 0)
+ fail("client: gnutls_record_recv did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_WR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_POST_HANDSHAKE 49
+
+static void parse_ext(void *priv, gnutls_datum_t *msg)
+{
+ if (msg->size != 0) {
+ fail("error in extension length: %d\n", (int)msg->size);
+ }
+}
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Post handshake extension seen in server hello!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, parse_ext))
+ client_hello_ok = 1;
+ else
+ fail("Post handshake extension NOT seen in client hello!\n");
+
+ return 0;
+}
+
+static void server(int fd, int err, int type)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH)) {
+ fail("server: session flags did not contain GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH\n");
+ }
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ if (debug)
+ success("server handshake completed\n");
+
+ gnutls_certificate_server_set_request(session, type);
+
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static
+void start(const char *name, int err, int cli_err, int type)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+ int status = 0;
+
+ success("testing %s\n", name);
+
+ client_hello_ok = 0;
+ server_hello_ok = 0;
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], err, type);
+ kill(child, SIGTERM);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], cli_err);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ const char *bin;
+ char buf[128];
+
+ if (gnutls_fips140_mode_enabled())
+ exit(77);
+
+ /* check if softhsm module is loadable */
+ (void) softhsm_lib();
+
+ /* initialize SoftHSM token that libpkcs11mock2.so internally uses */
+ bin = softhsm_bin();
+
+ set_softhsm_conf(CONFIG);
+ snprintf(buf, sizeof(buf),
+ "%s --init-token --slot 0 --label test --so-pin " PIN " --pin "
+ PIN, bin);
+ system(buf);
+
+ start("reauth-require", GNUTLS_E_CERTIFICATE_REQUIRED, GNUTLS_E_SUCCESS, GNUTLS_CERT_REQUIRE);
+ start("reauth-request", 0, GNUTLS_E_SUCCESS, GNUTLS_CERT_REQUEST);
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/post-handshake-with-cert-ticket.c b/tests/tls13/post-handshake-with-cert-ticket.c
new file mode 100644
index 0000000..b19720f
--- /dev/null
+++ b/tests/tls13/post-handshake-with-cert-ticket.c
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "../lib/handshake-defs.h"
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests whether the certificate seen in Post Handshake Auth
+ * is found in a resumed session under TLS 1.3.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+static unsigned tickets_seen = 0;
+static int ticket_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ gnutls_datum *d;
+ static int counter = 0;
+ int ret;
+
+ assert(htype == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET);
+
+ counter++;
+ if (counter <= TLS13_TICKETS_TO_SEND) /* ignore the default tickets sent */
+ return 0;
+
+ d = gnutls_session_get_ptr(session);
+
+ if (post == GNUTLS_HOOK_POST) {
+ tickets_seen++;
+ if (d->data)
+ gnutls_free(d->data);
+ ret = gnutls_session_get_data2(session, d);
+ assert(ret >= 0);
+ assert(d->size > 4);
+
+ return 0;
+ }
+
+ return 0;
+}
+
+static void client(int fd, unsigned tickets)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ char buf[64];
+ unsigned try = 0;
+ gnutls_datum_t session_data = {NULL, 0};
+
+ global_init();
+ tickets_seen = 0;
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ retry:
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (try == 0) {
+ gnutls_session_set_ptr(session, &session_data);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
+ GNUTLS_HOOK_BOTH,
+ ticket_callback);
+ } else {
+ assert(gnutls_session_set_data(session, session_data.data, session_data.size) >= 0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (try == 0) {
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != GNUTLS_E_REAUTH_REQUEST) {
+ fail("recv: unexpected error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("received reauth request\n");
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0)
+ fail("client: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ } else {
+ assert(gnutls_session_is_resumed(session) != 0);
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0) {
+ fail("error in recv: %s\n", gnutls_strerror(ret));
+ }
+
+ assert(tickets_seen == tickets+1);
+
+ gnutls_deinit(session);
+
+ if (try == 0) {
+ try++;
+ goto retry;
+ }
+
+ close(fd);
+ gnutls_free(session_data.data);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static void compare(const gnutls_datum_t *der, const void *ipem)
+{
+ gnutls_datum_t pem = {(void*)ipem, strlen((char*)ipem)};
+ gnutls_datum_t new_der;
+ int ret;
+
+ ret = gnutls_pem_base64_decode2("CERTIFICATE", &pem, &new_der);
+ if (ret < 0) {
+ fail("error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (der->size != new_der.size || memcmp(der->data, new_der.data, der->size) != 0) {
+ fail("error in %d: %s\n", __LINE__, "cert don't match");
+ exit(1);
+ }
+ gnutls_free(new_der.data);
+ return;
+}
+
+static void server(int fd, unsigned tickets)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned clist_size;
+ gnutls_datum_t skey;
+ const gnutls_datum_t *clist;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUIRE);
+
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ if (tickets == 0) {
+ /* test whether the expected error code would be returned */
+ ret = gnutls_session_ticket_send(session, 0, 0);
+ assert(ret == GNUTLS_E_INVALID_REQUEST);
+ } else {
+ /* send tickets after re-auth */
+ do {
+ ret = gnutls_session_ticket_send(session, tickets, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ }
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_deinit(session);
+
+ /* resume session
+ */
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+ gnutls_handshake_set_timeout(session, get_timeout());
+ assert(gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ assert(gnutls_session_is_resumed(session) != 0);
+
+ /* check if cert is visible */
+ if (tickets > 0) {
+ clist = gnutls_certificate_get_peers(session, &clist_size);
+ assert(clist != NULL);
+ assert(clist_size > 0);
+
+ compare(&clist[0], cli_ca3_cert.data);
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ gnutls_deinit(session);
+
+ gnutls_free(skey.data);
+ close(fd);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+static void start(const char *name, unsigned tickets)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ success("testing: %s\n", name);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], tickets);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1], tickets);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ start("no ticket", 0);
+ start("single ticket", 1);
+ start("8 tickets", 8);
+ start("16 tickets", 16);
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/post-handshake-with-cert.c b/tests/tls13/post-handshake-with-cert.c
new file mode 100644
index 0000000..d5912fe
--- /dev/null
+++ b/tests/tls13/post-handshake-with-cert.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+#define MAX_AUTHS 4
+
+/* This program tests whether the Post Handshake Auth extension is
+ * present in the client hello, and whether it is missing from server
+ * hello. In addition it contains basic functionality test for
+ * post handshake authentication.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+#define MAX_APP_DATA 3
+
+static void client(int fd, unsigned send_cert, unsigned max_auths)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ char buf[64];
+ unsigned i, j;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (send_cert) {
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (debug)
+ success("client handshake completed\n");
+
+ gnutls_record_set_timeout(session, 20 * 1000);
+
+ for (i=0;i<max_auths;i++) {
+ if (debug)
+ success("waiting for auth nr %d\n", i);
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != GNUTLS_E_REAUTH_REQUEST) {
+ fail("recv: unexpected error: %s\n", gnutls_strerror(ret));
+ }
+
+ /* send application data to check if server tolerates them */
+ if (i==0) {
+ for (j=0;j<MAX_APP_DATA;j++) {
+ memset(buf, j, sizeof(buf));
+ do {
+ ret = gnutls_record_send(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ assert(ret>=0);
+ }
+ }
+
+ if (debug)
+ success("received reauth request\n");
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0)
+ fail("client: gnutls_reauth %d did not succeed as expected: %s\n", i, gnutls_strerror(ret));
+ }
+
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_POST_HANDSHAKE 49
+
+static void parse_ext(void *priv, gnutls_datum_t *msg)
+{
+ if (msg->size != 0) {
+ fail("error in extension length: %d\n", (int)msg->size);
+ }
+}
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Post handshake extension seen in server hello!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, parse_ext))
+ client_hello_ok = 1;
+ else
+ fail("Post handshake extension NOT seen in client hello!\n");
+
+ return 0;
+}
+
+static void server(int fd, int err, int type, unsigned max_auths, int child)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned i, retries;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH)) {
+ fail("server: session flags did not contain GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH\n");
+ }
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ if (debug)
+ success("server handshake completed\n");
+
+ gnutls_certificate_server_set_request(session, type);
+
+ /* i = 0 */
+ /* ask peer for re-authentication */
+ retries = 0;
+ do {
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == GNUTLS_E_GOT_APPLICATION_DATA) {
+ int ret2;
+ do {
+ ret2 = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while (ret2 == GNUTLS_E_AGAIN || ret2 == GNUTLS_E_INTERRUPTED);
+
+ if (ret2 < 0)
+ fail("error receiving app data: %s\n", gnutls_strerror(ret2));
+
+ /* sender memsets the message with the retry attempt */
+ assert((uint8_t)buffer[0] == retries);
+ assert(retries < MAX_APP_DATA);
+ }
+
+ retries++;
+ } while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+
+ for (i=1;i<max_auths;i++) {
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0)
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+ waitpid(child, NULL, 0);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+static
+void start(const char *name, int err, int type, unsigned max_auths, unsigned send_cert)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ success("testing %s\n", name);
+
+ client_hello_ok = 0;
+ server_hello_ok = 0;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], err, type, max_auths, child);
+ } else {
+ close(fd[0]);
+ client(fd[1], send_cert, max_auths);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ start("multi-reauth", 0, GNUTLS_CERT_REQUIRE, MAX_AUTHS, 1);
+ start("reauth-require with no-cert", GNUTLS_E_CERTIFICATE_REQUIRED, GNUTLS_CERT_REQUIRE, 1, 0);
+ start("reauth-request with no-cert", 0, GNUTLS_CERT_REQUEST, 1, 0);
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/post-handshake-with-psk.c b/tests/tls13/post-handshake-with-psk.c
new file mode 100644
index 0000000..8921bbd
--- /dev/null
+++ b/tests/tls13/post-handshake-with-psk.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+#define MAX_AUTHS 4
+
+/* This program tests whether the Post Handshake Auth would work
+ * under PSK authentication. */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+
+static void client(int fd, unsigned send_cert, unsigned max_auths)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_psk_client_credentials_t pskcred;
+ const gnutls_datum_t key = { (void *) "DEADBEEF", 8 };
+ gnutls_session_t session;
+ char buf[64];
+ unsigned i;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ assert(gnutls_psk_allocate_client_credentials(&pskcred)>=0);
+ assert(gnutls_psk_set_client_credentials(pskcred, "test", &key,
+ GNUTLS_PSK_KEY_HEX)>=0);
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ /* Initialize TLS session
+ */
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0:+ECDHE-PSK:+PSK", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+
+ if (send_cert) {
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM)>=0);
+ }
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_credentials_set(session, GNUTLS_CRD_PSK, pskcred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (debug)
+ success("client handshake completed\n");
+
+ assert(gnutls_kx_get(session) == GNUTLS_KX_ECDHE_PSK);
+
+ gnutls_record_set_timeout(session, 20 * 1000);
+
+ for (i=0;i<max_auths;i++) {
+ if (debug)
+ success("waiting for post-handshake auth request\n");
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != GNUTLS_E_REAUTH_REQUEST) {
+ fail("recv: unexpected error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("received reauth request\n");
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret != 0)
+ fail("client: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_psk_free_client_credentials(pskcred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_POST_HANDSHAKE 49
+
+static void parse_ext(void *priv, gnutls_datum_t *msg)
+{
+ if (msg->size != 0) {
+ fail("error in extension length: %d\n", (int)msg->size);
+ }
+}
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Post handshake extension seen in server hello!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, parse_ext))
+ client_hello_ok = 1;
+ else
+ fail("Post handshake extension NOT seen in client hello!\n");
+
+ return 0;
+}
+
+static int
+pskfunc(gnutls_session_t session, const char *username,
+ gnutls_datum_t * key)
+{
+ if (debug)
+ printf("psk: username %s\n", username);
+ key->data = gnutls_malloc(4);
+ key->data[0] = 0xDE;
+ key->data[1] = 0xAD;
+ key->data[2] = 0xBE;
+ key->data[3] = 0xEF;
+ key->size = 4;
+ return 0;
+}
+
+static void server(int fd, int err, int type, unsigned max_auths)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_psk_server_credentials_t server_pskcred;
+ unsigned i;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ assert(gnutls_psk_allocate_server_credentials(&server_pskcred)>=0);
+ gnutls_psk_set_server_credentials_function(server_pskcred,
+ pskfunc);
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER|GNUTLS_POST_HANDSHAKE_AUTH)>=0);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ assert(gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK:+ECDHE-PSK", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_PSK, server_pskcred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != 0)
+ fail("handshake failed: %s\n", gnutls_strerror(ret));
+
+ if (!(gnutls_session_get_flags(session) & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH)) {
+ fail("server: session flags did not contain GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH\n");
+ }
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ if (debug)
+ success("server handshake completed\n");
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_certificate_server_set_request(session, type);
+
+ for (i=0;i<max_auths;i++) {
+ /* ask peer for re-authentication */
+ do {
+ ret = gnutls_reauth(session, 0);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (err) {
+ if (ret != err)
+ fail("server: expected error %s, got: %s\n", gnutls_strerror(err),
+ gnutls_strerror(ret));
+ } else if (ret != 0) {
+ fail("server: gnutls_reauth did not succeed as expected: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("server: sent post-handshake auth request\n");
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_psk_free_server_credentials(server_pskcred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+static
+void start(const char *name, int err, int type, unsigned max_auths, unsigned send_cert)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ success("testing %s\n", name);
+
+ client_hello_ok = 0;
+ server_hello_ok = 0;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0], err, type, max_auths);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1], send_cert, max_auths);
+ exit(0);
+ }
+
+}
+
+void doit(void)
+{
+ start("multi-reauth", 0, GNUTLS_CERT_REQUIRE, MAX_AUTHS, 1);
+ start("reauth-require with no-cert", GNUTLS_E_CERTIFICATE_REQUIRED, GNUTLS_CERT_REQUIRE, 1, 0);
+ start("reauth-request with no-cert", 0, GNUTLS_CERT_REQUEST, 1, 0);
+}
+#endif /* _WIN32 */
diff --git a/tests/tls13/post-handshake-without-cert.c b/tests/tls13/post-handshake-without-cert.c
new file mode 100644
index 0000000..cd3545e
--- /dev/null
+++ b/tests/tls13/post-handshake-without-cert.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+/* This program tests whether the Post Handshake Auth extension is missing
+ * from both hellos, when not enabled by client.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ assert(gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ /* try if gnutls_reauth() would fail as expected */
+ ret = gnutls_reauth(session, 0);
+ if (ret != GNUTLS_E_INVALID_REQUEST)
+ fail("server: gnutls_reauth did not fail as expected: %s", gnutls_strerror(ret));
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned server_hello_ok = 0;
+
+#define TLS_EXT_POST_HANDSHAKE 49
+
+static int hellos_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ if (find_server_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Post handshake extension seen in server hello!\n");
+ }
+ server_hello_ok = 1;
+
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL))
+ fail("Post handshake extension seen in client hello with no cert!\n");
+
+ return 0;
+}
+
+static void server(int fd)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ hellos_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH)) {
+ fail("server: session flags did contain GNUTLS_SFLAGS_POST_HANDSHAKE_AUTH\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ /* try if gnutls_reauth() would fail as expected */
+ ret = gnutls_reauth(session, 0);
+ if (ret != GNUTLS_E_INVALID_REQUEST)
+ fail("server: gnutls_reauth did not fail as expected: %s", gnutls_strerror(ret));
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/prf-early.c b/tests/tls13/prf-early.c
new file mode 100644
index 0000000..b97dc24
--- /dev/null
+++ b/tests/tls13/prf-early.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2015-2019 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined(__linux__) || !defined(__GNUC__)
+
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "virt-time.h"
+
+static void terminate(void);
+
+#define SESSIONS 2
+#define MAX_BUF 5*1024
+#define MSG "Hello TLS"
+
+extern unsigned int _gnutls_global_version;
+
+/* This program tests whether the gnutls_prf() works as
+ * expected.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+/* These are global */
+static pid_t child;
+
+static const
+gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+static const
+gnutls_datum_t hsrnd = {(void*)"\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+
+static int gnutls_rnd_works;
+
+int __attribute__ ((visibility ("protected")))
+gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len)
+{
+ gnutls_rnd_works = 1;
+
+ memset(data, 0xff, len);
+
+ /* Flip the first byte to avoid infinite loop in the RSA
+ * blinding code of Nettle */
+ if (len > 0)
+ memset(data, 0x0, 1);
+ return 0;
+}
+
+static gnutls_datum_t session_ticket_key = { NULL, 0 };
+
+static void dump(const char *name, const uint8_t *data, unsigned data_size)
+{
+ unsigned i;
+
+ fprintf(stderr, "%s", name);
+ for (i=0;i<data_size;i++)
+ fprintf(stderr, "\\x%.2x", (unsigned)data[i]);
+ fprintf(stderr, "\n");
+}
+
+#define TRY(label_size, label, extra_size, extra, size, exp) \
+ { \
+ ret = gnutls_prf_early(session, label_size, label, extra_size, extra, size, \
+ (void*)key_material); \
+ if (ret < 0) { \
+ fprintf(stderr, "gnutls_prf_early: error in %d\n", __LINE__); \
+ gnutls_perror(ret); \
+ exit(1); \
+ } \
+ if (memcmp(key_material, exp, size) != 0) { \
+ fprintf(stderr, "gnutls_prf_early: output doesn't match for '%s'\n", label); \
+ dump("got ", key_material, size); \
+ dump("expected ", exp, size); \
+ exit(1); \
+ } \
+ }
+
+#define KEY_EXP_VALUE "\xec\xc2\x4a\x6b\x07\x89\xd9\x19\xd9\x73\x6d\xd0\x00\x73\xc9\x7a\xd7\x92\xef\x56\x91\x61\xb4\xff\x5f\xef\x81\xc1\x98\x68\x4e\xdf\xd7\x7e"
+#define HELLO_VALUE "\x4f\x85\x33\x64\x48\xff\x0d\x8b\xd5\x50\x0f\x97\x91\x5b\x7d\x8d\xc9\x05\x91\x45\x4f\xb9\x4b\x4b\xbc\xbf\x58\x84\x1a\x46\xe3"
+#define CONTEXT_VALUE "\x11\x8d\x85\xa8\x91\xe5\x50\x75\x44\x88\x69\xaf\x95\x9a\xb0\x29\xd4\xae\xcd\x11\xcb\x1d\x29\x7c\xe6\x24\xd4\x7c\x95\xdb\x5c"
+#define NULL_CONTEXT_VALUE "\x56\x99\x41\x73\x5e\x73\x34\x7f\x3d\x69\x9f\xc0\x3b\x8b\x86\x33\xc6\xc3\x97\x46\x61\x62\x3f\x55\xab\x39\x60\xa5\xeb\xfe\x37"
+
+static int handshake_callback_called;
+
+static int handshake_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ unsigned char key_material[512];
+ int ret;
+
+ assert(post == GNUTLS_HOOK_POST);
+
+ handshake_callback_called++;
+
+ TRY(13, "key expansion", 0, NULL, 34, (uint8_t*)KEY_EXP_VALUE);
+ TRY(6, "hello", 0, NULL, 31, (uint8_t*)HELLO_VALUE);
+ TRY(7, "context", 5, "abcd\xfa", 31, (uint8_t*)CONTEXT_VALUE);
+ TRY(12, "null-context", 0, "", 31, (uint8_t*)NULL_CONTEXT_VALUE);
+
+ return 0;
+}
+
+static void client(int sds[])
+{
+ gnutls_session_t session;
+ int ret, ii;
+ gnutls_certificate_credentials_t clientx509cred;
+ const char *err;
+ int t;
+ gnutls_datum_t session_data = {NULL, 0};
+ char buffer[MAX_BUF + 1];
+
+ global_init();
+
+ /* date --date='TZ="UTC" 2019-04-12' +%s */
+ virt_time_init_at(1555027200);
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&clientx509cred);
+
+ for (t = 0; t < SESSIONS; t++) {
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ ret = gnutls_priority_set_direct(session,
+ "NONE:+VERS-TLS1.3:+AES-256-GCM:+AEAD:+SIGN-RSA-PSS-RSAE-SHA384:+GROUP-SECP256R1",
+ &err);
+ if (ret < 0) {
+ fail("client: priority set failed (%s): %s\n",
+ gnutls_strerror(ret), err);
+ exit(1);
+ }
+
+ ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_handshake_set_random(session, &hrnd);
+ gnutls_transport_set_int(session, sds[t]);
+
+ if (t > 0) {
+ gnutls_session_set_data(session, session_data.data,
+ session_data.size);
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ GNUTLS_HOOK_POST,
+ handshake_callback);
+ }
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n", strerror(ret));
+ exit(1);
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ ret = gnutls_cipher_get(session);
+ if (ret != GNUTLS_CIPHER_AES_256_GCM) {
+ fprintf(stderr, "negotiated unexpected cipher: %s\n", gnutls_cipher_get_name(ret));
+ exit(1);
+ }
+
+ ret = gnutls_mac_get(session);
+ if (ret != GNUTLS_MAC_AEAD) {
+ fprintf(stderr, "negotiated unexpected mac: %s\n", gnutls_mac_get_name(ret));
+ exit(1);
+ }
+
+ if (t == 0) {
+ /* get the session data size */
+ ret =
+ gnutls_session_get_data2(session,
+ &session_data);
+ if (ret < 0)
+ fail("Getting resume data failed\n");
+
+ if (handshake_callback_called != 0)
+ fail("client: handshake callback is called\n");
+ } else {
+ if (handshake_callback_called != t)
+ fail("client: handshake callback is not called\n");
+ }
+
+ gnutls_record_send(session, MSG, strlen(MSG));
+
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN);
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug) {
+ printf("- Received %d bytes: ", ret);
+ for (ii = 0; ii < ret; ii++) {
+ fputc(buffer[ii], stdout);
+ }
+ fputs("\n", stdout);
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sds[t]);
+
+ gnutls_deinit(session);
+ }
+
+ gnutls_free(session_data.data);
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+}
+
+static void terminate(void)
+{
+ int status = 0;
+
+ if (child) {
+ kill(child, SIGTERM);
+ wait(&status);
+ }
+ exit(1);
+}
+
+static void server(int sds[])
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t serverx509cred;
+ int t;
+ char buffer[MAX_BUF + 1];
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ /* date --date='TZ="UTC" 2019-04-12' +%s */
+ virt_time_init_at(1555027200);
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+
+ gnutls_session_ticket_key_generate(&session_ticket_key);
+
+ for (t = 0; t < SESSIONS; t++) {
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_session_ticket_enable_server(session,
+ &session_ticket_key);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ ret = gnutls_priority_set_direct(session,
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA384:-GROUP-ALL:+GROUP-SECP256R1", NULL);
+ if (ret < 0) {
+ fail("server: priority set failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ gnutls_handshake_set_random(session, &hsrnd);
+ gnutls_transport_set_int(session, sds[t]);
+
+ if (t > 0) {
+ if (!gnutls_rnd_works) {
+ fprintf(stderr, "gnutls_rnd() could not be overridden, skipping prf checks see #584\n");
+ exit(77);
+ } else {
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_CLIENT_HELLO,
+ GNUTLS_HOOK_POST,
+ handshake_callback);
+ }
+ }
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ close(sds[t]);
+ gnutls_deinit(session);
+ fail("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (debug)
+ success("server: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ if (t == 0) {
+ if (handshake_callback_called != 0)
+ fail("server: handshake callback is called\n");
+ } else {
+ if (handshake_callback_called != t)
+ fail("server: handshake callback is not called\n");
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ kill(child, SIGTERM);
+ fail("server: Received corrupted data(%d). Closing...\n", ret);
+ break;
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ }
+ }
+
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sds[t]);
+ gnutls_deinit(session);
+ }
+
+ gnutls_certificate_free_credentials(serverx509cred);
+
+ gnutls_free(session_ticket_key.data);
+ session_ticket_key.data = NULL;
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+void doit(void)
+{
+ int client_sds[SESSIONS], server_sds[SESSIONS];
+ int i;
+ int ret;
+
+ _gnutls_global_version = 0x030607;
+ signal(SIGPIPE, SIG_IGN);
+
+ for (i = 0; i < SESSIONS; i++) {
+ int sockets[2];
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (ret == -1) {
+ perror("socketpair");
+ fail("socketpair failed\n");
+ return;
+ }
+
+ server_sds[i] = sockets[0];
+ client_sds[i] = sockets[1];
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ int status = 0;
+ /* parent */
+
+ for (i = 0; i < SESSIONS; i++)
+ close(client_sds[i]);
+ server(server_sds);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ for (i = 0; i < SESSIONS; i++)
+ close(server_sds[i]);
+ client(client_sds);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/prf.c b/tests/tls13/prf.c
new file mode 100644
index 0000000..a83fda5
--- /dev/null
+++ b/tests/tls13/prf.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2015-2018 Red Hat, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if !defined(__linux__) || !defined(__GNUC__)
+
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+static void terminate(void);
+
+/* This program tests whether the gnutls_prf() works as
+ * expected.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+/* These are global */
+static pid_t child;
+
+static const
+gnutls_datum_t hrnd = {(void*)"\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+static const
+gnutls_datum_t hsrnd = {(void*)"\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32};
+
+static int gnutls_rnd_works;
+
+int __attribute__ ((visibility ("protected")))
+gnutls_rnd(gnutls_rnd_level_t level, void *data, size_t len)
+{
+ gnutls_rnd_works = 1;
+
+ memset(data, 0xff, len);
+
+ /* Flip the first byte to avoid infinite loop in the RSA
+ * blinding code of Nettle */
+ if (len > 0)
+ memset(data, 0x0, 1);
+ return 0;
+}
+
+static void dump(const char *name, const uint8_t *data, unsigned data_size)
+{
+ unsigned i;
+
+ fprintf(stderr, "%s", name);
+ for (i=0;i<data_size;i++)
+ fprintf(stderr, "\\x%.2x", (unsigned)data[i]);
+ fprintf(stderr, "\n");
+}
+
+#define TRY(label_size, label, extra_size, extra, size, exp) \
+ { \
+ ret = gnutls_prf_rfc5705(session, label_size, label, extra_size, extra, size, \
+ (void*)key_material); \
+ if (ret < 0) { \
+ fprintf(stderr, "gnutls_prf_rfc5705: error in %d\n", __LINE__); \
+ gnutls_perror(ret); \
+ exit(1); \
+ } \
+ if (memcmp(key_material, exp, size) != 0) { \
+ fprintf(stderr, "gnutls_prf_rfc5705: output doesn't match for '%s'\n", label); \
+ dump("got ", key_material, size); \
+ dump("expected ", exp, size); \
+ exit(1); \
+ } \
+ }
+
+#define TRY_OLD(label_size, label, size, exp) \
+ { \
+ ret = gnutls_prf(session, label_size, label, 0, 0, NULL, size, \
+ (void*)key_material); \
+ if (ret < 0) { \
+ fprintf(stderr, "gnutls_prf: error in %d\n", __LINE__); \
+ gnutls_perror(ret); \
+ exit(1); \
+ } \
+ if (memcmp(key_material, exp, size) != 0) { \
+ fprintf(stderr, "gnutls_prf: output doesn't match for '%s'\n", label); \
+ dump("got ", key_material, size); \
+ dump("expected ", exp, size); \
+ exit(1); \
+ } \
+ }
+
+#define KEY_EXP_VALUE "\x28\x70\xa8\x34\xd4\x43\x85\xfd\x55\xe0\x13\x78\x75\xa3\x25\xa7\xfd\x0b\x6b\x68\x5d\x62\x72\x02\xdf\x3d\x79\xca\x55\xab\xea\x24\xf3\x4d"
+#define HELLO_VALUE "\xd8\xcb\x72\x1e\x24\x2d\x79\x11\x41\x38\x05\x2b\x1b\x5d\x60\x12\x30\x0a\xf7\x1e\x23\x90\x4d\x64\xf8\xf5\x23\xea\xbf\xa3\x24"
+#define CONTEXT_VALUE "\xe6\xc0\x57\xbe\xda\x28\x9c\xc7\xf6\x4f\xb6\x18\x92\xce\x10\xf6\xe1\x5e\xab\x10\xc8\xd1\x94\xf8\xac\xc7\x3e\x93\xde\x57\x12"
+#define NULL_CONTEXT_VALUE "\xaf\xea\xd2\x64\xc9\x42\xbd\xe7\xdb\xf0\xd3\x16\x84\x39\xf3\xdb\x5d\x4f\x0e\x5e\x71\x1e\xc0\xd7\x23\xde\x8b\x1e\x80\xa1\xca"
+static void check_prfs(gnutls_session_t session)
+{
+ unsigned char key_material[512];
+ int ret;
+
+ if (!gnutls_rnd_works) {
+ fprintf(stderr, "gnutls_rnd() could not be overridden, see #584\n");
+ exit(77);
+ }
+
+ TRY_OLD(13, "key expansion", 34, (uint8_t*)KEY_EXP_VALUE);
+ TRY_OLD(6, "hello", 31, (uint8_t*)HELLO_VALUE);
+
+ TRY(13, "key expansion", 0, NULL, 34, (uint8_t*)KEY_EXP_VALUE);
+ TRY(6, "hello", 0, NULL, 31, (uint8_t*)HELLO_VALUE);
+ TRY(7, "context", 5, "abcd\xfa", 31, (uint8_t*)CONTEXT_VALUE);
+ TRY(12, "null-context", 0, "", 31, (uint8_t*)NULL_CONTEXT_VALUE);
+
+ /* Try whether calling gnutls_prf() with non-null context or server-first
+ * param, will fail */
+ ret = gnutls_prf(session, 3, (void*)"xxx", 0, 3, (void*)"yyy", 16, (void*)key_material);
+ if (ret != GNUTLS_E_INVALID_REQUEST)
+ fail("gnutls_prf: succeeded under TLS1.3!\n");
+
+ ret = gnutls_prf(session, 3, (void*)"xxx", 1, 0, NULL, 16, (void*)key_material);
+ if (ret != GNUTLS_E_INVALID_REQUEST)
+ fail("gnutls_prf: succeeded under TLS1.3!\n");
+}
+
+static void client(int fd)
+{
+ gnutls_session_t session;
+ int ret;
+ gnutls_certificate_credentials_t clientx509cred;
+ const char *err;
+ /* Need to enable anonymous KX specifically. */
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&clientx509cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ ret = gnutls_priority_set_direct(session,
+ "NONE:+VERS-TLS1.3:+AES-256-GCM:+AEAD:+SIGN-RSA-PSS-RSAE-SHA384:+GROUP-SECP256R1",
+ &err);
+ if (ret < 0) {
+ fail("client: priority set failed (%s): %s\n",
+ gnutls_strerror(ret), err);
+ exit(1);
+ }
+
+ ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_handshake_set_random(session, &hrnd);
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed: %s\n", strerror(ret));
+ exit(1);
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ ret = gnutls_cipher_get(session);
+ if (ret != GNUTLS_CIPHER_AES_256_GCM) {
+ fprintf(stderr, "negotiated unexpected cipher: %s\n", gnutls_cipher_get_name(ret));
+ exit(1);
+ }
+
+ ret = gnutls_mac_get(session);
+ if (ret != GNUTLS_MAC_AEAD) {
+ fprintf(stderr, "negotiated unexpected mac: %s\n", gnutls_mac_get_name(ret));
+ exit(1);
+ }
+
+ ret = gnutls_prf_hash_get(session);
+ if (ret != GNUTLS_DIG_SHA384) {
+ fprintf(stderr, "negotiated unexpected hash: %s\n", gnutls_digest_get_name(ret));
+ exit(1);
+ }
+
+ check_prfs(session);
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+}
+
+static void terminate(void)
+{
+ int status = 0;
+
+ if (child) {
+ kill(child, SIGTERM);
+ wait(&status);
+ }
+ exit(1);
+}
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t serverx509cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ ret = gnutls_priority_set_direct(session,
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-KX-ALL:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA384:-GROUP-ALL:+GROUP-SECP256R1", NULL);
+ if (ret < 0) {
+ fail("server: priority set failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ gnutls_handshake_set_random(session, &hsrnd);
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ close(fd);
+ gnutls_deinit(session);
+ fail("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ if (debug)
+ success("server: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ check_prfs(session);
+
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(serverx509cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ int status = 0;
+ /* parent */
+
+ server(fd[0]);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/psk-dumbfw.c b/tests/tls13/psk-dumbfw.c
new file mode 100644
index 0000000..c3e2e38
--- /dev/null
+++ b/tests/tls13/psk-dumbfw.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2004-2012 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Adam Sampson <ats@offog.org>
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#if defined(_WIN32)
+
+/* socketpair isn't supported on Win32. */
+int main(int argc, char **argv)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if !defined(_WIN32)
+#include <sys/wait.h>
+#endif
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <assert.h>
+#include <signal.h>
+
+#include "tls13/ext-parse.h"
+
+#include "utils.h"
+
+/* Tests whether the pre-shared key extension will always be last
+ * even if the dumbfw extension is present.
+ */
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+static void client(int sd, const char *prio)
+{
+ int ret, ii;
+ gnutls_session_t session;
+ char buffer[MAX_BUF + 1];
+ gnutls_psk_client_credentials_t pskcred;
+ /* Need to enable anonymous KX specifically. */
+ const gnutls_datum_t key = { (void *) "DEADBEEF", 8 };
+
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ side = "client";
+
+ gnutls_psk_allocate_client_credentials(&pskcred);
+ gnutls_psk_set_client_credentials(pskcred, "test", &key,
+ GNUTLS_PSK_KEY_HEX);
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_KEY_SHARE_TOP)>=0);
+
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+ assert(gnutls_credentials_set(session, GNUTLS_CRD_PSK, pskcred)>=0);
+
+ gnutls_transport_set_int(session, sd);
+
+ /* Perform the TLS handshake
+ */
+ ret = gnutls_handshake(session);
+
+ if (ret < 0) {
+ fail("client: Handshake failed\n");
+ gnutls_perror(ret);
+ goto end;
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ assert(gnutls_record_send(session, MSG, strlen(MSG))>=0);
+
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ goto end;
+ }
+
+ if (debug) {
+ printf("- Received %d bytes: ", ret);
+ for (ii = 0; ii < ret; ii++) {
+ fputc(buffer[ii], stdout);
+ }
+ fputs("\n", stdout);
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_RDWR);
+
+ end:
+
+ close(sd);
+
+ gnutls_deinit(session);
+
+ gnutls_psk_free_client_credentials(pskcred);
+
+ gnutls_global_deinit();
+}
+
+static int
+pskfunc(gnutls_session_t session, const char *username,
+ gnutls_datum_t * key)
+{
+ if (debug)
+ printf("psk: username %s\n", username);
+ key->data = gnutls_malloc(4);
+ key->data[0] = 0xDE;
+ key->data[1] = 0xAD;
+ key->data[2] = 0xBE;
+ key->data[3] = 0xEF;
+ key->size = 4;
+ return 0;
+}
+
+#define EXT_CLIENTHELLO_PADDING 21
+#define EXT_PRE_SHARED_KEY 41
+
+struct ctx_st {
+ unsigned long pos;
+ void *base;
+};
+
+static
+void check_ext_pos(void *priv, gnutls_datum_t *msg)
+{
+ struct ctx_st *ctx = priv;
+
+ ctx->pos = (ptrdiff_t)((ptrdiff_t)msg->data - (ptrdiff_t)ctx->base);
+}
+
+static int client_hello_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ unsigned long pos_psk;
+ unsigned long pos_pad;
+
+ if (htype == GNUTLS_HANDSHAKE_CLIENT_HELLO && post == GNUTLS_HOOK_POST) {
+ struct ctx_st ctx;
+
+ ctx.base = msg->data;
+ if (find_client_extension(msg, EXT_CLIENTHELLO_PADDING, &ctx, check_ext_pos) == 0)
+ fail("Could not find dumbfw/client hello padding extension!\n");
+ pos_pad = ctx.pos;
+
+ ctx.base = msg->data;
+ if (find_client_extension(msg, EXT_PRE_SHARED_KEY, &ctx, check_ext_pos) == 0)
+ fail("Could not find psk extension!\n");
+ pos_psk = ctx.pos;
+
+ if (pos_psk < pos_pad) {
+ fail("The dumbfw extension was sent after pre-shared key!\n");
+ }
+
+ /* check if we are the last extension in general */
+ if (!is_client_extension_last(msg, EXT_PRE_SHARED_KEY)) {
+ fail("pre-shared key extension wasn't the last one!\n");
+ }
+ }
+
+ return 0;
+}
+
+
+static void server(int sd, const char *prio)
+{
+ gnutls_psk_server_credentials_t server_pskcred;
+ int ret;
+ gnutls_session_t session;
+ char buffer[MAX_BUF + 1];
+
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ side = "server";
+
+
+ assert(gnutls_psk_allocate_server_credentials(&server_pskcred)>=0);
+ gnutls_psk_set_server_credentials_function(server_pskcred,
+ pskfunc);
+
+ assert(gnutls_init(&session, GNUTLS_SERVER)>=0);
+
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+ gnutls_credentials_set(session, GNUTLS_CRD_PSK, server_pskcred);
+
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ client_hello_callback);
+
+ gnutls_transport_set_int(session, sd);
+ ret = gnutls_handshake(session);
+ if (ret < 0) {
+ close(sd);
+ gnutls_deinit(session);
+ fail("server: Handshake has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ return;
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+ gnutls_record_set_timeout(session, 10000);
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ fail("server: Received corrupted data(%d). Closing...\n", ret);
+ break;
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ }
+ }
+
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(sd);
+ gnutls_deinit(session);
+
+ gnutls_psk_free_server_credentials(server_pskcred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+
+static
+void run_test(const char *prio)
+{
+ pid_t child;
+ int err;
+ int sockets[2];
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ success("trying with %s\n", prio);
+
+ err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
+ if (err == -1) {
+ perror("socketpair");
+ fail("socketpair failed\n");
+ return;
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ return;
+ }
+
+ if (child) {
+ int status = 0;
+ /* parent */
+ close(sockets[1]);
+ server(sockets[0], prio);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(sockets[0]);
+ client(sockets[1], prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ run_test("NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+PSK:%DUMBFW:-GROUP-ALL:+GROUP-FFDHE2048");
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/psk-ext.c b/tests/tls13/psk-ext.c
new file mode 100644
index 0000000..370a8e1
--- /dev/null
+++ b/tests/tls13/psk-ext.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2016, 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <stdint.h>
+#include "../lib/tls13/psk_ext_parser.h"
+
+#include "utils.h"
+
+/* Tests the PSK-extension decoding part */
+
+static void decode(const char *test_name, const gnutls_datum_t *raw, const gnutls_datum_t *id,
+ const gnutls_datum_t *b, unsigned idx, int res)
+{
+ int ret;
+ psk_ext_parser_st p;
+ psk_ext_iter_st iter;
+ struct psk_st psk;
+ gnutls_datum_t binder;
+ unsigned found = 0;
+ unsigned i, j;
+
+ ret = _gnutls13_psk_ext_parser_init(&p, raw->data, raw->size);
+ if (ret < 0) {
+ if (res == ret) /* expected */
+ return;
+ fail("%s: _gnutls13_psk_ext_parser_init: %d/%s\n", test_name, ret, gnutls_strerror(ret));
+ exit(1);
+ }
+
+ _gnutls13_psk_ext_iter_init(&iter, &p);
+ for (i = 0; ; i++) {
+ ret = _gnutls13_psk_ext_iter_next_identity(&iter, &psk);
+ if (ret < 0) {
+ if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ break;
+ if (res == ret) /* expected */
+ return;
+ }
+ if (i == idx) {
+ if (psk.identity.size == id->size && memcmp(psk.identity.data, id->data, id->size) == 0) {
+ if (debug)
+ success("%s: found id\n", test_name);
+ found = 1;
+ break;
+ } else {
+ fail("%s: did not found identity on index %d\n", test_name, idx);
+ }
+ }
+ }
+
+ if (found == 0)
+ fail("%s: did not found identity!\n", test_name);
+
+ _gnutls13_psk_ext_iter_init(&iter, &p);
+ for (j = 0; j <= i; j++) {
+ ret = _gnutls13_psk_ext_iter_next_binder(&iter, &binder);
+ if (ret < 0) {
+ if (res == ret) /* expected */
+ return;
+ fail("%s: could not extract binder: %s\n",
+ test_name, gnutls_strerror(ret));
+ }
+ }
+
+ if (debug)
+ success("%s: found binder\n", test_name);
+
+ if (binder.size != b->size || memcmp(binder.data, b->data, b->size) != 0) {
+ hexprint(binder.data, binder.size);
+ fail("%s: did not match binder on index %d\n", test_name, idx);
+ }
+
+ return;
+}
+
+struct decode_tests_st {
+ const char *name;
+ gnutls_datum_t psk;
+ unsigned idx; /* the ID index */
+ gnutls_datum_t id;
+ gnutls_datum_t binder;
+ int res;
+};
+
+struct decode_tests_st decode_tests[] = {
+ {
+ .name = "single PSK",
+ .psk = { (unsigned char*)"\x00\x0a\x00\x04\x6e\x6d\x61\x76\x00\x00\x00\x00\x00\x21\x20\xc4\xda\xe5\x7e\x05\x59\xf7\xae\x9b\xba\x90\xd2\x6e\x12\x68\xf6\xc1\xc7\xb9\x7e\xdc\xed\x9e\x67\x4e\xa5\x91\x2d\x7c\xb4\xf0\xab", 47},
+ .id = { (unsigned char*)"nmav", 4 },
+ .binder = { (unsigned char*)"\xc4\xda\xe5\x7e\x05\x59\xf7\xae\x9b\xba\x90\xd2\x6e\x12\x68\xf6\xc1\xc7\xb9\x7e\xdc\xed\x9e\x67\x4e\xa5\x91\x2d\x7c\xb4\xf0\xab", 32 },
+ .idx = 0,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id0",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x63"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x20\x71\x83\x89\x3d\xcc"
+ "\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53"
+ "\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc"
+ "\xca\x52\x16", 135},
+ .id = { (unsigned char*)"psk1", 4 },
+ .binder = { (unsigned char*)"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32},
+ .idx = 0,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id1",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x63"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x20\x71\x83\x89\x3d\xcc"
+ "\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53"
+ "\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc"
+ "\xca\x52\x16", 135},
+ .id = { (unsigned char*)"pskid", 6 },
+ .binder = { (unsigned char*)"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 32},
+ .idx = 1,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id2",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x63"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x20\x71\x83\x89\x3d\xcc"
+ "\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53"
+ "\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc"
+ "\xca\x52\x16", 135},
+ .id = { (unsigned char*)"test", 4 },
+ .binder = { (unsigned char*)"\x71\x83\x89\x3d\xcc\x46\xad\x83\x18\x98\x59\x46\x0b\xb2\x51\x24\x53\x41\xb4\x35\x04\x22\x90\x02\xac\x5e\xc1\xe7\xbc\xca\x52\x16", 32},
+ .idx = 2,
+ .res = 0
+ },
+ {
+ .name = "multiple psks id3",
+ .psk = { (unsigned char*)"\x00\x20\x00\x04\x70\x73\x6b\x31\x00\x00\x00\x00"
+ "\x00\x06\x70\x73\x6b\x69\x64\x00\x00\x00\x00\x00"
+ "\x00\x04\x74\x65\x73\x74\x00\x00\x00\x00\x00\x42"
+ "\x20\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x01\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00", 102},
+ .id = { (unsigned char*)"test", 4 },
+ .binder = { NULL, 0 },
+ .idx = 2,
+ .res = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ }
+};
+
+void doit(void)
+{
+ unsigned i;
+
+ for (i=0;i<sizeof(decode_tests)/sizeof(decode_tests[0]);i++) {
+ decode(decode_tests[i].name, &decode_tests[i].psk, &decode_tests[i].id,
+ &decode_tests[i].binder, decode_tests[i].idx, decode_tests[i].res);
+ }
+}
+
diff --git a/tests/tls13/psk-ke-modes.c b/tests/tls13/psk-ke-modes.c
new file mode 100644
index 0000000..2878569
--- /dev/null
+++ b/tests/tls13/psk-ke-modes.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2017-2022 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos, Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+
+#include "cert-common.h"
+#include "utils.h"
+#include "tls13/ext-parse.h"
+#include "eagain-common.h"
+
+/* This program tests the scenario described at:
+ * https://gitlab.com/gnutls/gnutls/-/issues/1303
+ *
+ * - the server only supports one mode
+ * - the client follows server's preference with %SERVER_PRECEDENCE
+ * - the client provides two modes, but the first one is not the one the server
+ * supports
+ *
+ * Previously the server was not able to enable PSK (and thus session
+ * resumption) at all in this case and didn't send NewSessionTicket.
+ */
+
+const char *testname = "";
+
+#define myfail(fmt, ...) \
+ fail("%s: "fmt, testname, ##__VA_ARGS__)
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static int
+new_session_ticket_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming,
+ const gnutls_datum_t *msg)
+{
+ bool *new_session_ticket_sent =
+ gnutls_session_get_ptr(session);
+ *new_session_ticket_sent = true;
+ return 0;
+}
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS, and hi and how are you and more data here... and more... and even more and even more more data..."
+
+static void start(const char *name, const char *prio, const char *sprio)
+{
+ int sret, cret;
+ gnutls_certificate_credentials_t scred, ccred;
+ gnutls_session_t server, client;
+ gnutls_datum_t skey;
+ char buffer[MAX_BUF + 1];
+ int transferred = 0;
+ bool new_session_ticket_sent = false;
+
+ testname = name;
+ success("== test %s ==\n", testname);
+
+ global_init();
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(9);
+
+ /* Init server */
+ assert(gnutls_certificate_allocate_credentials(&scred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(scred,
+ &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&server, GNUTLS_SERVER);
+
+ gnutls_handshake_set_hook_function(server,
+ GNUTLS_HANDSHAKE_NEW_SESSION_TICKET,
+ GNUTLS_HOOK_POST,
+ new_session_ticket_callback);
+ gnutls_session_set_ptr(server, &new_session_ticket_sent);
+
+ gnutls_priority_set_direct(server, sprio, NULL);
+
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+ assert(gnutls_session_ticket_enable_server(server, &skey) >= 0);
+
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE, scred);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+ gnutls_certificate_allocate_credentials(&ccred);
+ assert(gnutls_certificate_set_x509_trust_mem
+ (ccred, &ca3_cert, GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&client, GNUTLS_CLIENT);
+
+ cret = gnutls_priority_set_direct(client, prio, NULL);
+ if (cret < 0)
+ myfail("cannot set TLS 1.3 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE, ccred);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE(client, server);
+ if (debug)
+ success("Handshake established\n");
+
+ TRANSFER(client, server, MSG, strlen(MSG), buffer, MAX_BUF);
+ TRANSFER(server, client, MSG, strlen(MSG), buffer, MAX_BUF);
+ EMPTY_BUF(server, client, buffer, MAX_BUF);
+
+ if (!new_session_ticket_sent) {
+ fail("NewSessionTicket is not sent\n");
+ }
+
+ gnutls_bye(client, GNUTLS_SHUT_WR);
+ gnutls_bye(server, GNUTLS_SHUT_WR);
+
+ gnutls_deinit(client);
+ gnutls_deinit(server);
+
+ gnutls_certificate_free_credentials(scred);
+ gnutls_certificate_free_credentials(ccred);
+
+ gnutls_free(skey.data);
+
+ gnutls_global_deinit();
+ reset_buffers();
+}
+
+void doit(void)
+{
+ start("server only supports PSK, client advertises ECDHE-PSK first",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:+ECDHE-PSK:+PSK:%SERVER_PRECEDENCE",
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:+PSK");
+}
diff --git a/tests/tls13/rnd-check-rollback-val.c b/tests/tls13/rnd-check-rollback-val.c
new file mode 100644
index 0000000..ea61728
--- /dev/null
+++ b/tests/tls13/rnd-check-rollback-val.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program checks whether a TLS 1.3 client will detect
+ * a TLS 1.2 rollback attempt via the server random value.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#ifdef TLS12
+# define name "TLS1.2"
+# define RND tls12_rnd
+# define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2"
+#elif TLS11
+# define name "TLS1.1"
+# define RND tls11_rnd
+# define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1:+VERS-TLS1.0"
+#else
+# error unknown version to test
+#endif
+
+gnutls_datum_t tls12_rnd = {(void*)"\x44\x4F\x57\x4E\x47\x52\x44\x01",
+ 8};
+
+gnutls_datum_t tls11_rnd = {(void*)"\x44\x4F\x57\x4E\x47\x52\x44\x00",
+ 8};
+
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ gnutls_datum_t srandom;
+ unsigned try = 0;
+ gnutls_datum_t session_data = { NULL, 0 };
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM);
+
+ retry:
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, PRIO, NULL);
+ if (ret < 0)
+ fail("cannot set TLS priorities\n");
+
+ if (try > 0)
+ gnutls_session_set_data(session, session_data.data, session_data.size);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("error in handshake: %s\n", gnutls_strerror(ret));
+ }
+
+ if (try > 0)
+ assert(gnutls_session_is_resumed(session));
+
+ gnutls_session_get_random(session, NULL, &srandom);
+
+ if (srandom.size != 32)
+ fail("unexpected random size\n");
+
+ if (memcmp(&srandom.data[32-8], RND.data, 8) != 0) {
+ unsigned i;
+ printf("expected: ");
+ for (i=0;i<8;i++)
+ printf("%.2x", (unsigned)RND.data[i]);
+ printf("\n");
+ printf("got: ");
+ for (i=0;i<8;i++)
+ printf("%.2x", (unsigned)srandom.data[32-8+i]);
+ printf("\n");
+ fail("unexpected random data for %s\n", name);
+ }
+
+ do {
+ ret = gnutls_record_send(session, "\x00", 1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (try == 0) {
+ ret = gnutls_session_get_data2(session, &session_data);
+ if (ret < 0)
+ fail("couldn't retrieve session data: %s\n",
+ gnutls_strerror(ret));
+ }
+
+ gnutls_deinit(session);
+
+ if (try == 0) {
+ try++;
+ goto retry;
+ }
+
+ close(fd);
+
+ gnutls_free(session_data.data);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_datum_t skey;
+ unsigned try = 0;
+ unsigned char buf[16];
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ assert(gnutls_session_ticket_key_generate(&skey) >= 0);
+
+ retry:
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ assert(gnutls_priority_set_direct(session, "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0", NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0)
+ fail("error in handshake: %s\n", gnutls_strerror(ret));
+
+ if (try > 0)
+ assert(gnutls_session_is_resumed(session));
+
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0)
+ fail("server: recv did not succeed as expected: %s\n", gnutls_strerror(ret));
+
+ gnutls_deinit(session);
+
+ if (try == 0) {
+ try++;
+ goto retry;
+ }
+
+ close(fd);
+
+ gnutls_free(skey.data);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ client(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ server(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/rnd-rollback-detection.c b/tests/tls13/rnd-rollback-detection.c
new file mode 100644
index 0000000..d64daa0
--- /dev/null
+++ b/tests/tls13/rnd-rollback-detection.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program checks whether a TLS 1.3 client will detect
+ * a TLS 1.2 rollback attempt via the server random value.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.2 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret != GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER) {
+ fail("unexpected error during rollback: %s\n", gnutls_strerror(ret));
+ }
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+#ifdef TLS12
+# define RND tls12_rnd
+# define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.2"
+#elif TLS11
+# define RND tls11_rnd
+# define PRIO "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.1:+VERS-TLS1.0"
+#else
+# error unknown version to test
+#endif
+
+gnutls_datum_t tls12_rnd = {(void*)"\x00\x00\x00\x04\x00\x00\x00\x04"
+ "\x00\x00\x00\x04\x00\x00\x00\x04"
+ "\x00\x00\x00\x04\x00\x00\x00\x04"
+ "\x44\x4F\x57\x4E\x47\x52\x44\x01",
+ 32};
+
+gnutls_datum_t tls11_rnd = {(void*)"\x00\x00\x00\x04\x00\x00\x00\x04"
+ "\x00\x00\x00\x04\x00\x00\x00\x04"
+ "\x00\x00\x00\x04\x00\x00\x00\x04"
+ "\x44\x4F\x57\x4E\x47\x52\x44\x00",
+ 32};
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_random(session, &RND);
+
+ assert(gnutls_priority_set_direct(session, PRIO, NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ client(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ server(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/supported_versions.c b/tests/tls13/supported_versions.c
new file mode 100644
index 0000000..7d14eeb
--- /dev/null
+++ b/tests/tls13/supported_versions.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2017-2018 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+/* This program tests the ProtocolVersion of Client Hello
+ * and whether the supported_versions extension is present and
+ * contains 0x0304 (TLS 1.3).
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+
+#define MAX_BUF 1024
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.3 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+static unsigned server_hello_ok = 0;
+
+#define HANDSHAKE_SESSION_ID_POS 34
+#define TLS_EXT_SUPPORTED_VERSIONS 43
+
+#define SKIP16(pos, total) { \
+ uint16_t _s; \
+ if (pos+2 > total) fail("error\n"); \
+ _s = (msg->data[pos] << 8) | msg->data[pos+1]; \
+ if ((size_t)(pos+2+_s) > total) fail("error\n"); \
+ pos += 2+_s; \
+ }
+
+#define SKIP8(pos, total) { \
+ uint8_t _s; \
+ if (pos+1 > total) fail("error\n"); \
+ _s = msg->data[pos]; \
+ if ((size_t)(pos+1+_s) > total) fail("error\n"); \
+ pos += 1+_s; \
+ }
+
+static int client_hello_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ ssize_t pos;
+
+ if (htype == GNUTLS_HANDSHAKE_SERVER_HELLO && post == GNUTLS_HOOK_POST) {
+ /* check whether TLS 1.3 is negotiated */
+ pos = 0;
+ if (msg->size < 2) {
+ fail("error in server hello size\n");
+ }
+
+ success("server hello:\n\t%d.%d\n",
+ (int)msg->data[pos], (int)msg->data[pos+1]);
+
+ if (msg->data[pos] != 0x03 || msg->data[pos+1] != 0x03) {
+ fail("fail expected TLS 1.2 in server hello, got %d.%d\n", (int)msg->data[pos], (int)msg->data[pos+1]);
+ }
+
+ server_hello_ok = 1;
+ return GNUTLS_E_INTERRUPTED;
+ }
+
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (msg->size < HANDSHAKE_SESSION_ID_POS)
+ return -1;
+
+ /* we expect the legacy version to be present */
+ /* ProtocolVersion legacy_version = 0x0303 */
+ if (msg->data[0] != 0x03 || msg->data[1] != 0x03) {
+ fail("ProtocolVersion contains %d.%d\n", (int)msg->data[0], (int)msg->data[1]);
+ }
+
+ pos = HANDSHAKE_SESSION_ID_POS;
+ /* legacy_session_id */
+ SKIP8(pos, msg->size);
+
+ /* CipherSuites */
+ SKIP16(pos, msg->size);
+
+ /* legacy_compression_methods */
+ SKIP8(pos, msg->size);
+
+ pos += 2;
+
+ while (pos < msg->size) {
+ uint16_t type;
+
+ if (pos+4 > msg->size)
+ fail("invalid client hello\n");
+
+ type = (msg->data[pos] << 8) | msg->data[pos+1];
+ pos+=2;
+
+ success("Found extension %d\n", (int)type);
+
+ if (type != TLS_EXT_SUPPORTED_VERSIONS) {
+ SKIP16(pos, msg->size);
+ } else { /* found */
+ ssize_t size = (msg->data[pos] << 8) | msg->data[pos+1];
+ pos+=2;
+
+ size = msg->data[pos];
+
+ if (size > msg->size+pos) {
+ fail("error in extension length\n");
+ }
+
+ if (size % 2 == 1) {
+ fail("extension length is odd!\n");
+ }
+
+ if (size != 6) {
+ fail("expected three versions only (%d)!\n", (int)size);
+ }
+ pos++;
+
+ success("client hello:\n\t%d.%d\n\t%d.%d\n\t%d.%d\n",
+ (int)msg->data[pos], (int)msg->data[pos+1],
+ (int)msg->data[pos+2], (int)msg->data[pos+3],
+ (int)msg->data[pos+4], (int)msg->data[pos+5]);
+
+ if (msg->data[pos] != 0x03 || msg->data[pos+1] != 0x04) {
+ fail("fail expected TLS 1.3, got %d.%d\n", (int)msg->data[pos], (int)msg->data[pos+1]);
+ }
+ pos+=2;
+
+ if (msg->data[pos] != 0x03 || msg->data[pos+1] != 0x03) {
+ fail("fail expected TLS 1.2, got %d.%d\n", (int)msg->data[pos], (int)msg->data[pos+1]);
+ }
+ pos+=2;
+
+ if (msg->data[pos] != 0x03 || msg->data[pos+1] != 0x01) {
+ fail("fail expected TLS 1.0, got %d.%d\n", (int)msg->data[pos], (int)msg->data[pos+1]);
+ }
+ pos+=2;
+ client_hello_ok = 1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void server(int fd)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ client_hello_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL:+VERS-TLS1.3", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ if (server_hello_ok == 0) {
+ fail("server: did not verify the server hello contents\n");
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */
diff --git a/tests/tls13/tls12-no-tls13-exts.c b/tests/tls13/tls12-no-tls13-exts.c
new file mode 100644
index 0000000..03d3271
--- /dev/null
+++ b/tests/tls13/tls12-no-tls13-exts.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2017 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main()
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+
+#include "cert-common.h"
+#include "tls13/ext-parse.h"
+#include "utils.h"
+
+/* This program checks whether any TLS 1.3 extensions are
+ * present when TLS 1.2 is the only protocol supported by
+ * client.
+ */
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+
+
+static void client(int fd)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &cli_ca3_cert,
+ &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ ret = gnutls_priority_set_direct(session, "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.0", NULL);
+ if (ret < 0)
+ fail("cannot set TLS 1.2 priorities\n");
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ /* Perform the TLS handshake
+ */
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static unsigned client_hello_ok = 0;
+
+static int client_hello_callback(gnutls_session_t session, unsigned int htype,
+ unsigned post, unsigned int incoming, const gnutls_datum_t *msg)
+{
+ if (htype != GNUTLS_HANDSHAKE_CLIENT_HELLO || post != GNUTLS_HOOK_PRE)
+ return 0;
+
+ if (find_client_extension(msg, TLS_EXT_SUPPORTED_VERSIONS, NULL, NULL)) {
+ fail("Found TLS 1.3 supported versions extension in TLS 1.2!\n");
+ }
+
+ if (find_client_extension(msg, TLS_EXT_POST_HANDSHAKE, NULL, NULL)) {
+ fail("Found TLS 1.3 supported versions extension in TLS 1.2!\n");
+ }
+
+ client_hello_ok = 1;
+
+ return 0;
+}
+
+static void server(int fd)
+{
+ int ret;
+ gnutls_session_t session;
+ gnutls_certificate_credentials_t x509_cred;
+
+ /* this must be called once in the program
+ */
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_handshake_set_hook_function(session, GNUTLS_HANDSHAKE_ANY,
+ GNUTLS_HOOK_BOTH,
+ client_hello_callback);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session, "NORMAL", NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+ gnutls_transport_set_int(session, fd);
+
+ do {
+ ret = gnutls_handshake(session);
+ if (ret == GNUTLS_E_INTERRUPTED) { /* expected */
+ break;
+ }
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+
+ if (client_hello_ok == 0) {
+ fail("server: did not verify the client hello\n");
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: client/server hello were verified\n");
+}
+
+static void ch_handler(int sig)
+{
+ int status = 0;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ int fd[2];
+ int ret;
+ pid_t child;
+
+ signal(SIGCHLD, ch_handler);
+ signal(SIGPIPE, SIG_IGN);
+
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
+ if (ret < 0) {
+ perror("socketpair");
+ exit(1);
+ }
+
+ child = fork();
+ if (child < 0) {
+ perror("fork");
+ fail("fork");
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ close(fd[1]);
+ server(fd[0]);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1]);
+ exit(0);
+ }
+}
+
+#endif /* _WIN32 */