[gnutls-dev] External signing API

Simon Josefsson simon at josefsson.org
Fri Aug 10 15:41:28 CEST 2007


The entire patch for the external sign callback, now applied to the
trunk, is as follows.  It is not very large, so I'd thought I'd post it.
It include a self-test.  Please review.

/Simon

commit 4431c4369db575dc8ecd8ec3622bc2dfc9bee725
Author: Simon Josefsson <simon at josefsson.org>
Date:   Fri Aug 10 15:20:40 2007 +0200

    External signing callback interface.
    	* includes/gnutls/gnutls.h.in (gnutls_sign_func): New type.
    	(gnutls_sign_callback_set): New function.
    
    	* includes/gnutls/x509.h (gnutls_x509_privkey_sign_hash): New
    	function.
    
    	* lib/gnutls_x509.c (gnutls_certificate_set_x509_key_mem): Handle
    	NULL key.  Doc fix.
    
    	* lib/gnutls_sig.c (_gnutls_tls_sign_hdata): Pass session to
    	_gnutls_tls_sign.
    	(_gnutls_tls_sign_params): Likewise.
    	(_gnutls_tls_sign): Add new parameter 'session'.  Call sign
    	callback if appropriate.
    	(gnutls_sign_callback_set): New function.
    
    	* lib/gnutls_x509.c (read_key_mem): Support a NULL key.
    
    	* lib/gnutls_int.h (internals_st): Add sign_func,
    	sign_func_userdata.
    
    	* lib/auth_dhe.c (gen_dhe_server_kx): Use length of certificate
    	list to decide wheter to sign, not presence of private key.
    	* lib/auth_cert.c (_gnutls_gen_cert_client_cert_vrfy): Likewise.
    
    	* lib/auth_rsa_export.c (gen_rsa_export_server_kx): Likewise.
    
    	* lib/auth_cert.c(_gnutls_get_selected_cert): Don't require that
    	private key is present.
    
    	* lib/auth_rsa_export.c (gen_rsa_export_server_kx): Don't check
    	key size when key is not present, assume it is > 512 bits.
    
    	* lib/x509/privkey.c (gnutls_x509_privkey_sign_hash): New
    	function.
    
    	* tests/Makefile.am: Add x509signself.

diff --git a/NEWS b/NEWS
index 908da11..9f39fd1 100644
--- a/NEWS
+++ b/NEWS
@@ -5,8 +5,14 @@ See the end for copying conditions.
 
 * Version 1.7.17 (unreleased)
 
+** New functions to perform external signing.
+Set the signing callback function (of the gnutls_sign_func prototype)
+using gnutls_sign_callback_set.  In the callback, you may find the new
+function gnutls_x509_privkey_sign_hash useful.
+
 ** New self test of client and server authenticated X.509 TLS sessions.
-See tests/x509self.c.
+See tests/x509self.c and tests/x509signself.c.  The latter also tests
+the new external signing callback interface.
 
 ** gnutls_set_default_priority now disable TLS 1.2 by default.
 The RFC is not released yet, and we're approaching a major release so
@@ -24,7 +30,9 @@ Thanks to Jakub Bogusz <qboosh at pld-linux.org> and Daniel Nylander
 <po at danielnylander.se>.
 
 ** API and ABI modifications:
-No changes since last version.
+gnutls_sign_func: ADD, new type for sign callback.
+gnutls_sign_callback_set: ADD, new function to set sign callback.
+gnutls_x509_privkey_sign_hash: ADD, new function useful in sign callback.
 
 * Version 1.7.16 (released 2007-08-07)
 
diff --git a/includes/gnutls/gnutls.h.in b/includes/gnutls/gnutls.h.in
index 4071a10..8f1e364 100644
--- a/includes/gnutls/gnutls.h.in
+++ b/includes/gnutls/gnutls.h.in
@@ -703,7 +703,6 @@ extern "C"
 				       gnutls_x509_crl_t * crl_list,
 				       int crl_list_size);
 
-
 /* global state functions
  */
   int gnutls_global_init (void);
@@ -1048,6 +1047,18 @@ extern "C"
 
   /* X509PKI */
 
