diff options
Diffstat (limited to '')
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 */ |