summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Josefsson <simon@josefsson.org>2007-09-20 14:52:39 +0200
committerSimon Josefsson <simon@josefsson.org>2007-09-20 14:52:39 +0200
commitbdcd70e2b483f15189855cf966d96759686555a1 (patch)
tree495c4d558b82c8ec266980e9c3f56f82d773e200
parent15688b6cb74ad0a89b90a536db0771fc89eb3685 (diff)
downloadgnutls-bdcd70e2b483f15189855cf966d96759686555a1.tar.gz
Support for Opaque PRF Input TLS extension.
-rw-r--r--NEWS2
-rw-r--r--configure.in23
-rw-r--r--doc/gnutls.texi14
-rw-r--r--includes/gnutls/gnutls.h.in20
-rw-r--r--lib/Makefile.am6
-rw-r--r--lib/ext_oprfi.c227
-rw-r--r--lib/ext_oprfi.h33
-rw-r--r--lib/gnutls_extensions.c6
-rw-r--r--lib/gnutls_int.h11
-rw-r--r--lib/gnutls_kx.c72
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/oprfi.c410
12 files changed, 821 insertions, 6 deletions
diff --git a/NEWS b/NEWS
index a8748aea38..6f601b8da3 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,8 @@ See the end for copying conditions.
* Version 2.1.0 (unreleased)
+** Support for draft-rescorla-tls-opaque-prf-input-00.txt.
+
** Example code: Fix compilation flaw under MinGW.
** API and ABI modifications:
diff --git a/configure.in b/configure.in
index d9d3a3c4b7..ad23d965d9 100644
--- a/configure.in
+++ b/configure.in
@@ -368,6 +368,29 @@ AC_ARG_WITH(builtin-crypto,
ac_cv_libgcrypt=yes)
LTLIBGCRYPT=$LIBGCRYPT_LIBS
+AC_MSG_CHECKING([whether to enable Opaque PRF input support])
+AC_ARG_ENABLE(opaque-prf-input,
+ AS_HELP_STRING([--enable-opaque-prf-input=DD],
+ [enable Opaque PRF input using DD as extension type]),
+ ac_opaque_prf_input=$enableval, ac_opaque_prf_input=no)
+if test "$ac_opaque_prf_input" != "no"; then
+ if ! echo $ac_opaque_prf_input | egrep -q '^[[0-9]]+$'; then
+ ac_opaque_prf_input=no
+ AC_MSG_WARN([[
+*** Could not parse Opaque PRF Input extension type.
+*** Use --enable-opaque-prf-input=XX where XX is decimal, for example
+*** to use extension value 42 use --enable-opqaue-prf-input=42]])
+ fi
+fi
+if test "$ac_opaque_prf_input" != "no"; then
+ AC_MSG_RESULT([yes (extension value $ac_opaque_prf_input)])
+ AC_DEFINE_UNQUOTED(ENABLE_OPRFI, $ac_opaque_prf_input,
+ [enable Opaque PRF Input])
+else
+ AC_MSG_RESULT(no)
+fi
+AM_CONDITIONAL(ENABLE_OPRFI, test "$ac_opaque_prf_input" != "no")
+
AC_MSG_CHECKING([whether to disable SRP authentication support])
AC_ARG_ENABLE(srp-authentication,
AS_HELP_STRING([--disable-srp-authentication],
diff --git a/doc/gnutls.texi b/doc/gnutls.texi
index 39aee0f21d..f2ba8f3ad5 100644
--- a/doc/gnutls.texi
+++ b/doc/gnutls.texi
@@ -2399,6 +2399,20 @@ Current limitations imposed by the compatibility layer include:
@end itemize
+@node Support for Opaque PRF Input extension
+@section Support for Opaque PRF Input extension
+@cindex Opaque PRF Input
+
+GnuTLS supports the Opaque PRF Input TLS extension
+(@code{draft-rescorla-tls-opaque-prf-input-00.txt}). The API consists
+of one API for use in the client, @ref{gnutls_oprfi_enable_client},
+and one API for use in the server, @ref{gnutls_oprfi_enable_server}.
+You must invoke both functions before calling @ref{gnutls_handshake}.
+The server utilizes a callback function into the application. The
+callback can look at the random string provided by the client, and
+also set the server string. The string lengths must be equal
+according to the protocol.
+
@node Included programs
@chapter Included Programs
diff --git a/includes/gnutls/gnutls.h.in b/includes/gnutls/gnutls.h.in
index e448a03ebd..3889d09eff 100644
--- a/includes/gnutls/gnutls.h.in
+++ b/includes/gnutls/gnutls.h.in
@@ -448,6 +448,26 @@ extern "C"
void *data, size_t * data_length,
unsigned int *type, unsigned int indx);
+ /* Opaque PRF Input
+ * http://tools.ietf.org/id/draft-rescorla-tls-opaque-prf-input-00.txt
+ */
+
+ void
+ gnutls_oprfi_enable_client (gnutls_session_t session,
+ size_t len,
+ unsigned char *data);
+
+ typedef int (*gnutls_oprfi_callback_func) (gnutls_session_t session,
+ void *userdata,
+ size_t oprfi_len,
+ const unsigned char *in_oprfi,
+ unsigned char *out_oprfi);
+
+ void
+ gnutls_oprfi_enable_server (gnutls_session_t session,
+ gnutls_oprfi_callback_func cb,
+ void *userdata);
+
/* Supplemental data, RFC 4680. */
typedef enum
{
diff --git a/lib/Makefile.am b/lib/Makefile.am
index b20abb621d..50c83e31a3 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -80,6 +80,10 @@ if ENABLE_AUTHZ
COBJECTS += ext_authz.c
endif
+if ENABLE_OPRFI
+COBJECTS += ext_oprfi.c
+endif
+
HFILES = debug.h gnutls_compress.h defines.h gnutls_cipher.h \
gnutls_buffers.h gnutls_errors.h gnutls_int.h \
gnutls_handshake.h gnutls_num.h gnutls_algorithms.h \
@@ -95,7 +99,7 @@ HFILES = debug.h gnutls_compress.h defines.h gnutls_cipher.h \
ext_srp.h gnutls_srp.h auth_srp.h auth_srp_passwd.h \
gnutls_helper.h auth_psk.h auth_psk_passwd.h \
ext_inner_application.h gnutls_extra_hooks.h \
- gnutls_supplemental.h ext_authz.h
+ gnutls_supplemental.h ext_authz.h ext_oprfi.h
# Separate so we can create the documentation
diff --git a/lib/ext_oprfi.c b/lib/ext_oprfi.c
new file mode 100644
index 0000000000..9741641e5e
--- /dev/null
+++ b/lib/ext_oprfi.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+/* Implementation of Opaque PRF Input:
+ * http://tools.ietf.org/id/draft-rescorla-tls-opaque-prf-input-00.txt
+ *
+ */
+
+#include <ext_oprfi.h>
+
+#include <gnutls_errors.h>
+#include <gnutls_num.h>
+
+int
+oprfi_recv_server (gnutls_session_t session,
+ const opaque * data,
+ size_t _data_size)
+{
+ ssize_t data_size = _data_size;
+ uint16_t len;
+ int ret;
+
+ if (!session->security_parameters.extensions.oprfi_cb)
+ {
+ gnutls_assert ();
+ return 0;
+ }
+
+ DECR_LEN (data_size, 2);
+ len = _gnutls_read_uint16 (data);
+ data += 2;
+
+ if (len != data_size)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ }
+
+ /* Store incoming data. */
+ session->security_parameters.extensions.oprfi_client_len = len;
+ session->security_parameters.extensions.oprfi_client = gnutls_malloc (len);
+ if (!session->security_parameters.extensions.oprfi_client)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy (session->security_parameters.extensions.oprfi_client, data, len);
+
+ return 0;
+}
+
+int
+oprfi_recv_client (gnutls_session_t session,
+ const opaque * data,
+ size_t _data_size)
+{
+ ssize_t data_size = _data_size;
+ uint16_t len;
+ int ret;
+
+ if (session->security_parameters.extensions.oprfi_client == NULL)
+ {
+ gnutls_assert ();
+ return 0;
+ }
+
+ DECR_LEN (data_size, 2);
+ len = _gnutls_read_uint16 (data);
+ data += 2;
+
+ if (len != data_size)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_UNEXPECTED_PACKET_LENGTH;
+ }
+
+ if (len != session->security_parameters.extensions.oprfi_client_len)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER;
+ }
+
+ /* Store incoming data. */
+ session->security_parameters.extensions.oprfi_server_len = len;
+ session->security_parameters.extensions.oprfi_server = gnutls_malloc (len);
+ if (!session->security_parameters.extensions.oprfi_server)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+ memcpy (session->security_parameters.extensions.oprfi_server, data, len);
+
+ return 0;
+}
+
+int
+_gnutls_oprfi_recv_params (gnutls_session_t session,
+ const opaque * data,
+ size_t data_size)
+{
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ return oprfi_recv_client (session, data, data_size);
+ else
+ return oprfi_recv_server (session, data, data_size);
+}
+
+int
+oprfi_send_client (gnutls_session_t session,
+ opaque * data,
+ size_t _data_size)
+{
+ opaque *p = data;
+ ssize_t data_size = _data_size;
+ int oprf_size = session->security_parameters.extensions.oprfi_client_len;
+
+ if (oprf_size == 0)
+ return 0;
+
+ DECR_LENGTH_RET (data_size, 2, GNUTLS_E_SHORT_MEMORY_BUFFER);
+ _gnutls_write_uint16 (oprf_size, p);
+ p += 2;
+
+ DECR_LENGTH_RET (data_size, oprf_size, GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+ memcpy (p, session->security_parameters.extensions.oprfi_client, oprf_size);
+
+ return 2 + oprf_size;
+}
+
+int
+oprfi_send_server (gnutls_session_t session,
+ opaque * data,
+ size_t _data_size)
+{
+ opaque *p = data;
+ int ret;
+ ssize_t data_size = _data_size;
+ size_t len;
+
+ /* Allocate buffer for outgoing data. */
+ session->security_parameters.extensions.oprfi_server_len =
+ session->security_parameters.extensions.oprfi_client_len;
+ session->security_parameters.extensions.oprfi_server =
+ gnutls_malloc (session->security_parameters.extensions.oprfi_server_len);
+ if (!session->security_parameters.extensions.oprfi_server)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ /* Get outgoing data. */
+ ret = session->security_parameters.extensions.oprfi_cb
+ (session, session->security_parameters.extensions.oprfi_userdata,
+ session->security_parameters.extensions.oprfi_client_len,
+ session->security_parameters.extensions.oprfi_client,
+ session->security_parameters.extensions.oprfi_server);
+ if (ret < 0)
+ {
+ gnutls_assert ();
+ gnutls_free (session->security_parameters.extensions.oprfi_server);
+ return ret;
+ }
+
+ DECR_LENGTH_RET (data_size, 2, GNUTLS_E_SHORT_MEMORY_BUFFER);
+ _gnutls_write_uint16 (session->security_parameters.
+ extensions.oprfi_server_len, p);
+ p += 2;
+
+ DECR_LENGTH_RET (data_size, session->security_parameters.
+ extensions.oprfi_server_len, GNUTLS_E_SHORT_MEMORY_BUFFER);
+
+ memcpy (p, session->security_parameters.extensions.oprfi_server,
+ session->security_parameters.extensions.oprfi_server_len);
+
+ return 2 + session->security_parameters.extensions.oprfi_server_len;
+}
+
+int
+_gnutls_oprfi_send_params (gnutls_session_t session,
+ opaque * data,
+ size_t data_size)
+{
+ if (session->security_parameters.entity == GNUTLS_CLIENT)
+ return oprfi_send_client (session, data, data_size);
+ else
+ return oprfi_send_server (session, data, data_size);
+}
+
+void
+gnutls_oprfi_enable_client (gnutls_session_t session,
+ size_t len,
+ unsigned char *data)
+{
+ session->security_parameters.extensions.oprfi_client_len = len;
+ session->security_parameters.extensions.oprfi_client = data;
+}
+
+
+void
+gnutls_oprfi_enable_server (gnutls_session_t session,
+ gnutls_oprfi_callback_func cb,
+ void *userdata)
+{
+ session->security_parameters.extensions.oprfi_cb = cb;
+ session->security_parameters.extensions.oprfi_userdata = userdata;
+}
diff --git a/lib/ext_oprfi.h b/lib/ext_oprfi.h
new file mode 100644
index 0000000000..7f75be9c7e
--- /dev/null
+++ b/lib/ext_oprfi.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 Free Software Foundation
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GNUTLS.
+ *
+ * The GNUTLS library 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 library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA
+ *
+ */
+
+#include <gnutls_int.h>
+
+int _gnutls_oprfi_recv_params (gnutls_session_t state,
+ const opaque * data,
+ size_t data_size);
+
+int _gnutls_oprfi_send_params (gnutls_session_t state,
+ opaque * data,
+ size_t data_size);
diff --git a/lib/gnutls_extensions.c b/lib/gnutls_extensions.c
index bb6a64d49a..20b1bbd414 100644
--- a/lib/gnutls_extensions.c
+++ b/lib/gnutls_extensions.c
@@ -33,6 +33,7 @@
#include "ext_max_record.h"
#include <ext_cert_type.h>
#include <ext_server_name.h>
+#include <ext_oprfi.h>
#include <ext_srp.h>
#include <ext_inner_application.h>
#include <ext_authz.h>
@@ -64,6 +65,11 @@ gnutls_extension_entry _gnutls_extensions[MAX_EXT_SIZE] = {
GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_SERVER_NAME,
_gnutls_server_name_recv_params,
_gnutls_server_name_send_params),
+#ifdef ENABLE_OPRFI
+ GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_OPAQUE_PRF_INPUT,
+ _gnutls_oprfi_recv_params,
+ _gnutls_oprfi_send_params),
+#endif
#ifdef ENABLE_SRP
GNUTLS_EXTENSION_ENTRY (GNUTLS_EXTENSION_SRP,
_gnutls_srp_recv_params,
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 3e6ef39575..1c9f10e5f5 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -144,6 +144,9 @@ typedef enum extensions_t
GNUTLS_EXTENSION_AUTHZ_CLIENT = 7,
GNUTLS_EXTENSION_AUTHZ_SERVER = 8,
GNUTLS_EXTENSION_CERT_TYPE = 9,
+#ifdef ENABLE_OPRFI
+ GNUTLS_EXTENSION_OPAQUE_PRF_INPUT = ENABLE_OPRFI,
+#endif
GNUTLS_EXTENSION_SRP = 12,
GNUTLS_EXTENSION_INNER_APPLICATION = 37703
} extensions_t;
@@ -284,6 +287,14 @@ typedef struct
gnutls_authz_recv_callback_func authz_recv_callback;
gnutls_authz_send_callback_func authz_send_callback;
gnutls_buffer authz_data;
+
+ /* Opaque PRF input. */
+ gnutls_oprfi_callback_func oprfi_cb;
+ void *oprfi_userdata;
+ opaque *oprfi_client;
+ uint16_t oprfi_client_len;
+ opaque *oprfi_server;
+ uint16_t oprfi_server_len;
} tls_ext_st;
/* auth_info_t structures now MAY contain malloced
diff --git a/lib/gnutls_kx.c b/lib/gnutls_kx.c
index edd1fb696a..05a5dc95dd 100644
--- a/lib/gnutls_kx.c
+++ b/lib/gnutls_kx.c
@@ -59,13 +59,8 @@ static int
generate_normal_master (gnutls_session_t session, int keep_premaster)
{
int ret = 0;
- opaque rnd[2 * TLS_RANDOM_SIZE + 1];
char buf[512];
- memcpy (rnd, session->security_parameters.client_random, TLS_RANDOM_SIZE);
- memcpy (&rnd[TLS_RANDOM_SIZE],
- session->security_parameters.server_random, TLS_RANDOM_SIZE);
-
_gnutls_hard_log ("INT: PREMASTER SECRET[%d]: %s\n", PREMASTER.size,
_gnutls_bin2hex (PREMASTER.data, PREMASTER.size, buf,
sizeof (buf)));
@@ -78,6 +73,12 @@ generate_normal_master (gnutls_session_t session, int keep_premaster)
if (gnutls_protocol_get_version (session) == GNUTLS_SSL3)
{
+ opaque rnd[2 * TLS_RANDOM_SIZE + 1];
+
+ memcpy (rnd, session->security_parameters.client_random, TLS_RANDOM_SIZE);
+ memcpy (&rnd[TLS_RANDOM_SIZE],
+ session->security_parameters.server_random, TLS_RANDOM_SIZE);
+
ret =
_gnutls_ssl3_generate_random (PREMASTER.data, PREMASTER.size,
rnd, 2 * TLS_RANDOM_SIZE,
@@ -86,8 +87,69 @@ generate_normal_master (gnutls_session_t session, int keep_premaster)
master_secret);
}
+ else if (session->security_parameters.extensions.oprfi_client_len > 0 &&
+ session->security_parameters.extensions.oprfi_server_len > 0)
+ {
+ opaque *rnd;
+ size_t rndlen = 2 * TLS_RANDOM_SIZE;
+
+ rndlen += session->security_parameters.extensions.oprfi_client_len;
+ rndlen += session->security_parameters.extensions.oprfi_server_len;
+
+ rnd = gnutls_malloc (rndlen + 1);
+ if (!rnd)
+ {
+ gnutls_assert ();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ _gnutls_hard_log ("INT: CLIENT OPRFI[%d]: %s\n",
+ session->security_parameters.
+ extensions.oprfi_server_len,
+ _gnutls_bin2hex (session->security_parameters.
+ extensions.oprfi_client,
+ session->security_parameters.
+ extensions.oprfi_client_len,
+ buf, sizeof (buf)));
+ _gnutls_hard_log ("INT: SERVER OPRFI[%d]: %s\n",
+ session->security_parameters.
+ extensions.oprfi_server_len,
+ _gnutls_bin2hex (session->security_parameters.
+ extensions.oprfi_server,
+ session->security_parameters.
+ extensions.oprfi_server_len,
+ buf, sizeof (buf)));
+
+ memcpy (rnd, session->security_parameters.client_random,
+ TLS_RANDOM_SIZE);
+ memcpy (rnd + TLS_RANDOM_SIZE,
+ session->security_parameters.extensions.oprfi_client,
+ session->security_parameters.extensions.oprfi_client_len);
+ memcpy (rnd + TLS_RANDOM_SIZE +
+ session->security_parameters.extensions.oprfi_client_len,
+ session->security_parameters.server_random,
+ TLS_RANDOM_SIZE);
+ memcpy (rnd + TLS_RANDOM_SIZE +
+ session->security_parameters.extensions.oprfi_client_len +
+ TLS_RANDOM_SIZE,
+ session->security_parameters.extensions.oprfi_server,
+ session->security_parameters.extensions.oprfi_server_len);
+
+ ret = _gnutls_PRF (session, PREMASTER.data, PREMASTER.size,
+ MASTER_SECRET, strlen (MASTER_SECRET),
+ rnd, rndlen, TLS_MASTER_SIZE,
+ session->security_parameters.master_secret);
+
+ gnutls_free (rnd);
+ }
else
{
+ opaque rnd[2 * TLS_RANDOM_SIZE + 1];
+
+ memcpy (rnd, session->security_parameters.client_random, TLS_RANDOM_SIZE);
+ memcpy (&rnd[TLS_RANDOM_SIZE],
+ session->security_parameters.server_random, TLS_RANDOM_SIZE);
+
ret =
_gnutls_PRF (session, PREMASTER.data, PREMASTER.size,
MASTER_SECRET, strlen (MASTER_SECRET),
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 209f343d71..eb9a16a59b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -43,6 +43,9 @@ if HAVE_FORK
ctests += x509self x509signself anonself pskself dhepskself tlsia resume
tlsia_LDADD = ../libextra/libgnutls-extra.la $(LDADD) @LTLIBREADLINE@
endif
+if ENABLE_OPRFI
+ctests += oprfi
+endif
gc_LDADD = $(LDADD) $(LIBGCRYPT_LIBS)
check_PROGRAMS = $(ctests)
diff --git a/tests/oprfi.c b/tests/oprfi.c
new file mode 100644
index 0000000000..303d008197
--- /dev/null
+++ b/tests/oprfi.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation
+ *
+ * Author: Simon Josefsson
+ *
+ * This file is part of GNUTLS.
+ *
+ * GNUTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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
+ */
+
+/* Parts copied from GnuTLS example programs. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#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 "utils.h"
+
+static void
+tls_log_func (int level, const char *str)
+{
+ fprintf (stderr, "|<%d>| %s", level, str);
+}
+
+/* A very basic TLS client, with anonymous authentication.
+ */
+
+#define MAX_BUF 1024
+#define MSG "Hello TLS"
+
+/* Connects to the peer and returns a socket
+ * descriptor.
+ */
+int
+tcp_connect (void)
+{
+ const char *PORT = "5556";
+ const char *SERVER = "127.0.0.1";
+ int err, sd;
+ struct sockaddr_in sa;
+
+ /* connects to server
+ */
+ sd = socket (AF_INET, SOCK_STREAM, 0);
+
+ memset (&sa, '\0', sizeof (sa));
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (atoi (PORT));
+ inet_pton (AF_INET, SERVER, &sa.sin_addr);
+
+ err = connect (sd, (struct sockaddr *) &sa, sizeof (sa));
+ if (err < 0)
+ {
+ fprintf (stderr, "Connect error\n");
+ exit (1);
+ }
+
+ return sd;
+}
+
+/* closes the given socket descriptor.
+ */
+void
+tcp_close (int sd)
+{
+ shutdown (sd, SHUT_RDWR); /* no more receptions */
+ close (sd);
+}
+
+void
+client (void)
+{
+ int ret, sd, ii;
+ gnutls_session_t session;
+ char buffer[MAX_BUF + 1];
+ gnutls_anon_client_credentials_t anoncred;
+ /* Need to enable anonymous KX specifically. */
+ const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+
+ gnutls_global_init ();
+
+ gnutls_global_set_log_function (tls_log_func);
+ gnutls_global_set_log_level (4711);
+
+ gnutls_anon_allocate_client_credentials (&anoncred);
+
+ /* Initialize TLS session
+ */
+ gnutls_init (&session, GNUTLS_CLIENT);
+
+ /* Use default priorities */
+ gnutls_set_default_priority (session);
+ gnutls_kx_set_priority (session, kx_prio);
+
+ /* put the anonymous credentials to the current session
+ */
+ gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+
+ gnutls_oprfi_enable_client (session, 3, "foo");
+
+ /* connect to the peer
+ */
+ sd = tcp_connect ();
+
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
+
+ /* Perform the TLS handshake
+ */
+ ret = gnutls_handshake (session);
+
+ if (ret < 0)
+ {
+ fail ("client: Handshake failed\n");
+ gnutls_perror (ret);
+ goto end;
+ }
+ else
+ {
+ success ("client: Handshake was completed\n");
+ }
+
+ success ("client: TLS version is: %s\n",
+ gnutls_protocol_get_name (gnutls_protocol_get_version (session)));
+
+ gnutls_record_send (session, MSG, strlen (MSG));
+
+ ret = gnutls_record_recv (session, buffer, MAX_BUF);
+ 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));
+ goto end;
+ }
+
+ printf ("- Received %d bytes: ", ret);
+ for (ii = 0; ii < ret; ii++)
+ {
+ fputc (buffer[ii], stdout);
+ }
+ fputs ("\n", stdout);
+
+ gnutls_bye (session, GNUTLS_SHUT_RDWR);
+
+end:
+
+ tcp_close (sd);
+
+ gnutls_deinit (session);
+
+ gnutls_anon_free_client_credentials (anoncred);
+
+ gnutls_global_deinit ();
+}
+
+/* This is a sample TLS 1.0 echo server, for anonymous authentication only.
+ */
+
+#define SA struct sockaddr
+#define MAX_BUF 1024
+#define PORT 5556 /* listen to 5556 port */
+#define DH_BITS 1024
+
+/* These are global */
+gnutls_anon_server_credentials_t anoncred;
+
+int
+oprfi_callback (gnutls_session_t session,
+ void *userdata,
+ size_t oprfi_len,
+ const unsigned char *in_oprfi,
+ unsigned char *out_oprfi)
+{
+ size_t i;
+
+ puts("cb");
+
+ for (i = 0; i < oprfi_len; i++)
+ printf ("OPRF[%d]: %02x %03d %c\n", i, in_oprfi[i],
+ in_oprfi[i], in_oprfi[i]);
+
+ memset (out_oprfi, 42, oprfi_len);
+
+ return 0;
+}
+
+gnutls_session_t
+initialize_tls_session (void)
+{
+ gnutls_session_t session;
+ const int kx_prio[] = { GNUTLS_KX_ANON_DH, 0 };
+
+ gnutls_init (&session, GNUTLS_SERVER);
+
+ /* avoid calling all the priority functions, since the defaults
+ * are adequate.
+ */
+ gnutls_set_default_priority (session);
+ gnutls_kx_set_priority (session, kx_prio);
+
+ gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred);
+
+ gnutls_dh_set_prime_bits (session, DH_BITS);
+
+ gnutls_oprfi_enable_server (session, oprfi_callback, NULL);
+
+ return session;
+}
+
+static gnutls_dh_params_t dh_params;
+
+static int
+generate_dh_params (void)
+{
+ const gnutls_datum_t p3 = { pkcs3, strlen (pkcs3) };
+ /* Generate Diffie Hellman parameters - for use with DHE
+ * kx algorithms. These should be discarded and regenerated
+ * once a day, once a week or once a month. Depending on the
+ * security requirements.
+ */
+ gnutls_dh_params_init (&dh_params);
+ return gnutls_dh_params_import_pkcs3 (dh_params, &p3, GNUTLS_X509_FMT_PEM);
+}
+
+int err, listen_sd, i;
+int sd, ret;
+struct sockaddr_in sa_serv;
+struct sockaddr_in sa_cli;
+int client_len;
+char topbuf[512];
+gnutls_session_t session;
+char buffer[MAX_BUF + 1];
+int optval = 1;
+
+void
+server_start (void)
+{
+ /* this must be called once in the program
+ */
+ gnutls_global_init ();
+
+ gnutls_global_set_log_function (tls_log_func);
+ gnutls_global_set_log_level (4711);
+
+ gnutls_anon_allocate_server_credentials (&anoncred);
+
+ success ("Launched, generating DH parameters...\n");
+
+ generate_dh_params ();
+
+ gnutls_anon_set_server_dh_params (anoncred, dh_params);
+
+ /* Socket operations
+ */
+ listen_sd = socket (AF_INET, SOCK_STREAM, 0);
+ if (err == -1)
+ {
+ perror ("socket");
+ fail ("server: socket failed\n");
+ return;
+ }
+
+ memset (&sa_serv, '\0', sizeof (sa_serv));
+ sa_serv.sin_family = AF_INET;
+ sa_serv.sin_addr.s_addr = INADDR_ANY;
+ sa_serv.sin_port = htons (PORT); /* Server Port number */
+
+ setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof (int));
+
+ err = bind (listen_sd, (SA *) & sa_serv, sizeof (sa_serv));
+ if (err == -1)
+ {
+ perror ("bind");
+ fail ("server: bind failed\n");
+ return;
+ }
+
+ err = listen (listen_sd, 1024);
+ if (err == -1)
+ {
+ perror ("listen");
+ fail ("server: listen failed\n");
+ return;
+ }
+
+ success ("server: ready. Listening to port '%d'.\n", PORT);
+}
+
+void
+server (void)
+{
+ client_len = sizeof (sa_cli);
+
+ session = initialize_tls_session ();
+
+ sd = accept (listen_sd, (SA *) & sa_cli, &client_len);
+
+ success ("server: connection from %s, port %d\n",
+ inet_ntop (AF_INET, &sa_cli.sin_addr, topbuf,
+ sizeof (topbuf)), ntohs (sa_cli.sin_port));
+
+ gnutls_transport_set_ptr (session, (gnutls_transport_ptr_t) sd);
+ ret = gnutls_handshake (session);
+ if (ret < 0)
+ {
+ close (sd);
+ gnutls_deinit (session);
+ fail ("server: Handshake has failed (%s)\n\n", gnutls_strerror (ret));
+ return;
+ }
+ success ("server: Handshake was completed\n");
+
+ 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); */
+
+ i = 0;
+ for (;;)
+ {
+ bzero (buffer, MAX_BUF + 1);
+ ret = gnutls_record_recv (session, buffer, MAX_BUF);
+
+ if (ret == 0)
+ {
+ success ("server: Peer has closed the GNUTLS connection\n");
+ break;
+ }
+ else if (ret < 0)
+ {
+ fail ("server: Received corrupted data(%d). Closing...\n", ret);
+ break;
+ }
+ else if (ret > 0)
+ {
+ /* echo data back to the client
+ */
+ gnutls_record_send (session, buffer, strlen (buffer));
+ }
+ }
+ /* do not wait for the peer to close the connection.
+ */
+ gnutls_bye (session, GNUTLS_SHUT_WR);
+
+ close (sd);
+ gnutls_deinit (session);
+
+ close (listen_sd);
+
+ gnutls_anon_free_server_credentials (anoncred);
+
+ gnutls_global_deinit ();
+
+ success ("server: finished\n");
+}
+
+void
+doit (void)
+{
+ pid_t child;
+
+ server_start ();
+ if (error_count)
+ return;
+
+ child = fork ();
+ if (child < 0)
+ {
+ perror ("fork");
+ fail ("fork");
+ return;
+ }
+
+ if (child)
+ {
+ int status;
+ /* parent */
+ server ();
+ wait (&status);
+ }
+ else
+ client ();
+}