summaryrefslogtreecommitdiffstats
path: root/tests/dtls
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tests/dtls-client-with-seccomp.c311
-rw-r--r--tests/dtls-etm.c346
-rw-r--r--tests/dtls-handshake-versions.c143
-rw-r--r--tests/dtls-max-record.c153
-rw-r--r--tests/dtls-pthread.c369
-rw-r--r--tests/dtls-rehandshake-anon.c382
-rw-r--r--tests/dtls-rehandshake-cert-2.c400
-rw-r--r--tests/dtls-rehandshake-cert-3.c392
-rw-r--r--tests/dtls-rehandshake-cert.c388
-rw-r--r--tests/dtls-repro-20170915.c47
-rw-r--r--tests/dtls-session-ticket-lost.c247
-rw-r--r--tests/dtls-sliding-window.c508
-rw-r--r--tests/dtls-with-seccomp.c307
-rwxr-xr-xtests/dtls/dtls-resume.sh45
-rw-r--r--tests/dtls/dtls-stress.c1558
-rwxr-xr-xtests/dtls/dtls.sh44
-rw-r--r--tests/dtls1-2-mtu-check.c242
-rw-r--r--tests/dtls10-cert-key-exchange.c65
-rw-r--r--tests/dtls12-cert-key-exchange.c76
19 files changed, 6023 insertions, 0 deletions
diff --git a/tests/dtls-client-with-seccomp.c b/tests/dtls-client-with-seccomp.c
new file mode 100644
index 0000000..c39068c
--- /dev/null
+++ b/tests/dtls-client-with-seccomp.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) 2015 Nikos Mavrogiannopoulos
+ *
+ * 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32) || !defined(HAVE_LIBSECCOMP)
+
+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 <signal.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <assert.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+static void terminate(void);
+
+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 ssize_t
+push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ int fd = (long int) tr;
+
+ return send(fd, data, len, 0);
+}
+
+static void client(int fd, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t xcred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&xcred);
+
+ ret = disable_system_calls();
+ if (ret < 0) {
+ fprintf(stderr, "could not enable seccomp\n");
+ exit(2);
+ }
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ /* Use default priorities */
+ assert(gnutls_priority_set_direct(session,
+ prio,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ /* 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)));
+
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer)-1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ 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));
+ exit(1);
+ }
+
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if (ret < 0) {
+ fail("server: error in closing session: %s\n", gnutls_strerror(ret));
+ }
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(xcred);
+
+ gnutls_global_deinit();
+}
+
+
+/* These are global */
+pid_t child;
+
+static void terminate(void)
+{
+ int status;
+ assert(child);
+ kill(child, SIGTERM);
+ wait(&status);
+ exit(1);
+}
+
+static void server(int fd, const char *prio)
+{
+ int ret;
+ gnutls_certificate_credentials_t xcred;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+
+ /* 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(&xcred);
+
+ ret = gnutls_certificate_set_x509_key_mem(xcred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_dtls_set_mtu(session, 1500);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ assert(gnutls_priority_set_direct(session,
+ prio,
+ NULL)>=0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ 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)));
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ memset(buffer, 1, sizeof(buffer));
+ do {
+ ret = gnutls_record_send(session, buffer, sizeof(buffer)-1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0) {
+ close(fd);
+ gnutls_deinit(session);
+ fail("server: data sending has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if (ret < 0) {
+ fail("server: error in closing session: %s\n", gnutls_strerror(ret));
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(xcred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static
+void run(const char *name, const char *prio)
+{
+ int fd[2];
+ int ret;
+
+ success("testing seccomp with %s\n", name);
+ 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;
+ /* parent */
+
+ close(fd[1]);
+ server(fd[0], prio);
+
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ run("dtls1.2", "NORMAL:-KX-ALL:+ECDHE-RSA:-VERS-ALL:+VERS-DTLS1.2");
+}
+#endif /* _WIN32 */
diff --git a/tests/dtls-etm.c b/tests/dtls-etm.c
new file mode 100644
index 0000000..1a8c1b5
--- /dev/null
+++ b/tests/dtls-etm.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2015 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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 "utils.h"
+#include "cert-common.h"
+
+static void terminate(void);
+
+/* This program tests whether EtM is negotiated 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);
+}
+
+/* A very basic TLS client, with anonymous authentication.
+ */
+
+#define MAX_BUF 1024
+
+static void client(int fd, const char *prio, unsigned etm)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_anon_client_credentials_t anoncred;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ /* Need to enable anonymous KX specifically. */
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_anon_allocate_client_credentials(&anoncred);
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_DATAGRAM);
+
+ /* Use default priorities */
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ 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)));
+
+ if (etm != 0 && gnutls_session_etm_status(session) == 0) {
+ fail("client: EtM was not negotiated with %s!\n", prio);
+ exit(1);
+ } else if (etm == 0 && gnutls_session_etm_status(session) != 0) {
+ fail("client: EtM was negotiated with %s!\n", prio);
+ exit(1);
+ }
+
+ if (etm != 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) == 0)) {
+ fail("client: EtM was not negotiated with %s!\n", prio);
+ exit(1);
+ } else if (etm == 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) != 0)) {
+ fail("client: EtM was negotiated with %s!\n", prio);
+ exit(1);
+ }
+
+ do {
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ } while (ret > 0);
+
+ if (ret == 0) {
+ 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_anon_free_client_credentials(anoncred);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+
+/* 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, unsigned etm)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+ gnutls_anon_server_credentials_t anoncred;
+ gnutls_certificate_credentials_t x509_cred;
+ unsigned to_send = sizeof(buffer)/4;
+
+ /* 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_anon_allocate_server_credentials(&anoncred);
+
+ gnutls_init(&session, GNUTLS_SERVER|GNUTLS_DATAGRAM);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ assert(gnutls_priority_set_direct(session, prio, NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ 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 (etm != 0 && gnutls_session_etm_status(session) == 0) {
+ fail("server: EtM was not negotiated with %s!\n", prio);
+ exit(1);
+ } else if (etm == 0 && gnutls_session_etm_status(session) != 0) {
+ fail("server: EtM was negotiated with %s!\n", prio);
+ exit(1);
+ }
+
+ if (etm != 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) == 0)) {
+ fail("server: EtM was not negotiated with %s!\n", prio);
+ exit(1);
+ } else if (etm == 0 && ((gnutls_session_get_flags(session) & GNUTLS_SFLAGS_ETM) != 0)) {
+ fail("server: EtM was negotiated with %s!\n", prio);
+ exit(1);
+ }
+
+ 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)));
+
+ do {
+ 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", to_send,
+ gnutls_strerror(ret));
+ terminate();
+ }
+ to_send++;
+ }
+ while (to_send < 64);
+
+ to_send = -1;
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_anon_free_server_credentials(anoncred);
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void start(const char *prio, unsigned etm)
+{
+ int fd[2];
+ int ret;
+
+ 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], prio, etm);
+ kill(child, SIGTERM);
+ } else {
+ close(fd[0]);
+ client(fd[1], prio, etm);
+ exit(0);
+ }
+}
+
+#define AES_CBC "NONE:+VERS-DTLS1.0:-CIPHER-ALL:+AES-128-CBC:+SHA1:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL"
+#define AES_CBC_SHA256 "NONE:+VERS-DTLS1.2:-CIPHER-ALL:+RSA:+AES-128-CBC:+AES-256-CBC:+SHA256:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL"
+#define AES_GCM "NONE:+VERS-DTLS1.2:-CIPHER-ALL:+RSA:+AES-128-GCM:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL"
+
+static void ch_handler(int sig)
+{
+ int status;
+ wait(&status);
+ check_wait_status(status);
+ return;
+}
+
+void doit(void)
+{
+ signal(SIGCHLD, ch_handler);
+
+ start(AES_CBC, 1);
+ start(AES_CBC_SHA256, 1);
+ start(AES_GCM, 0);
+}
+
+#endif /* _WIN32 */
diff --git a/tests/dtls-handshake-versions.c b/tests/dtls-handshake-versions.c
new file mode 100644
index 0000000..507aa06
--- /dev/null
+++ b/tests/dtls-handshake-versions.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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/dtls.h>
+#include "utils.h"
+#include "eagain-common.h"
+#include "cert-common.h"
+
+/* This tests whether the server reacts as expected on various client
+ * hello TLS versions */
+
+void _gnutls_hello_set_default_version(gnutls_session_t session,
+ unsigned char major,
+ unsigned char minor);
+
+const char *side;
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static void try(unsigned char major, unsigned char minor, int ret1, int ret2)
+{
+ 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;
+
+ /* General init. */
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ /* 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_DATAGRAM | GNUTLS_NONBLOCK);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ gnutls_priority_set_direct(server,
+ "NORMAL",
+ NULL);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_pull_timeout_function(server,
+ server_pull_timeout_func);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_priority_set_direct(client, "NORMAL", NULL);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_pull_timeout_function(client,
+ client_pull_timeout_func);
+ gnutls_transport_set_ptr(client, client);
+ _gnutls_hello_set_default_version(client, major, minor);
+
+ HANDSHAKE_DTLS_EXPECT(client, server, ret1, ret2);
+
+ 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);
+}
+
+void doit(void)
+{
+ global_init();
+
+ try(255, 255, GNUTLS_E_AGAIN, GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+ reset_buffers();
+ try(255, 128, GNUTLS_E_AGAIN, GNUTLS_E_UNSUPPORTED_VERSION_PACKET);
+ reset_buffers();
+ try(254, 128, 0, 0);
+ reset_buffers();
+ try(252, 64, 0, 0);
+ reset_buffers();
+
+ gnutls_global_deinit();
+}
diff --git a/tests/dtls-max-record.c b/tests/dtls-max-record.c
new file mode 100644
index 0000000..7934668
--- /dev/null
+++ b/tests/dtls-max-record.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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/dtls.h>
+#include <assert.h>
+#include "utils.h"
+
+#define SERVER_PUSH_ADD if (len > 512 + 5+8+32) fail("max record set to 512, len: %d\n", (int)len);
+#include "eagain-common.h"
+
+#include "cert-common.h"
+
+/* This tests whether the max-record extension is respected.
+ */
+
+const char *side;
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+static
+void run(const char *prio)
+{
+ global_init();
+
+ int ret;
+ char buf[1024];
+ /* 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;
+
+ /* General init. */
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ /* Init server */
+ gnutls_certificate_allocate_credentials(&serverx509cred);
+ gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server2_cert, &server2_key,
+ GNUTLS_X509_FMT_PEM);
+
+ gnutls_init(&server, GNUTLS_SERVER|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ assert(gnutls_priority_set_direct(server,
+ prio,
+ NULL)>=0);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_pull_timeout_function(server,
+ server_pull_timeout_func);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+
+ ret = gnutls_certificate_allocate_credentials(&clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca2_cert, GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM | GNUTLS_NONBLOCK);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+ if (ret < 0)
+ exit(1);
+
+ ret = gnutls_priority_set_direct(client, prio, NULL);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_record_set_max_size(client, 512);
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_pull_timeout_function(client,
+ client_pull_timeout_func);
+ gnutls_transport_set_ptr(client, client);
+
+ HANDSHAKE_DTLS(client, server);
+
+ memset(buf, 1, sizeof(buf));
+ ret = gnutls_record_send(server, buf, 513);
+ if (ret != GNUTLS_E_LARGE_PACKET && ret < 0) {
+ gnutls_perror(ret);
+ exit(1);
+ }
+ success("did not send a 513-byte packet\n");
+
+ ret = gnutls_record_send(server, buf, 512);
+ if (ret < 0) {
+ gnutls_perror(ret);
+ exit(1);
+ }
+ success("did send a 512-byte packet\n");
+
+ 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();
+}
+
+void doit(void)
+{
+ run("NORMAL:-VERS-ALL:+VERS-DTLS1.2");
+}
diff --git a/tests/dtls-pthread.c b/tests/dtls-pthread.c
new file mode 100644
index 0000000..aab1e73
--- /dev/null
+++ b/tests/dtls-pthread.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2015 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include <signal.h>
+#include <unistd.h>
+#ifndef _WIN32
+# include <netinet/in.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/wait.h>
+# include <pthread.h>
+#endif
+#include <assert.h>
+#include "utils.h"
+#include "cert-common.h"
+
+#ifdef _WIN32
+
+void doit(void)
+{
+ exit(77);
+}
+
+#else
+
+/* These are global */
+pid_t child;
+
+/* Tests whether we can send and receive from different threads
+ * using DTLS, either as server or client. DTLS is a superset of
+ * TLS, so correct behavior under fork means TLS would operate too.
+ */
+
+const char *side = "";
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "%s|<%d>| %s", side, level, str);
+}
+
+#define MSG "hello1111"
+#define MSG2 "xxxxxxxxxxxx"
+
+#define NO_MSGS 128
+
+static void *recv_thread(void *arg)
+{
+ gnutls_session_t session = arg;
+ int ret;
+ unsigned i;
+ char buf[64];
+
+ if (debug)
+ success("client: TLS version is: %s\n",
+ gnutls_protocol_get_name
+ (gnutls_protocol_get_version(session)));
+
+ for (i=0;i<NO_MSGS;i++) {
+ /* the peer should reflect our messages */
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("client: recv failed: %s\n", gnutls_strerror(ret));
+ if (ret != sizeof(MSG)-1 || memcmp(buf, MSG, sizeof(MSG)-1) != 0) {
+ fail("client: recv failed; not the expected values (got: %d, exp: %d)\n", ret, (int)sizeof(MSG)-1);
+ }
+
+ if (debug)
+ success("%d: client received: %.*s\n", i, ret, buf);
+ }
+
+ /* final MSG is MSG2 */
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0)
+ fail("client: recv2 failed: %s\n", gnutls_strerror(ret));
+
+ if (ret != sizeof(MSG2)-1 || memcmp(buf, MSG2, sizeof(MSG2)-1) != 0) {
+ fail("client: recv2 failed; not the expected values\n");
+ }
+
+ if (debug) {
+ success("client received: %.*s\n", ret, buf);
+ success("closing recv thread\n");
+ }
+
+ pthread_exit(0);
+}
+
+static
+void do_thread_stuff(gnutls_session_t session)
+{
+ int ret;
+ unsigned i;
+ pthread_t id;
+ void *rval;
+
+ sec_sleep(1);
+ /* separate sending from receiving */
+ ret = pthread_create(&id, NULL, recv_thread, session);
+ if (ret != 0) {
+ fail("error in pthread_create\n");
+ }
+
+ for (i=0;i<NO_MSGS;i++) {
+ do {
+ ret = gnutls_record_send(session, MSG, sizeof(MSG)-1);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret != sizeof(MSG)-1) {
+ fail("client: send failed: %s\n", gnutls_strerror(ret));
+ }
+ }
+
+ do {
+ ret = gnutls_record_send(session, MSG2, sizeof(MSG2)-1);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret != sizeof(MSG2)-1) {
+ fail("client: send2 failed: %s\n", gnutls_strerror(ret));
+ }
+
+ if (debug)
+ success("closing sending thread\n");
+ assert(pthread_join(id, &rval)==0);
+ assert(rval == 0);
+
+ do {
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+}
+
+static void do_reflect_stuff(gnutls_session_t session)
+{
+ char buf[64];
+ unsigned buf_size;
+ int ret;
+
+ do {
+ do {
+ ret = gnutls_record_recv(session, buf, sizeof(buf));
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0) {
+ fail("server: recv failed: %s\n", gnutls_strerror(ret));
+ }
+
+ if (ret == 0) {
+ break;
+ }
+
+ buf_size = ret;
+ if (debug) {
+ success("server received: %.*s\n", buf_size, buf);
+ }
+
+ do {
+ ret = gnutls_record_send(session, buf, buf_size);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret < 0) {
+ fail("server: send failed: %s\n", gnutls_strerror(ret));
+ }
+ if (debug)
+ success("reflected %d\n", ret);
+ } while(1);
+
+ do {
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+}
+
+static void client(int fd, const char *prio, unsigned do_thread, unsigned false_start)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+ unsigned flags = GNUTLS_CLIENT;
+
+ global_init();
+
+ if (debug) {
+ side = "client";
+ gnutls_global_set_log_function(tls_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&x509_cred)>=0);
+
+ if (false_start)
+ flags |= GNUTLS_ENABLE_FALSE_START;
+
+ assert(gnutls_init(&session, flags|GNUTLS_DATAGRAM) >= 0);
+ gnutls_dtls_set_mtu(session, 1500);
+ gnutls_dtls_set_timeouts(session, get_dtls_retransmit_timeout(), get_timeout());
+
+ assert(gnutls_priority_set_direct(session, prio, NULL)>=0);
+
+ assert(gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred)>=0);
+
+ 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", gnutls_strerror(ret));
+ } else {
+ if (debug)
+ success("client: Handshake was completed\n");
+ }
+
+ if (do_thread)
+ do_thread_stuff(session);
+ else
+ do_reflect_stuff(session);
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+static void server(int fd, const char *prio, unsigned do_thread)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+#if 0
+ if (debug) {
+ side = "server";
+ gnutls_global_set_log_function(tls_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+#endif
+
+ 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_DATAGRAM)>=0);
+ gnutls_dtls_set_timeouts(session, get_dtls_retransmit_timeout(), get_timeout());
+ gnutls_dtls_set_mtu(session, 400);
+
+ 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);
+ } 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));
+ }
+ 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 (do_thread)
+ do_thread_stuff(session);
+ else
+ do_reflect_stuff(session);
+
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static
+void run(const char *str, const char *prio, unsigned do_thread, unsigned false_start)
+{
+ int fd[2];
+ int ret;
+
+ if (str)
+ success("running %s\n", str);
+
+ 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;
+ /* parent */
+
+ close(fd[1]);
+ client(fd[0], prio, do_thread, false_start);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ server(fd[1], prio, 1-do_thread);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ signal(SIGPIPE, SIG_IGN);
+ run("default, threaded client", "NORMAL", 0, 0);
+ run("default, threaded server", "NORMAL", 1, 0);
+ run("dtls1.2, threaded client", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 0, 0);
+ run("dtls1.2, threaded server", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 1, 0);
+ run("dtls1.2 false start, threaded client", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 0, 1);
+ run("dtls1.2 false start, threaded server", "NORMAL:-VERS-ALL:+VERS-DTLS1.2", 1, 1);
+}
+#endif /* _WIN32 */
diff --git a/tests/dtls-rehandshake-anon.c b/tests/dtls-rehandshake-anon.c
new file mode 100644
index 0000000..f281f5d
--- /dev/null
+++ b/tests/dtls-rehandshake-anon.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2012 Free Software Foundation, 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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 "utils.h"
+
+static void terminate(void);
+
+/* This program tests the rehandshake in DTLS
+ */
+
+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, with anonymous authentication.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+static ssize_t
+push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ int fd = (long int) tr;
+
+ return send(fd, data, len, 0);
+}
+
+static void client(int fd, int server_init)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_anon_client_credentials_t anoncred;
+ gnutls_session_t session;
+
+ /* 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_anon_allocate_client_credentials(&anoncred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+
+ /* Use default priorities */
+ gnutls_priority_set_direct(session,
+ "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL",
+ NULL);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ /* 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)));
+
+ if (!server_init) {
+ sec_sleep(60);
+ if (debug)
+ success("Initiating client rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("2nd client gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ } else {
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ }
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ if (server_init && ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to server request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ }
+
+ if (ret != 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+
+ do {
+ ret = gnutls_record_send(session, MSG, strlen(MSG));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_anon_free_client_credentials(anoncred);
+
+ gnutls_global_deinit();
+}
+
+
+/* These are global */
+pid_t child;
+
+
+static void terminate(void)
+{
+ int status;
+ assert(child);
+ kill(child, SIGTERM);
+ wait(&status);
+ exit(1);
+}
+
+static void server(int fd, int server_init)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_anon_server_credentials_t anoncred;
+ gnutls_session_t session;
+ /* 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_anon_allocate_server_credentials(&anoncred);
+
+ gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_priority_set_direct(session,
+ "NONE:+VERS-DTLS1.0:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+ANON-ECDH:+CURVE-ALL",
+ NULL);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ 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)));
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ if (server_init) {
+ if (debug)
+ success("server: Sending dummy packet\n");
+ ret = gnutls_rehandshake(session);
+ if (ret < 0) {
+ fail("gnutls_rehandshake: %s\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ if (debug)
+ success("server: Initiating rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("server: 2nd gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ if (!server_init && ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to client request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0
+ && gnutls_error_is_fatal(ret) == 0);
+ if (ret == 0)
+ break;
+ }
+
+ fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret));
+ terminate();
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ do {
+ ret =
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ }
+ }
+
+
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_anon_free_server_credentials(anoncred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void start(int server_initiated)
+{
+ int fd[2];
+ int ret;
+
+ 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], server_initiated);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], server_initiated);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ start(0);
+ start(1);
+}
+
+#endif /* _WIN32 */
diff --git a/tests/dtls-rehandshake-cert-2.c b/tests/dtls-rehandshake-cert-2.c
new file mode 100644
index 0000000..c9d5058
--- /dev/null
+++ b/tests/dtls-rehandshake-cert-2.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (C) 2016 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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 "cert-common.h"
+#include "utils.h"
+
+static void terminate(void);
+
+/* This program tests the rehandshake from anon
+ * to certification auth in DTLS.
+ */
+
+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);
+}
+
+/* Tests the operation of rehandshake under DTLS using
+ * certificates.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+static ssize_t push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ int fd = (long int)tr;
+
+ return send(fd, data, len, 0);
+}
+
+static void client(int fd, int server_init, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_anon_client_credentials_t anoncred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ assert(gnutls_anon_allocate_client_credentials(&anoncred) >= 0);
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+
+ snprintf(buffer, sizeof(buffer), "%s:+ANON-ECDH", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, clientx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ /* 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)));
+
+ /* update priorities to allow cert auth */
+ snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ if (!server_init) {
+ sec_sleep(60);
+ if (debug)
+ success("Initiating client rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("2nd client gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ } else {
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ }
+
+ if (ret == 0) {
+ if (debug)
+ success("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ if (server_init && ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to server request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ }
+
+ if (ret != 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+
+ do {
+ ret = gnutls_record_send(session, MSG, strlen(MSG));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(clientx509cred);
+ gnutls_anon_free_client_credentials(anoncred);
+
+ gnutls_global_deinit();
+}
+
+/* These are global */
+pid_t child;
+
+static void terminate(void)
+{
+ int status;
+ assert(child);
+ kill(child, SIGTERM);
+ wait(&status);
+ exit(1);
+}
+
+static void server(int fd, int server_init, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_anon_server_credentials_t anoncred;
+ gnutls_session_t session;
+ /* 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);
+ }
+
+ assert(gnutls_anon_allocate_server_credentials(&anoncred) >= 0);
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA:+ANON-ECDH", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ 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)));
+
+ if (gnutls_kx_get(session) != GNUTLS_KX_ANON_ECDH) {
+ fail("did not negotiate an anonymous ciphersuite on initial auth\n");
+ }
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ if (server_init) {
+ if (debug)
+ success("server: Sending dummy packet\n");
+ ret = gnutls_rehandshake(session);
+ if (ret < 0) {
+ fail("gnutls_rehandshake: %s\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ if (debug)
+ success("server: Initiating rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("server: 2nd gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ if (!server_init && ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to client request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0
+ && gnutls_error_is_fatal(ret) == 0);
+ if (ret == 0)
+ break;
+ }
+
+ fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret));
+ terminate();
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ do {
+ ret =
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ }
+ }
+
+ if (gnutls_kx_get(session) != GNUTLS_KX_ECDHE_RSA) {
+ fail("did not negotiate a certificate ciphersuite on second auth\n");
+ }
+
+ /* 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_anon_free_server_credentials(anoncred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void start(int server_initiated, const char *prio)
+{
+ int fd[2];
+ int ret;
+
+ 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], server_initiated, prio);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], server_initiated, prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ start(0, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL");
+ start(1, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL");
+}
+
+#endif /* _WIN32 */
diff --git a/tests/dtls-rehandshake-cert-3.c b/tests/dtls-rehandshake-cert-3.c
new file mode 100644
index 0000000..855f63c
--- /dev/null
+++ b/tests/dtls-rehandshake-cert-3.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Guillaume Roguez
+ *
+ * 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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 "cert-common.h"
+#include "utils.h"
+
+#define MTU 1500
+#define MAX_BUF 4096
+#define MSG "Hello TLS"
+
+static int server_fd = -1;
+static char pkt_buf[MAX_BUF];
+static int pkt_found = 0;
+static int pkt_delivered = 0;
+
+static void terminate(void);
+
+/* This program tests the rehandshake from anon
+ * to certificate auth in DTLS, but will account for
+ * packet reconstruction (with loss/delay) for the certificate packet.
+ */
+
+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 ssize_t push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ int fd = (long int)tr;
+
+ if (fd == server_fd) {
+ if (!pkt_found && len > 1200) {
+ memcpy(pkt_buf, data, len);
+ pkt_found = 1;
+ if (debug)
+ success("*** packet delayed\n");
+ return len;
+ }
+ if (pkt_found && !pkt_delivered) {
+ int res = send(fd, data, len, 0);
+ send(fd, pkt_buf, MTU, 0);
+ pkt_delivered = 1;
+ if (debug)
+ success("*** swap done\n");
+ return res;
+ }
+ }
+
+ return send(fd, data, len, 0);
+}
+
+static void client(int fd, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_anon_client_credentials_t anoncred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ assert(gnutls_anon_allocate_client_credentials(&anoncred) >= 0);
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, MTU);
+
+ snprintf(buffer, sizeof(buffer), "%s:+ANON-ECDH", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, clientx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+ gnutls_dtls_set_timeouts(session, get_dtls_retransmit_timeout(), get_timeout());
+
+ /* 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)));
+
+ /* update priorities to allow cert auth */
+ snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == 0) {
+ if (debug)
+ success("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ if (ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to server request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ }
+
+ if (ret != 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+
+ do {
+ ret = gnutls_record_send(session, MSG, strlen(MSG));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(clientx509cred);
+ gnutls_anon_free_client_credentials(anoncred);
+
+ gnutls_global_deinit();
+}
+
+/* These are global */
+pid_t child;
+
+static void terminate(void)
+{
+ int status;
+ assert(child);
+ kill(child, SIGTERM);
+ wait(&status);
+ exit(1);
+}
+
+static void server(int fd, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_anon_server_credentials_t anoncred;
+ gnutls_session_t session;
+ /* 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);
+ }
+
+ assert(gnutls_anon_allocate_server_credentials(&anoncred) >= 0);
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, MTU);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA:+ANON-ECDH", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, anoncred);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, serverx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ 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)));
+
+ if (gnutls_kx_get(session) != GNUTLS_KX_ANON_ECDH) {
+ fail("did not negotiate an anonymous ciphersuite on initial auth\n");
+ }
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ if (debug)
+ success("server: Sending dummy packet\n");
+ ret = gnutls_rehandshake(session);
+ if (ret < 0) {
+ fail("gnutls_rehandshake: %s\n", gnutls_strerror(ret));
+ terminate();
+ }
+
+ if (debug)
+ success("server: Initiating rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("server: 2nd gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret));
+ terminate();
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ do {
+ ret =
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ }
+ }
+
+ if (gnutls_kx_get(session) != GNUTLS_KX_ECDHE_RSA) {
+ fail("did not negotiate a certificate ciphersuite on second auth\n");
+ }
+
+ /* 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_anon_free_server_credentials(anoncred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void start(const char *prio)
+{
+ int fd[2];
+ int ret;
+
+ 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 = fd[0];
+ server(fd[0], prio);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ start("NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL");
+}
+
+#endif /* _WIN32 */
diff --git a/tests/dtls-rehandshake-cert.c b/tests/dtls-rehandshake-cert.c
new file mode 100644
index 0000000..3439ee9
--- /dev/null
+++ b/tests/dtls-rehandshake-cert.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2016 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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 "cert-common.h"
+#include "utils.h"
+
+static void terminate(void);
+
+/* This program tests the rehandshake in DTLS
+ */
+
+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);
+}
+
+/* Tests the operation of rehandshake under DTLS using
+ * certificates.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+static ssize_t
+push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ int fd = (long int) tr;
+
+ return send(fd, data, len, 0);
+}
+
+static void client(int fd, int server_init, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t clientx509cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+
+ /* Use default priorities */
+ snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ /* 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)));
+
+ if (!server_init) {
+ sec_sleep(60);
+ if (debug)
+ success("Initiating client rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("2nd client gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ exit(1);
+ }
+ } else {
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ }
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("client: Peer has closed the TLS connection\n");
+ goto end;
+ } else if (ret < 0) {
+ if (server_init && ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to server request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ }
+
+ if (ret != 0) {
+ fail("client: Error: %s\n", gnutls_strerror(ret));
+ exit(1);
+ }
+ }
+
+ do {
+ ret = gnutls_record_send(session, MSG, strlen(MSG));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ gnutls_bye(session, GNUTLS_SHUT_WR);
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(clientx509cred);
+
+ gnutls_global_deinit();
+}
+
+
+/* These are global */
+pid_t child;
+
+
+static void terminate(void)
+{
+ int status;
+ assert(child);
+ kill(child, SIGTERM);
+ wait(&status);
+ exit(1);
+}
+
+static void server(int fd, int server_init, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t serverx509cred;
+ gnutls_session_t session;
+ /* 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);
+ }
+
+ assert(gnutls_certificate_allocate_credentials(&serverx509cred) >= 0);
+ assert(gnutls_certificate_set_x509_key_mem(serverx509cred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM) >= 0);
+
+ gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ snprintf(buffer, sizeof(buffer), "%s:+ECDHE-RSA", prio);
+ assert(gnutls_priority_set_direct(session,
+ buffer,
+ NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ 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)));
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ if (server_init) {
+ if (debug)
+ success("server: Sending dummy packet\n");
+ ret = gnutls_rehandshake(session);
+ if (ret < 0) {
+ fail("gnutls_rehandshake: %s\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ if (debug)
+ success("server: Initiating rehandshake\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("server: 2nd gnutls_handshake: %s\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+ }
+
+ for (;;) {
+ memset(buffer, 0, MAX_BUF + 1);
+
+ do {
+ ret = gnutls_record_recv(session, buffer, MAX_BUF);
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret == 0) {
+ if (debug)
+ success
+ ("server: Peer has closed the GnuTLS connection\n");
+ break;
+ } else if (ret < 0) {
+ if (!server_init && ret == GNUTLS_E_REHANDSHAKE) {
+ if (debug)
+ success
+ ("Initiating rehandshake due to client request\n");
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0
+ && gnutls_error_is_fatal(ret) == 0);
+ if (ret == 0)
+ break;
+ }
+
+ fail("server: Received corrupted data(%s). Closing...\n", gnutls_strerror(ret));
+ terminate();
+ } else if (ret > 0) {
+ /* echo data back to the client
+ */
+ do {
+ ret =
+ gnutls_record_send(session, buffer,
+ strlen(buffer));
+ } while (ret == GNUTLS_E_AGAIN
+ || ret == GNUTLS_E_INTERRUPTED);
+ }
+ }
+
+
+ /* 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");
+}
+
+static void start(int server_initiated, const char *prio)
+{
+ int fd[2];
+ int ret;
+
+ 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], server_initiated, prio);
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ client(fd[1], server_initiated, prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ start(0, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL");
+ start(1, "NONE:+VERS-DTLS1.2:+CIPHER-ALL:+MAC-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL");
+}
+
+#endif /* _WIN32 */
diff --git a/tests/dtls-repro-20170915.c b/tests/dtls-repro-20170915.c
new file mode 100644
index 0000000..78910a0
--- /dev/null
+++ b/tests/dtls-repro-20170915.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015-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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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-repro-20170915.h"
+
+void doit(void)
+{
+ global_init();
+
+ dtls_try_with_key_mtu("DTLS 1.2 with cli-cert", "NONE:+VERS-DTLS1.0:+MAC-ALL:+KX-ALL:+CIPHER-ALL:+SIGN-ALL:+COMP-ALL:+CURVE-ALL", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256,
+ &server_repro_cert, &server_repro_key, &client_repro_cert, &client_repro_key, USE_CERT, 1452);
+
+ gnutls_global_deinit();
+}
diff --git a/tests/dtls-session-ticket-lost.c b/tests/dtls-session-ticket-lost.c
new file mode 100644
index 0000000..df3a606
--- /dev/null
+++ b/tests/dtls-session-ticket-lost.c
@@ -0,0 +1,247 @@
+/*
+ * 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>
+
+#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 is a reproducer for issue #543; the timeout
+ * of DTLS handshake when a NewSessionTicket is lost.
+ */
+
+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)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ assert(gnutls_init(&session, GNUTLS_CLIENT|GNUTLS_DATAGRAM)>=0);
+
+ 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);
+ }
+ 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");
+ }
+
+ gnutls_record_set_timeout(session, 30*1000);
+
+ 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);
+}
+
+
+static ssize_t
+server_push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ const uint8_t *d = data;
+ static int dropped = 0;
+
+ if (d[13] == GNUTLS_HANDSHAKE_NEW_SESSION_TICKET) {
+ if (dropped == 0) {
+ success("dropping message: %s\n", gnutls_handshake_description_get_name(d[13]));
+ dropped = 1;
+ return len;
+ }
+ }
+
+ return send((long)tr, data, len, 0);
+}
+
+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;
+ gnutls_datum_t skey;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(6);
+ }
+
+ 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_DATAGRAM)>=0);
+
+ assert(gnutls_session_ticket_key_generate(&skey)>=0);
+ assert(gnutls_session_ticket_enable_server(session, &skey) >= 0);
+
+ gnutls_transport_set_push_function(session, server_push);
+
+ 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);
+ } while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+ if (ret < 0) {
+ /* failure is expected here */
+ goto end;
+ }
+
+ if (debug) {
+ success("server: Handshake was completed\n");
+ }
+
+ gnutls_record_set_timeout(session, 30*1000);
+
+ success("waiting for EOF\n");
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer));
+ } while(ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+ if (ret != 0)
+ fail("error waiting for EOF: %s\n", gnutls_strerror(ret));
+
+ end:
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_free(skey.data);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static void ch_handler(int sig)
+{
+ return;
+}
+
+static
+void start(const char *prio)
+{
+ int fd[2];
+ int ret, status = 0;
+ pid_t child;
+
+ success("trying %s\n", prio);
+ 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], prio);
+ waitpid(child, &status, 0);
+ check_wait_status(status);
+ } else {
+ close(fd[0]);
+ server(fd[1], prio);
+ exit(0);
+ }
+
+ return;
+}
+
+void doit(void)
+{
+ start("NORMAL:-VERS-ALL:+VERS-DTLS1.2");
+}
+
+#endif /* _WIN32 */
diff --git a/tests/dtls-sliding-window.c b/tests/dtls-sliding-window.c
new file mode 100644
index 0000000..d8e3c78
--- /dev/null
+++ b/tests/dtls-sliding-window.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) 2016 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <config.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <limits.h>
+#include <stdint.h>
+#include <string.h>
+
+/* Unit test for DTLS window handling */
+
+#define LARGE_INT 4194304
+#define INT_OVER_32_BITS 281474976708836LL
+
+struct record_parameters_st {
+ uint64_t dtls_sw_bits;
+ uint64_t dtls_sw_next;
+ unsigned dtls_sw_have_recv;
+ unsigned epoch;
+};
+
+#define gnutls_assert_val(x) x
+
+void _dtls_reset_window(struct record_parameters_st *rp);
+int _dtls_record_check(struct record_parameters_st *rp, uint64_t _seq);
+
+#define DTLS_SW_NO_INCLUDES
+#include "../lib/dtls-sw.c"
+
+#define RESET_WINDOW \
+ memset(&state, 0, sizeof(state))
+
+#define SET_WINDOW_NEXT(x) \
+ state.dtls_sw_next = (((x)&DTLS_SEQ_NUM_MASK))
+
+#define SET_WINDOW_LAST_RECV(x) \
+ t = x; \
+ state.dtls_sw_have_recv = 1
+
+static void check_dtls_window_uninit_0(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+
+ t = 0;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_uninit_large(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+
+ t = LARGE_INT+1+64;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_uninit_very_large(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+
+ t = INT_OVER_32_BITS;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_12(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(1);
+
+ t = 2;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_19(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(1);
+
+ t = 9;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_skip1(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+ unsigned i;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(1);
+
+ for (i=2;i<256;i+=2) {
+ t = i;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+ }
+}
+
+static void check_dtls_window_skip3(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+ unsigned i;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(1);
+
+ for (i=5;i<256;i+=2) {
+ t = i;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+ }
+}
+
+static void check_dtls_window_21(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(2);
+
+ t = 1;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_91(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(9);
+
+ t = 1;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_large_21(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT);
+ SET_WINDOW_LAST_RECV(LARGE_INT+2);
+
+ t = LARGE_INT+1;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_large_12(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT);
+ SET_WINDOW_LAST_RECV(LARGE_INT+1);
+
+ t = LARGE_INT+2;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_large_91(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT);
+ SET_WINDOW_LAST_RECV(LARGE_INT+9);
+
+ t = LARGE_INT+1;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_large_19(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT);
+ SET_WINDOW_LAST_RECV(LARGE_INT+1);
+
+ t = LARGE_INT+9;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_very_large_12(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(INT_OVER_32_BITS);
+ SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+1);
+
+ t = INT_OVER_32_BITS+2;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_very_large_91(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(INT_OVER_32_BITS);
+ SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+9);
+
+ t = INT_OVER_32_BITS+1;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_very_large_19(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(INT_OVER_32_BITS);
+ SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+1);
+
+ t = INT_OVER_32_BITS+9;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_outside(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(0);
+ SET_WINDOW_LAST_RECV(1);
+
+ t = 1+64;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_large_outside(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT);
+ SET_WINDOW_LAST_RECV(LARGE_INT+1);
+
+ t = LARGE_INT+1+64;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_very_large_outside(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(INT_OVER_32_BITS);
+ SET_WINDOW_LAST_RECV(INT_OVER_32_BITS+1);
+
+ t = INT_OVER_32_BITS+1+64;
+
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_dup1(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT-1);
+ SET_WINDOW_LAST_RECV(LARGE_INT);
+
+ t = LARGE_INT;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+1;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+16;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+1;
+ assert_int_equal(_dtls_record_check(&state, t), -3);
+}
+
+static void check_dtls_window_dup2(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT-1);
+ SET_WINDOW_LAST_RECV(LARGE_INT);
+
+ t = LARGE_INT;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+16;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+1;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+16;
+ assert_int_equal(_dtls_record_check(&state, t), -3);
+}
+
+static void check_dtls_window_dup3(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT-1);
+ SET_WINDOW_LAST_RECV(LARGE_INT);
+
+ t = LARGE_INT;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+16;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+15;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+14;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+5;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+5;
+ assert_int_equal(_dtls_record_check(&state, t), -3);
+}
+
+static void check_dtls_window_out_of_order(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT-1);
+ SET_WINDOW_LAST_RECV(LARGE_INT);
+
+ t = LARGE_INT;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+8;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+7;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+6;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+5;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+4;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+3;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+2;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+1;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = LARGE_INT+9;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+}
+
+static void check_dtls_window_epoch_higher(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ SET_WINDOW_NEXT(LARGE_INT-1);
+ SET_WINDOW_LAST_RECV(LARGE_INT);
+
+ t = LARGE_INT;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = (LARGE_INT+8)|0x1000000000000LL;
+ assert_int_equal(_dtls_record_check(&state, t), -1);
+}
+
+static void check_dtls_window_epoch_lower(void **glob_state)
+{
+ struct record_parameters_st state;
+ uint64_t t;
+
+ RESET_WINDOW;
+ t = 0x1000000000000LL;
+
+ state.epoch = 1;
+ SET_WINDOW_NEXT(0x1000000000000LL);
+ SET_WINDOW_LAST_RECV((0x1000000000000LL) + 1);
+
+ t = 2 | 0x1000000000000LL;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = 3 | 0x1000000000000LL;
+ assert_int_equal(_dtls_record_check(&state, t), 0);
+
+ t = 5;
+ assert_int_equal(_dtls_record_check(&state, t), -1);
+}
+
+
+int main(void)
+{
+ const struct CMUnitTest tests[] = {
+ cmocka_unit_test(check_dtls_window_uninit_0),
+ cmocka_unit_test(check_dtls_window_uninit_large),
+ cmocka_unit_test(check_dtls_window_uninit_very_large),
+ cmocka_unit_test(check_dtls_window_12),
+ cmocka_unit_test(check_dtls_window_21),
+ cmocka_unit_test(check_dtls_window_19),
+ cmocka_unit_test(check_dtls_window_91),
+ cmocka_unit_test(check_dtls_window_large_21),
+ cmocka_unit_test(check_dtls_window_large_12),
+ cmocka_unit_test(check_dtls_window_large_19),
+ cmocka_unit_test(check_dtls_window_large_91),
+ cmocka_unit_test(check_dtls_window_dup1),
+ cmocka_unit_test(check_dtls_window_dup2),
+ cmocka_unit_test(check_dtls_window_dup3),
+ cmocka_unit_test(check_dtls_window_outside),
+ cmocka_unit_test(check_dtls_window_large_outside),
+ cmocka_unit_test(check_dtls_window_out_of_order),
+ cmocka_unit_test(check_dtls_window_epoch_lower),
+ cmocka_unit_test(check_dtls_window_epoch_higher),
+ cmocka_unit_test(check_dtls_window_very_large_12),
+ cmocka_unit_test(check_dtls_window_very_large_19),
+ cmocka_unit_test(check_dtls_window_very_large_91),
+ cmocka_unit_test(check_dtls_window_very_large_outside),
+ cmocka_unit_test(check_dtls_window_skip3),
+ cmocka_unit_test(check_dtls_window_skip1)
+ };
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/dtls-with-seccomp.c b/tests/dtls-with-seccomp.c
new file mode 100644
index 0000000..357c333
--- /dev/null
+++ b/tests/dtls-with-seccomp.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2015 Nikos Mavrogiannopoulos
+ *
+ * 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32) || !defined(HAVE_LIBSECCOMP)
+
+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 <signal.h>
+#include <assert.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+static void terminate(void);
+
+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 ssize_t
+push(gnutls_transport_ptr_t tr, const void *data, size_t len)
+{
+ int fd = (long int) tr;
+
+ return send(fd, data, len, 0);
+}
+
+static void client(int fd, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t xcred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(4711);
+ }
+
+ gnutls_certificate_allocate_credentials(&xcred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init(&session, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
+ gnutls_dtls_set_mtu(session, 1500);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ assert(gnutls_priority_set_direct(session, prio, NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ /* 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)));
+
+ do {
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer)-1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ 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));
+ exit(1);
+ }
+
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if (ret < 0) {
+ fail("server: error in closing session: %s\n", gnutls_strerror(ret));
+ }
+
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(xcred);
+
+ gnutls_global_deinit();
+}
+
+
+/* These are global */
+pid_t child;
+
+static void terminate(void)
+{
+ int status;
+ assert(child);
+ kill(child, SIGTERM);
+ wait(&status);
+ exit(1);
+}
+
+static void server(int fd, const char *prio)
+{
+ int ret;
+ gnutls_certificate_credentials_t xcred;
+ char buffer[MAX_BUF + 1];
+ gnutls_session_t session;
+
+ /* 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(&xcred);
+
+ ret = gnutls_certificate_set_x509_key_mem(xcred,
+ &server_cert, &server_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ exit(1);
+
+ ret = disable_system_calls();
+ if (ret < 0) {
+ fprintf(stderr, "could not enable seccomp\n");
+ exit(2);
+ }
+
+ gnutls_init(&session, GNUTLS_SERVER | GNUTLS_DATAGRAM);
+ gnutls_handshake_set_timeout(session, get_timeout());
+ gnutls_dtls_set_mtu(session, 1500);
+
+ assert(gnutls_priority_set_direct(session, prio, NULL) >= 0);
+
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+ gnutls_transport_set_int(session, fd);
+ gnutls_transport_set_push_function(session, push);
+
+ 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)));
+
+ /* see the Getting peer's information example */
+ /* print_info(session); */
+
+ memset(buffer, 1, sizeof(buffer));
+ do {
+ ret = gnutls_record_send(session, buffer, sizeof(buffer)-1);
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0) {
+ close(fd);
+ gnutls_deinit(session);
+ fail("server: data sending has failed (%s)\n\n",
+ gnutls_strerror(ret));
+ terminate();
+ }
+
+ ret = gnutls_bye(session, GNUTLS_SHUT_RDWR);
+ if (ret < 0) {
+ fail("server: error in closing session: %s\n", gnutls_strerror(ret));
+ }
+
+ close(fd);
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(xcred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+}
+
+static
+void run(const char *name, const char *prio)
+{
+ int fd[2];
+ int ret;
+
+ success("trying: %s\n", name);
+ 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;
+ /* parent */
+
+ close(fd[0]);
+ client(fd[1], prio);
+
+ wait(&status);
+ check_wait_status(status);
+ } else {
+ close(fd[1]);
+ server(fd[0], prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ run("dtls1.0", "NORMAL:-KX-ALL:+ECDHE-RSA:-VERS-ALL:+VERS-DTLS1.0");
+ run("dtls1.2", "NORMAL:-KX-ALL:+ECDHE-RSA:-VERS-ALL:+VERS-DTLS1.2");
+ run("default", "NORMAL");
+}
+
+
+#endif /* _WIN32 */
diff --git a/tests/dtls/dtls-resume.sh b/tests/dtls/dtls-resume.sh
new file mode 100755
index 0000000..debd59e
--- /dev/null
+++ b/tests/dtls/dtls-resume.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# Copyright (C) 2016 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 General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+set -e
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d
+./dtls-stress -resume -sfinished 210 -cfinished 01 -d
+./dtls-stress -resume -sfinished 120 -cfinished 01 -d
+./dtls-stress -resume -sfinished 210 -cfinished 10 -d
+./dtls-stress -resume -sfinished 120 -cfinished 10 -d
+
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d SHello
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d CChangeCipherSpec
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d SChangeCipherSpec
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d SHello SChangeCipherSpec
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d CChangeCipherSpec SChangeCipherSpec CFinished
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d CChangeCipherSpec SChangeCipherSpec SFinished
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d CFinished SFinished
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d CFinished SFinished SChangeCipherSpec
+./dtls-stress -resume -sfinished 012 -cfinished 01 -d CFinished SFinished CChangeCipherSpec
+
+exit 0
diff --git a/tests/dtls/dtls-stress.c b/tests/dtls/dtls-stress.c
new file mode 100644
index 0000000..826bd29
--- /dev/null
+++ b/tests/dtls/dtls-stress.c
@@ -0,0 +1,1558 @@
+/*
+ * Copyright (C) 2012-2016 Sean Buckheister
+ * Copyright (C) 2016 Nikos Mavrogiannopoulos
+ * Copyright (C) 2016 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * DTLS stress test utility
+ *
+ * **** Available parameters ****
+ *
+ * -nb enable nonblocking operations on sessions
+ * -batch read test identifiers from stdin and run them
+ * -d increase debug level by one
+ * -r replay messages (very crude replay mechanism)
+ * -d <n> set debug level to <n>
+ * -die don't start new tests after the first detected failure
+ * -timeout <n> set handshake timeout to <n> seconds. Tests that don't make progress
+ * within twice this time will be forcibly killed. (default: 120)
+ * -retransmit <n> set retransmit timeout to <n> milliseconds (default: 100)
+ * -j <n> run up to <n> tests in parallel
+ * -full use full handshake with mutual certificate authentication
+ * -resume use resumed handshake
+ * -shello <perm> run only one test, with the server hello flight permuted as <perm>
+ * -sfinished <perm> run only one test, with the server finished flight permuted as <perm>
+ * -cfinished <perm> run only one test, with the client finished flight permuted as <perm>
+ * <packet name> run only one test, drop <packet name> three times
+ * valid values for <packet name> are:
+ * SHello, SCertificate, SKeyExchange, SCertificateRequest, SHelloDone,
+ * CCertificate, CKeyExchange, CCertificateVerify, CChangeCipherSpec,
+ * CFinished, SChangeCipherSpec, SFinished
+ * using *Certificate* without -full will yield unexpected results
+ *
+ *
+ * **** Permutation handling ****
+ *
+ * Flight length for -sfinished is 2, for -shello and -cfinished they are 5 with -full, 3 otherwise.
+ * Permutations are given with base 0 and specify the order in which reordered packets are transmitted.
+ * For example, -full -shello 42130 will transmit server hello flight packets in the order
+ * SHelloDone, SKeyExchange, SCertificate, SCertificateRequest, SHello
+ *
+ * When -resume is specified the -sfinished flight length is 3 (same as shello), cfinished is 2.
+ * The -resume option has to be combined with sfinished or cfinished.
+ *
+ * **** Output format ****
+ *
+ * Every line printed for any given test is prefixed by a unique id for that test. See run_test_by_id for
+ * exact composition. Errors encountered during execution are printed, with one status line after test
+ * completen. The format for this line is as follows:
+ *
+ * <id> <status> SHello(<shperm>), SFinished(<sfinperm>), CFinished(<cfinperm>) :- <drops>
+ *
+ * The format for error lines is <id> <role>| <text>, with <role> being the role of the child process
+ * that encountered the error, and <text> being obvious.
+ *
+ * <id> is the unique id for the test, it can be used as input to -batch.
+ * <status> can be ++ for a successful test, -- for a failure, TT for a deadlock timeout killed test,
+ * or !! for a test has died due to some unforeseen circumstances like syscall failures.
+ * <shperm>, <sfinperm>, <cfinperm> show the permutation for the respective flights used.
+ * They can be used as input to -shello, -sfinished, and -cfinished, respectively.
+ * <drops> is a comma separated list of <packet name>, one for every packet dropped thrice
+ *
+ *
+ * **** Exit status ****
+ *
+ * 0 all tests have passed
+ * 1 some tests have failed
+ * 4 the master processed has encountered unexpected errors
+ * 8 error parsing command line
+ */
+
+#include <config.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/openpgp.h>
+#include <gnutls/dtls.h>
+#include <unistd.h>
+#include "../utils.h"
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <poll.h>
+#include <time.h>
+#include <assert.h>
+#include <sys/wait.h>
+
+#if _POSIX_TIMERS && (_POSIX_TIMERS - 200112L) >= 0
+
+// {{{ types
+
+#define log(fmt, ...) \
+ if (debug) fprintf(stdout, "%i %s| "fmt, run_id, role_name, ##__VA_ARGS__)
+
+typedef struct {
+ int count;
+} filter_packet_state_t;
+
+typedef struct {
+ const char *name;
+ gnutls_datum_t packets[5];
+ int *order;
+ int count;
+} filter_permute_state_t;
+
+typedef void (*filter_fn) (gnutls_transport_ptr_t, const unsigned char *,
+ size_t);
+
+typedef int (*match_fn) (const unsigned char *, size_t);
+
+enum role { SERVER, CLIENT };
+
+// }}}
+
+// {{{ static data
+
+static int permutations2[2][2]
+= { {0, 1}, {1, 0} };
+
+static const char *permutation_names2[]
+= { "01", "10", 0 };
+
+static int permutations3[6][3]
+= { {0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0} };
+
+static const char *permutation_names3[]
+= { "012", "021", "102", "120", "201", "210", 0 };
+
+static int permutations5[120][5] = {
+ {0, 1, 2, 3, 4}, {0, 2, 1, 3, 4}, {1, 0, 2, 3, 4}, {1, 2, 0, 3, 4},
+ {2, 0, 1, 3, 4}, {2, 1, 0, 3, 4}, {0, 1, 3, 2, 4}, {0, 2, 3, 1, 4},
+ {1, 0, 3, 2, 4}, {1, 2, 3, 0, 4}, {2, 0, 3, 1, 4}, {2, 1, 3, 0, 4},
+ {0, 3, 1, 2, 4}, {0, 3, 2, 1, 4}, {1, 3, 0, 2, 4}, {1, 3, 2, 0, 4},
+ {2, 3, 0, 1, 4}, {2, 3, 1, 0, 4}, {3, 0, 1, 2, 4}, {3, 0, 2, 1, 4},
+ {3, 1, 0, 2, 4}, {3, 1, 2, 0, 4}, {3, 2, 0, 1, 4}, {3, 2, 1, 0, 4},
+ {0, 1, 2, 4, 3}, {0, 2, 1, 4, 3}, {1, 0, 2, 4, 3}, {1, 2, 0, 4, 3},
+ {2, 0, 1, 4, 3}, {2, 1, 0, 4, 3}, {0, 1, 3, 4, 2}, {0, 2, 3, 4, 1},
+ {1, 0, 3, 4, 2}, {1, 2, 3, 4, 0}, {2, 0, 3, 4, 1}, {2, 1, 3, 4, 0},
+ {0, 3, 1, 4, 2}, {0, 3, 2, 4, 1}, {1, 3, 0, 4, 2}, {1, 3, 2, 4, 0},
+ {2, 3, 0, 4, 1}, {2, 3, 1, 4, 0}, {3, 0, 1, 4, 2}, {3, 0, 2, 4, 1},
+ {3, 1, 0, 4, 2}, {3, 1, 2, 4, 0}, {3, 2, 0, 4, 1}, {3, 2, 1, 4, 0},
+ {0, 1, 4, 2, 3}, {0, 2, 4, 1, 3}, {1, 0, 4, 2, 3}, {1, 2, 4, 0, 3},
+ {2, 0, 4, 1, 3}, {2, 1, 4, 0, 3}, {0, 1, 4, 3, 2}, {0, 2, 4, 3, 1},
+ {1, 0, 4, 3, 2}, {1, 2, 4, 3, 0}, {2, 0, 4, 3, 1}, {2, 1, 4, 3, 0},
+ {0, 3, 4, 1, 2}, {0, 3, 4, 2, 1}, {1, 3, 4, 0, 2}, {1, 3, 4, 2, 0},
+ {2, 3, 4, 0, 1}, {2, 3, 4, 1, 0}, {3, 0, 4, 1, 2}, {3, 0, 4, 2, 1},
+ {3, 1, 4, 0, 2}, {3, 1, 4, 2, 0}, {3, 2, 4, 0, 1}, {3, 2, 4, 1, 0},
+ {0, 4, 1, 2, 3}, {0, 4, 2, 1, 3}, {1, 4, 0, 2, 3}, {1, 4, 2, 0, 3},
+ {2, 4, 0, 1, 3}, {2, 4, 1, 0, 3}, {0, 4, 1, 3, 2}, {0, 4, 2, 3, 1},
+ {1, 4, 0, 3, 2}, {1, 4, 2, 3, 0}, {2, 4, 0, 3, 1}, {2, 4, 1, 3, 0},
+ {0, 4, 3, 1, 2}, {0, 4, 3, 2, 1}, {1, 4, 3, 0, 2}, {1, 4, 3, 2, 0},
+ {2, 4, 3, 0, 1}, {2, 4, 3, 1, 0}, {3, 4, 0, 1, 2}, {3, 4, 0, 2, 1},
+ {3, 4, 1, 0, 2}, {3, 4, 1, 2, 0}, {3, 4, 2, 0, 1}, {3, 4, 2, 1, 0},
+ {4, 0, 1, 2, 3}, {4, 0, 2, 1, 3}, {4, 1, 0, 2, 3}, {4, 1, 2, 0, 3},
+ {4, 2, 0, 1, 3}, {4, 2, 1, 0, 3}, {4, 0, 1, 3, 2}, {4, 0, 2, 3, 1},
+ {4, 1, 0, 3, 2}, {4, 1, 2, 3, 0}, {4, 2, 0, 3, 1}, {4, 2, 1, 3, 0},
+ {4, 0, 3, 1, 2}, {4, 0, 3, 2, 1}, {4, 1, 3, 0, 2}, {4, 1, 3, 2, 0},
+ {4, 2, 3, 0, 1}, {4, 2, 3, 1, 0}, {4, 3, 0, 1, 2}, {4, 3, 0, 2, 1},
+ {4, 3, 1, 0, 2}, {4, 3, 1, 2, 0}, {4, 3, 2, 0, 1}, {4, 3, 2, 1, 0}
+};
+
+static const char *permutation_names5[]
+ = { "01234", "02134", "10234", "12034", "20134", "21034", "01324",
+ "02314", "10324", "12304", "20314", "21304", "03124", "03214",
+ "13024", "13204", "23014", "23104", "30124", "30214", "31024",
+ "31204", "32014", "32104", "01243", "02143", "10243", "12043",
+ "20143", "21043", "01342", "02341", "10342", "12340", "20341",
+ "21340", "03142", "03241", "13042", "13240", "23041", "23140",
+ "30142", "30241", "31042", "31240", "32041", "32140", "01423",
+ "02413", "10423", "12403", "20413", "21403", "01432", "02431",
+ "10432", "12430", "20431", "21430", "03412", "03421", "13402",
+ "13420", "23401", "23410", "30412", "30421", "31402", "31420",
+ "32401", "32410", "04123", "04213", "14023", "14203", "24013",
+ "24103", "04132", "04231", "14032", "14230", "24031", "24130",
+ "04312", "04321", "14302", "14320", "24301", "24310", "34012",
+ "34021", "34102", "34120", "34201", "34210", "40123", "40213",
+ "41023", "41203", "42013", "42103", "40132", "40231", "41032",
+ "41230", "42031", "42130", "40312", "40321", "41302", "41320",
+ "42301", "42310", "43012", "43021", "43102", "43120", "43201",
+ "43210", 0
+};
+
+static const char *filter_names[8]
+ = { "SHello",
+ "SKeyExchange",
+ "SHelloDone",
+ "CKeyExchange",
+ "CChangeCipherSpec",
+ "CFinished",
+ "SChangeCipherSpec",
+ "SFinished"
+};
+
+static const char *filter_names_resume[]
+ = { "SHello",
+ "SChangeCipherSpec",
+ "SFinished",
+ "CChangeCipherSpec",
+ "CFinished"
+};
+
+static const char *filter_names_full[12]
+ = { "SHello",
+ "SCertificate",
+ "SKeyExchange",
+ "SCertificateRequest",
+ "SHelloDone",
+ "CCertificate",
+ "CKeyExchange",
+ "CCertificateVerify",
+ "CChangeCipherSpec",
+ "CFinished",
+ "SChangeCipherSpec",
+ "SFinished"
+};
+
+#include "cert-common.h"
+
+// }}}
+
+// {{{ other global state
+
+enum role role;
+
+#define role_name (role == SERVER ? "server" : "client")
+
+int debug;
+int nonblock;
+int replay;
+int full;
+int resume;
+int timeout_seconds;
+int retransmit_milliseconds;
+int run_to_end;
+
+int run_id;
+
+// }}}
+
+// {{{ logging and error handling
+
+static void logfn(int level, const char *s)
+{
+ if (debug) {
+ fprintf(stdout, "%i %s|<%i> %s", run_id, role_name, level, s);
+ }
+}
+
+static void auditfn(gnutls_session_t session, const char *s)
+{
+ if (debug) {
+ fprintf(stdout, "%i %s| %s", run_id, role_name, s);
+ }
+}
+
+static void drop(const char *packet)
+{
+ if (debug) {
+ log("dropping %s\n", packet);
+ }
+}
+
+static int _process_error(int loc, int code, int die)
+{
+ if (code < 0 && (die || code != GNUTLS_E_AGAIN)) {
+ fprintf(stdout, "%i <%s tls> line %i: %s", run_id,
+ role_name, loc, gnutls_strerror(code));
+ if (gnutls_error_is_fatal(code) || die) {
+ fprintf(stdout, " (fatal)\n");
+ exit(1);
+ } else {
+ fprintf(stdout, "\n");
+ }
+ }
+ return code;
+}
+
+#define die_on_error(code) _process_error(__LINE__, code, 1)
+#define process_error(code) _process_error(__LINE__, code, 0)
+
+static void _process_error_or_timeout(int loc, int err, time_t tdiff)
+{
+ if (err < 0) {
+ if (err != GNUTLS_E_TIMEDOUT || tdiff >= 60) {
+ _process_error(loc, err, 0);
+ } else {
+ log("line %i: {spurious timeout} (fatal)", loc);
+ exit(1);
+ }
+ }
+}
+
+#define process_error_or_timeout(code, tdiff) _process_error_or_timeout(__LINE__, code, tdiff)
+
+static void rperror(const char *name)
+{
+ fprintf(stdout, "%i %s| %s\n", run_id, role_name, name);
+}
+
+// }}}
+
+// {{{ init, shared, and teardown code and data for packet stream filters
+
+filter_packet_state_t state_packet_ServerHello = { 0 };
+filter_packet_state_t state_packet_ServerCertificate = { 0 };
+filter_packet_state_t state_packet_ServerKeyExchange = { 0 };
+filter_packet_state_t state_packet_ServerCertificateRequest = { 0 };
+filter_packet_state_t state_packet_ServerHelloDone = { 0 };
+filter_packet_state_t state_packet_ClientCertificate = { 0 };
+filter_packet_state_t state_packet_ClientKeyExchange = { 0 };
+filter_packet_state_t state_packet_ClientCertificateVerify = { 0 };
+filter_packet_state_t state_packet_ClientChangeCipherSpec = { 0 };
+filter_packet_state_t state_packet_ClientFinished = { 0 };
+filter_packet_state_t state_packet_ClientFinishedResume = { 0 };
+filter_packet_state_t state_packet_ServerChangeCipherSpec = { 0 };
+filter_packet_state_t state_packet_ServerFinished = { 0 };
+filter_packet_state_t state_packet_ServerFinishedResume = { 0 };
+
+static filter_permute_state_t state_permute_ServerHello =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+static filter_permute_state_t state_permute_ServerHelloFull =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+static filter_permute_state_t state_permute_ServerFinished =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+static filter_permute_state_t state_permute_ServerFinishedResume =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+static filter_permute_state_t state_permute_ClientFinished =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+static filter_permute_state_t state_permute_ClientFinishedResume =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+static filter_permute_state_t state_permute_ClientFinishedFull =
+ { 0, {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}}, 0, 0 };
+
+filter_fn filter_chain[32];
+int filter_current_idx;
+
+static void filter_permute_state_free_buffer(filter_permute_state_t * state)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof(state->packets) / sizeof(state->packets[0]); i++) {
+ free(state->packets[i].data);
+ state->packets[i].data = NULL;
+ }
+}
+
+static void filter_clear_state(void)
+{
+ filter_current_idx = 0;
+
+ filter_permute_state_free_buffer(&state_permute_ServerHello);
+ filter_permute_state_free_buffer(&state_permute_ServerHelloFull);
+ filter_permute_state_free_buffer(&state_permute_ServerFinished);
+ filter_permute_state_free_buffer(&state_permute_ServerFinishedResume);
+ filter_permute_state_free_buffer(&state_permute_ClientFinished);
+ filter_permute_state_free_buffer(&state_permute_ClientFinishedResume);
+ filter_permute_state_free_buffer(&state_permute_ClientFinishedFull);
+
+ memset(&state_packet_ServerHello, 0, sizeof(state_packet_ServerHello));
+ memset(&state_packet_ServerCertificate, 0,
+ sizeof(state_packet_ServerCertificate));
+ memset(&state_packet_ServerKeyExchange, 0,
+ sizeof(state_packet_ServerKeyExchange));
+ memset(&state_packet_ServerCertificateRequest, 0,
+ sizeof(state_packet_ServerCertificateRequest));
+ memset(&state_packet_ServerHelloDone, 0,
+ sizeof(state_packet_ServerHelloDone));
+ memset(&state_packet_ClientCertificate, 0,
+ sizeof(state_packet_ClientCertificate));
+ memset(&state_packet_ClientKeyExchange, 0,
+ sizeof(state_packet_ClientKeyExchange));
+ memset(&state_packet_ClientCertificateVerify, 0,
+ sizeof(state_packet_ClientCertificateVerify));
+ memset(&state_packet_ClientChangeCipherSpec, 0,
+ sizeof(state_packet_ClientChangeCipherSpec));
+ memset(&state_packet_ClientFinished, 0,
+ sizeof(state_packet_ClientFinished));
+ memset(&state_packet_ClientFinishedResume, 0,
+ sizeof(state_packet_ClientFinishedResume));
+ memset(&state_packet_ServerChangeCipherSpec, 0,
+ sizeof(state_packet_ServerChangeCipherSpec));
+ memset(&state_packet_ServerFinished, 0,
+ sizeof(state_packet_ServerFinished));
+ memset(&state_packet_ServerFinishedResume, 0,
+ sizeof(state_packet_ServerFinishedResume));
+ memset(&state_permute_ServerHello, 0,
+ sizeof(state_permute_ServerHello));
+ memset(&state_permute_ServerHelloFull, 0,
+ sizeof(state_permute_ServerHelloFull));
+ memset(&state_permute_ServerFinished, 0,
+ sizeof(state_permute_ServerFinished));
+ memset(&state_permute_ClientFinished, 0,
+ sizeof(state_permute_ClientFinished));
+ memset(&state_permute_ClientFinishedResume, 0,
+ sizeof(state_permute_ClientFinishedResume));
+ memset(&state_permute_ClientFinishedFull, 0,
+ sizeof(state_permute_ClientFinishedFull));
+
+ state_permute_ServerHello.name = "ServerHello";
+ state_permute_ServerHelloFull.name = "ServerHelloFull";
+ state_permute_ServerFinished.name = "ServerFinished";
+ state_permute_ServerFinishedResume.name = "ServerFinishedResume";
+ state_permute_ClientFinished.name = "ClientFinished";
+ state_permute_ClientFinishedResume.name = "ClientFinishedResume";
+ state_permute_ClientFinishedFull.name = "ClientFinishedFull";
+}
+
+/* replay buffer */
+static int rbuffer[5 * 1024];
+unsigned rbuffer_size = 0;
+
+static void filter_run_next(gnutls_transport_ptr_t fd,
+ const unsigned char *buffer, size_t len)
+{
+ int ret = 0;
+ filter_fn fn = filter_chain[filter_current_idx];
+ filter_current_idx++;
+ if (fn) {
+ fn(fd, buffer, len);
+ } else {
+ ret = send((int)(intptr_t) fd, buffer, len, 0);
+ }
+ filter_current_idx--;
+
+ if (ret > 0 && replay != 0) {
+ if (rbuffer_size == 0 && len < sizeof(rbuffer)) {
+ memcpy(rbuffer, buffer, len);
+ rbuffer_size = len;
+ } else if (rbuffer_size != 0) {
+ send((int)(intptr_t) fd, rbuffer, rbuffer_size, 0);
+ if (len < sizeof(rbuffer) && len > rbuffer_size) {
+ memcpy(rbuffer, buffer, len);
+ rbuffer_size = len;
+ }
+ }
+ }
+}
+
+// }}}
+
+// {{{ packet match functions
+
+static int match_ServerHello(const unsigned char *buffer, size_t len)
+{
+ return role == SERVER && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 2;
+}
+
+static int match_ServerCertificate(const unsigned char *buffer, size_t len)
+{
+ return role == SERVER && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 11;
+}
+
+static int match_ServerKeyExchange(const unsigned char *buffer, size_t len)
+{
+ return role == SERVER && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 12;
+}
+
+static int match_ServerCertificateRequest(const unsigned char *buffer,
+ size_t len)
+{
+ return role == SERVER && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 13;
+}
+
+static int match_ServerHelloDone(const unsigned char *buffer, size_t len)
+{
+ return role == SERVER && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 14;
+}
+
+static int match_ClientCertificate(const unsigned char *buffer, size_t len)
+{
+ return role == CLIENT && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 11;
+}
+
+static int match_ClientKeyExchange(const unsigned char *buffer, size_t len)
+{
+ return role == CLIENT && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 16;
+}
+
+static int match_ClientCertificateVerify(const unsigned char *buffer,
+ size_t len)
+{
+ return role == CLIENT && len >= 13 + 1 && buffer[0] == 22
+ && buffer[13] == 15;
+}
+
+static int match_ClientChangeCipherSpec(const unsigned char *buffer, size_t len)
+{
+ return role == CLIENT && len >= 13 && buffer[0] == 20;
+}
+
+static int match_ClientFinished(const unsigned char *buffer, size_t len)
+{
+ return role == CLIENT && len >= 13 && buffer[0] == 22 && buffer[4] == 1;
+}
+
+static int match_ServerChangeCipherSpec(const unsigned char *buffer, size_t len)
+{
+ return role == SERVER && len >= 13 && buffer[0] == 20;
+}
+
+static int match_ServerFinished(const unsigned char *buffer, size_t len)
+{
+ return role == SERVER && len >= 13 && buffer[0] == 22 && buffer[4] == 1;
+}
+
+// }}}
+
+// {{{ packet drop filters
+
+#define FILTER_DROP_COUNT 3
+#define DECLARE_FILTER(packet) \
+ static void filter_packet_##packet(gnutls_transport_ptr_t fd, \
+ const unsigned char* buffer, size_t len) \
+ { \
+ if (match_##packet(buffer, len) && (state_packet_##packet).count++ < FILTER_DROP_COUNT) { \
+ drop(#packet); \
+ } else { \
+ filter_run_next(fd, buffer, len); \
+ } \
+ }
+
+DECLARE_FILTER(ServerHello)
+ DECLARE_FILTER(ServerCertificate)
+ DECLARE_FILTER(ServerKeyExchange)
+ DECLARE_FILTER(ServerCertificateRequest)
+ DECLARE_FILTER(ServerHelloDone)
+ DECLARE_FILTER(ClientCertificate)
+ DECLARE_FILTER(ClientKeyExchange)
+ DECLARE_FILTER(ClientCertificateVerify)
+ DECLARE_FILTER(ClientChangeCipherSpec)
+ DECLARE_FILTER(ClientFinished)
+ DECLARE_FILTER(ServerChangeCipherSpec)
+ DECLARE_FILTER(ServerFinished)
+// }}}
+// {{{ flight permutation filters
+static void filter_permute_state_run(filter_permute_state_t * state,
+ int packetCount,
+ gnutls_transport_ptr_t fd,
+ const unsigned char *buffer, size_t len)
+{
+ unsigned char *data;
+ int packet = state->order[state->count];
+
+ if (debug > 2)
+ log("running permutation for %s/%d/%d\n", state->name, packetCount, state->count);
+
+ data = malloc(len);
+ assert(data);
+ memcpy(data, buffer, len);
+ state->packets[packet].data = data;
+ state->packets[packet].size = len;
+ state->count++;
+
+ if (state->count == packetCount) {
+ for (packet = 0; packet < packetCount; packet++) {
+ filter_run_next(fd, state->packets[packet].data,
+ state->packets[packet].size);
+ }
+ filter_permute_state_free_buffer(state);
+ state->count = 0;
+ }
+}
+
+#define DECLARE_PERMUTE(flight) \
+ static void filter_permute_##flight(gnutls_transport_ptr_t fd, \
+ const unsigned char* buffer, size_t len) \
+ { \
+ int count = sizeof(permute_match_##flight) / sizeof(permute_match_##flight[0]); \
+ int i; \
+ for (i = 0; i < count; i++) { \
+ if (permute_match_##flight[i](buffer, len)) { \
+ filter_permute_state_run(&state_permute_##flight, count, fd, buffer, len); \
+ return; \
+ } \
+ } \
+ filter_run_next(fd, buffer, len); \
+ }
+
+static match_fn permute_match_ServerHello[] =
+ { match_ServerHello, match_ServerKeyExchange, match_ServerHelloDone };
+
+static match_fn permute_match_ServerHelloFull[] =
+ { match_ServerHello, match_ServerCertificate, match_ServerKeyExchange,
+ match_ServerCertificateRequest, match_ServerHelloDone
+};
+
+static match_fn permute_match_ServerFinished[] =
+ { match_ServerChangeCipherSpec, match_ServerFinished };
+
+static match_fn permute_match_ServerFinishedResume[] =
+ { match_ServerHello, match_ServerChangeCipherSpec, match_ServerFinished };
+
+static match_fn permute_match_ClientFinished[] =
+ { match_ClientKeyExchange, match_ClientChangeCipherSpec,
+ match_ClientFinished
+};
+
+static match_fn permute_match_ClientFinishedResume[] =
+ { match_ClientChangeCipherSpec, match_ClientFinished
+};
+
+static match_fn permute_match_ClientFinishedFull[] =
+ { match_ClientCertificate, match_ClientKeyExchange,
+ match_ClientCertificateVerify, match_ClientChangeCipherSpec,
+ match_ClientFinished
+};
+
+DECLARE_PERMUTE(ServerHello)
+ DECLARE_PERMUTE(ServerHelloFull)
+ DECLARE_PERMUTE(ServerFinishedResume)
+ DECLARE_PERMUTE(ServerFinished)
+ DECLARE_PERMUTE(ClientFinished)
+ DECLARE_PERMUTE(ClientFinishedResume)
+ DECLARE_PERMUTE(ClientFinishedFull)
+// }}}
+// {{{ emergency deadlock resolution time bomb
+timer_t killtimer_tid = 0;
+
+static void killtimer_set(void)
+{
+ struct sigevent sig;
+ struct itimerspec tout = { {0, 0}, {2 * timeout_seconds, 0} };
+
+ if (killtimer_tid != 0) {
+ timer_delete(killtimer_tid);
+ }
+
+ memset(&sig, 0, sizeof(sig));
+ sig.sigev_notify = SIGEV_SIGNAL;
+ sig.sigev_signo = 15;
+ if (timer_create(CLOCK_MONOTONIC, &sig, &killtimer_tid) < 0) {
+ rperror("timer_create");
+ exit(3);
+ }
+
+ timer_settime(killtimer_tid, 0, &tout, 0);
+}
+
+// }}}
+
+// {{{ actual gnutls operations
+
+gnutls_certificate_credentials_t cred;
+gnutls_session_t session;
+
+static ssize_t writefn(gnutls_transport_ptr_t fd, const void *buffer,
+ size_t len)
+{
+ filter_run_next(fd, (const unsigned char *)buffer, len);
+ return len;
+}
+
+static void await(int fd, int timeout)
+{
+ if (nonblock) {
+ struct pollfd p = { fd, POLLIN, 0 };
+ if (poll(&p, 1, timeout) < 0 && errno != EAGAIN
+ && errno != EINTR) {
+ rperror("poll");
+ exit(3);
+ }
+ }
+}
+
+static void cred_init(void)
+{
+ assert(gnutls_certificate_allocate_credentials(&cred)>=0);
+
+ gnutls_certificate_set_x509_key_mem(cred, &cli_ca3_cert, &cli_ca3_key,
+ GNUTLS_X509_FMT_PEM);
+}
+
+static void session_init(int sock, int server)
+{
+ gnutls_init(&session,
+ GNUTLS_DATAGRAM | (server ? GNUTLS_SERVER : GNUTLS_CLIENT)
+ | GNUTLS_NONBLOCK * nonblock);
+ gnutls_priority_set_direct(session,
+ "NORMAL:+ECDHE-RSA:+ANON-ECDH",
+ 0);
+ gnutls_transport_set_int(session, sock);
+
+ if (full) {
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
+ if (server) {
+ gnutls_certificate_server_set_request(session,
+ GNUTLS_CERT_REQUIRE);
+ }
+ } else if (server) {
+ gnutls_anon_server_credentials_t acred;
+ assert(gnutls_anon_allocate_server_credentials(&acred)>=0);
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, acred);
+ } else {
+ gnutls_anon_client_credentials_t acred;
+ assert(gnutls_anon_allocate_client_credentials(&acred)>=0);
+ gnutls_credentials_set(session, GNUTLS_CRD_ANON, acred);
+ }
+
+ gnutls_dtls_set_mtu(session, 1400);
+ gnutls_dtls_set_timeouts(session, retransmit_milliseconds,
+ timeout_seconds * 1000);
+}
+
+static void client(int sock)
+{
+ int err = 0;
+ time_t started = time(0);
+ const char *line = "foobar!";
+ char buffer[8192];
+ int len, ret;
+ gnutls_datum_t data = {NULL, 0};
+
+ session_init(sock, 0);
+
+ killtimer_set();
+
+ if (resume) {
+ do {
+ err = process_error(gnutls_handshake(session));
+ if (err != 0) {
+ int t = gnutls_dtls_get_timeout(session);
+ await(sock, t ? t : 100);
+ }
+ } while (err != 0);
+ process_error_or_timeout(err, time(0) - started);
+
+ ret = gnutls_session_get_data2(session, &data);
+ if (ret < 0) {
+ exit(1);
+ }
+ gnutls_deinit(session);
+
+ session_init(sock, 0);
+ gnutls_session_set_data(session, data.data, data.size);
+ gnutls_free(data.data);
+ data.data = NULL;
+
+ if (debug) {
+ fprintf(stdout, "%i %s| initial handshake complete\n", run_id, role_name);
+ }
+ }
+
+ gnutls_transport_set_push_function(session, writefn);
+
+ killtimer_set();
+ do {
+ err = process_error(gnutls_handshake(session));
+ if (err != 0) {
+ int t = gnutls_dtls_get_timeout(session);
+ await(sock, t ? t : 100);
+ }
+ } while (err != 0);
+ process_error_or_timeout(err, time(0) - started);
+
+ if (debug) {
+ fprintf(stdout, "%i %s| handshake complete\n", run_id, role_name);
+ }
+
+ if (resume) {
+ killtimer_set();
+
+ do {
+ await(sock, -1);
+ len =
+ process_error(gnutls_record_recv
+ (session, buffer, sizeof(buffer)));
+ } while (len < 0);
+
+ log("received data\n");
+
+ die_on_error(gnutls_record_send(session, buffer, len));
+
+ log("sent data\n");
+ exit(0);
+
+ } else {
+ killtimer_set();
+ die_on_error(gnutls_record_send(session, line, strlen(line)));
+
+ log("sent data\n");
+
+ do {
+ await(sock, -1);
+ len =
+ process_error(gnutls_record_recv
+ (session, buffer, sizeof(buffer)));
+ } while (len < 0);
+
+ log("received data\n");
+
+ if (len > 0 && strncmp(line, buffer, len) == 0) {
+ exit(0);
+ } else {
+ exit(1);
+ }
+ }
+
+}
+
+static gnutls_datum_t saved_data = {NULL, 0};
+
+static gnutls_datum_t db_fetch(void *dbf, gnutls_datum_t key)
+{
+ gnutls_datum_t t = {NULL, 0};
+ t.data = malloc(saved_data.size);
+ if (t.data == NULL)
+ return t;
+ memcpy(t.data, saved_data.data, saved_data.size);
+ t.size = saved_data.size;
+
+ return t;
+}
+
+static int db_delete(void *dbf, gnutls_datum_t key)
+{
+ return 0;
+}
+
+static int db_store(void *dbf, gnutls_datum_t key, gnutls_datum_t data)
+{
+ saved_data.data = malloc(data.size);
+ if (saved_data.data == NULL)
+ return -1;
+ memcpy(saved_data.data, data.data, data.size);
+ saved_data.size = data.size;
+ return 0;
+}
+
+static void server(int sock)
+{
+ int err;
+ const char *line = "server foobar!";
+ time_t started = time(0);
+ char buffer[8192];
+ int len;
+
+ session_init(sock, 1);
+
+ await(sock, -1);
+
+ killtimer_set();
+ if (resume) {
+ gnutls_db_set_retrieve_function(session, db_fetch);
+ gnutls_db_set_store_function(session, db_store);
+ gnutls_db_set_remove_function(session, db_delete);
+ gnutls_db_set_ptr(session, NULL);
+
+ do {
+ err = process_error(gnutls_handshake(session));
+ if (err != 0) {
+ int t = gnutls_dtls_get_timeout(session);
+ await(sock, t ? t : 100);
+ }
+ } while (err != 0);
+ process_error_or_timeout(err, time(0) - started);
+
+ gnutls_deinit(session);
+
+ session_init(sock, 1);
+ gnutls_db_set_retrieve_function(session, db_fetch);
+ gnutls_db_set_store_function(session, db_store);
+ gnutls_db_set_remove_function(session, db_delete);
+ gnutls_db_set_ptr(session, NULL);
+
+ if (debug) {
+ fprintf(stdout, "%i %s| initial handshake complete\n", run_id, role_name);
+ }
+ }
+
+ gnutls_transport_set_push_function(session, writefn);
+
+ await(sock, -1);
+
+ killtimer_set();
+ do {
+ err = process_error(gnutls_handshake(session));
+ if (err != 0) {
+ int t = gnutls_dtls_get_timeout(session);
+ await(sock, t ? t : 100);
+ }
+ } while (err != 0);
+ process_error_or_timeout(err, time(0) - started);
+
+ log("handshake complete\n");
+
+ if (resume) {
+ free(saved_data.data);
+ saved_data.data = NULL;
+ }
+
+ if (resume) {
+ killtimer_set();
+ die_on_error(gnutls_record_send(session, line, strlen(line)));
+
+ log("sent data\n");
+
+ do {
+ await(sock, -1);
+ len =
+ process_error(gnutls_record_recv
+ (session, buffer, sizeof(buffer)));
+ } while (len < 0);
+
+ log("received data\n");
+
+ if (len > 0 && strncmp(line, buffer, len) == 0) {
+ exit(0);
+ } else {
+ exit(1);
+ }
+ } else {
+ killtimer_set();
+
+ do {
+ await(sock, -1);
+ len =
+ process_error(gnutls_record_recv
+ (session, buffer, sizeof(buffer)));
+ } while (len < 0);
+
+ log("received data\n");
+
+ die_on_error(gnutls_record_send(session, buffer, len));
+
+ log("sent data\n");
+ }
+
+ exit(0);
+}
+
+// }}}
+
+// {{{ test running/handling itself
+
+#if 0
+static void udp_sockpair(int *socks)
+{
+ struct sockaddr_in6 sa =
+ { AF_INET6, htons(30000), 0, in6addr_loopback, 0 };
+ struct sockaddr_in6 sb =
+ { AF_INET6, htons(20000), 0, in6addr_loopback, 0 };
+
+ socks[0] = socket(AF_INET6, SOCK_DGRAM, 0);
+ socks[1] = socket(AF_INET6, SOCK_DGRAM, 0);
+
+ bind(socks[0], (struct sockaddr *)&sa, sizeof(sa));
+ bind(socks[1], (struct sockaddr *)&sb, sizeof(sb));
+
+ connect(socks[1], (struct sockaddr *)&sa, sizeof(sa));
+ connect(socks[0], (struct sockaddr *)&sb, sizeof(sb));
+}
+#endif
+
+static int run_test(void)
+{
+ int fds[2];
+ int pid1, pid2;
+ int status2;
+
+ if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) < 0) {
+ rperror("socketpair");
+ exit(2);
+ }
+
+ if (nonblock) {
+ fcntl(fds[0], F_SETFL, (long)O_NONBLOCK);
+ fcntl(fds[1], F_SETFL, (long)O_NONBLOCK);
+ }
+
+ if (!(pid1 = fork())) {
+ role = SERVER;
+ server(fds[1]); // noreturn
+ } else if (pid1 < 0) {
+ rperror("fork server");
+ exit(2);
+ }
+ if (!(pid2 = fork())) {
+ role = CLIENT;
+ client(fds[0]); // noreturn
+ } else if (pid2 < 0) {
+ rperror("fork client");
+ exit(2);
+ }
+ while (waitpid(pid2, &status2, 0) < 0 && errno == EINTR) ;
+ kill(pid1, 15);
+ while (waitpid(pid1, 0, 0) < 0 && errno == EINTR) ;
+
+ close(fds[0]);
+ close(fds[1]);
+
+ if (!WIFSIGNALED(status2) && WEXITSTATUS(status2) != 3) {
+ return ! !WEXITSTATUS(status2);
+ } else {
+ return 3;
+ }
+}
+
+static filter_fn filters[]
+ = { filter_packet_ServerHello,
+ filter_packet_ServerKeyExchange,
+ filter_packet_ServerHelloDone,
+ filter_packet_ClientKeyExchange,
+ filter_packet_ClientChangeCipherSpec,
+ filter_packet_ClientFinished,
+ filter_packet_ServerChangeCipherSpec,
+ filter_packet_ServerFinished
+};
+
+static filter_fn filters_resume[]
+ = { filter_packet_ServerHello,
+ filter_packet_ServerChangeCipherSpec,
+ filter_packet_ServerFinished,
+ filter_packet_ClientChangeCipherSpec,
+ filter_packet_ClientFinished
+};
+
+static filter_fn filters_full[]
+ = { filter_packet_ServerHello,
+ filter_packet_ServerCertificate,
+ filter_packet_ServerKeyExchange,
+ filter_packet_ServerCertificateRequest,
+ filter_packet_ServerHelloDone,
+ filter_packet_ClientCertificate,
+ filter_packet_ClientKeyExchange,
+ filter_packet_ClientCertificateVerify,
+ filter_packet_ClientChangeCipherSpec,
+ filter_packet_ClientFinished,
+ filter_packet_ServerChangeCipherSpec,
+ filter_packet_ServerFinished
+};
+
+static int run_one_test(int dropMode, int serverFinishedPermute,
+ int serverHelloPermute, int clientFinishedPermute)
+{
+ int fnIdx = 0;
+ int res, filterIdx;
+ filter_fn *local_filters;
+ const char **local_filter_names;
+ const char **client_finished_permutation_names;
+ const char **server_finished_permutation_names;
+ const char **server_hello_permutation_names;
+ int filter_count;
+
+ if (full) {
+ local_filters = filters_full;
+ local_filter_names = filter_names_full;
+ filter_count = sizeof(filters_full)/sizeof(filters_full[0]);
+ client_finished_permutation_names = permutation_names5;
+ server_finished_permutation_names = permutation_names2;
+ server_hello_permutation_names = permutation_names5;
+ } else if (resume) {
+ local_filters = filters_resume;
+ local_filter_names = filter_names_resume;
+ filter_count = sizeof(filters_resume)/sizeof(filters_resume[0]);
+ client_finished_permutation_names = permutation_names2;
+ server_finished_permutation_names = permutation_names3;
+ server_hello_permutation_names = NULL;
+ } else {
+ local_filters = filters;
+ local_filter_names = filter_names;
+ filter_count = sizeof(filters)/sizeof(filters[0]);
+ client_finished_permutation_names = permutation_names3;
+ server_finished_permutation_names = permutation_names2;
+ server_hello_permutation_names = permutation_names3;
+ }
+
+ run_id =
+ ((dropMode * 2 + serverFinishedPermute) * (full ? 120 : 6) +
+ serverHelloPermute) * (full ? 120 : 6) + clientFinishedPermute;
+
+ filter_clear_state();
+
+ if (full) {
+ filter_chain[fnIdx++] = filter_permute_ServerHelloFull;
+ state_permute_ServerHelloFull.order =
+ permutations5[serverHelloPermute];
+
+ filter_chain[fnIdx++] = filter_permute_ClientFinishedFull;
+ state_permute_ClientFinishedFull.order =
+ permutations5[clientFinishedPermute];
+
+ filter_chain[fnIdx++] = filter_permute_ServerFinished;
+ state_permute_ServerFinished.order =
+ permutations2[serverFinishedPermute];
+ } else if (resume) {
+ filter_chain[fnIdx++] = filter_permute_ServerFinishedResume;
+ state_permute_ServerFinishedResume.order =
+ permutations3[serverFinishedPermute];
+
+ filter_chain[fnIdx++] = filter_permute_ClientFinishedResume;
+ state_permute_ClientFinishedResume.order =
+ permutations2[clientFinishedPermute];
+ } else {
+ filter_chain[fnIdx++] = filter_permute_ServerHello;
+ state_permute_ServerHello.order =
+ permutations3[serverHelloPermute];
+
+ filter_chain[fnIdx++] = filter_permute_ClientFinished;
+ state_permute_ClientFinished.order =
+ permutations3[clientFinishedPermute];
+
+ filter_chain[fnIdx++] = filter_permute_ServerFinished;
+ state_permute_ServerFinished.order =
+ permutations2[serverFinishedPermute];
+ }
+
+ if (dropMode) {
+ for (filterIdx = 0; filterIdx < filter_count; filterIdx++) {
+ if (dropMode & (1 << filterIdx)) {
+ filter_chain[fnIdx++] =
+ local_filters[filterIdx];
+ }
+ }
+ }
+ filter_chain[fnIdx++] = NULL;
+
+ res = run_test();
+
+ switch (res) {
+ case 0:
+ fprintf(stdout, "%i ++ ", run_id);
+ break;
+ case 1:
+ fprintf(stdout, "%i -- ", run_id);
+ break;
+ case 2:
+ fprintf(stdout, "%i !! ", run_id);
+ break;
+ case 3:
+ fprintf(stdout, "%i TT ", run_id);
+ break;
+ }
+
+ if (!resume)
+ fprintf(stdout, "SHello(%s), ", server_hello_permutation_names[serverHelloPermute]);
+ fprintf(stdout, "SFinished(%s), ",
+ server_finished_permutation_names[serverFinishedPermute]);
+ fprintf(stdout, "CFinished(%s) :- ",
+ client_finished_permutation_names[clientFinishedPermute]);
+ if (dropMode) {
+ for (filterIdx = 0; filterIdx < filter_count; filterIdx++) {
+ if (dropMode & (1 << filterIdx)) {
+ if (dropMode & ((1 << filterIdx) - 1)) {
+ fprintf(stdout, ", ");
+ }
+ fprintf(stdout, "%s",
+ local_filter_names[filterIdx]);
+ }
+ }
+ }
+ fprintf(stdout, "\n");
+
+ return res;
+}
+
+static int run_test_by_id(int id)
+{
+ int pscale = full ? 120 : 6;
+ int dropMode, serverFinishedPermute, serverHelloPermute,
+ clientFinishedPermute;
+
+ clientFinishedPermute = id % pscale;
+ id /= pscale;
+
+ serverHelloPermute = id % pscale;
+ id /= pscale;
+
+ serverFinishedPermute = id % 2;
+ id /= 2;
+
+ dropMode = id;
+
+ return run_one_test(dropMode, serverFinishedPermute,
+ serverHelloPermute, clientFinishedPermute);
+}
+
+int *job_pids;
+int job_limit;
+int children = 0;
+
+static void register_child(int pid)
+{
+ int idx;
+
+ children++;
+ for (idx = 0; idx < job_limit; idx++) {
+ if (job_pids[idx] == 0) {
+ job_pids[idx] = pid;
+ return;
+ }
+ }
+}
+
+static int wait_children(int child_limit)
+{
+ int fail = 0;
+ int result = 1;
+
+ while (children > child_limit) {
+ int status;
+ int idx;
+ int pid = waitpid(0, &status, 0);
+ if (pid < 0 && errno == ECHILD) {
+ break;
+ }
+ for (idx = 0; idx < job_limit; idx++) {
+ if (job_pids[idx] == pid) {
+ children--;
+ if (WEXITSTATUS(status)) {
+ result = 1;
+ if (!run_to_end && !fail) {
+ fprintf(stderr,
+ "One test failed, waiting for remaining tests\n");
+ fail = 1;
+ child_limit = 0;
+ }
+ }
+ job_pids[idx] = 0;
+ break;
+ }
+ }
+ }
+
+ if (fail) {
+ exit(1);
+ }
+
+ return result;
+}
+
+static int run_tests_from_id_list(int childcount)
+{
+ int test_id;
+ int ret;
+ int result = 0;
+
+ while ((ret = fscanf(stdin, "%i\n", &test_id)) > 0) {
+ int pid;
+ if (test_id < 0
+ || test_id >
+ 2 * (full ? 120 * 120 * (1 << 12) : 6 * 6 * 256)) {
+ fprintf(stderr, "Invalid test id %i\n", test_id);
+ break;
+ }
+ if (!(pid = fork())) {
+ exit(run_test_by_id(test_id));
+ } else if (pid < 0) {
+ rperror("fork");
+ result = 4;
+ break;
+ } else {
+ register_child(pid);
+ result |= wait_children(childcount);
+ }
+ }
+
+ if (ret < 0 && ret != EOF) {
+ fprintf(stderr, "Error reading test id list\n");
+ }
+
+ result |= wait_children(0);
+
+ return result;
+}
+
+static int run_all_tests(int childcount)
+{
+ int dropMode, serverFinishedPermute, serverHelloPermute,
+ clientFinishedPermute;
+ int result = 0;
+
+ for (dropMode = 0; dropMode != 1 << (full ? 12 : 8); dropMode++)
+ for (serverFinishedPermute = 0; serverFinishedPermute < 2;
+ serverFinishedPermute++)
+ for (serverHelloPermute = 0;
+ serverHelloPermute < (full ? 120 : 6);
+ serverHelloPermute++)
+ for (clientFinishedPermute = 0;
+ clientFinishedPermute <
+ (full ? 120 : 6);
+ clientFinishedPermute++) {
+ int pid;
+ if (!(pid = fork())) {
+ exit(run_one_test
+ (dropMode,
+ serverFinishedPermute,
+ serverHelloPermute,
+ clientFinishedPermute));
+ } else if (pid < 0) {
+ rperror("fork");
+ result = 4;
+ break;
+ } else {
+ register_child(pid);
+ result |=
+ wait_children(childcount);
+ }
+ }
+
+ result |= wait_children(0);
+
+ return result;
+}
+
+// }}}
+
+static int parse_permutation(const char *arg, const char *permutations[],
+ int *val)
+{
+ *val = 0;
+ while (permutations[*val]) {
+ if (strcmp(permutations[*val], arg) == 0) {
+ return 1;
+ } else {
+ *val += 1;
+ }
+ }
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ int dropMode = 0;
+ int serverFinishedPermute = 0;
+ int serverHelloPermute = 0;
+ int clientFinishedPermute = 0;
+ int batch = 0;
+ unsigned single = 0;
+ int arg;
+
+ nonblock = 0;
+ replay = 0;
+ debug = 0;
+ timeout_seconds = 120;
+ retransmit_milliseconds = 100;
+ full = 0;
+ run_to_end = 1;
+ job_limit = 1;
+
+#define NEXT_ARG(name) \
+ do { \
+ if (++arg >= argc) { \
+ fprintf(stderr, "No argument for -" #name "\n"); \
+ exit(8); \
+ } \
+ } while (0);
+#define FAIL_ARG(name) \
+ do { \
+ fprintf(stderr, "Invalid argument for -" #name "\n"); \
+ exit(8); \
+ } while (0);
+
+ for (arg = 1; arg < argc; arg++) {
+ if (strcmp("-die", argv[arg]) == 0) {
+ run_to_end = 0;
+ } else if (strcmp("-batch", argv[arg]) == 0) {
+ batch = 1;
+ } else if (strcmp("-d", argv[arg]) == 0) {
+ char *end;
+ int level;
+
+ if (arg+1 < argc) {
+ level = strtol(argv[arg + 1], &end, 10);
+ if (*end == '\0') {
+ debug = level;
+ arg++;
+ } else
+ debug++;
+ } else {
+ debug++;
+ }
+ } else if (strcmp("-nb", argv[arg]) == 0) {
+ nonblock = 1;
+ } else if (strcmp("-r", argv[arg]) == 0) {
+ replay = 1;
+ } else if (strcmp("-timeout", argv[arg]) == 0) {
+ char *end;
+ int val;
+
+ NEXT_ARG(timeout);
+ val = strtol(argv[arg], &end, 10);
+ if (*end == '\0') {
+ timeout_seconds = val;
+ } else {
+ FAIL_ARG(timeout);
+ }
+ } else if (strcmp("-retransmit", argv[arg]) == 0) {
+ char *end;
+ int val;
+
+ NEXT_ARG(retransmit);
+ val = strtol(argv[arg], &end, 10);
+ if (*end == '\0') {
+ retransmit_milliseconds = val;
+ } else {
+ FAIL_ARG(retransmit);
+ }
+ } else if (strcmp("-j", argv[arg]) == 0) {
+ char *end;
+ int val;
+
+ NEXT_ARG(timeout);
+ val = strtol(argv[arg], &end, 10);
+ if (*end == '\0') {
+ job_limit = val;
+ } else {
+ FAIL_ARG(j);
+ }
+ } else if (strcmp("-full", argv[arg]) == 0) {
+ if (resume) {
+ fprintf(stderr, "You cannot combine full with resume\n");
+ exit(1);
+ }
+
+ full = 1;
+ } else if (strcmp("-resume", argv[arg]) == 0) {
+ if (full) {
+ fprintf(stderr, "You cannot combine full with resume\n");
+ exit(1);
+ }
+
+ resume = 1;
+ } else if (strcmp("-shello", argv[arg]) == 0) {
+ if (resume) {
+ fprintf(stderr, "Please use -sfinished instead of -shello\n");
+ exit(1);
+ }
+
+ NEXT_ARG(shello);
+ if (!parse_permutation
+ (argv[arg],
+ full ? permutation_names5 :
+ permutation_names3, &serverHelloPermute)) {
+ FAIL_ARG(shell);
+ }
+ single++;
+ } else if (strcmp("-sfinished", argv[arg]) == 0) {
+ const char **pname;
+ NEXT_ARG(cfinished);
+ if (resume) pname = permutation_names3;
+ else pname = permutation_names2;
+ if (!parse_permutation
+ (argv[arg], pname,
+ &serverFinishedPermute)) {
+ FAIL_ARG(cfinished);
+ }
+ single++;
+ } else if (strcmp("-cfinished", argv[arg]) == 0) {
+ const char **pname;
+ NEXT_ARG(cfinished);
+ if (full) pname = permutation_names5;
+ else if (resume) pname = permutation_names2;
+ else pname = permutation_names3;
+ if (!parse_permutation
+ (argv[arg], pname,
+ &clientFinishedPermute)) {
+ FAIL_ARG(cfinished);
+ }
+ single++;
+ } else {
+ int drop;
+ int filter_count;
+ const char **local_filter_names;
+
+ if (full) {
+ local_filter_names = filter_names_full;
+ filter_count = sizeof(filters_full)/sizeof(filters_full[0]);
+ } else if (resume) {
+ local_filter_names = filter_names_resume;
+ filter_count = sizeof(filters_resume)/sizeof(filters_resume[0]);
+ } else {
+ local_filter_names = filter_names;
+ filter_count = sizeof(filters)/sizeof(filters[0]);
+ }
+
+ for (drop = 0; drop < filter_count; drop++) {
+ if (strcmp
+ (local_filter_names[drop],
+ argv[arg]) == 0) {
+ dropMode |= (1 << drop);
+ break;
+ }
+ }
+ if (drop == filter_count) {
+ fprintf(stderr, "Unknown packet %s\n",
+ argv[arg]);
+ exit(8);
+ }
+ single++;
+ }
+ }
+
+ setlinebuf(stdout);
+ global_init();
+ cred_init();
+ gnutls_global_set_log_function(logfn);
+ gnutls_global_set_audit_log_function(auditfn);
+ gnutls_global_set_log_level(debug);
+
+ if (single) {
+ if (debug)
+ fprintf(stderr, "single test mode\n");
+ return run_one_test(dropMode, serverFinishedPermute,
+ serverHelloPermute, clientFinishedPermute);
+ } else {
+ if (debug)
+ fprintf(stderr, "multi test mode\n");
+
+ if (resume) {
+ fprintf(stderr, "full run not implemented yet for resumed runs\n");
+ exit(5);
+ }
+
+ job_pids = calloc(sizeof(int), job_limit);
+ if (batch) {
+ return run_tests_from_id_list(job_limit);
+ } else {
+ return run_all_tests(job_limit);
+ }
+ }
+}
+
+// vim: foldmethod=marker
+
+#else /* NO POSIX TIMERS */
+
+int main(int argc, const char *argv[])
+{
+ exit(77);
+}
+
+#endif
diff --git a/tests/dtls/dtls.sh b/tests/dtls/dtls.sh
new file mode 100755
index 0000000..0b79ae8
--- /dev/null
+++ b/tests/dtls/dtls.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+# Copyright (C) 2012 Free Software Foundation, 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 General Public License
+# along with GnuTLS; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+set -e
+
+if test "${WINDIR}" != ""; then
+ exit 77
+fi
+
+./dtls-stress -full -shello 01234 -sfinished 01 -cfinished 01234 CCertificate CKeyExchange CCertificateVerify CChangeCipherSpec CFinished
+./dtls-stress -full -r -shello 42130 -sfinished 10 -cfinished 43210 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SChangeCipherSpec SCertificate SFinished
+
+./dtls-stress -shello 021 -sfinished 01 -cfinished 012 SKeyExchange CKeyExchange CFinished
+./dtls-stress -shello 012 -sfinished 10 -cfinished 210 SHello SKeyExchange SHelloDone
+./dtls-stress -shello 012 -sfinished 01 -cfinished 021 SHello SKeyExchange SHelloDone
+./dtls-stress -shello 021 -sfinished 01 -cfinished 201 SHello SHelloDone CChangeCipherSpec SChangeCipherSpec SFinished
+./dtls-stress -shello 102 -sfinished 01 -cfinished 120 SHello SHelloDone CKeyExchange CFinished SChangeCipherSpec SFinished
+./dtls-stress -shello 210 -sfinished 01 -cfinished 201 CChangeCipherSpec SChangeCipherSpec SFinished
+./dtls-stress -shello 021 -sfinished 10 -cfinished 210 SHello SHelloDone SChangeCipherSpec CChangeCipherSpec CFinished
+./dtls-stress -shello 210 -sfinished 10 -cfinished 210 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SChangeCipherSpec SFinished
+
+./dtls-stress -full -shello 42130 -sfinished 10 -cfinished 43210 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SChangeCipherSpec SCertificate SFinished
+./dtls-stress -full -shello 12430 -sfinished 01 -cfinished 01324 SHello SKeyExchange SHelloDone CKeyExchange CChangeCipherSpec CFinished SCertificate SFinished
+
+exit 0
diff --git a/tests/dtls1-2-mtu-check.c b/tests/dtls1-2-mtu-check.c
new file mode 100644
index 0000000..f27929b
--- /dev/null
+++ b/tests/dtls1-2-mtu-check.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2016 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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* This program tests the MTU calculation in various cipher/mac algorithm combinations
+ * in gnutls */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/dtls.h>
+#include "eagain-common.h"
+#include "cert-common.h"
+#include "utils.h"
+#include <assert.h>
+
+#define myfail(fmt, ...) \
+ fail("%s: "fmt, name, ##__VA_ARGS__)
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "|<%d>| %s", level, str);
+}
+
+static void dtls_mtu_try(const char *name, const char *client_prio,
+ unsigned link_mtu, unsigned tunnel_mtu)
+{
+ 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;
+ unsigned dmtu;
+ unsigned i;
+
+ /* General init. */
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(6);
+
+ reset_buffers();
+ /* Init server */
+ 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|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK) >= 0);
+ gnutls_credentials_set(server, GNUTLS_CRD_CERTIFICATE,
+ serverx509cred);
+
+ assert(gnutls_priority_set_direct(server,
+ "NORMAL:+ANON-ECDH:+ANON-DH:+3DES-CBC:+ECDHE-RSA:+DHE-RSA:+RSA:+ECDHE-ECDSA:+SHA256:+CURVE-X25519",
+ NULL) >= 0);
+ gnutls_transport_set_push_function(server, server_push);
+ gnutls_transport_set_pull_function(server, server_pull);
+ gnutls_transport_set_pull_timeout_function(server, server_pull_timeout_func);
+ gnutls_transport_set_ptr(server, server);
+
+ /* Init client */
+
+ ret = gnutls_init(&client, GNUTLS_CLIENT|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK);
+ if (ret < 0)
+ exit(1);
+
+ assert(gnutls_certificate_allocate_credentials(&clientx509cred) >= 0);
+ assert(gnutls_certificate_set_x509_trust_mem(clientx509cred, &ca_cert, GNUTLS_X509_FMT_PEM)>=0);
+
+ assert(gnutls_credentials_set(client, GNUTLS_CRD_CERTIFICATE,
+ clientx509cred) >= 0);
+
+ gnutls_transport_set_push_function(client, client_push);
+ gnutls_transport_set_pull_function(client, client_pull);
+ gnutls_transport_set_pull_timeout_function(client, client_pull_timeout_func);
+
+ gnutls_transport_set_ptr(client, client);
+
+ ret = gnutls_priority_set_direct(client, client_prio, NULL);
+ if (ret < 0) {
+ fail("%s: error in priority setting\n", name);
+ exit(1);
+ }
+ success("negotiating %s\n", name);
+ HANDSHAKE_DTLS(client, server);
+
+ gnutls_dtls_set_mtu(client, link_mtu);
+ dmtu = gnutls_dtls_get_data_mtu(client);
+ if (dmtu != tunnel_mtu) {
+ fail("%s: Calculated MTU (%d) does not match expected (%d)\n", name, dmtu, tunnel_mtu);
+ }
+
+ {
+ char *msg = gnutls_malloc(dmtu+1);
+ assert(msg);
+ memset(msg, 1, dmtu+1);
+ ret = gnutls_record_send(client, msg, dmtu+1);
+ if (ret != (int)GNUTLS_E_LARGE_PACKET) {
+ myfail("could send larger packet than MTU (%d), ret: %d\n", dmtu, ret);
+ }
+
+ ret = gnutls_record_send(client, msg, dmtu);
+ if (ret != (int)dmtu) {
+ myfail("could not send %d bytes (sent %d)\n", dmtu, ret);
+ }
+
+ memset(msg, 2, dmtu);
+ ret = gnutls_record_recv(server, msg, dmtu);
+ if (ret != (int)dmtu) {
+ myfail("could not receive %d bytes (received %d)\n", dmtu, ret);
+ }
+
+ for (i=0;i<dmtu;i++)
+ assert(msg[i]==1);
+
+ gnutls_free(msg);
+ }
+
+ gnutls_dtls_set_data_mtu(client, link_mtu);
+ dmtu = gnutls_dtls_get_data_mtu(client);
+ if (dmtu != link_mtu) {
+ if (gnutls_mac_get(client) == GNUTLS_MAC_AEAD)
+ fail("%s: got MTU (%d) which does not match expected (%d)\n", name, dmtu, link_mtu);
+ else if (dmtu < link_mtu)
+ fail("%s: got MTU (%d) smaller than expected (%d)\n", name, dmtu, link_mtu);
+ }
+
+ gnutls_dtls_set_mtu(client, link_mtu);
+ dmtu = gnutls_dtls_get_mtu(client);
+ if (dmtu != link_mtu) {
+ fail("%s: got MTU (%d) which does not match expected (%d)\n", name, dmtu, link_mtu);
+ }
+
+ 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);
+}
+
+
+void doit(void)
+{
+ global_init();
+
+ /* check padding in CBC */
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1500, 1435);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1501", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1501, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1502", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1502, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1503", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1503, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1504", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1504, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1505", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1505, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1506", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1506, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1507", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1507, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1508", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1508, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1509", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1509, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1510", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1510, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1511", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1511, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1512", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1512, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1513", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1513, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1514", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1514, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1515", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1515, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1516", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1516, 1451);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1517", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1517, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1518", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1518, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1519", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1519, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1520", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1520, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1521", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1521, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1522", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1522, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1523", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1523, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1524", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1524, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1525", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1525, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1526", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1526, 1467);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1536", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1536, 1483);
+
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA256", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA256", 1500, 1423);
+ if (!gnutls_fips140_mode_enabled())
+ dtls_mtu_try("DTLS 1.2 with 3DES-CBC-HMAC-SHA1", "NORMAL:%NO_ETM:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+3DES-CBC:-MAC-ALL:+SHA1", 1500, 1451);
+
+ /* check non-CBC ciphers */
+ dtls_mtu_try("DTLS 1.2 with AES-128-GCM", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-GCM", 1500, 1463);
+ if (!gnutls_fips140_mode_enabled())
+ dtls_mtu_try("DTLS 1.2 with CHACHA20-POLY1305", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+CHACHA20-POLY1305", 1500, 1471);
+
+ /* check EtM CBC */
+ dtls_mtu_try("DTLS 1.2/EtM with AES-128-CBC-HMAC-SHA1", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1500, 1439);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1501", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1501, 1439);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1502", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1502, 1439);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1503", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1503, 1439);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1504", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1504, 1439);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1505", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1505, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1506", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1506, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1507", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1507, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1508", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1508, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1509", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1509, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1510", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1510, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1511", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1511, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1512", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1512, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1513", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1513, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1514", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1514, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1515", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1515, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1516", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1516, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1517", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1517, 1455);
+ dtls_mtu_try("DTLS 1.2 with AES-128-CBC-HMAC-SHA1 - mtu:1518", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1", 1518, 1455);
+
+ dtls_mtu_try("DTLS 1.2/EtM with AES-128-CBC-HMAC-SHA256", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA256", 1500, 1423);
+ if (!gnutls_fips140_mode_enabled())
+ dtls_mtu_try("DTLS 1.2/EtM with 3DES-CBC-HMAC-SHA1", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-CIPHER-ALL:+3DES-CBC:-MAC-ALL:+SHA1", 1500, 1455);
+
+ gnutls_global_deinit();
+}
diff --git a/tests/dtls10-cert-key-exchange.c b/tests/dtls10-cert-key-exchange.c
new file mode 100644
index 0000000..90b19bd
--- /dev/null
+++ b/tests/dtls10-cert-key-exchange.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015-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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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();
+
+ dtls_try("DTLS 1.0 with anon-ecdh", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ANON-ECDH", GNUTLS_KX_ANON_ECDH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.0 with anon-dh", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ANON-DH", GNUTLS_KX_ANON_DH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.0 with dhe-rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.0 with ecdhe x25519 rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA:-CURVE-ALL:+CURVE-X25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.0 with ecdhe rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try_with_key("DTLS 1.0 with ecdhe ecdsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0);
+
+ dtls_try("DTLS 1.0 with rsa no cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN);
+ dtls_try_cli("DTLS 1.0 with dhe-rsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT);
+ dtls_try_cli("DTLS 1.0 with ecdhe-rsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT);
+ dtls_try_cli("DTLS 1.0 with rsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_RSA_SHA256, USE_CERT);
+ dtls_try_with_key("DTLS 1.0 with ecdhe ecdsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_RSA_SHA256,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, USE_CERT);
+
+ dtls_try_cli("DTLS 1.0 with dhe-rsa ask cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT);
+ dtls_try_cli("DTLS 1.0 with ecdhe-rsa ask cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT);
+ dtls_try_cli("DTLS 1.0 with rsa ask cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN, ASK_CERT);
+ dtls_try_with_key("DTLS 1.0 with ecdhe ecdsa cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.0:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, ASK_CERT);
+
+ gnutls_global_deinit();
+}
diff --git a/tests/dtls12-cert-key-exchange.c b/tests/dtls12-cert-key-exchange.c
new file mode 100644
index 0000000..8202804
--- /dev/null
+++ b/tests/dtls12-cert-key-exchange.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015-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 General Public License
+ * along with GnuTLS; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#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();
+
+ dtls_try("DTLS 1.2 with anon-ecdh", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ANON-ECDH", GNUTLS_KX_ANON_ECDH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.2 with anon-dh", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ANON-DH", GNUTLS_KX_ANON_DH, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.2 with dhe-rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.2 with ecdhe x25519 rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-CURVE-ALL:+CURVE-X25519", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.2 with ecdhe rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, NULL, NULL, 0);
+
+ dtls_try("DTLS 1.2 with ecdhe rsa-pss sig no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try("DTLS 1.2 with ecdhe rsa-pss no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_UNKNOWN);
+ dtls_try_with_key("TLS 1.2 with ecdhe rsa-pss/rsa-pss no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-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);
+ dtls_try("DTLS 1.2 with rsa no-cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN);
+
+ dtls_try_cli("DTLS 1.2 with dhe-rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT);
+ dtls_try_cli("DTLS 1.2 with ecdhe-rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_RSA_SHA256, USE_CERT);
+ dtls_try_cli("DTLS 1.2 with rsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_RSA_SHA256, USE_CERT);
+ dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_RSA_SHA256,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, USE_CERT);
+ dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa/ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_ECDSA_SHA256,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, USE_CERT);
+
+
+ dtls_try_cli("DTLS 1.2 with ecdhe-rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-RSAE-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, GNUTLS_SIGN_RSA_PSS_RSAE_SHA256, USE_CERT);
+ dtls_try_with_key("DTLS 1.2 with ecdhe-rsa-pss/rsa-pss cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA:-SIGN-ALL:+SIGN-RSA-PSS-SHA256", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_PSS_SHA256, GNUTLS_SIGN_RSA_PSS_SHA256,
+ &server_ca3_rsa_pss_cert, &server_ca3_rsa_pss_key, &cli_ca3_rsa_pss_cert, &cli_ca3_rsa_pss_key, USE_CERT);
+ dtls_try_cli("DTLS 1.2 with dhe-rsa ask cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+DHE-RSA", GNUTLS_KX_DHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT);
+ dtls_try_cli("DTLS 1.2 with ecdhe-rsa ask cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-RSA", GNUTLS_KX_ECDHE_RSA, GNUTLS_SIGN_RSA_SHA256, GNUTLS_SIGN_UNKNOWN, ASK_CERT);
+ dtls_try_cli("DTLS 1.2 with rsa ask cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+RSA", GNUTLS_KX_RSA, GNUTLS_SIGN_UNKNOWN, GNUTLS_SIGN_UNKNOWN, ASK_CERT);
+ dtls_try_with_key("DTLS 1.2 with ecdhe ecdsa cli-cert", "NORMAL:-VERS-ALL:+VERS-DTLS1.2:-KX-ALL:+ECDHE-ECDSA", GNUTLS_KX_ECDHE_ECDSA, GNUTLS_SIGN_ECDSA_SHA256, GNUTLS_SIGN_UNKNOWN,
+ &server_ca3_localhost_ecc_cert, &server_ca3_ecc_key, &cli_ca3_cert, &cli_ca3_key, ASK_CERT);
+
+ gnutls_global_deinit();
+}