]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added initial support for TPM keys.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 4 Jul 2012 20:53:00 +0000 (22:53 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 4 Jul 2012 21:17:31 +0000 (23:17 +0200)
12 files changed:
NEWS
configure.ac
doc/Makefile.am
lib/Makefile.am
lib/gnutls_errors.c
lib/includes/gnutls/abstract.h
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/pkcs11.c
lib/pkcs11_int.h
lib/pkcs11_privkey.c
lib/tpm.c [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index ada7b42896a1e3d00d125619e2031fed565fad2b..20e8886854c52c87de8ef1ee0bb9bd7b691d761f 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,7 @@ by Alexandre Bique.
 ** API and ABI modifications:
 GNUTLS_CERT_SIGNATURE_FAILURE: Added
 GNUTLS_CAMELLIA_192_CBC: Added
+gnutls_privkey_import_tpm_raw: Added
 gnutls_privkey_import_pkcs11_url: Added
 gnutls_privkey_import_openpgp_raw: Added
 gnutls_privkey_import_x509_raw: Added
index eba91ad04434bbbf14b6a8d18a55c2d852527f84..e774be4910aedbf4031cc76f04190e4d3a611319 100644 (file)
@@ -153,6 +153,33 @@ fi
 
 AM_CONDITIONAL(ENABLE_PKCS11, test "$with_p11_kit" != "no")
 
+AC_ARG_WITH(tpm,
+       AS_HELP_STRING([--without-tpm],
+               [Build without TPM (trousers) support]))
+if test "$with_tpm" != "no"; then
+    LIBS="$oldlibs -ltspi"
+    AC_MSG_CHECKING([for tss library])
+    AC_LINK_IFELSE([AC_LANG_PROGRAM([
+                  #include <trousers/tss.h>
+                  #include <trousers/trousers.h>],[
+                  int err = Tspi_Context_Create((void *)0);
+                  Trspi_Error_String(err);])],
+                 [AC_MSG_RESULT(yes)
+                  AC_SUBST([TSS_LIBS], [-ltspi])
+                  AC_SUBST([TSS_CFLAGS], [])
+                  AC_DEFINE([HAVE_TROUSERS], 1, [Enable TPM])
+                  with_tpm=yes],
+                 [AC_MSG_RESULT(no)
+                  AC_MSG_WARN([[
+*** 
+*** trousers was not found. TPM support will be disabled.
+*** ]])
+                 with_tpm=no])
+    LIBS="$oldlibs"
+fi
+
+AM_CONDITIONAL(ENABLE_TROUSERS, test "$with_tpm" != "no")
+
 enable_local_libopts=yes
 dnl PKG_CHECK_MODULES([autoopts], autoopts >= 36.2.11,, [enable_local_libopts=yes])
 
@@ -208,6 +235,7 @@ AC_SUBST(GNUTLS_REQUIRES_PRIVATE)
 AC_SUBST(GNUTLS_ZLIB_LIBS_PRIVATE)
 
 
+
 gl_INIT
 
 dnl GCC warnings to enable
@@ -581,4 +609,5 @@ AC_MSG_NOTICE([Hardware acceleration/support:
   /dev/crypto:      $enable_cryptodev
   Hardware accel:   $hw_accel
   PKCS#11 support:  $with_p11_kit
+  TPM support:      $with_tpm
 ])
index 685d387229698709d76b130f60c43fee3b23c9e6..ce3e25be9880ee3100fc2fe6964ee7a676b22646 100644 (file)
@@ -514,8 +514,10 @@ FUNCS += functions/gnutls_privkey_import_x509
 FUNCS += functions/gnutls_privkey_import_openpgp
 FUNCS += functions/gnutls_privkey_import_openpgp_raw
 FUNCS += functions/gnutls_privkey_import_x509_raw
+FUNCS += functions/gnutls_privkey_import_tpm_raw
 FUNCS += functions/gnutls_privkey_import_pkcs11_url
 FUNCS += functions/gnutls_privkey_import_ext
+FUNCS += functions/gnutls_privkey_import_ext2
 FUNCS += functions/gnutls_privkey_sign_data
 FUNCS += functions/gnutls_privkey_sign_hash
 FUNCS += functions/gnutls_privkey_decrypt_data
index f3289b9b9c500cd20030065c9a004b05dec54cd4..69d98b15f7e2459774dda6f8b99cbd7fced110cb 100644 (file)
@@ -76,6 +76,10 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c \
        gnutls_pubkey.c locks.c hash.c gnutls_dtls.c system_override.c  \
        crypto-backend.c verify-tofu.c
 
+if ENABLE_TROUSERS
+COBJECTS += tpm.c
+endif
+
 if ENABLE_PKCS11
 COBJECTS += pkcs11.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c
 endif
@@ -120,7 +124,7 @@ libgnutls_la_LIBADD = ../gl/libgnu.la x509/libgnutls_x509.la \
        auth/libgnutls_auth.la algorithms/libgnutls_alg.la \
        extras/libgnutls_extras.la \
        $(LTLIBZ)  $(LTLIBINTL) $(LIBSOCKET) $(LTLIBDL) \
-       $(LTLIBPTHREAD) $(P11_KIT_LIBS) $(LIB_SELECT)
+       $(LTLIBPTHREAD) $(P11_KIT_LIBS) $(LIB_SELECT) $(TSS_LIBS)
 
 if ENABLE_OPENPGP
 libgnutls_la_LIBADD += openpgp/libgnutls_openpgp.la
index 71b6e6acf1123cd8d2d3da402d9727c6bb93b17d..83e485d88571bd5146a00a710cfd0d27218c1468 100644 (file)
@@ -286,16 +286,16 @@ static const gnutls_error_entry error_algorithms[] = {
   ERROR_ENTRY (N_("Channel binding data not available"),
                GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE, 1),
 
+  ERROR_ENTRY (N_("TPM error."),
+               GNUTLS_E_TPM_ERROR, 1),
   ERROR_ENTRY (N_("PKCS #11 error."),
                GNUTLS_E_PKCS11_ERROR, 1),
   ERROR_ENTRY (N_("PKCS #11 initialization error."),
                GNUTLS_E_PKCS11_LOAD_ERROR, 1),
   ERROR_ENTRY (N_("Error in parsing."),
                GNUTLS_E_PARSING_ERROR, 1),
-  ERROR_ENTRY (N_("PKCS #11 error in PIN."),
-               GNUTLS_E_PKCS11_PIN_ERROR, 1),
-  ERROR_ENTRY (N_("PKCS #11 PIN should be saved."),
-               GNUTLS_E_PKCS11_ERROR, 1),
+  ERROR_ENTRY (N_("Error in provided PIN."),
+               GNUTLS_E_PIN_ERROR, 1),
   ERROR_ENTRY (N_("PKCS #11 error in slot"),
                GNUTLS_E_PKCS11_SLOT_ERROR, 1),
   ERROR_ENTRY (N_("Thread locking error"),
index af798f97301f2ad87774770e3cd9570b26d6cf95..bb7586462a3f3878324956a6fec52a4f268cf5c9 100644 (file)
@@ -50,7 +50,7 @@ typedef int (*gnutls_privkey_decrypt_func) (gnutls_privkey_t key,
                                             const gnutls_datum_t * ciphertext,
                                             gnutls_datum_t * plaintext);
 
-typedef int (*gnutls_privkey_deinit_func) (gnutls_privkey_t key,
+typedef void (*gnutls_privkey_deinit_func) (gnutls_privkey_t key,
                                            void *userdata);
 
 int gnutls_pubkey_init (gnutls_pubkey_t * key);
@@ -189,6 +189,11 @@ int gnutls_privkey_import_x509_raw (gnutls_privkey_t pkey,
                                     gnutls_x509_crt_fmt_t format,
                                     const char* password);
 
+int gnutls_privkey_import_tpm_raw (gnutls_privkey_t pkey,
+                                   const gnutls_datum_t * fdata,
+                                   gnutls_x509_crt_fmt_t format,
+                                   const char* password);
+
 int gnutls_privkey_import_pkcs11_url (gnutls_privkey_t key, const char *url);
 
 int
index d27070f4abef4322e2e1880774b64b47e7145622..92bd6d3866c6793b5a85ad462a6917cc005beef4 100644 (file)
@@ -1914,6 +1914,9 @@ int gnutls_load_file(const char* filename, gnutls_datum_t * data);
 #define GNUTLS_E_X509_UNSUPPORTED_EXTENSION -327
 #define GNUTLS_E_SESSION_EOF -328
 
+#define GNUTLS_E_TPM_ERROR -329
+#define GNUTLS_E_PIN_ERROR GNUTLS_E_PKCS11_PIN_ERROR
+
 #define GNUTLS_E_UNIMPLEMENTED_FEATURE -1250
 
 
index 746281395f376198c8233e8f0a1bc7df42d5b490..cf7a7dc6081c687cf9474203e3f7a41526a36d6e 100644 (file)
@@ -806,6 +806,7 @@ GNUTLS_3_1_0 {
        gnutls_x509_privkey_import_openssl;
        gnutls_x509_privkey_import2;
        gnutls_privkey_import_ext2;
+       gnutls_privkey_import_tpm_raw;
 } GNUTLS_3_0_0;
 
 GNUTLS_PRIVATE {
index 2a62f992081ec0767fb9890dccc26caa8fc58f72..ec8a482cf629d9613016b9843c81b5930b41d34b 100644 (file)
@@ -74,11 +74,11 @@ static struct gnutls_pkcs11_provider_s providers[MAX_PROVIDERS];
 static unsigned int active_providers = 0;
 static unsigned int initialized_registered = 0;
 
-static gnutls_pkcs11_pin_callback_t pin_func;
-static void *pin_data;
+gnutls_pkcs11_pin_callback_t _gnutls_pin_func;
+void *_gnutls_pin_data;
 
-gnutls_pkcs11_token_callback_t token_func;
-void *token_data;
+gnutls_pkcs11_token_callback_t _gnutls_token_func;
+void *_gnutls_token_data;
 
 int
 pkcs11_rv_to_err (ck_rv_t rv)
@@ -651,8 +651,8 @@ void
 gnutls_pkcs11_set_pin_function (gnutls_pkcs11_pin_callback_t fn,
                                 void *userdata)
 {
-  pin_func = fn;
-  pin_data = userdata;
+  _gnutls_pin_func = fn;
+  _gnutls_pin_data = userdata;
 }
 
 /**
@@ -669,8 +669,8 @@ void
 gnutls_pkcs11_set_token_function (gnutls_pkcs11_token_callback_t fn,
                                   void *userdata)
 {
-  token_func = fn;
-  token_data = userdata;
+  _gnutls_token_func = fn;
+  _gnutls_token_data = userdata;
 }
 
 int
@@ -1953,7 +1953,7 @@ retrieve_pin_for_callback (struct ck_token_info *token_info, int attempts,
   if (attempts > 0)
     flags |= GNUTLS_PKCS11_PIN_WRONG;
 
-  ret = pin_func (pin_data, attempts, (char*)token_str, label,
+  ret = _gnutls_pin_func (_gnutls_pin_data, attempts, (char*)token_str, label,
                   flags, pin_value, GNUTLS_PKCS11_MAX_PIN_LEN);
   free (token_str);
   free (label);
@@ -1987,7 +1987,7 @@ retrieve_pin (struct p11_kit_uri *info, struct ck_token_info *token_info,
     }
 
   /* The global gnutls pin callback */
-  if (pin_func && ret < 0)
+  if (_gnutls_pin_func && ret < 0)
     ret = retrieve_pin_for_callback (token_info, attempts, user_type, pin);
 
   /* Otherwise, PIN entry is necessary for login, so fail if there's
@@ -2102,7 +2102,7 @@ pkcs11_call_token_func (struct p11_kit_uri *info, const unsigned retry)
 
   tinfo = p11_kit_uri_get_token_info (info);
   label = p11_kit_space_strdup (tinfo->label, sizeof (tinfo->label));
-  ret = (token_func) (token_data, label, retry);
+  ret = (_gnutls_token_func) (_gnutls_token_data, label, retry);
   free (label);
 
   return ret;
index 538da55fe3298a209ee29dfdf229c92b6b25c2c0..4da410767066e983dd75b77d233d17c5281ca56d 100644 (file)
@@ -34,6 +34,9 @@
 #include <p11-kit/uri.h>
 typedef unsigned char ck_bool_t;
 
+extern gnutls_pkcs11_pin_callback_t _gnutls_pin_func;
+extern void *_gnutls_pin_data;
+
 struct pkcs11_session_info {
   struct ck_function_list * module;
   struct ck_token_info tinfo;
@@ -83,8 +86,8 @@ int pkcs11_login (struct pkcs11_session_info * sinfo,
 
 int pkcs11_call_token_func (struct p11_kit_uri *info, const unsigned retry);
 
-extern gnutls_pkcs11_token_callback_t token_func;
-extern void *token_data;
+extern gnutls_pkcs11_token_callback_t _gnutls_token_func;
+extern void *_gnutls_token_data;
 
 void pkcs11_rescan_slots (void);
 int pkcs11_info_to_url (struct p11_kit_uri *info,
index f377a9d289bc37250f5aa6de4d688f96cea2ab2d..487230cdcff0af3728ba0e9d403bed9a8c490c24 100644 (file)
@@ -135,7 +135,7 @@ gnutls_pkcs11_privkey_get_info (gnutls_pkcs11_privkey_t pkey,
                ret = pkcs11_find_object (sinfo, &obj, key->info, \
                                          SESSION_LOGIN); \
                if (ret == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { \
-                       if (token_func) \
+                       if (_gnutls_token_func) \
                          { \
                            rret = pkcs11_call_token_func (key->info, retries++); \
                            if (rret == 0) continue; \
diff --git a/lib/tpm.c b/lib/tpm.c
new file mode 100644 (file)
index 0000000..650a846
--- /dev/null
+++ b/lib/tpm.c
@@ -0,0 +1,376 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2012 Free Software Foundation.
+ * Copyright © 2008-2012 Intel Corporation.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * 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 3 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 <http://www.gnu.org/licenses/>
+ *
+ */
+
+/*
+ * TPM code based on client-tpm.c from
+ * Carolin Latze <latze@angry-red-pla.net> and Tobias Soder
+ */
+
+#include <gnutls/gnutls.h>
+#include <gnutls/abstract.h>
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <pkcs11_int.h>
+
+#include <trousers/tss.h>
+#include <trousers/trousers.h>
+
+/* Signing function for TPM privkeys, set with gnutls_privkey_import_ext() */
+
+struct tpm_ctx_st
+{
+  TSS_HCONTEXT tpm_context;
+  TSS_HKEY tpm_key;
+  TSS_HPOLICY tpm_key_policy;
+  TSS_HKEY srk;
+  TSS_HPOLICY srk_policy;
+};
+
+static void
+tpm_deinit_fn (gnutls_privkey_t key, void *_s)
+{
+  struct tpm_ctx_st *s = _s;
+
+  Tspi_Context_CloseObject (s->tpm_context, s->tpm_key_policy);
+  Tspi_Context_CloseObject (s->tpm_context, s->tpm_key);
+  Tspi_Context_CloseObject (s->tpm_context, s->srk_policy);
+  Tspi_Context_CloseObject (s->tpm_context, s->srk);
+  Tspi_Context_Close (s->tpm_context);
+  gnutls_free (s);
+}
+
+static int
+tpm_sign_fn (gnutls_privkey_t key, void *_s,
+            const gnutls_datum_t * data, gnutls_datum_t * sig)
+{
+  struct tpm_ctx_st *s = _s;
+  TSS_HHASH hash;
+  int err;
+
+  _gnutls_debug_log ("TPM sign function called for %u bytes.\n",
+                    data->size);
+
+  err =
+      Tspi_Context_CreateObject (s->tpm_context,
+                                TSS_OBJECT_TYPE_HASH, TSS_HASH_OTHER,
+                                &hash);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Failed to create TPM hash object: %s\n",
+                        Trspi_Error_String (err));
+      return GNUTLS_E_PK_SIGN_FAILED;
+    }
+  err = Tspi_Hash_SetHashValue (hash, data->size, data->data);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Failed to set value in TPM hash object: %s\n",
+                        Trspi_Error_String (err));
+      Tspi_Context_CloseObject (s->tpm_context, hash);
+      return GNUTLS_E_PK_SIGN_FAILED;
+    }
+  err = Tspi_Hash_Sign (hash, s->tpm_key, &sig->size, &sig->data);
+  Tspi_Context_CloseObject (s->tpm_context, hash);
+  if (err)
+    {
+      if (s->tpm_key_policy || err != TPM_E_AUTHFAIL)
+       _gnutls_debug_log ("TPM hash signature failed: %s\n",
+                          Trspi_Error_String (err));
+      if (err == TPM_E_AUTHFAIL)
+       return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+      else
+       return GNUTLS_E_PK_SIGN_FAILED;
+    }
+  return 0;
+}
+
+
+/**
+ * gnutls_privkey_import_tpm_raw:
+ * @pkey: The private key
+ * @fdata: The TPM key to be imported
+ * @format: The format of the private key
+ * @password: A password (optional)
+ *
+ * This function will import the given private key to the abstract
+ * #gnutls_privkey_t structure. 
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.1.0
+ *
+ **/
+int
+gnutls_privkey_import_tpm_raw (gnutls_privkey_t pkey,
+                              const gnutls_datum_t * fdata,
+                              gnutls_x509_crt_fmt_t format,
+                              const char *password)
+{
+  static const TSS_UUID SRK_UUID = TSS_UUID_SRK;
+  char pin_value[GNUTLS_PKCS11_MAX_PIN_LEN];
+  gnutls_datum_t asn1;
+  unsigned int tss_len;
+  unsigned int attempts = 0;
+  int ofs, err, ret;
+  struct tpm_ctx_st *s;
+  gnutls_datum_t tmp_sig;
+  static const char nullpass[20];
+
+  err = gnutls_pem_base64_decode_alloc ("TSS KEY BLOB", fdata, &asn1);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Error decoding TSS key blob: %s\n",
+                        gnutls_strerror (err));
+      return GNUTLS_E_INVALID_REQUEST;
+    }
+
+  /* FIXME: do proper decoding */
+
+  /* Ick. We have to parse the ASN1 OCTET_STRING for ourselves. */
+  if (asn1.size < 2 || asn1.data[0] != 0x04 /* OCTET_STRING */ )
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Error in TSS key blob\n");
+      ret = GNUTLS_E_PARSING_ERROR;
+      goto out_blob;
+    }
+
+  s = gnutls_malloc (sizeof (*s));
+  if (s == NULL)
+    {
+      gnutls_assert ();
+      ret = GNUTLS_E_MEMORY_ERROR;
+      goto out_ctx;
+    }
+
+  tss_len = asn1.data[1];
+  ofs = 2;
+  if (tss_len & 0x80)
+    {
+      unsigned int lenlen = tss_len & 0x7f;
+
+      if (asn1.size < 2 + lenlen || lenlen > 3)
+       {
+         gnutls_assert ();
+         _gnutls_debug_log ("Error in TSS key blob\n");
+         ret = GNUTLS_E_PARSING_ERROR;
+         goto out_blob;
+       }
+
+      tss_len = 0;
+      while (lenlen)
+       {
+         tss_len <<= 8;
+         tss_len |= asn1.data[ofs++];
+         lenlen--;
+       }
+    }
+  if (tss_len + ofs != asn1.size)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Error in TSS key blob\n");
+      ret = GNUTLS_E_PARSING_ERROR;
+      goto out_blob;
+    }
+
+  err = Tspi_Context_Create (&s->tpm_context);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Failed to create TPM context: %s\n",
+                        Trspi_Error_String (err));
+      ret = GNUTLS_E_TPM_ERROR;
+      goto out_blob;
+    }
+  err = Tspi_Context_Connect (s->tpm_context, NULL);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Failed to connect TPM context: %s\n",
+                        Trspi_Error_String (err));
+      ret = GNUTLS_E_TPM_ERROR;
+      goto out_context;
+    }
+  err =
+      Tspi_Context_LoadKeyByUUID (s->tpm_context, TSS_PS_TYPE_SYSTEM,
+                                 SRK_UUID, &s->srk);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log
+         ("Failed to load TPM SRK key: %s\n", Trspi_Error_String (err));
+      ret = GNUTLS_E_TPM_ERROR;
+      goto out_context;
+    }
+  err = Tspi_GetPolicyObject (s->srk, TSS_POLICY_USAGE, &s->srk_policy);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Failed to load TPM SRK policy object: %s\n",
+                        Trspi_Error_String (err));
+      ret = GNUTLS_E_TPM_ERROR;
+      goto out_srk;
+    }
+
+  /* We don't seem to get the error here... */
+  if (password)
+    err = Tspi_Policy_SetSecret (s->srk_policy,
+                                TSS_SECRET_MODE_PLAIN,
+                                strlen (password), (BYTE *) password);
+  else                         /* Well-known NULL key */
+    err = Tspi_Policy_SetSecret (s->srk_policy,
+                                TSS_SECRET_MODE_SHA1,
+                                sizeof (nullpass), (BYTE *) nullpass);
+  if (err)
+    {
+      gnutls_assert ();
+      _gnutls_debug_log ("Failed to set TPM PIN: %s\n",
+                        Trspi_Error_String (err));
+      ret = GNUTLS_E_TPM_ERROR;
+      goto out_srkpol;
+    }
+
+  /* ... we get it here instead. */
+  err = Tspi_Context_LoadKeyByBlob (s->tpm_context, s->srk,
+                                   tss_len, asn1.data + ofs, &s->tpm_key);
+  if (err != 0)
+    {
+      if (password)
+       {
+         gnutls_assert ();
+         _gnutls_debug_log
+             ("Failed to load TPM key blob: %s\n",
+              Trspi_Error_String (err));
+       }
+
+      if (err != TPM_E_AUTHFAIL)
+       {
+         gnutls_assert ();
+         ret = GNUTLS_E_TPM_ERROR;
+         goto out_srkpol;
+       }
+      else
+       {
+         ret = gnutls_assert_val (GNUTLS_E_PIN_ERROR);
+         goto out_srkpol;
+       }
+    }
+
+  ret =
+      gnutls_privkey_import_ext2 (pkey, GNUTLS_PK_RSA, s,
+                                 tpm_sign_fn, NULL, tpm_deinit_fn, 0);
+  if (ret < 0)
+    {
+      gnutls_assert ();
+      goto out_srkpol;
+    }
+
+retry_sign:
+  ret =
+      gnutls_privkey_sign_data (pkey, GNUTLS_DIG_SHA1, 0, fdata, &tmp_sig);
+  if (ret == GNUTLS_E_INSUFFICIENT_CREDENTIALS)
+    {
+      if (!s->tpm_key_policy)
+       {
+         err = Tspi_Context_CreateObject (s->tpm_context,
+                                          TSS_OBJECT_TYPE_POLICY,
+                                          TSS_POLICY_USAGE,
+                                          &s->tpm_key_policy);
+         if (err)
+           {
+             gnutls_assert ();
+             _gnutls_debug_log
+                 ("Failed to create key policy object: %s\n",
+                  Trspi_Error_String (err));
+             ret = GNUTLS_E_TPM_ERROR;
+             goto out_key;
+           }
+         err = Tspi_Policy_AssignToObject (s->tpm_key_policy, s->tpm_key);
+         if (err)
+           {
+             gnutls_assert ();
+             _gnutls_debug_log ("Failed to assign policy to key: %s\n",
+                                Trspi_Error_String (err));
+             ret = GNUTLS_E_TPM_ERROR;
+             goto out_key_policy;
+           }
+       }
+
+      ret =
+         _gnutls_pin_func (_gnutls_pin_data, attempts++, "tpm:",
+                           "TPM key", 0, pin_value,
+                           GNUTLS_PKCS11_MAX_PIN_LEN);
+      if (ret < 0)
+       {
+         ret = gnutls_assert_val (GNUTLS_E_PIN_ERROR);
+         goto out_key_policy;
+       }
+
+      err = Tspi_Policy_SetSecret (s->tpm_key_policy,
+                                  TSS_SECRET_MODE_PLAIN,
+                                  strlen (pin_value), (void *) pin_value);
+
+      if (err)
+       {
+         gnutls_assert ();
+         _gnutls_debug_log ("Failed to set key PIN: %s\n",
+                            Trspi_Error_String (err));
+         goto out_key_policy;
+       }
+      goto retry_sign;
+    }
+  else if (ret < 0)
+    {
+      gnutls_assert ();
+      goto out_blob;
+    }
+
+  gnutls_free (asn1.data);
+  return 0;
+out_key_policy:
+  Tspi_Context_CloseObject (s->tpm_context, s->tpm_key_policy);
+  s->tpm_key_policy = 0;
+out_key:
+  Tspi_Context_CloseObject (s->tpm_context, s->tpm_key);
+  s->tpm_key = 0;
+out_srkpol:
+  Tspi_Context_CloseObject (s->tpm_context, s->srk_policy);
+  s->srk_policy = 0;
+out_srk:
+  Tspi_Context_CloseObject (s->tpm_context, s->srk);
+  s->srk = 0;
+out_context:
+  Tspi_Context_Close (s->tpm_context);
+  s->tpm_context = 0;
+out_ctx:
+  gnutls_free (s);
+out_blob:
+  gnutls_free (asn1.data);
+  return ret;
+}
+