tests/x509sign-verify-rsa
tests/x509sign-verify2
tests/x509signself
+tests/gnutls_ktls
*.tmp
tmp-*
*.trs
Random gen. variant: $rnd_variant
PKCS#11 support: $with_p11_kit
TPM support: $with_tpm
+ KTLS support: $enable_ktls
])
if test -n "$ac_trousers_lib";then
AC_MSG_NOTICE([
cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \
crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \
hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c \
- iov.c iov.h
+ iov.c iov.h system/ktls.c system/ktls.h
if ENABLE_GOST
COBJECTS += vko.c
#include <record.h>
#include <debug.h>
#include "str.h"
+#include <system/ktls.h>
typedef struct {
gnutls_alert_description_t alert;
return ret;
}
- if ((ret =
- _gnutls_send_int(session, GNUTLS_ALERT, -1,
- EPOCH_WRITE_CURRENT, data, 2,
- MBUFFER_FLUSH)) >= 0)
- return 0;
- else
- return ret;
+ if (IS_KTLS_ENABLED(session)) {
+ ret =
+ _gnutls_ktls_send_control_msg(session, GNUTLS_ALERT, data, 2);
+ } else {
+ ret =
+ _gnutls_send_int(session, GNUTLS_ALERT, -1,
+ EPOCH_WRITE_CURRENT, data, 2,
+ MBUFFER_FLUSH);
+ }
+ return (ret < 0) ? ret : 0;
}
/**
/* To check whether we have a DTLS session */
#define IS_DTLS(session) (session->internals.transport == GNUTLS_DGRAM)
+/* To check whether we have a KTLS enabled */
+#define IS_KTLS_ENABLED(session) (session->internals.ktls_enabled)
+
/* the maximum size of encrypted packets */
#define DEFAULT_MAX_RECORD_SIZE 16384
#define DEFAULT_MAX_EARLY_DATA_SIZE 16384
* called in parallel when false start is used and false start is used. */
void *epoch_lock;
+ /* indicates whether or not was KTLS initialized properly. */
+ bool ktls_enabled;
+ int recv_fd;
+ int send_fd;
+
/* If you add anything here, check _gnutls_handshake_internal_state_clear().
*/
} internals_st;
#include "tls13/early_data.h"
#include "tls13/session_ticket.h"
#include "locks.h"
+#include "system/ktls.h"
+
static int check_if_null_comp_present(gnutls_session_t session,
uint8_t * data, int datalen);
const version_entry_st *vers = get_version(session);
int ret;
+#ifdef ENABLE_KTLS
+ int sockin, sockout;
+ gnutls_transport_get_int2(session, &sockin, &sockout);
+ _gnutls_ktls_enable(session, sockin, sockout);
+#endif
+
if (unlikely(session->internals.initial_negotiation_completed)) {
if (vers->tls13_sem) {
if (session->security_parameters.entity == GNUTLS_CLIENT) {
}
}
+#ifdef ENABLE_KTLS
+ if (IS_KTLS_ENABLED(session)) {
+ ret = _gnutls_ktls_set_keys(session);
+ if (ret < 0)
+ return ret;
+ }
+#endif
+
return 0;
}
#include <random.h>
#include <xsize.h>
#include "locks.h"
+#include "system/ktls.h"
struct tls_record_st {
uint16_t header_size;
switch (BYE_STATE) {
case BYE_STATE0:
- ret = _gnutls_io_write_flush(session);
+ if (!IS_KTLS_ENABLED(session))
+ ret = _gnutls_io_write_flush(session);
BYE_STATE = BYE_STATE0;
if (ret < 0) {
gnutls_assert();
}
FALLTHROUGH;
case BYE_STATE1:
- ret =
- gnutls_alert_send(session, GNUTLS_AL_WARNING,
- GNUTLS_A_CLOSE_NOTIFY);
+ ret = gnutls_alert_send(session, GNUTLS_AL_WARNING,
+ GNUTLS_A_CLOSE_NOTIFY);
BYE_STATE = BYE_STATE1;
if (ret < 0) {
gnutls_assert();
case BYE_STATE2:
BYE_STATE = BYE_STATE2;
if (how == GNUTLS_SHUT_RDWR) {
- do {
- ret =
- _gnutls_recv_int(session, GNUTLS_ALERT,
- NULL, 0, NULL,
- session->internals.
- record_timeout_ms);
+ if (IS_KTLS_ENABLED(session)){
+ do {
+ ret = _gnutls_ktls_recv_int(session,
+ GNUTLS_ALERT, NULL, 0);
+ }
+ while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
+ } else {
+ do {
+ ret =
+ _gnutls_recv_int(session, GNUTLS_ALERT,
+ NULL, 0, NULL,
+ session->internals.
+ record_timeout_ms);
+ }
+ while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
}
- while (ret == GNUTLS_E_GOT_APPLICATION_DATA);
if (ret >= 0)
session->internals.may_not_read = 1;
switch(session->internals.rsend_state) {
case RECORD_SEND_NORMAL:
- return _gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
- -1, EPOCH_WRITE_CURRENT, data,
- data_size, pad, MBUFFER_FLUSH);
+ if (IS_KTLS_ENABLED(session)) {
+ return _gnutls_ktls_send(session, data, data_size);
+ } else {
+ return _gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
+ -1, EPOCH_WRITE_CURRENT, data,
+ data_size, pad, MBUFFER_FLUSH);
+ }
case RECORD_SEND_CORKED:
case RECORD_SEND_CORKED_TO_KU:
return append_data_to_corked(session, data, data_size);
return gnutls_assert_val(GNUTLS_E_UNAVAILABLE_DURING_HANDSHAKE);
}
- return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA,
- data, data_size, NULL,
- session->internals.record_timeout_ms);
+ if (IS_KTLS_ENABLED(session)) {
+ return _gnutls_ktls_recv(session, data, data_size);
+ } else {
+ return _gnutls_recv_int(session, GNUTLS_APPLICATION_DATA,
+ data, data_size, NULL,
+ session->internals.record_timeout_ms);
+ }
}
/**
--- /dev/null
+/*
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * Author: Fratnišek Krenželok
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "config.h"
+#include "system/ktls.h"
+
+#ifdef ENABLE_KTLS
+
+#include <linux/tls.h>
+#include <record.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <errno.h>
+#include "ext/session_ticket.h"
+
+/**
+ * gnutls_transport_set_ktls:
+ * @session: is a #gnutls_session_t type.
+ * @sockin: is a socket descriptor.
+ * @sockout: is a socket descriptor.
+ *
+ * Enables Kernel TLS for the @session
+ * Requieres `tls` kernel module and
+ * gnutls configuration with `--enable-ktls`
+ *
+ * Returns: 0 on success error otherwise
+ *
+ * Since: 3.7.2
+ **/
+int _gnutls_ktls_enable(gnutls_session_t session, int sockin, int sockout)
+{
+ if (setsockopt(sockin, SOL_TCP, TCP_ULP, "tls", sizeof ("tls")) < 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+
+ session->internals.recv_fd = sockin;
+ session->internals.send_fd = sockin;
+
+ if (sockin != sockout){
+ if (setsockopt(sockout, SOL_TCP, TCP_ULP, "tls", sizeof ("tls")) < 0)
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ session->internals.send_fd = sockout;
+ }
+
+ session->internals.ktls_enabled = 1;
+ return 0;
+}
+
+int _gnutls_ktls_set_keys(gnutls_session_t session)
+{
+ gnutls_cipher_algorithm_t cipher = gnutls_cipher_get(session);
+ gnutls_datum_t mac_key;
+ gnutls_datum_t iv;
+ gnutls_datum_t cipher_key;
+ unsigned char seq_number[8];
+ int ret;
+
+ session->internals.ktls_enabled = 0;
+
+ /* check whether or not cipher suite supports ktls
+ */
+ int version = gnutls_protocol_get_version(session);
+ if ((version != GNUTLS_TLS1_3 && version != GNUTLS_TLS1_2) ||
+ (gnutls_cipher_get(session) != GNUTLS_CIPHER_AES_128_GCM &&
+ gnutls_cipher_get(session) != GNUTLS_CIPHER_AES_256_GCM)) {
+ return GNUTLS_E_UNIMPLEMENTED_FEATURE;
+ }
+
+ version = (version == GNUTLS_TLS1_2) ? TLS_1_2_VERSION : TLS_1_3_VERSION;
+
+ ret = gnutls_record_get_state(session, 1, &mac_key, &iv, &cipher_key,
+ seq_number);
+ if (ret < 0) {
+ return ret;
+ }
+
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ {
+ struct tls12_crypto_info_aes_gcm_128 crypto_info;
+
+ crypto_info.info.version = version;
+ crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+
+ assert(cipher_key.size == TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+
+ /* for TLS 1.2 IV is generated in kernel */
+ if (version == TLS_1_2_VERSION) {
+ assert(iv.size == TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ } else {
+ assert(iv.size == TLS_CIPHER_AES_GCM_128_SALT_SIZE
+ + TLS_CIPHER_AES_GCM_128_IV_SIZE);
+
+ memcpy(crypto_info.iv, iv.data +
+ TLS_CIPHER_AES_GCM_128_SALT_SIZE,
+ TLS_CIPHER_AES_GCM_128_IV_SIZE);
+ }
+
+ memcpy(crypto_info.salt, iv.data,
+ TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ memcpy(crypto_info.rec_seq, seq_number,
+ TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ memcpy(crypto_info.key, cipher_key.data,
+ TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+
+ if (setsockopt(session->internals.recv_fd, SOL_TLS, TLS_RX,
+ &crypto_info, sizeof (crypto_info))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+ break;
+ case GNUTLS_CIPHER_AES_256_GCM:
+ {
+ struct tls12_crypto_info_aes_gcm_256 crypto_info;
+
+ crypto_info.info.version = version;
+ crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_256;
+
+ assert(cipher_key.size == TLS_CIPHER_AES_GCM_256_KEY_SIZE);
+
+ /* for TLS 1.2 IV is generated in kernel */
+ if (version == TLS_1_2_VERSION) {
+ assert(iv.size == TLS_CIPHER_AES_GCM_256_SALT_SIZE);
+ } else {
+ assert(iv.size == TLS_CIPHER_AES_GCM_256_SALT_SIZE
+ + TLS_CIPHER_AES_GCM_256_IV_SIZE);
+
+ memcpy(crypto_info.iv, iv.data + TLS_CIPHER_AES_GCM_256_SALT_SIZE,
+ TLS_CIPHER_AES_GCM_256_IV_SIZE);
+ }
+
+ memcpy(crypto_info.salt, iv.data,
+ TLS_CIPHER_AES_GCM_256_SALT_SIZE);
+ memcpy(crypto_info.rec_seq, seq_number,
+ TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE);
+ memcpy(crypto_info.key, cipher_key.data,
+ TLS_CIPHER_AES_GCM_256_KEY_SIZE);
+
+ if (setsockopt(session->internals.recv_fd, SOL_TLS, TLS_RX,
+ &crypto_info, sizeof(crypto_info))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+ break;
+ default:
+ assert(0);
+ }
+
+ ret = gnutls_record_get_state(session, 0, &mac_key, &iv, &cipher_key,
+ seq_number);
+ if (ret < 0) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+
+ switch (cipher) {
+ case GNUTLS_CIPHER_AES_128_GCM:
+ {
+ struct tls12_crypto_info_aes_gcm_128 crypto_info;
+
+ crypto_info.info.version = version;
+ crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_128;
+
+ assert(cipher_key.size == TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+
+ /* for TLS 1.2 IV is generated in kernel */
+ if (version == TLS_1_2_VERSION) {
+ assert(iv.size == TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ } else {
+ assert(iv.size == TLS_CIPHER_AES_GCM_128_SALT_SIZE
+ + TLS_CIPHER_AES_GCM_128_IV_SIZE);
+
+ memcpy(crypto_info.iv, iv.data + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
+ TLS_CIPHER_AES_GCM_128_IV_SIZE);
+ }
+
+ memcpy(crypto_info.salt, iv.data,
+ TLS_CIPHER_AES_GCM_128_SALT_SIZE);
+ memcpy(crypto_info.rec_seq, seq_number,
+ TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
+ memcpy(crypto_info.key, cipher_key.data,
+ TLS_CIPHER_AES_GCM_128_KEY_SIZE);
+
+ if (setsockopt(session->internals.send_fd, SOL_TLS, TLS_TX,
+ &crypto_info, sizeof(crypto_info))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+ break;
+ case GNUTLS_CIPHER_AES_256_GCM:
+ {
+ struct tls12_crypto_info_aes_gcm_256 crypto_info;
+
+ crypto_info.info.version = version;
+ crypto_info.info.cipher_type = TLS_CIPHER_AES_GCM_256;
+ assert(cipher_key.size == TLS_CIPHER_AES_GCM_256_KEY_SIZE);
+
+ /* for TLS 1.2 IV is generated in kernel */
+ if (version == TLS_1_2_VERSION) {
+ assert(iv.size == TLS_CIPHER_AES_GCM_256_SALT_SIZE);
+ } else {
+ assert(iv.size == TLS_CIPHER_AES_GCM_256_SALT_SIZE +
+ TLS_CIPHER_AES_GCM_256_IV_SIZE);
+
+ memcpy(crypto_info.iv, iv.data + TLS_CIPHER_AES_GCM_256_SALT_SIZE,
+ TLS_CIPHER_AES_GCM_256_IV_SIZE);
+ }
+
+ memcpy(crypto_info.salt, iv.data,
+ TLS_CIPHER_AES_GCM_256_SALT_SIZE);
+ memcpy(crypto_info.rec_seq, seq_number,
+ TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE);
+ memcpy(crypto_info.key, cipher_key.data,
+ TLS_CIPHER_AES_GCM_256_KEY_SIZE);
+
+ if (setsockopt(session->internals.send_fd, SOL_TLS, TLS_TX,
+ &crypto_info, sizeof(crypto_info))) {
+ return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+ }
+ }
+ break;
+ default:
+ assert(0);
+
+ }
+
+ session->internals.ktls_enabled = 1;
+ return 0;
+}
+
+int _gnutls_ktls_send_control_msg(gnutls_session_t session,
+ unsigned char record_type, const void *data, size_t data_size)
+{
+ const char *buf = data;
+ ssize_t ret;
+
+ assert(session != NULL);
+
+ while (data_size > 0) {
+ char cmsg[CMSG_SPACE(sizeof (unsigned char))];
+ struct msghdr msg = { 0 };
+ struct iovec msg_iov; /* Vector of data to send/receive into. */
+ struct cmsghdr *hdr;
+
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof cmsg;
+
+ hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_TLS;
+ hdr->cmsg_type = TLS_SET_RECORD_TYPE;
+ hdr->cmsg_len = CMSG_LEN(sizeof (unsigned char));
+
+ // construct record header
+ *CMSG_DATA(hdr) = record_type;
+ msg.msg_controllen = hdr->cmsg_len;
+
+ msg_iov.iov_base = (void *)buf;
+ msg_iov.iov_len = data_size;
+
+ msg.msg_iov = &msg_iov;
+ msg.msg_iovlen = 1;
+
+ ret = sendmsg(session->internals.send_fd, &msg, MSG_DONTWAIT);
+
+ if (ret == -1) {
+ switch (errno) {
+ case EINTR:
+ return GNUTLS_E_INTERRUPTED;
+ case EAGAIN:
+ return GNUTLS_E_AGAIN;
+ default:
+ return GNUTLS_E_PUSH_ERROR;
+ }
+ }
+
+ buf += ret;
+ data_size -= ret;
+ }
+
+ return 0;
+}
+
+int _gnutls_ktls_recv_control_msg(gnutls_session_t session,
+ unsigned char *record_type, void *data, size_t data_size)
+{
+ char *buf = data;
+ ssize_t ret;
+
+ char cmsg[CMSG_SPACE(sizeof (unsigned char))];
+ struct msghdr msg = { 0 };
+ struct iovec msg_iov;
+ struct cmsghdr *hdr;
+
+ assert(session != NULL);
+
+ if (session->internals.read_eof != 0) {
+ return 0;
+ } else if (session->internals.invalid_connection != 0 ||
+ session->internals.may_not_read != 0)
+ return GNUTLS_E_INVALID_SESSION;
+
+ /* receive message */
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof cmsg;
+
+ msg_iov.iov_base = buf;
+ msg_iov.iov_len = data_size;
+
+ msg.msg_iov = &msg_iov;
+ msg.msg_iovlen = 1;
+
+ ret = recvmsg(session->internals.recv_fd, &msg, MSG_DONTWAIT);
+
+ if (ret == -1){
+ switch(errno){
+ case EAGAIN:
+ return GNUTLS_E_AGAIN;
+ case EINVAL:
+ return GNUTLS_E_UNSUPPORTED_VERSION_PACKET;
+ case EMSGSIZE:
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ case EBADMSG:
+ return GNUTLS_E_DECRYPTION_FAILED;
+ default:
+ return GNUTLS_E_PULL_ERROR;
+ }
+ }
+
+ /* connection closed */
+ if (ret == 0)
+ return 0;
+
+ /* get record type from header */
+ hdr = CMSG_FIRSTHDR(&msg);
+ if (hdr == NULL){
+ return GNUTLS_E_PULL_ERROR;
+ }
+ if (hdr->cmsg_level == SOL_TLS && hdr->cmsg_type == TLS_GET_RECORD_TYPE)
+ *record_type = *(unsigned char *)CMSG_DATA(hdr);
+ else
+ *record_type = GNUTLS_APPLICATION_DATA;
+
+ return ret;
+}
+
+int _gnutls_ktls_recv_int(gnutls_session_t session, content_type_t type,
+ void *data, size_t data_size)
+{
+ unsigned char record_type;
+ int ret;
+
+ ret = _gnutls_ktls_recv_control_msg(session,
+ &record_type, data, data_size);
+
+ if (ret > 0) {
+ switch (record_type){
+ case GNUTLS_CHANGE_CIPHER_SPEC:
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+ break;
+ case GNUTLS_ALERT:
+ session_invalidate(session);
+ ret = 0;
+ break;
+ case GNUTLS_HANDSHAKE:
+ // ignore post-handshake messages
+ if (type != record_type)
+ return GNUTLS_E_AGAIN;
+ break;
+ case GNUTLS_APPLICATION_DATA:
+ if (type != record_type)
+ ret = GNUTLS_E_GOT_APPLICATION_DATA;
+ break;
+ case GNUTLS_HEARTBEAT:
+ break;
+ default:
+ gnutls_assert();
+ return GNUTLS_E_UNEXPECTED_PACKET;
+ }
+ }
+ return ret;
+}
+
+#else //ENABLE_KTLS
+
+int _gnutls_ktls_enable(gnutls_session_t session, int sockin, int sockout){
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+}
+
+int _gnutls_ktls_set_keys(gnutls_session_t session) {
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+}
+
+int _gnutls_ktls_send_control_msg(gnutls_session_t session,
+ unsigned char record_type, const void *data, size_t data_size) {
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+}
+
+int _gnutls_ktls_recv_int(gnutls_session_t session, content_type_t type, void *data, size_t data_size) {
+ return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE);
+}
+
+#endif //ENABLE_KTLS
--- /dev/null
+#ifndef GNUTLS_LIB_ACCELERATED_KTLS_H
+#define GNUTLS_LIB_ACCELERATED_KTLS_H
+
+#include "gnutls_int.h"
+
+int _gnutls_ktls_enable(gnutls_session_t session, int sockin, int sockout);
+int _gnutls_ktls_set_keys(gnutls_session_t session);
+int _gnutls_ktls_send_control_msg(gnutls_session_t session, unsigned char record_type,
+ const void *data, size_t data_size);
+#define _gnutls_ktls_send(x, y, z) _gnutls_ktls_send_control_msg(x, GNUTLS_APPLICATION_DATA, y, z);
+int _gnutls_ktls_recv_control_msg(gnutls_session_t session, unsigned char *record_type,
+ void *data, size_t data_size);
+int _gnutls_ktls_recv_int(gnutls_session_t session, content_type_t type, void *data, size_t data_size);
+#define _gnutls_ktls_recv(x, y, z) _gnutls_ktls_recv_int(x, GNUTLS_APPLICATION_DATA, y, z)
+
+#endif /* GNUTLS_LIB_ACCELERATED_KTLS_H */
fi
AM_CONDITIONAL(ENABLE_AFALG, test "$enable_afalg" != "no")
+ # For KTLS
+ AC_MSG_CHECKING([whether to add KTLS support])
+ AC_ARG_ENABLE(ktls,
+ AS_HELP_STRING([--enable-ktls], [enable KTLS support]),
+ enable_ktls=$enableval,enable_ktls=no)
+ AC_MSG_RESULT($enable_ktls)
+
+ if test "$enable_ktls" = "yes"; then
+ AC_CHECK_HEADERS([linux/tls.h], [
+ AC_DEFINE([HAVE_KTLS],[1],[KTLS headers found at compile time])
+ ], [
+ AC_MSG_ERROR([<linux/tls.h> not found])
+ ])
+ AC_DEFINE([ENABLE_KTLS], 1, [Enable KTLS support])
+ fi
+ AM_CONDITIONAL(ENABLE_KTLS, test "$enable_ktls" != "no")
+
+ # For OCSP
AC_MSG_CHECKING([whether to disable OCSP support])
AC_ARG_ENABLE(ocsp,
AS_HELP_STRING([--disable-ocsp],
resume-with-record-size-limit
endif
+if ENABLE_KTLS
+ctests += gnutls_ktls
+endif
+
gc_CPPFLAGS = $(AM_CPPFLAGS) \
-I$(top_srcdir)/gl \
-I$(top_builddir)/gl
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#if defined(_WIN32)
+
+int main(void)
+{
+ exit(77);
+}
+
+#else
+
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/dtls.h>
+#include <gnutls/socket.h>
+#include <signal.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "cert-common.h"
+#include "utils.h"
+
+static void server_log_func(int level, const char *str)
+{
+ fprintf(stderr, "server|<%d>| %s", level, str);
+}
+
+static void client_log_func(int level, const char *str)
+{
+ fprintf(stderr, "client|<%d>| %s", level, str);
+}
+
+#define MAX_BUF 1024
+#define MSG "Hello world!"
+
+
+static void client(int fd, const char *prio)
+{
+ int ret;
+ char buffer[MAX_BUF + 1];
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(client_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+
+ gnutls_init(&session, GNUTLS_CLIENT);
+ gnutls_handshake_set_timeout(session, get_timeout());
+ assert(gnutls_priority_set_direct(session, prio, NULL) >= 0);
+ gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+ gnutls_transport_set_int(session, fd);
+ if (ret < 0)
+ fail("client: error in enabling KTLS: %s\n", gnutls_strerror(ret));
+
+ do {
+ ret = gnutls_handshake(session);
+ }
+ while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
+
+ if (ret < 0) {
+ fail("client: Handshake failed\n");
+ close(fd);
+ gnutls_deinit(session);
+ exit(1);
+ }
+ if (debug)
+ success("client: Handshake was completed\n");
+
+ /* server send message via gnutls_record_send */
+ int i = 0;
+ do{
+ memset(buffer, 0, MAX_BUF + 1);
+ do{
+ ret = gnutls_record_recv(session, buffer, sizeof(buffer));
+ }
+ while(ret == GNUTLS_E_AGAIN);
+
+ if(strncmp(buffer, MSG+i*MAX_BUF, MAX_BUF))
+ fail("client: Message doesn't match\n");
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (debug)
+ success ("client: messages received\n");
+
+ if (ret == 0) {
+ 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("client: error in closing session: %s\n", gnutls_strerror(ret));
+ }
+ end:
+
+ close(fd);
+
+ gnutls_deinit(session);
+
+ gnutls_certificate_free_credentials(x509_cred);
+
+ gnutls_global_deinit();
+}
+
+pid_t child;
+static void terminate(void)
+{
+ kill(child, SIGTERM);
+ exit(1);
+}
+
+static void server(int fd, const char *prio)
+{
+ int ret;
+ gnutls_certificate_credentials_t x509_cred;
+ gnutls_session_t session;
+
+ global_init();
+
+ if (debug) {
+ gnutls_global_set_log_function(server_log_func);
+ gnutls_global_set_log_level(7);
+ }
+
+ gnutls_certificate_allocate_credentials(&x509_cred);
+ ret = gnutls_certificate_set_x509_key_mem(x509_cred, &server_cert,
+ &server_key,
+ GNUTLS_X509_FMT_PEM);
+ if (ret < 0)
+ exit(1);
+
+ gnutls_init(&session, GNUTLS_SERVER);
+ gnutls_handshake_set_timeout(session, get_timeout());
+
+ 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));
+ terminate();
+ }
+ if (debug)
+ success("server: Handshake was completed\n");
+
+ do {
+ ret = gnutls_record_send(session, MSG, strlen(MSG));
+ } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+ if (ret < 0) {
+ close(fd);
+ gnutls_deinit(session);
+ gnutls_certificate_free_credentials(x509_cred);
+ gnutls_global_deinit();
+ 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(x509_cred);
+
+ gnutls_global_deinit();
+
+ if (debug)
+ success("server: finished\n");
+ }
+}
+
+static void run(const char *prio)
+{
+ int ret;
+ struct sockaddr_in saddr;
+ socklen_t addrlen;
+ int listener;
+ int fd;
+
+ success("running ktls test with %s\n", prio);
+
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ listener = socket(AF_INET, SOCK_STREAM, 0);
+ if (listener == -1){
+ fail("error in listener(): %s\n", strerror(errno));
+ }
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ saddr.sin_port = 0;
+
+ ret = bind(listener, (struct sockaddr*)&saddr, sizeof(saddr));
+ if (ret == -1){
+ fail("error in bind(): %s\n", strerror(errno));
+ }
+
+ addrlen = sizeof(saddr);
+ ret = getsockname(listener, (struct sockaddr*)&saddr, &addrlen);
+ if (ret == -1){
+ fail("error in getsockname(): %s\n", strerror(errno));
+ }
+
+ child = fork();
+ if (child < 0) {
+ fail("error in fork(): %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (child) {
+ /* parent */
+ ret = listen(listener, 1);
+ if (ret == -1) {
+ fail("error in listen(): %s\n", strerror(errno));
+ }
+
+ fd = accept(listener, NULL, NULL);
+ if (fd == -1) {
+ fail("error in accept(): %s\n", strerror(errno));
+ }
+ server(fd, prio);
+ kill(child, SIGTERM);
+ } else {
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd == -1){
+ fail("error in socket(): %s\n", strerror(errno));
+ exit(1);
+ }
+ usleep(1000000);
+ connect(fd, (struct sockaddr*)&saddr, addrlen);
+ client(fd, prio);
+ exit(0);
+ }
+}
+
+void doit(void)
+{
+ run("NORMAL:-VERS-ALL:+VERS-TLS1.2:+AES-128-GCM");
+ run("NORMAL:-VERS-ALL:+VERS-TLS1.2:+AES-256-GCM");
+ run("NORMAL:-VERS-ALL:+VERS-TLS1.3:+AES-128-GCM");
+ run("NORMAL:-VERS-ALL:+VERS-TLS1.3:+AES-256-GCM");
+}
+
+#endif /* _WIN32 */