+  /* External signing callback.  Experimental. */
+  typedef int (*gnutls_sign_func) (gnutls_session_t session,
+				   void *userdata,
+				   gnutls_certificate_type_t cert_type,
+				   gnutls_datum_t cert,
+				   const gnutls_datum_t hash,
+				   gnutls_datum_t * signature);
+
+  void gnutls_sign_callback_set (gnutls_session_t session,
+				 gnutls_sign_func sign_func,
+				 void *userdata);
+
   /* These are set on the credentials structure.
    */
   void gnutls_certificate_client_set_retrieve_function
diff --git a/includes/gnutls/x509.h b/includes/gnutls/x509.h
index 8f570df..8357dc2 100644
--- a/includes/gnutls/x509.h
+++ b/includes/gnutls/x509.h
@@ -605,6 +605,10 @@ extern "C"
 				   const gnutls_datum_t * data,
 				   const gnutls_datum_t * signature);
 
+  int gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key,
+				     const gnutls_datum_t hash,
+				     gnutls_datum_t *signature);
+
 
 /* Certificate request stuff.
  */
diff --git a/lib/auth_cert.c b/lib/auth_cert.c
index f0cb427..54b4a50 100644
--- a/lib/auth_cert.c
+++ b/lib/auth_cert.c
@@ -1307,7 +1307,7 @@ _gnutls_gen_cert_client_cert_vrfy (gnutls_session_t session, opaque ** data)
       return ret;
     }
 
-  if (apr_pkey != NULL)
+  if (apr_cert_list_length > 0)
     {
       if ((ret =
 	   _gnutls_tls_sign_hdata (session,
@@ -1483,8 +1483,7 @@ _gnutls_get_selected_cert (gnutls_session_t session,
       *apr_pkey = session->internals.selected_key;
       *apr_cert_list_length = session->internals.selected_cert_list_length;
 
-      if (*apr_cert_list_length == 0 || *apr_pkey == NULL ||
-	  *apr_cert_list == NULL)
+      if (*apr_cert_list_length == 0 || *apr_cert_list == NULL)
 	{
 	  gnutls_assert ();
 	  return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
diff --git a/lib/auth_dhe.c b/lib/auth_dhe.c
index c9ff9f5..6cf658a 100644
--- a/lib/auth_dhe.c
+++ b/lib/auth_dhe.c
@@ -145,7 +145,7 @@ gen_dhe_server_kx (gnutls_session_t session, opaque ** data)
   ddata.data = *data;
   ddata.size = data_size;
 
-  if (apr_pkey != NULL)
+  if (apr_cert_list_length > 0)
     {
       if ((ret =
 	   _gnutls_tls_sign_params (session, &apr_cert_list[0],
diff --git a/lib/auth_rsa_export.c b/lib/auth_rsa_export.c
index a16ed17..a1b867a 100644
--- a/lib/auth_rsa_export.c
+++ b/lib/auth_rsa_export.c
@@ -100,7 +100,7 @@ gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data)
   /* abort sending this message if we have a certificate
    * of 512 bits or less.
    */
-  if (_gnutls_mpi_get_nbits (apr_pkey->params[0]) <= 512)
+  if (apr_pkey && _gnutls_mpi_get_nbits (apr_pkey->params[0]) <= 512)
     {
       gnutls_assert ();
       return GNUTLS_E_INT_RET_0;
@@ -153,7 +153,7 @@ gen_rsa_export_server_kx (gnutls_session_t session, opaque ** data)
   ddata.data = *data;
   ddata.size = data_size;
 
-  if (apr_pkey != NULL)
+  if (apr_cert_list_length > 0)
     {
       if ((ret =
 	   _gnutls_tls_sign_params (session, &apr_cert_list[0],
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index b9a9dcd..6f01c96 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -627,6 +627,12 @@ typedef struct
    */
   int errnum;
 
+  /* Function used to perform public-key signing operation during
+     handshake.  Used by gnutls_sig.c:_gnutls_tls_sign(), see also
+     gnutls_sign_callback_set(). */
+  gnutls_sign_func sign_func;
+  void *sign_func_userdata;
+
   /* If you add anything here, check _gnutls_handshake_internal_state_clear().
    */
 } internals_st;
diff --git a/lib/gnutls_sig.c b/lib/gnutls_sig.c
index 31c845c..31db1cf 100644
--- a/lib/gnutls_sig.c
+++ b/lib/gnutls_sig.c
@@ -37,10 +37,11 @@
 #include <gnutls_sig.h>
 #include <gnutls_kx.h>
 
-static
-  int _gnutls_tls_sign (gnutls_cert * cert, gnutls_privkey * pkey,
-			const gnutls_datum_t * hash_concat,
-			gnutls_datum_t * signature);
+static int
+_gnutls_tls_sign (gnutls_session_t session,
+		  gnutls_cert * cert, gnutls_privkey * pkey,
+		  const gnutls_datum_t * hash_concat,
+		  gnutls_datum_t * signature);
 
 
 /* Generates a signature of all the previous sent packets in the 
@@ -111,7 +112,7 @@ _gnutls_tls_sign_hdata (gnutls_session_t session,
       gnutls_assert ();
       return GNUTLS_E_INTERNAL_ERROR;
     }
-  ret = _gnutls_tls_sign (cert, pkey, &dconcat, signature);
+  ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature);
   if (ret < 0)
     {
       gnutls_assert ();
@@ -202,7 +203,7 @@ _gnutls_tls_sign_params (gnutls_session_t session, gnutls_cert * cert,
       _gnutls_hash_deinit (td_sha, NULL);
       return GNUTLS_E_INTERNAL_ERROR;
     }
-  ret = _gnutls_tls_sign (cert, pkey, &dconcat, signature);
+  ret = _gnutls_tls_sign (session, cert, pkey, &dconcat, signature);
   if (ret < 0)
     {
       gnutls_assert ();
@@ -257,7 +258,8 @@ _gnutls_sign (gnutls_pk_algorithm_t algo, mpi_t * params,
  * it supports signing.
  */
 static int
-_gnutls_tls_sign (gnutls_cert * cert, gnutls_privkey * pkey,
+_gnutls_tls_sign (gnutls_session_t session,
+		  gnutls_cert * cert, gnutls_privkey * pkey,
 		  const gnutls_datum_t * hash_concat,
 		  gnutls_datum_t * signature)
 {
@@ -273,11 +275,49 @@ _gnutls_tls_sign (gnutls_cert * cert, gnutls_privkey * pkey,
 	  return GNUTLS_E_KEY_USAGE_VIOLATION;
 	}
 
+  /* External signing. */
+  if (!pkey || pkey->params_size == 0)
+    {
+      if (!session->internals.sign_func)
+	return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+
+      return (*session->internals.sign_func)
+	(session, session->internals.sign_func_userdata,
+	 cert->cert_type, cert->raw,
+	 *hash_concat, signature);
+    }
+
   return _gnutls_sign (pkey->pk_algorithm, pkey->params,
 		       pkey->params_size, hash_concat, signature);
-
 }
 
+/**
+ * gnutls_sign_callback_set:
+ * @session:
+ * @sign_func:
+ * @userdata:
+ *
+ * Set the callback function.  The function must have this prototype:
+ *
+ * typedef int (*gnutls_sign_func) (gnutls_session_t session,
+ *                                  void *userdata,
+ *                                  gnutls_certificate_type_t cert_type,
+ *                                  gnutls_datum_t cert,
+ *                                  const gnutls_datum_t hash,
+ *                                  gnutls_datum_t * signature);
+ *
+ * The @userdata parameter is passed to the @sign_func verbatim, and
+ * can be used to store application-specific data needed in the
+ * callback function.
+ **/
+void
+gnutls_sign_callback_set (gnutls_session_t session,
+			  gnutls_sign_func sign_func,
+			  void *userdata)
+{
+  session->internals.sign_func = sign_func;
+  session->internals.sign_func_userdata = userdata;
+}
 
 static int
 _gnutls_verify_sig (gnutls_cert * cert,
diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c
index d4b66ac..352f002 100644
--- a/lib/gnutls_x509.c
+++ b/lib/gnutls_x509.c
@@ -702,9 +702,9 @@ _gnutls_x509_raw_privkey_to_gkey (gnutls_privkey * privkey,
   return 0;
 }
 
-/* Reads a PEM encoded PKCS-1 RSA private key from memory
- * 2002-01-26: Added ability to read DSA keys.
- * type indicates the certificate format.
+/* Reads a PEM encoded PKCS-1 RSA/DSA private key from memory.  Type
+ * indicates the certificate format.  KEY can be NULL, to indicate
+ * that GnuTLS doesn't know the private key.
  */
 static int
 read_key_mem (gnutls_certificate_credentials_t res,
@@ -724,16 +724,21 @@ read_key_mem (gnutls_certificate_credentials_t res,
       return GNUTLS_E_MEMORY_ERROR;
     }
 
-  tmp.data = (opaque *) key;
-  tmp.size = key_size;
-
-  ret =
-    _gnutls_x509_raw_privkey_to_gkey (&res->pkey[res->ncerts], &tmp, type);
-  if (ret < 0)
+  if (key)
     {
-      gnutls_assert ();
-      return ret;
+      tmp.data = (opaque *) key;
+      tmp.size = key_size;
+
+      ret =
+	_gnutls_x509_raw_privkey_to_gkey (&res->pkey[res->ncerts], &tmp, type);
+      if (ret < 0)
+	{
+	  gnutls_assert ();
+	  return ret;
+	}
     }
+  else
+    memset (&res->pkey[res->ncerts], 0, sizeof (gnutls_privkey));
 
   return 0;
 }
@@ -790,7 +795,7 @@ read_key_file (gnutls_certificate_credentials_t res,
   * gnutls_certificate_set_x509_key_mem - Used to set keys in a gnutls_certificate_credentials_t structure
   * @res: is an #gnutls_certificate_credentials_t structure.
   * @cert: contains a certificate list (path) for the specified private key
-  * @key: is the private key
+  * @key: is the private key, or %NULL
   * @type: is PEM or DER
   *
   * This function sets a certificate/private key pair in the 
@@ -811,6 +816,9 @@ read_key_file (gnutls_certificate_credentials_t res,
   * If the certificate and the private key are given in PEM encoding
   * then the strings that hold their values must be null terminated.
   *
+  * The @key may be %NULL if you are using a sign callback, see
+  * gnutls_sign_callback_set().
+  *
   **/
 int
 gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t
@@ -822,7 +830,8 @@ gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t
 
   /* this should be first 
    */
-  if ((ret = read_key_mem (res, key->data, key->size, type)) < 0)
+  if ((ret = read_key_mem (res, key ? key->data : NULL,
+			   key ? key->size : 0, type)) < 0)
     return ret;
 
   if ((ret = read_cert_mem (res, cert->data, cert->size, type)) < 0)
@@ -830,7 +839,7 @@ gnutls_certificate_set_x509_key_mem (gnutls_certificate_credentials_t
 
   res->ncerts++;
 
-  if ((ret = _gnutls_check_key_cert_match (res)) < 0)
+  if (key && (ret = _gnutls_check_key_cert_match (res)) < 0)
     {
       gnutls_assert ();
       return ret;
diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c
index dd29e89..5044828 100644
--- a/lib/x509/privkey.c
+++ b/lib/x509/privkey.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2004, 2005 Free Software Foundation
+ * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation
  *
  * Author: Nikos Mavroyanopoulos
  *
@@ -27,6 +27,7 @@
 #include <gnutls_global.h>
 #include <gnutls_errors.h>
 #include <gnutls_rsa_export.h>
+#include <gnutls_sig.h>
 #include <common.h>
 #include <gnutls_x509.h>
 #include <x509_b64.h>
@@ -1555,6 +1556,41 @@ gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key,
 }
 
 /**
+ * gnutls_x509_privkey_sign_hash - This function will sign the given data using the private key params
+ * @key: Holds the key
+ * @hash: holds the data to be signed
+ * @signature: will contain newly allocated signature
+ *
+ * This function will sign the given hash using the private key.
+ *
+ * Return value: In case of failure a negative value will be returned,
+ * and 0 on success.
+ **/
+int
+gnutls_x509_privkey_sign_hash (gnutls_x509_privkey_t key,
+			       const gnutls_datum_t hash,
+			       gnutls_datum_t *signature)
+{
+  int result;
+
+  if (key == NULL)
+    {
+      gnutls_assert ();
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  result = _gnutls_sign (key->pk_algorithm, key->params,
+			 key->params_size, &hash, signature);
+  if (result < 0)
+    {
+      gnutls_assert ();
+      return result;
+    }
+
+  return 0;
+}
+
+/**
   * gnutls_x509_privkey_verify_data - This function will verify the given signed data.
   * @key: Holds the key
   * @flags: should be 0 for now
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 15c63e5..209f343 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -40,7 +40,7 @@ ctests = simple openssl gc set_pkcs12_cred certder	\
 	certificate_set_x509_crl dn parse_ca
 openssl_LDADD = $(LDADD) ../libextra/libgnutls-openssl.la
 if HAVE_FORK
-ctests +=  x509self anonself pskself dhepskself tlsia resume
+ctests +=  x509self x509signself anonself pskself dhepskself tlsia resume
 tlsia_LDADD = ../libextra/libgnutls-extra.la $(LDADD) @LTLIBREADLINE@
 endif
 gc_LDADD = $(LDADD) $(LIBGCRYPT_LIBS)
diff --git a/tests/x509signself.c b/tests/x509signself.c
new file mode 100644
index 0000000..2a0a44c
--- /dev/null
+++ b/tests/x509signself.c
@@ -0,0 +1,527 @@
+/*
+ * 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 <gnutls/x509.h>
+
+#include "utils.h"
+
+pid_t child;
+
+static void
+tls_log_func (int level, const char *str)
+{
+  fprintf (stderr, "%s |<%d>| %s", child ? "server" : "client", 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);
+}
+
+const char ca_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIIB5zCCAVKgAwIBAgIERiYdJzALBgkqhkiG9w0BAQUwGTEXMBUGA1UEAxMOR251\n"
+  "VExTIHRlc3QgQ0EwHhcNMDcwNDE4MTMyOTExWhcNMDgwNDE3MTMyOTExWjAZMRcw\n"
+  "FQYDVQQDEw5HbnVUTFMgdGVzdCBDQTCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA\n"
+  "vuyYeh1vfmslnuggeEKgZAVmQ5ltSdUY7H25WGSygKMUYZ0KT74v8C780qtcNt9T\n"
+  "7EPH/N6RvB4BprdssgcQLsthR3XKA84jbjjxNCcaGs33lvOz8A1nf8p3hD+cKfRi\n"
+  "kfYSW2JazLrtCC4yRCas/SPOUxu78of+3HiTfFm/oXUCAwEAAaNDMEEwDwYDVR0T\n"
+  "AQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBTpPBz7rZJu5gak\n"
+  "Viyi4cBTJ8jylTALBgkqhkiG9w0BAQUDgYEAiaIRqGfp1jPpNeVhABK60SU0KIAy\n"
+  "njuu7kHq5peUgYn8Jd9zNzExBOEp1VOipGsf6G66oQAhDFp2o8zkz7ZH71zR4HEW\n"
+  "KoX6n5Emn6DvcEH/9pAhnGxNHJAoS7czTKv/JDZJhkqHxyrE1fuLsg5Qv25DTw7+\n"
+  "PfqUpIhz5Bbm7J4=\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t ca = { ca_pem, sizeof (ca_pem) };
+
+const char cert_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIICHjCCAYmgAwIBAgIERiYdNzALBgkqhkiG9w0BAQUwGTEXMBUGA1UEAxMOR251\n"
+  "VExTIHRlc3QgQ0EwHhcNMDcwNDE4MTMyOTI3WhcNMDgwNDE3MTMyOTI3WjAdMRsw\n"
+  "GQYDVQQDExJHbnVUTFMgdGVzdCBjbGllbnQwgZwwCwYJKoZIhvcNAQEBA4GMADCB\n"
+  "iAKBgLtmQ/Xyxde2jMzF3/WIO7HJS2oOoa0gUEAIgKFPXKPQ+GzP5jz37AR2ExeL\n"
+  "ZIkiW8DdU3w77XwEu4C5KL6Om8aOoKUSy/VXHqLnu7czSZ/ju0quak1o/8kR4jKN\n"
+  "zj2AC41179gAgY8oBAOgIo1hBAf6tjd9IQdJ0glhaZiQo1ipAgMBAAGjdjB0MAwG\n"
+  "A1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAweg\n"
+  "ADAdBgNVHQ4EFgQUTLkKm/odNON+3svSBxX+odrLaJEwHwYDVR0jBBgwFoAU6Twc\n"
+  "+62SbuYGpFYsouHAUyfI8pUwCwYJKoZIhvcNAQEFA4GBALujmBJVZnvaTXr9cFRJ\n"
+  "jpfc/3X7sLUsMvumcDE01ls/cG5mIatmiyEU9qI3jbgUf82z23ON/acwJf875D3/\n"
+  "U7jyOsBJ44SEQITbin2yUeJMIm1tievvdNXBDfW95AM507ShzP12sfiJkJfjjdhy\n"
+  "dc8Siq5JojruiMizAf0pA7in\n"
+  "-----END CERTIFICATE-----\n";
+const gnutls_datum_t cert = { cert_pem, sizeof (cert_pem) };
+
+int
+sign_func (gnutls_session_t session,
+	   void *userdata,
+	   gnutls_certificate_type_t cert_type,
+	   gnutls_datum_t cert,
+	   const gnutls_datum_t hash,
+	   gnutls_datum_t * signature)
+{
+  gnutls_x509_privkey_t key;
+  const char key_pem[] =
+    "-----BEGIN RSA PRIVATE KEY-----\n"
+    "MIICXAIBAAKBgQC7ZkP18sXXtozMxd/1iDuxyUtqDqGtIFBACIChT1yj0Phsz+Y8\n"
+    "9+wEdhMXi2SJIlvA3VN8O+18BLuAuSi+jpvGjqClEsv1Vx6i57u3M0mf47tKrmpN\n"
+    "aP/JEeIyjc49gAuNde/YAIGPKAQDoCKNYQQH+rY3fSEHSdIJYWmYkKNYqQIDAQAB\n"
+    "AoGADpmARG5CQxS+AesNkGmpauepiCz1JBF/JwnyiX6vEzUh0Ypd39SZztwrDxvF\n"
+    "PJjQaKVljml1zkJpIDVsqvHdyVdse8M+Qn6hw4x2p5rogdvhhIL1mdWo7jWeVJTF\n"
+    "RKB7zLdMPs3ySdtcIQaF9nUAQ2KJEvldkO3m/bRJFEp54k0CQQDYy+RlTmwRD6hy\n"
+    "7UtMjR0H3CSZJeQ8svMCxHLmOluG9H1UKk55ZBYfRTsXniqUkJBZ5wuV1L+pR9EK\n"
+    "ca89a+1VAkEA3UmBelwEv2u9cAU1QjKjmwju1JgXbrjEohK+3B5y0ESEXPAwNQT9\n"
+    "TrDM1m9AyxYTWLxX93dI5QwNFJtmbtjeBQJARSCWXhsoaDRG8QZrCSjBxfzTCqZD\n"
+    "ZXtl807ymCipgJm60LiAt0JLr4LiucAsMZz6+j+quQbSakbFCACB8SLV1QJBAKZQ\n"
+    "YKf+EPNtnmta/rRKKvySsi3GQZZN+Dt3q0r094XgeTsAqrqujVNfPhTMeP4qEVBX\n"
+    "/iVX2cmMTSh3w3z8MaECQEp0XJWDVKOwcTW6Ajp9SowtmiZ3YDYo1LF9igb4iaLv\n"
+    "sWZGfbnU3ryjvkb6YuFjgtzbZDZHWQCo8/cOtOBmPdk=\n"
+    "-----END RSA PRIVATE KEY-----\n";
+  const gnutls_datum_t key_dat = { key_pem, sizeof (key_pem) };
+  int ret;
+
+  ret = gnutls_x509_privkey_init (&key);
+  if (ret < 0)
+    return ret;
+
+  ret = gnutls_x509_privkey_import (key, &key_dat, GNUTLS_X509_FMT_PEM);
+  if (ret < 0)
+    goto done;
+
+  ret = gnutls_x509_privkey_sign_hash (key, hash, signature);
+  if (ret < 0)
+    goto done;
+
+  ret = 0;
+
+ done:
+  gnutls_x509_privkey_deinit (key);
+  return ret;
+}
+
+void
+client (void)
+{
+  int ret, sd, ii;
+  gnutls_session_t session;
+  char buffer[MAX_BUF + 1];
+  gnutls_certificate_credentials_t xcred;
+
+  gnutls_global_init ();
+
+  gnutls_global_set_log_function (tls_log_func);
+  gnutls_global_set_log_level (4711);
+
+  gnutls_certificate_allocate_credentials (&xcred);
+
+  /* sets the trusted cas file
+   */
+  gnutls_certificate_set_x509_trust_mem (xcred, &ca, GNUTLS_X509_FMT_PEM);
+  gnutls_certificate_set_x509_key_mem (xcred, &cert, NULL,
+				       GNUTLS_X509_FMT_PEM);
+
+  /* Initialize TLS session
+   */
+  gnutls_init (&session, GNUTLS_CLIENT);
+
+  /* Set sign callback. */
+  gnutls_sign_callback_set (session, sign_func, NULL);
+
+  /* Use default priorities */
+  gnutls_set_default_priority (session);
+
+  /* put the x509 credentials to the current session
+   */
+  gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, xcred);
+
+  /* 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)));
+
+  /* see the Getting peer's information example */
+  print_info(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_certificate_free_credentials (xcred);
+
+  gnutls_global_deinit ();
+}
+
+/* This is a sample TLS 1.0 echo server, using X.509 authentication.
+ */
+
+#define SA struct sockaddr
+#define MAX_BUF 1024
+#define PORT 5556		/* listen to 5556 port */
+#define DH_BITS 1024
+
+/* These are global */
+gnutls_certificate_credentials_t x509_cred;
+
+gnutls_session_t
+initialize_tls_session (void)
+{
+  gnutls_session_t session;
+
+  gnutls_init (&session, GNUTLS_SERVER);
+
+  /* avoid calling all the priority functions, since the defaults
+   * are adequate.
+   */
+  gnutls_set_default_priority (session);
+
+  gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
+
+  /* request client certificate if any.
+   */
+  gnutls_certificate_server_set_request (session, GNUTLS_CERT_REQUEST);
+
+  gnutls_dh_set_prime_bits (session, DH_BITS);
+
+  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;
+
+
+const char server_cert_pem[] =
+  "-----BEGIN CERTIFICATE-----\n"
+  "MIICVjCCAcGgAwIBAgIERiYdMTALBgkqhkiG9w0BAQUwGTEXMBUGA1UEAxMOR251\n"
+  "VExTIHRlc3QgQ0EwHhcNMDcwNDE4MTMyOTIxWhcNMDgwNDE3MTMyOTIxWjA3MRsw\n"
+  "GQYDVQQKExJHbnVUTFMgdGVzdCBzZXJ2ZXIxGDAWBgNVBAMTD3Rlc3QuZ251dGxz\n"
+  "Lm9yZzCBnDALBgkqhkiG9w0BAQEDgYwAMIGIAoGA17pcr6MM8C6pJ1aqU46o63+B\n"
+  "dUxrmL5K6rce+EvDasTaDQC46kwTHzYWk95y78akXrJutsoKiFV1kJbtple8DDt2\n"
+  "DZcevensf9Op7PuFZKBroEjOd35znDET/z3IrqVgbtm2jFqab7a+n2q9p/CgMyf1\n"
+  "tx2S5Zacc1LWn9bIjrECAwEAAaOBkzCBkDAMBgNVHRMBAf8EAjAAMBoGA1UdEQQT\n"
+  "MBGCD3Rlc3QuZ251dGxzLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHQ8B\n"
+  "Af8EBQMDB6AAMB0GA1UdDgQWBBTrx0Vu5fglyoyNgw106YbU3VW0dTAfBgNVHSME\n"
+  "GDAWgBTpPBz7rZJu5gakViyi4cBTJ8jylTALBgkqhkiG9w0BAQUDgYEAaFEPTt+7\n"
+  "bzvBuOf7+QmeQcn29kT6Bsyh1RHJXf8KTk5QRfwp6ogbp94JQWcNQ/S7YDFHglD1\n"
+  "AwUNBRXwd3riUsMnsxgeSDxYBfJYbDLeohNBsqaPDJb7XailWbMQKfAbFQ8cnOxg\n"
+  "rOKLUQRWJ0K3HyXRMhbqjdLIaQiCvQLuizo=\n"
+  "-----END CERTIFICATE-----\n";
+
+const gnutls_datum_t server_cert = { server_cert_pem,
+				     sizeof (server_cert_pem) };
+
+const char server_key_pem[] =
+  "-----BEGIN RSA PRIVATE KEY-----\n"
+  "MIICXAIBAAKBgQDXulyvowzwLqknVqpTjqjrf4F1TGuYvkrqtx74S8NqxNoNALjq\n"
+  "TBMfNhaT3nLvxqResm62ygqIVXWQlu2mV7wMO3YNlx696ex/06ns+4VkoGugSM53\n"
+  "fnOcMRP/PciupWBu2baMWppvtr6far2n8KAzJ/W3HZLllpxzUtaf1siOsQIDAQAB\n"
+  "AoGAYAFyKkAYC/PYF8e7+X+tsVCHXppp8AoP8TEZuUqOZz/AArVlle/ROrypg5kl\n"
+  "8YunrvUdzH9R/KZ7saNZlAPLjZyFG9beL/am6Ai7q7Ma5HMqjGU8kTEGwD7K+lbG\n"
+  "iomokKMOl+kkbY/2sI5Czmbm+/PqLXOjtVc5RAsdbgvtmvkCQQDdV5QuU8jap8Hs\n"
+  "Eodv/tLJ2z4+SKCV2k/7FXSKWe0vlrq0cl2qZfoTUYRnKRBcWxc9o92DxK44wgPi\n"
+  "oMQS+O7fAkEA+YG+K9e60sj1K4NYbMPAbYILbZxORDecvP8lcphvwkOVUqbmxOGh\n"
+  "XRmTZUuhBrJhJKKf6u7gf3KWlPl6ShKEbwJASC118cF6nurTjuLf7YKARDjNTEws\n"
+  "qZEeQbdWYINAmCMj0RH2P0mvybrsXSOD5UoDAyO7aWuqkHGcCLv6FGG+qwJAOVqq\n"
+  "tXdUucl6GjOKKw5geIvRRrQMhb/m5scb+5iw8A4LEEHPgGiBaF5NtJZLALgWfo5n\n"
+  "hmC8+G8F0F78znQtPwJBANexu+Tg5KfOnzSILJMo3oXiXhf5PqXIDmbN0BKyCKAQ\n"
+  "LfkcEcUbVfmDaHpvzwY9VEaoMOKVLitETXdNSxVpvWM=\n"
+  "-----END RSA PRIVATE KEY-----\n";
+
+const gnutls_datum_t server_key = { server_key_pem,
+				    sizeof (server_key_pem) };
+
+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_certificate_allocate_credentials (&x509_cred);
+  gnutls_certificate_set_x509_trust_mem (x509_cred, &ca, GNUTLS_X509_FMT_PEM);
+
+  gnutls_certificate_set_x509_key_mem (x509_cred, &server_cert, &server_key,
+				       GNUTLS_X509_FMT_PEM);
+
+  success ("Launched, generating DH parameters...\n");
+
+  generate_dh_params ();
+
+  gnutls_certificate_set_dh_params (x509_cred, 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);
+}
+
+#include "ex-session-info.c"
+#include "ex-x509-info.c"
+
+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_certificate_free_credentials (x509_cred);
+
+  gnutls_global_deinit ();
+
+  success ("server: finished\n");
+}
+
+
+void
+doit (void)
+{
+  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 ();
+}



More information about the Gnutls-dev mailing list