[PATCH 3/5] tpm2d: Add tpm2daemon code

James Bottomley James.Bottomley at HansenPartnership.com
Sun Jun 14 19:26:47 CEST 2020


This commit adds and plumbs in a tpm2daemon to the build to mirror the
operation of scdaemon.  The architecture of the code is that
tpm2daemon.c itself is pretty much a clone of scd/scdaemon.c just with
updated function prefixes (this argues there could be some further
consolidation of the daemon handling code).  Note that although this
commit causes the daemon to be built and installed, nothing actually
starts it or uses it yet.

Command handling
----------------

command.c is copied from the command handler in scd.c except that the
command implementation is now done in terms of tpm2 commands and the
wire protocol is far simpler.  The tpm2daemon only responds to 4
commands

IMPORT:    import a standard s-expression private key and export it to
           TPM2 format.  This conversion cannot be undone and the
           private key now can *only* be used by the TPM2.  To anyone
           who gets hold of the private key now, it's just an
           encrypted binary blob.

PKSIGN:    create a signature from the tpm2 key.  The TPM2 form private
           key is retrieved by KEYDATA and the hash to be signed by
           EXTRA.  Note there is no hash specifier because the tpm2
           tss deduces the hash type from the length of the EXTRA
           data.  This is actually a limitation of the tpm2 command
           API and it will be interesting to see how this fares if the
           tpm2 ever supports say sha3-256 hashes.

PKDECRYPT: decrypt (RSA case) or derive (ECC case) a symmetric key.
	   The tpm2 for private key is retrieved by KEYDATA and the
	   information used to create the symmetric key by EXTRA.

KILLTPM2D: stop the daemon

All the tpm2 primitives used by command.c are in tpm2.h and all the
tpm2 specific gunk is confined to tpm2.c, which is the only piece of
this that actually does calls into the tss library.

Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
---
 Makefile.am          |    7 +-
 am/cmacros.am        |    3 +
 common/homedir.c     |    7 +
 common/util.h        |    1 +
 configure.ac         |   37 ++
 tools/gpgconf-comp.c |   15 +
 tpm2d/Makefile.am    |   18 +
 tpm2d/command.c      |  570 +++++++++++++++++
 tpm2d/tpm2.c         |  969 ++++++++++++++++++++++++++++
 tpm2d/tpm2.h         |   39 ++
 tpm2d/tpm2daemon.c   | 1434 ++++++++++++++++++++++++++++++++++++++++++
 tpm2d/tpm2daemon.h   |  130 ++++
 12 files changed, 3229 insertions(+), 1 deletion(-)
 create mode 100644 tpm2d/Makefile.am
 create mode 100644 tpm2d/command.c
 create mode 100644 tpm2d/tpm2.c
 create mode 100644 tpm2d/tpm2.h
 create mode 100644 tpm2d/tpm2daemon.c
 create mode 100644 tpm2d/tpm2daemon.h

diff --git a/Makefile.am b/Makefile.am
index 88b7bdf8b..1da466b6c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -100,10 +100,15 @@ doc = doc
 else
 doc =
 endif
+if HAVE_LIBTSS
+tpm2d = tpm2d
+else
+tpm2d =
+endif
 
 SUBDIRS = m4 common kbx \
           ${gpg} ${sm} ${agent} ${scd} ${g13} ${dirmngr} \
-          tools po ${doc} tests
+          tools po ${doc} tests ${tpm2d}
 
 dist_doc_DATA = README
 
diff --git a/am/cmacros.am b/am/cmacros.am
index 9610e4efe..e71bc4e9d 100644
--- a/am/cmacros.am
+++ b/am/cmacros.am
@@ -44,6 +44,9 @@ endif
 if GNUPG_SCDAEMON_PGM
 AM_CPPFLAGS += -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
 endif
+if GNUPG_TPM2DAEMON_PGM
+AM_CPPFLAGS += -DGNUPG_DEFAULT_TPM2DAEMON="\"@GNUPG_TPM2DAEMON_PGM@\""
+endif
 if GNUPG_DIRMNGR_PGM
 AM_CPPFLAGS += -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
 endif
diff --git a/common/homedir.c b/common/homedir.c
index 4b6e46e88..71d631f5f 100644
--- a/common/homedir.c
+++ b/common/homedir.c
@@ -1100,6 +1100,13 @@ gnupg_module_name (int which)
       X(libexecdir, "scd", "scdaemon");
 #endif
 
+    case GNUPG_MODULE_NAME_TPM2DAEMON:
+#ifdef GNUPG_DEFAULT_TPM2DAEMON
+      return GNUPG_DEFAULT_TPM2DAEMON;
+#else
+      X(libexecdir, "tpm2d", TPM2DAEMON_NAME);
+#endif
+
     case GNUPG_MODULE_NAME_DIRMNGR:
 #ifdef GNUPG_DEFAULT_DIRMNGR
       return GNUPG_DEFAULT_DIRMNGR;
diff --git a/common/util.h b/common/util.h
index 5002039d6..c1ae410eb 100644
--- a/common/util.h
+++ b/common/util.h
@@ -290,6 +290,7 @@ char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info);
 #define GNUPG_MODULE_NAME_GPGCONF       10
 #define GNUPG_MODULE_NAME_DIRMNGR_LDAP  11
 #define GNUPG_MODULE_NAME_GPGV          12
+#define GNUPG_MODULE_NAME_TPM2DAEMON    13
 const char *gnupg_module_name (int which);
 void gnupg_module_name_flush_some (void);
 void gnupg_set_builddir (const char *newdir);
diff --git a/configure.ac b/configure.ac
index 1d05d3991..ca2ba27ff 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,7 @@ have_gnutls=no
 have_sqlite=no
 have_npth=no
 have_libusb=no
+have_libtss=no
 have_system_resolver=no
 gnupg_have_ldap="n/a"
 
@@ -183,6 +184,15 @@ show_gnupg_scdaemon_pgm="(default)"
 test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM"
 
 
+AC_ARG_WITH(tpm2daemon-pgm,
+    [  --with-tpm2daemon-pgm=PATH  Use PATH as the default for the tpm2daemon)],
+          GNUPG_TPM2DAEMON_PGM="$withval", GNUPG_TPM2DAEMON_PGM="" )
+AC_SUBST(GNUPG_TPM2DAEMON_PGM)
+AM_CONDITIONAL(GNUPG_TPM2DAEMON_PGM, test -n "$GNUPG_TPM2DAEMON_PGM")
+show_gnupg_tpm2daemon_pgm="(default)"
+test -n "$GNUPG_TPM2DAEMON_PGM" && show_gnupg_tpm2daemon_pgm="$GNUPG_TPM2DAEMON_PGM"
+
+
 AC_ARG_WITH(dirmngr-pgm,
     [  --with-dirmngr-pgm=PATH  Use PATH as the default for the dirmngr)],
           GNUPG_DIRMNGR_PGM="$withval", GNUPG_DIRMNGR_PGM="" )
@@ -1606,6 +1616,24 @@ fi
 AC_SUBST(NETLIBS)
 AC_SUBST(W32SOCKLIBS)
 
+#
+# TPM libtss library .. don't compile TPM support if we don't have it
+#
+LIBS=""
+AC_SEARCH_LIBS([TSS_Create], [tss ibmtss],have_libtss=yes,)
+if test "$have_libtss" = yes; then
+   CFLAGS="$CFLAGS -DTPM_POSIX"
+   AC_CHECK_HEADER([tss2/tss.h],[AC_DEFINE(TSS_INCLUDE,tss2, [tss2 include location])],
+	AC_CHECK_HEADER([ibmtss/tss.h],[AC_DEFINE(TSS_INCLUDE,ibmtss, [ibmtss include location])],
+            AC_MSG_ERROR([No TSS2 include directory found])))
+   LIBTSS_LIBS=$LIBS
+   AC_DEFINE(HAVE_LIBTSS, 1, [Defined if we have TPM2 support library])
+   AC_SUBST(TSS_INCLUDE)
+fi
+AC_SUBST(LIBTSS_LIBS)
+AM_CONDITIONAL(HAVE_LIBTSS, test "$have_libtss" = yes)
+AC_SUBST(HAVE_LIBTSS)
+
 #
 # Setup gcc specific options
 #
@@ -1841,6 +1869,10 @@ AC_DEFINE_UNQUOTED(GPG_AGENT_NAME, "gpg-agent", [The name of the agent])
 AC_DEFINE_UNQUOTED(GPG_AGENT_DISP_NAME, "GPG Agent",
                                         [The displayed name of gpg-agent])
 
+AC_DEFINE_UNQUOTED(TPM2DAEMON_NAME, "tpm2daemon", [The name of the TPM2 daemon])
+AC_DEFINE_UNQUOTED(TPM2DAEMON_DISP_NAME, "TPM2 Daemon",
+                                       [The displayed name of TPM2 daemon])
+
 AC_DEFINE_UNQUOTED(SCDAEMON_NAME, "scdaemon", [The name of the scdaemon])
 AC_DEFINE_UNQUOTED(SCDAEMON_DISP_NAME, "SCDaemon",
                                        [The displayed name of scdaemon])
@@ -1870,6 +1902,8 @@ AC_DEFINE_UNQUOTED(DIRMNGR_INFO_NAME, "DIRMNGR_INFO",
                    [The name of the dirmngr info envvar])
 AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon",
                    [The name of the SCdaemon socket])
+AC_DEFINE_UNQUOTED(TPM2DAEMON_SOCK_NAME, "S.tpm2daemon",
+                   [The name of the TPM2 daemon socket])
 AC_DEFINE_UNQUOTED(DIRMNGR_SOCK_NAME, "S.dirmngr",
                    [The name of the dirmngr socket])
 AC_DEFINE_UNQUOTED(DIRMNGR_DEFAULT_KEYSERVER,
@@ -2029,6 +2063,7 @@ g10/Makefile
 sm/Makefile
 agent/Makefile
 scd/Makefile
+tpm2d/Makefile
 g13/Makefile
 dirmngr/Makefile
 tools/gpg-zip
@@ -2068,6 +2103,7 @@ echo "
         Default agent:     $show_gnupg_agent_pgm
         Default pinentry:  $show_gnupg_pinentry_pgm
         Default scdaemon:  $show_gnupg_scdaemon_pgm
+	Default tpm2daemon: $show_gnupg_tpm2daemon_pgm
         Default dirmngr:   $show_gnupg_dirmngr_pgm
 
         Dirmngr auto start:  $dirmngr_auto_start
@@ -2076,6 +2112,7 @@ echo "
         TLS support:         $use_tls_library
         TOFU support:        $use_tofu
         Tor support:         $show_tor_support
+        TPM support:         $have_libtss
 "
 if test x"$use_regex" != xyes ; then
 echo "
diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c
index 135d8004d..5b472361e 100644
--- a/tools/gpgconf-comp.c
+++ b/tools/gpgconf-comp.c
@@ -134,6 +134,9 @@ typedef enum
     /* The GnuPG SCDaemon.  */
     GC_BACKEND_SCDAEMON,
 
+    /* The TPM2 daemon */
+    GC_BACKEND_TPM2DAEMON,
+
     /* The GnuPG directory manager.  */
     GC_BACKEND_DIRMNGR,
 
@@ -190,6 +193,8 @@ static const struct
       gpg_agent_runtime_change, GPGCONF_NAME"-" GPG_AGENT_NAME ".conf" },
     { SCDAEMON_DISP_NAME, SCDAEMON_NAME, GNUPG_MODULE_NAME_SCDAEMON,
       scdaemon_runtime_change, GPGCONF_NAME"-" SCDAEMON_NAME ".conf" },
+    { TPM2DAEMON_DISP_NAME, TPM2DAEMON_NAME, GNUPG_MODULE_NAME_TPM2DAEMON,
+      NULL, GPGCONF_NAME"-" TPM2DAEMON_NAME ".conf" },
     { DIRMNGR_DISP_NAME, DIRMNGR_NAME, GNUPG_MODULE_NAME_DIRMNGR,
       dirmngr_runtime_change, GPGCONF_NAME "-" DIRMNGR_NAME ".conf" },
     { DIRMNGR_DISP_NAME " LDAP Server List", NULL, 0,
@@ -600,6 +605,15 @@ static gc_option_t gc_options_gpg_agent[] =
  };
 #endif /*BUILD_WITH_AGENT*/
 
+static gc_option_t gc_options_tpm2daemon[] =
+  {
+   /* The configuration file to which we write the changes.  */
+   { GPGCONF_NAME"-"TPM2DAEMON_NAME".conf",
+     GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+     NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_TPM2DAEMON },
+
+    GC_OPTION_NULL,
+  };
 
 #ifndef BUILD_WITH_SCDAEMON
 #define gc_options_scdaemon NULL
@@ -1094,6 +1108,7 @@ static const struct
     { "gpg",      "gnupg", N_("OpenPGP"), gc_options_gpg },
     { "gpg-agent","gnupg", N_("Private Keys"), gc_options_gpg_agent },
     { "scdaemon", "gnupg", N_("Smartcards"), gc_options_scdaemon },
+    { "tpm2daemon", "gnupg", N_("TPM2"), gc_options_tpm2daemon },
     { "gpgsm",    "gnupg", N_("S/MIME"), gc_options_gpgsm },
     { "dirmngr",  "gnupg", N_("Network"), gc_options_dirmngr },
     { "pinentry", "gnupg", N_("Passphrase Entry"), gc_options_pinentry }
diff --git a/tpm2d/Makefile.am b/tpm2d/Makefile.am
new file mode 100644
index 000000000..85e9f4267
--- /dev/null
+++ b/tpm2d/Makefile.am
@@ -0,0 +1,18 @@
+AM_CPPFLAGS =
+
+include $(top_srcdir)/am/cmacros.am
+
+if HAVE_LIBTSS
+libexec_PROGRAMS = tpm2daemon
+
+AM_CFLAGS =  $(LIBGCRYPT_CFLAGS) \
+	     $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
+
+tpm2daemon_SOURCES =  command.c \
+	tpm2daemon.c \
+	tpm2.c tpm2.h
+
+tpm2daemon_LDADD = $(libcommonpth) \
+	$(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
+	$(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) $(LIBTSS_LIBS)
+endif
diff --git a/tpm2d/command.c b/tpm2d/command.c
new file mode 100644
index 000000000..7506125ae
--- /dev/null
+++ b/tpm2d/command.c
@@ -0,0 +1,570 @@
+/* command.c - TPM2daemon command handler
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ *               2007, 2008, 2009, 2011  Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef USE_NPTH
+# include <npth.h>
+#endif
+
+#include "tpm2daemon.h"
+#include "tpm2.h"
+#include <assuan.h>
+#include <ksba.h>
+//#include "app-common.h"
+//#include "iso7816.h"
+//#include "apdu.h" /* Required for apdu_*_reader (). */
+//#include "atr.h"
+#include "../common/asshelp.h"
+#include "../common/server-help.h"
+
+/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
+#define MAXLEN_PIN 100
+
+/* Maximum allowed size of key data as used in inquiries. */
+#define MAXLEN_KEYDATA 4096
+
+/* Maximum allowed total data size for SETDATA.  */
+#define MAXLEN_SETDATA 4096
+
+/* Maximum allowed size of certificate data as used in inquiries. */
+#define MAXLEN_CERTDATA 16384
+
+
+#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
+
+/* Data used to associate an Assuan context with local server data.
+   This object describes the local properties of one session.  */
+struct server_local_s
+{
+  /* We keep a list of all active sessions with the anchor at
+     SESSION_LIST (see below).  This field is used for linking. */
+  struct server_local_s *next_session;
+
+  /* This object is usually assigned to a CTRL object (which is
+     globally visible).  While enumerating all sessions we sometimes
+     need to access data of the CTRL object; thus we keep a
+     backpointer here. */
+  ctrl_t ctrl_backlink;
+
+  /* The Assuan context used by this session/server. */
+  assuan_context_t assuan_ctx;
+
+#ifdef HAVE_W32_SYSTEM
+  unsigned long event_signal;   /* Or 0 if not used. */
+#else
+  int event_signal;             /* Or 0 if not used. */
+#endif
+
+  /* True if the card has been removed and a reset is required to
+     continue operation. */
+  int card_removed;
+
+  /* If set to true we will be terminate ourself at the end of the
+     this session.  */
+  int stopme;
+
+};
+
+
+/* To keep track of all running sessions, we link all active server
+   contexts and the anchor in this variable.  */
+static struct server_local_s *session_list;
+
+
+static gpg_error_t
+reset_notify (assuan_context_t ctx, char *line)
+{
+  (void) ctx;
+  (void) line;
+
+  return 0;
+}
+
+
+static gpg_error_t
+option_handler (assuan_context_t ctx, const char *key, const char *value)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  if (!strcmp (key, "event-signal"))
+    {
+      /* A value of 0 is allowed to reset the event signal. */
+#ifdef HAVE_W32_SYSTEM
+      if (!*value)
+        return gpg_error (GPG_ERR_ASS_PARAMETER);
+      ctrl->server_local->event_signal = strtoul (value, NULL, 16);
+#else
+      int i = *value? atoi (value) : -1;
+      if (i < 0)
+        return gpg_error (GPG_ERR_ASS_PARAMETER);
+      ctrl->server_local->event_signal = i;
+#endif
+    }
+
+ return 0;
+}
+
+
+static gpg_error_t
+pin_cb (ctrl_t ctrl, const char *info, char **retstr)
+{
+  assuan_context_t ctx = ctrl->ctx;
+  char *command;
+  int rc;
+  unsigned char *value;
+  size_t valuelen;
+
+  *retstr = NULL;
+  log_debug ("asking for PIN '%s'\n", info);
+
+  rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
+  if (rc < 0)
+    return gpg_error (gpg_err_code_from_errno (errno));
+
+  /* Fixme: Write an inquire function which returns the result in
+     secure memory and check all further handling of the PIN. */
+  rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+  xfree (command);
+  if (rc)
+    return rc;
+
+  if (!valuelen)
+    {
+      /* We require that the returned value is an UTF-8 string */
+      xfree (value);
+      return gpg_error (GPG_ERR_INV_RESPONSE);
+    }
+  *retstr = (char*)value;
+  return 0;
+}
+
+static const char hlp_import[] =
+  "IMPORT\n"
+  "\n"
+  "This command is used to convert a public and secret key to tpm format.\n"
+  "keydata is communicated via an inquire KEYDATA command\n"
+  "The keydata is expected to be the usual canonical encoded\n"
+  "S-expression.\n"
+  "\n"
+  "A PIN will be requested.";
+static gpg_error_t
+cmd_import (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  int rc;
+  unsigned char *keydata;
+  size_t keydatalen;
+  TSS_CONTEXT *tssc;
+  gcry_sexp_t s_key;
+  unsigned char *shadow_info = NULL;
+  size_t shadow_len;
+
+  line = skip_options (line);
+
+  if (*line)
+    return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
+
+  /* Now get the actual keydata. */
+  assuan_begin_confidential (ctx);
+  rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
+  assuan_end_confidential (ctx);
+  if (rc)
+    return rc;
+
+  if ((rc = tpm2_start (&tssc)))
+    goto out;
+  gcry_sexp_new(&s_key, keydata, keydatalen, 0);
+  rc = tpm2_import_key (ctrl, tssc, pin_cb, &shadow_info, &shadow_len, s_key);
+  gcry_sexp_release(s_key);
+  tpm2_end (tssc);
+  if (rc)
+    goto out;
+
+  rc = assuan_send_data (ctx, shadow_info, shadow_len);
+
+ out:
+  xfree (shadow_info);
+  xfree (keydata);
+
+  return rc;
+}
+
+static const char hlp_pksign[] =
+  "PKSIGN\n"
+  "\n"
+  "This command is used to convert a public and secret key to tpm format.\n"
+  "keydata is communicated via an inquire KEYDATA command\n"
+  "The keydata is expected to be the usual canonical encoded\n"
+  "S-expression.\n"
+  "\n"
+  "A PIN will be requested.";
+static gpg_error_t
+cmd_pksign (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  int rc;
+  unsigned char *shadow_info;
+  size_t len;
+  TSS_CONTEXT *tssc;
+  TPM_HANDLE key;
+  TPMI_ALG_PUBLIC type;
+  unsigned char *digest;
+  size_t digestlen;
+  unsigned char *sig;
+  size_t siglen;
+
+  line = skip_options (line);
+
+  if (*line)
+    return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
+
+  /* Now get the actual keydata. */
+  rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
+  if (rc)
+    return rc;
+
+  rc = assuan_inquire (ctx, "EXTRA", &digest, &digestlen, MAXLEN_KEYDATA);
+  if (rc)
+    goto out_freeshadow;
+
+  rc = tpm2_start(&tssc);
+  if (rc)
+    goto out;
+
+  rc = tpm2_load_key(tssc, shadow_info, &key, &type);
+  if (rc)
+    goto end_out;
+
+  rc = tpm2_sign(ctrl, tssc, key, pin_cb, type, digest, digestlen,
+		 &sig, &siglen);
+
+  tpm2_flush_handle(tssc, key);
+
+ end_out:
+  tpm2_end(tssc);
+
+  if (rc)
+    goto out;
+
+  rc = assuan_send_data (ctx, sig, siglen);
+  xfree(sig);
+
+ out:
+  xfree (digest);
+ out_freeshadow:
+  xfree (shadow_info);
+
+  return rc;
+}
+
+static const char hlp_pkdecrypt[] =
+  "PKDECRYPT\n"
+  "\n"
+  "This command is used to convert a public and secret key to tpm format.\n"
+  "keydata is communicated via an inquire KEYDATA command\n"
+  "The keydata is expected to be the usual canonical encoded\n"
+  "S-expression.\n"
+  "\n"
+  "A PIN will be requested.";
+static gpg_error_t
+cmd_pkdecrypt (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  int rc;
+  unsigned char *shadow_info;
+  size_t len;
+  TSS_CONTEXT *tssc;
+  TPM_HANDLE key;
+  TPMI_ALG_PUBLIC type;
+  unsigned char *crypto;
+  size_t cryptolen;
+  char *buf;
+  size_t buflen;
+
+  line = skip_options (line);
+
+  if (*line)
+    return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
+
+  /* Now get the actual keydata. */
+  rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
+  if (rc)
+    return rc;
+
+  rc = assuan_inquire (ctx, "EXTRA", &crypto, &cryptolen, MAXLEN_KEYDATA);
+  if (rc)
+    goto out_freeshadow;
+
+  rc = tpm2_start(&tssc);
+  if (rc)
+    goto out;
+
+  rc = tpm2_load_key(tssc, shadow_info, &key, &type);
+  if (rc)
+    goto end_out;
+
+  if (type == TPM_ALG_RSA)
+    rc = tpm2_rsa_decrypt(ctrl, tssc, key, pin_cb, crypto, cryptolen, &buf, &buflen);
+  else if (type == TPM_ALG_ECC)
+    rc = tpm2_ecc_decrypt(ctrl, tssc, key, pin_cb, crypto, cryptolen, &buf, &buflen);
+
+  tpm2_flush_handle(tssc, key);
+
+ end_out:
+  tpm2_end(tssc);
+
+  if (rc)
+    goto out;
+
+  rc = assuan_send_data (ctx, buf, buflen);
+  xfree(buf);
+
+ out:
+  xfree (crypto);
+ out_freeshadow:
+  xfree (shadow_info);
+
+  return rc;
+}
+
+static const char hlp_killtpm2d[] =
+  "KILLTPM2D\n"
+  "\n"
+  "Commit suicide.";
+static gpg_error_t
+cmd_killtpm2d (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+
+  (void)line;
+
+  ctrl->server_local->stopme = 1;
+  assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
+  return 0;
+}
+
+
+
+/* Tell the assuan library about our commands */
+static int
+register_commands (assuan_context_t ctx)
+{
+  static struct {
+    const char *name;
+    assuan_handler_t handler;
+    const char * const help;
+  } table[] = {
+    { "IMPORT",       cmd_import,     hlp_import },
+    { "PKSIGN",       cmd_pksign,     hlp_pksign },
+    { "PKDECRYPT",    cmd_pkdecrypt,  hlp_pkdecrypt },
+    { "KILLTPM2D",    cmd_killtpm2d,  hlp_killtpm2d },
+    { NULL }
+  };
+  int i, rc;
+
+  for (i=0; table[i].name; i++)
+    {
+      rc = assuan_register_command (ctx, table[i].name, table[i].handler,
+                                    table[i].help);
+      if (rc)
+        return rc;
+    }
+  assuan_set_hello_line (ctx, "GNU Privacy Guard's TPM2 server ready");
+
+  assuan_register_reset_notify (ctx, reset_notify);
+  assuan_register_option_handler (ctx, option_handler);
+  return 0;
+}
+
+
+/* Startup the server.  If FD is given as -1 this is simple pipe
+   server, otherwise it is a regular server.  Returns true if there
+   are no more active asessions.  */
+int
+tpm2d_command_handler (ctrl_t ctrl, int fd)
+{
+  int rc;
+  assuan_context_t ctx = NULL;
+  int stopme;
+
+  rc = assuan_new (&ctx);
+  if (rc)
+    {
+      log_error ("failed to allocate assuan context: %s\n",
+                 gpg_strerror (rc));
+      tpm2d_exit (2);
+    }
+
+  if (fd == -1)
+    {
+      assuan_fd_t filedes[2];
+
+      filedes[0] = assuan_fdopen (0);
+      filedes[1] = assuan_fdopen (1);
+      rc = assuan_init_pipe_server (ctx, filedes);
+    }
+  else
+    {
+      rc = assuan_init_socket_server (ctx, INT2FD(fd),
+                                      ASSUAN_SOCKET_SERVER_ACCEPTED);
+    }
+  if (rc)
+    {
+      log_error ("failed to initialize the server: %s\n",
+                 gpg_strerror(rc));
+      tpm2d_exit (2);
+    }
+  rc = register_commands (ctx);
+  if (rc)
+    {
+      log_error ("failed to register commands with Assuan: %s\n",
+                 gpg_strerror(rc));
+      tpm2d_exit (2);
+    }
+  assuan_set_pointer (ctx, ctrl);
+  ctrl->ctx = ctx;
+
+  /* Allocate and initialize the server object.  Put it into the list
+     of active sessions. */
+  ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
+  ctrl->server_local->next_session = session_list;
+  session_list = ctrl->server_local;
+  ctrl->server_local->ctrl_backlink = ctrl;
+  ctrl->server_local->assuan_ctx = ctx;
+
+  /* Command processing loop. */
+  for (;;)
+    {
+      rc = assuan_accept (ctx);
+      if (rc == -1)
+        {
+          break;
+        }
+      else if (rc)
+        {
+          log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
+          break;
+        }
+
+      rc = assuan_process (ctx);
+      if (rc)
+        {
+          log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
+          continue;
+        }
+    }
+
+  /* Release the server object.  */
+  if (session_list == ctrl->server_local)
+    session_list = ctrl->server_local->next_session;
+  else
+    {
+      struct server_local_s *sl;
+
+      for (sl=session_list; sl->next_session; sl = sl->next_session)
+        if (sl->next_session == ctrl->server_local)
+          break;
+      if (!sl->next_session)
+          BUG ();
+      sl->next_session = ctrl->server_local->next_session;
+    }
+  stopme = ctrl->server_local->stopme;
+  xfree (ctrl->server_local);
+  ctrl->server_local = NULL;
+
+  /* Release the Assuan context.  */
+  assuan_release (ctx);
+
+  if (stopme)
+    tpm2d_exit (0);
+
+  /* If there are no more sessions return true.  */
+  return !session_list;
+}
+
+
+/* Send a line with status information via assuan and escape all given
+   buffers. The variable elements are pairs of (char *, size_t),
+   terminated with a (NULL, 0). */
+void
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
+{
+  va_list arg_ptr;
+  const unsigned char *value;
+  size_t valuelen;
+  char buf[950], *p;
+  size_t n;
+  assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+  va_start (arg_ptr, keyword);
+
+  p = buf;
+  n = 0;
+  while ( (value = va_arg (arg_ptr, const unsigned char *))
+           && n < DIM (buf)-2 )
+    {
+      valuelen = va_arg (arg_ptr, size_t);
+      if (!valuelen)
+        continue; /* empty buffer */
+      if (n)
+        {
+          *p++ = ' ';
+          n++;
+        }
+      for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+        {
+          if (*value == '+' || *value == '\"' || *value == '%'
+              || *value < ' ')
+            {
+              sprintf (p, "%%%02X", *value);
+              p += 3;
+              n += 2;
+            }
+          else if (*value == ' ')
+            *p++ = '+';
+          else
+            *p++ = *value;
+        }
+    }
+  *p = 0;
+  assuan_write_status (ctx, keyword, buf);
+
+  va_end (arg_ptr);
+}
+
+
+/* Send a ready formatted status line via assuan.  */
+void
+send_status_direct (ctrl_t ctrl, const char *keyword, const char *args)
+{
+  assuan_context_t ctx = ctrl->server_local->assuan_ctx;
+
+  if (strchr (args, '\n'))
+    log_error ("error: LF detected in status line - not sending\n");
+  else
+    assuan_write_status (ctx, keyword, args);
+}
diff --git a/tpm2d/tpm2.c b/tpm2d/tpm2.c
new file mode 100644
index 000000000..9a0e9de3c
--- /dev/null
+++ b/tpm2d/tpm2.c
@@ -0,0 +1,969 @@
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include "tpm2.h"
+
+#include "../common/i18n.h"
+#include "../common/sexp-parse.h"
+
+#include TSSINCLUDE(tssutils.h)
+#include TSSINCLUDE(tssresponsecode.h)
+#include TSSINCLUDE(tssmarshal.h)
+#include TSSINCLUDE(Unmarshal_fp.h)
+#include TSSINCLUDE(tsscryptoh.h)
+
+static const char *tpm2_dir;
+
+/* The TPM builds a small database of active files representing key
+ * parameters used for authentication and session encryption.  Make sure
+ * they're contained in a separate directory to avoid stepping on any
+ * other application uses of the TPM */
+static const char *
+tpm2_set_unique_tssdir(void)
+{
+  char *prefix = getenv("XDG_RUNTIME_DIR"), *template,
+    *dir;
+  int len = 0;
+
+  if (!prefix)
+    prefix = "/tmp";
+
+  len = snprintf(NULL, 0, "%s/tss2.XXXXXX", prefix);
+  if (len <= 0)
+    return NULL;
+  template = xtrymalloc(len + 1);
+  if (!template)
+    return NULL;
+
+  len++;
+  len = snprintf(template, len, "%s/tss2.XXXXXX", prefix);
+
+  dir = mkdtemp(template);
+
+  return dir;
+}
+
+static void
+tpm2_error(TPM_RC rc, char *prefix)
+{
+  const char *msg, *submsg, *num;
+
+  TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+  log_error("%s gave TPM2 Error: %s%s%s", prefix, msg, submsg, num);
+}
+
+#define _TSS_CHECK(f)           \
+  rc = f;                       \
+  if (rc != TPM_RC_SUCCESS)     \
+    {                           \
+      tpm2_error(rc, #f);       \
+      return GPG_ERR_CARD;      \
+    }
+
+int
+tpm2_start(TSS_CONTEXT **tssc)
+{
+  TPM_RC rc;
+
+  tpm2_dir = tpm2_set_unique_tssdir();
+  if (!tpm2_dir)
+    /* make this non fatal */
+    log_error("Failed to set unique TPM directory\n");
+
+  _TSS_CHECK(TSS_Create(tssc));
+  _TSS_CHECK(TSS_SetProperty(*tssc, TPM_DATA_DIR, tpm2_dir));
+  return 0;
+}
+
+void
+tpm2_end(TSS_CONTEXT *tssc)
+{
+  TSS_Delete(tssc);
+}
+
+void
+tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h)
+{
+        FlushContext_In in;
+
+        if (!h)
+                return;
+
+        in.flushHandle = h;
+        TSS_Execute(tssc, NULL,
+		    (COMMAND_PARAMETERS *)&in,
+		    NULL,
+		    TPM_CC_FlushContext,
+		    TPM_RH_NULL, NULL, 0);
+}
+
+static int
+tpm2_get_hmac_handle(TSS_CONTEXT *tssc, TPM_HANDLE *handle,
+                     TPM_HANDLE salt_key)
+{
+  TPM_RC rc;
+  StartAuthSession_In in;
+  StartAuthSession_Out out;
+  StartAuthSession_Extra extra;
+
+  memset(&in, 0, sizeof(in));
+  memset(&extra, 0 , sizeof(extra));
+  in.bind = TPM_RH_NULL;
+  in.sessionType = TPM_SE_HMAC;
+  in.authHash = TPM_ALG_SHA256;
+  in.tpmKey = TPM_RH_NULL;
+  in.symmetric.algorithm = TPM_ALG_AES;
+  in.symmetric.keyBits.aes = 128;
+  in.symmetric.mode.aes = TPM_ALG_CFB;
+  if (salt_key) {
+    ReadPublic_In rin;
+    ReadPublic_Out rout;
+
+    rin.objectHandle = salt_key;
+    rc = TSS_Execute (tssc,
+		      (RESPONSE_PARAMETERS *)&rout,
+		      (COMMAND_PARAMETERS *)&rin,
+		      NULL,
+		      TPM_CC_ReadPublic,
+		      TPM_RH_NULL, NULL, 0);
+    if (rc) {
+      tpm2_error(rc, "TPM2_ReadPublic");
+      return GPG_ERR_CARD;
+    }
+
+    /* don't care what rout returns, the purpose of the operation was
+     * to get the public key parameters into the tss so it can
+     * construct the salt */
+    in.tpmKey = salt_key;
+  }
+  rc = TSS_Execute(tssc,
+		   (RESPONSE_PARAMETERS *)&out,
+		   (COMMAND_PARAMETERS *)&in,
+		   (EXTRA_PARAMETERS *)&extra,
+		   TPM_CC_StartAuthSession,
+		   TPM_RH_NULL, NULL, 0);
+  if (rc) {
+    tpm2_error(rc, "TPM2_StartAuthSession");
+    return GPG_ERR_CARD;
+  }
+
+  *handle = out.sessionHandle;
+
+  return 0;
+}
+
+static int
+tpm2_exec_with_auth(ctrl_t ctrl, TSS_CONTEXT *tssc,
+		    gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+					  char **retstr),
+		    int cmd, char *cmd_str,
+                    void *out, void *in)
+{
+  TPM_HANDLE ah;
+  char *auth;
+  TPM_RC rc;
+
+  rc = pin_cb(ctrl, _("TPM Key Passphrase"), &auth);
+  if (rc)
+    return rc;
+
+  rc = tpm2_get_hmac_handle(tssc, &ah, 0);
+  if (rc)
+    return rc;
+
+  rc = TSS_Execute(tssc, out, in, NULL,
+		   cmd,
+		   ah, auth, 0,
+		   TPM_RH_NULL, NULL, 0);
+  gcry_free (auth);
+  if (rc)
+    {
+    tpm2_error(rc, cmd_str);
+    tpm2_flush_handle(tssc, ah);
+    switch (rc & 0xFF) {
+    case TPM_RC_BAD_AUTH:
+    case TPM_RC_AUTH_FAIL:
+      return GPG_ERR_BAD_PASSPHRASE;
+    default:
+      return GPG_ERR_CARD;
+    }
+  }
+  return 0;
+}
+
+static unsigned char *
+make_tpm2_shadow_info (uint32_t parent, const char *pub, int pub_len,
+                       const char *priv, int priv_len, size_t *len)
+{
+  gcry_sexp_t s_exp;
+  char *info;
+
+  gcry_sexp_build(&s_exp, NULL, "(%u%b%b)", parent, pub_len, pub,
+		  priv_len, priv);
+
+  *len = gcry_sexp_sprint(s_exp, GCRYSEXP_FMT_CANON, NULL, 0);
+  info = xtrymalloc(*len);
+  if (!info)
+	  goto out;
+  gcry_sexp_sprint(s_exp, GCRYSEXP_FMT_CANON, info, *len);
+
+ out:
+  gcry_sexp_release(s_exp);
+  return (unsigned char *)info;
+}
+
+static gpg_error_t
+parse_tpm2_shadow_info (const unsigned char *shadow_info,
+                        uint32_t *parent,
+                        const char **pub, int *pub_len,
+                        const char **priv, int *priv_len)
+{
+  const unsigned char *s;
+  size_t n;
+  int i;
+
+  s = shadow_info;
+  if (*s != '(')
+    return gpg_error (GPG_ERR_INV_SEXP);
+  s++;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  *parent = 0;
+  for (i = 0; i < n; i++) {
+    *parent *= 10;
+    *parent += atoi_1(s+i);
+  }
+
+  s += n;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+
+  *pub_len = n;
+  *pub = s;
+
+  s += n;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+
+  *priv_len = n;
+  *priv = s;
+
+  return 0;
+}
+
+int
+tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
+              TPM_HANDLE *key, TPMI_ALG_PUBLIC *type)
+{
+  uint32_t parent;
+  Load_In in;
+  Load_Out out;
+  const char *pub, *priv;
+  int ret, pub_len, priv_len;
+  TPM_RC rc;
+  BYTE *buf;
+  uint32_t size;
+
+  ret = parse_tpm2_shadow_info (shadow_info, &parent, &pub, &pub_len,
+                                &priv, &priv_len);
+  if (ret)
+    return ret;
+
+  in.parentHandle = parent;
+
+  buf = (BYTE *)priv;
+  size = priv_len;
+  TPM2B_PRIVATE_Unmarshal(&in.inPrivate, &buf, &size);
+
+  buf = (BYTE *)pub;
+  size = pub_len;
+  TPM2B_PUBLIC_Unmarshal(&in.inPublic, &buf, &size, FALSE);
+
+  *type = in.inPublic.publicArea.type;
+
+  rc = TSS_Execute(tssc,
+		   (RESPONSE_PARAMETERS *)&out,
+		   (COMMAND_PARAMETERS *)&in,
+		   NULL,
+		   TPM_CC_Load,
+		   TPM_RS_PW, NULL, 0,
+		   TPM_RH_NULL, NULL, 0);
+  if (rc != TPM_RC_SUCCESS) {
+    tpm2_error(rc, "TPM2_Load");
+    return GPG_ERR_CARD;
+  }
+
+  *key = out.objectHandle;
+
+  return 0;
+}
+
+int
+tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+	  gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+				char **retstr),
+	  TPMI_ALG_PUBLIC type,
+	  const unsigned char *digest, size_t digestlen,
+          unsigned char **r_sig, size_t *r_siglen)
+{
+  Sign_In in;
+  Sign_Out out;
+  int ret;
+
+  /* The TPM insists on knowing the digest type, so
+   * calculate that from the size */
+  switch (digestlen) {
+  case 20:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
+    break;
+  case 32:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA256;
+    break;
+  case 48:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA384;
+    break;
+#ifdef TPM_ALG_SHA512
+  case 64:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA512;
+    break;
+#endif
+  default:
+    log_error("Unknown signature digest length, cannot deduce hash type for TPM\n");
+    return GPG_ERR_NO_SIGNATURE_SCHEME;
+  }
+  in.digest.t.size = digestlen;
+  memcpy(in.digest.t.buffer, digest, digestlen);
+  in.keyHandle = key;
+  in.validation.tag = TPM_ST_HASHCHECK;
+  in.validation.hierarchy = TPM_RH_NULL;
+  in.validation.digest.t.size = 0;
+
+  if (type == TPM_ALG_RSA)
+    in.inScheme.scheme = TPM_ALG_RSASSA;
+  else if (type == TPM_ALG_ECC)
+    in.inScheme.scheme = TPM_ALG_ECDSA;
+  else
+    return GPG_ERR_PUBKEY_ALGO;
+
+
+  ret = tpm2_exec_with_auth(ctrl, tssc, pin_cb,
+			    TPM_CC_Sign, "TPM2_Sign", &out, &in);
+  if (ret)
+    return ret;
+
+  if (type == TPM_ALG_RSA)
+    *r_siglen = out.signature.signature.rsassa.sig.t.size;
+  else if (type == TPM_ALG_ECC)
+    *r_siglen = out.signature.signature.ecdsa.signatureR.t.size
+      + out.signature.signature.ecdsa.signatureS.t.size;
+
+  *r_sig = xtrymalloc(*r_siglen);
+  if (!r_sig)
+    return GPG_ERR_ENOMEM;
+
+  if (type == TPM_ALG_RSA)
+    {
+      memcpy(*r_sig, out.signature.signature.rsassa.sig.t.buffer, *r_siglen);
+    }
+  else if (type == TPM_ALG_ECC)
+    {
+      memcpy(*r_sig, out.signature.signature.ecdsa.signatureR.t.buffer,
+	     out.signature.signature.ecdsa.signatureR.t.size);
+      memcpy(*r_sig + out.signature.signature.ecdsa.signatureR.t.size,
+	     out.signature.signature.ecdsa.signatureS.t.buffer,
+	     out.signature.signature.ecdsa.signatureS.t.size);
+    }
+
+  return 0;
+}
+
+static int
+sexp_to_tpm2_sensitive_ecc(TPMT_SENSITIVE *s, gcry_sexp_t key)
+{
+  gcry_mpi_t d;
+  gcry_sexp_t l;
+  int rc = -1;
+  size_t len;
+
+  s->sensitiveType = TPM_ALG_ECC;
+  s->seedValue.b.size = 0;
+
+  l = gcry_sexp_find_token (key, "d", 0);
+  if (!l)
+    return rc;
+  d = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof(s->sensitive.ecc.t.buffer);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, s->sensitive.ecc.t.buffer, len, &len, d);
+  s->sensitive.ecc.t.size = len;
+  gcry_mpi_release (d);
+
+  return rc;
+}
+
+/* try to match the libgcrypt curve names to known TPM parameters.
+ *
+ * As of 2018 the TCG defined curves are only NIST
+ * (192,224,256,384,521) Barreto-Naehring (256,638) and the Chinese
+ * SM2 (256), which means only the NIST ones overlap with libgcrypt */
+static struct {
+  const char *name;
+  TPMI_ECC_CURVE c;
+} tpm2_curves[] = {
+  { "NIST P-192", TPM_ECC_NIST_P192 },
+  { "prime192v1", TPM_ECC_NIST_P192 },
+  { "secp192r1", TPM_ECC_NIST_P192 },
+  { "nistp192", TPM_ECC_NIST_P192 },
+  { "NIST P-224", TPM_ECC_NIST_P224 },
+  { "secp224r1", TPM_ECC_NIST_P224 },
+  { "nistp224", TPM_ECC_NIST_P224 },
+  { "NIST P-256", TPM_ECC_NIST_P256 },
+  { "prime256v1", TPM_ECC_NIST_P256 },
+  { "secp256r1", TPM_ECC_NIST_P256 },
+  { "nistp256", TPM_ECC_NIST_P256 },
+  { "NIST P-384", TPM_ECC_NIST_P384 },
+  { "secp384r1", TPM_ECC_NIST_P384 },
+  { "nistp384", TPM_ECC_NIST_P384 },
+  { "NIST P-521", TPM_ECC_NIST_P521 },
+  { "secp521r1", TPM_ECC_NIST_P521 },
+  { "nistp521", TPM_ECC_NIST_P521 },
+};
+
+static int
+tpm2_ecc_curve (const char *curve_name, TPMI_ECC_CURVE *c)
+{
+  int i;
+
+  for (i = 0; i < DIM (tpm2_curves); i++)
+    if (strcmp (tpm2_curves[i].name, curve_name) == 0)
+      break;
+  if (i == DIM (tpm2_curves)) {
+    log_error ("curve %s does not match any available TPM curves\n", curve_name);
+    return GPG_ERR_UNKNOWN_CURVE;
+  }
+
+  *c = tpm2_curves[i].c;
+
+  return 0;
+}
+
+static int
+sexp_to_tpm2_public_ecc(TPMT_PUBLIC *p, gcry_sexp_t key)
+{
+  const char *q;
+  gcry_sexp_t l;
+  int rc = GPG_ERR_BAD_PUBKEY;
+  size_t len;
+  TPMI_ECC_CURVE curve;
+  char *curve_name;
+
+  l = gcry_sexp_find_token (key, "curve", 0);
+  if (!l)
+    return rc;
+  curve_name = gcry_sexp_nth_string (l, 1);
+  if (!curve_name)
+    goto out;
+  rc = tpm2_ecc_curve (curve_name, &curve);
+  gcry_free (curve_name);
+  if (rc)
+    goto out;
+  gcry_sexp_release(l);
+
+  l = gcry_sexp_find_token (key, "q", 0);
+  if (!l)
+    return rc;
+  q = gcry_sexp_nth_data (l, 1, &len);
+  /* This is a point representation, the first byte tells you what
+   * type.  The only format we understand is uncompressed (0x04)
+   * which has layout 0x04 | x | y */
+  if (q[0] != 0x04)
+    {
+      log_error ("Point format for q is not uncompressed\n");
+      goto out;
+    }
+  q++;
+  len--;
+  /* now should have to equal sized big endian point numbers */
+  if ((len & 0x01) == 1)
+    {
+      log_error ("Point format for q has incorrect length\n");
+      goto out;
+    }
+
+  len >>= 1;
+
+  p->type = TPM_ALG_ECC;
+  p->nameAlg = TPM_ALG_SHA256;
+  p->objectAttributes.val = TPMA_OBJECT_NODA |
+    TPMA_OBJECT_SIGN |
+    TPMA_OBJECT_DECRYPT |
+    TPMA_OBJECT_USERWITHAUTH;
+  p->authPolicy.t.size = 0;
+  p->parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
+  p->parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+  p->parameters.eccDetail.curveID = curve;
+  p->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+  memcpy(p->unique.ecc.x.t.buffer, q, len);
+  p->unique.ecc.x.t.size = len;
+  memcpy(p->unique.ecc.y.t.buffer, q + len, len);
+  p->unique.ecc.y.t.size = len;
+ out:
+  gcry_sexp_release (l);
+  return rc;
+}
+
+static int
+sexp_to_tpm2_sensitive_rsa(TPMT_SENSITIVE *s, gcry_sexp_t key)
+{
+  gcry_mpi_t p;
+  gcry_sexp_t l;
+  int rc = -1;
+  size_t len;
+
+  s->sensitiveType = TPM_ALG_RSA;
+  s->seedValue.b.size = 0;
+
+  l = gcry_sexp_find_token (key, "p", 0);
+  if (!l)
+    return rc;
+  p = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof(s->sensitive.rsa.t.buffer);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, s->sensitive.rsa.t.buffer, len, &len, p);
+  s->sensitive.rsa.t.size = len;
+  gcry_mpi_release (p);
+
+  return rc;
+}
+
+static int
+sexp_to_tpm2_public_rsa(TPMT_PUBLIC *p, gcry_sexp_t key)
+{
+  gcry_mpi_t n, e;
+  gcry_sexp_t l;
+  int rc = -1, i;
+  size_t len;
+  /* longer than an int */
+  unsigned char ebuf[5];
+  uint32_t exp = 0;
+
+  p->type = TPM_ALG_RSA;
+  p->nameAlg = TPM_ALG_SHA256;
+  /* note: all our keys are decrypt only.  This is because
+   * we use the TPM2_RSA_Decrypt operation for both signing
+   * and decryption (see e_tpm2.c for details) */
+  p->objectAttributes.val = TPMA_OBJECT_NODA |
+    TPMA_OBJECT_DECRYPT |
+    TPMA_OBJECT_SIGN |
+    TPMA_OBJECT_USERWITHAUTH;
+  p->authPolicy.t.size = 0;
+  p->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
+  p->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+
+  l = gcry_sexp_find_token (key, "n", 0);
+  if (!l)
+    return rc;
+  n = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof(p->unique.rsa.t.buffer);
+  p->parameters.rsaDetail.keyBits = gcry_mpi_get_nbits (n);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, p->unique.rsa.t.buffer, len, &len, n);
+  p->unique.rsa.t.size = len;
+  gcry_mpi_release (n);
+  if (rc)
+    return rc;
+  rc = -1;
+  l = gcry_sexp_find_token (key, "e", 0);
+  if (!l)
+    return rc;
+  e = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof (ebuf);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, ebuf, len, &len, e);
+  gcry_mpi_release (e);
+  if (rc)
+    return rc;
+  if (len > 4)
+    return -1;
+
+  /* MPI are simply big endian integers, so convert to uint32 */
+  for (i = 0; i < len; i++) {
+    exp <<= 8;
+    exp += ebuf[i];
+  }
+  if (exp == 0x10001)
+    p->parameters.rsaDetail.exponent = 0;
+  else
+    p->parameters.rsaDetail.exponent = exp;
+  return 0;
+}
+
+static int
+sexp_to_tpm2(TPMT_PUBLIC *p, TPMT_SENSITIVE *s, gcry_sexp_t s_skey)
+{
+  gcry_sexp_t l1, l2;
+  int rc = -1;
+
+  /* find the value of (private-key */
+  l1 = gcry_sexp_nth (s_skey, 1);
+  if (!l1)
+    return rc;
+
+  l2 = gcry_sexp_find_token (l1, "rsa", 0);
+  if (l2) {
+    rc = sexp_to_tpm2_public_rsa (p, l2);
+    if (!rc)
+      rc = sexp_to_tpm2_sensitive_rsa (s, l2);
+  } else {
+    l2 = gcry_sexp_find_token (l1, "ecc", 0);
+    if (!l2)
+      goto out;
+    rc = sexp_to_tpm2_public_ecc (p, l2);
+    if (!rc)
+      rc = sexp_to_tpm2_sensitive_ecc (s, l2);
+  }
+
+  gcry_sexp_release(l2);
+
+ out:
+  gcry_sexp_release(l1);
+  return rc;
+}
+
+/* copied from TPM implementation code */
+static TPM_RC
+tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
+                          TPMT_PUBLIC *tpmtPublic)
+{
+  TPM_RC rc = 0;
+  uint16_t written = 0;
+  TPMT_HA digest;
+  uint32_t sizeInBytes;
+  uint8_t buffer[MAX_RESPONSE_SIZE];
+
+  /* marshal the TPMT_PUBLIC */
+  if (rc == 0) {
+    INT32 size = MAX_RESPONSE_SIZE;
+    uint8_t *buffer1 = buffer;
+    rc = TSS_TPMT_PUBLIC_Marshal(tpmtPublic, &written, &buffer1, &size);
+  }
+  /* hash the public area */
+  if (rc == 0) {
+    sizeInBytes = TSS_GetDigestSize(tpmtPublic->nameAlg);
+    digest.hashAlg = tpmtPublic->nameAlg;       /* Name digest algorithm */
+    /* generate the TPMT_HA */
+    rc = TSS_Hash_Generate(&digest,
+			   written, buffer,
+			   0, NULL);
+  }
+  if (rc == 0) {
+    /* copy the digest */
+    memcpy(name->t.name + sizeof(TPMI_ALG_HASH), (uint8_t *)&digest.digest, sizeInBytes);
+    /* copy the hash algorithm */
+    TPMI_ALG_HASH nameAlgNbo = htons(tpmtPublic->nameAlg);
+    memcpy(name->t.name, (uint8_t *)&nameAlgNbo, sizeof(TPMI_ALG_HASH));
+    /* set the size */
+    name->t.size = sizeInBytes + sizeof(TPMI_ALG_HASH);
+  }
+  return rc;
+}
+
+/*
+ * Cut down version of Part 4 Supporting Routines 7.6.3.10
+ *
+ * Hard coded to symmetrically encrypt with aes128 as the inner
+ * wrapper and no outer wrapper but with a prototype that allows
+ * drop in replacement with a tss equivalent
+ */
+TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
+                                 TPM2B_NAME *name,
+                                 TPM_ALG_ID nalg,
+                                 TPMT_SYM_DEF_OBJECT *symdef,
+                                 TPM2B_DATA *innerkey,
+                                 TPM2B_PRIVATE *p)
+{
+  BYTE *buf = p->t.buffer;
+
+  p->t.size = 0;
+  memset(p, 0, sizeof(*p));
+
+  /* hard code AES CFB */
+  if (symdef->algorithm == TPM_ALG_AES
+      && symdef->mode.aes == TPM_ALG_CFB) {
+    TPMT_HA hash;
+    const int hlen = TSS_GetDigestSize(nalg);
+    TPM2B *digest = (TPM2B *)buf;
+    TPM2B *s2b;
+    int32_t size;
+    unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
+    UINT16 bsize, written = 0;
+    gcry_cipher_hd_t hd;
+
+    /* WARNING: don't use the static null_iv trick here:
+     * the AES routines alter the passed in iv */
+    memset(null_iv, 0, sizeof(null_iv));
+
+    /* reserve space for hash before the encrypted sensitive */
+    bsize = sizeof(digest->size) + hlen;
+    buf += bsize;
+    p->t.size += bsize;
+    s2b = (TPM2B *)buf;
+
+    /* marshal the digest size */
+    buf = (BYTE *)&digest->size;
+    bsize = hlen;
+    size = 2;
+    TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+    /* marshal the unencrypted sensitive in place */
+    size = sizeof(*s);
+    bsize = 0;
+    buf = s2b->buffer;
+    TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+    buf = (BYTE *)&s2b->size;
+    size = 2;
+    TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+    bsize = bsize + sizeof(s2b->size);
+    p->t.size += bsize;
+
+    /* compute hash of unencrypted marshalled sensitive and
+     * write to the digest buffer */
+    hash.hashAlg = nalg;
+    TSS_Hash_Generate(&hash, bsize, s2b,
+		      name->t.size, name->t.name,
+		      0, NULL);
+    memcpy(digest->buffer, &hash.digest, hlen);
+    gcry_cipher_open (&hd, GCRY_CIPHER_AES128,
+		      GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
+    gcry_cipher_setiv(hd, null_iv, sizeof(null_iv));
+    gcry_cipher_setkey(hd, innerkey->b.buffer, innerkey->b.size);
+    /* encrypt the hash and sensitive in-place */
+    gcry_cipher_encrypt(hd, p->t.buffer, p->t.size, NULL, 0);
+    gcry_cipher_close(hd);
+
+  } else if (symdef->algorithm == TPM_ALG_NULL) {
+    TPM2B *s2b = (TPM2B *)buf;
+    int32_t size = sizeof(*s);
+    UINT16 bsize = 0, written = 0;
+
+    buf = s2b->buffer;
+
+    /* marshal the unencrypted sensitive in place */
+    TSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+    buf = (BYTE *)&s2b->size;
+    size = 2;
+    TSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+    p->b.size += bsize + sizeof(s2b->size);
+  } else {
+    log_error ("Unknown symmetric algorithm\n");
+    return TPM_RC_SYMMETRIC;
+  }
+
+  return TPM_RC_SUCCESS;
+}
+
+static void
+tpm2_encrypt_duplicate(Import_In *iin, TPMT_SENSITIVE *s)
+{
+  TPM2B_NAME name;
+  TPMT_PUBLIC *p = &iin->objectPublic.publicArea;
+  const int aes_key_bits = 128;
+  const int aes_key_bytes = aes_key_bits/8;
+
+  tpm2_ObjectPublic_GetName(&name, p);
+  gcry_randomize(iin->encryptionKey.t.buffer,
+                 aes_key_bytes, GCRY_STRONG_RANDOM);
+  iin->encryptionKey.t.size = aes_key_bytes;
+
+  /* set random iin.symSeed */
+  iin->inSymSeed.t.size = 0;
+  iin->symmetricAlg.algorithm = TPM_ALG_AES;
+  iin->symmetricAlg.keyBits.aes = aes_key_bits;
+  iin->symmetricAlg.mode.aes = TPM_ALG_CFB;
+
+  tpm2_SensitiveToDuplicate(s, &name, p->nameAlg, &iin->symmetricAlg,
+                            &iin->encryptionKey, &iin->duplicate);
+}
+
+int
+tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc,
+		gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+				      char **retstr),
+		unsigned char **shadow_info, size_t *shadow_len,
+		gcry_sexp_t s_skey)
+{
+  Import_In iin;
+  Import_Out iout;
+  TPMT_SENSITIVE s;
+  TPM_HANDLE ah;
+  TPM_RC rc;
+
+  uint32_t size;
+  uint16_t len;
+  BYTE *buffer;
+  int ret;
+  char *passphrase;
+
+  char pub[sizeof(TPM2B_PUBLIC)];
+  int pub_len;
+  char priv[sizeof(TPM2B_PRIVATE)];
+  int priv_len;
+
+  iin.parentHandle = TPM2_PARENT;
+  ret = sexp_to_tpm2(&iin.objectPublic.publicArea, &s, s_skey);
+  if (ret) {
+    log_error("Failed to parse Key s-expression: key corrupt?\n");
+    return ret;
+  }
+
+  /* add an authorization password to the key which the TPM will check */
+
+  ret = pin_cb (ctrl,  _("Please enter the TPM Authorization passphrase for the key."), &passphrase);
+  if (ret)
+    return ret;
+  s.authValue.b.size = strlen(passphrase);
+  memcpy(s.authValue.b.buffer, passphrase, s.authValue.b.size);
+
+  /* We're responsible for securing the data in transmission to the
+   * TPM here.  The TPM provides parameter encryption via a session,
+   * but only for the first parameter.  For TPM2_Import, the first
+   * parameter is a symmetric key used to encrypt the sensitive data,
+   * so we must populate this key with random value and encrypt the
+   * sensitive data with it */
+  tpm2_encrypt_duplicate(&iin, &s);
+
+  /* use salted parameter encryption to hide the key.  First we read
+   * the public parameters of the parent key and use them to agree an
+   * encryption for the first parameter */
+  rc = tpm2_get_hmac_handle(tssc, &ah, iin.parentHandle);
+  if (rc)
+    return GPG_ERR_CARD;
+
+  rc = TSS_Execute(tssc,
+		   (RESPONSE_PARAMETERS *)&iout,
+		   (COMMAND_PARAMETERS *)&iin,
+		   NULL,
+		   TPM_CC_Import,
+		   ah, NULL, TPMA_SESSION_DECRYPT,
+		   TPM_RH_NULL, NULL, 0);
+  if (rc) {
+    tpm2_error(rc, "TPM2_Import");
+    /* failure means auth handle is not flushed */
+    tpm2_flush_handle(tssc, ah);
+    return GPG_ERR_CARD;
+  }
+
+  size = sizeof(pub);
+  buffer = pub;
+  len = 0;
+  TSS_TPM2B_PUBLIC_Marshal(&iin.objectPublic,
+                            &len, &buffer, &size);
+  pub_len = len;
+
+  size = sizeof(priv);
+  buffer = priv;
+  len = 0;
+  TSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate,
+			    &len, &buffer, &size);
+  priv_len = len;
+
+  *shadow_info = make_tpm2_shadow_info(iin.parentHandle, pub, pub_len,
+				       priv, priv_len, shadow_len);
+  return rc;
+}
+
+int
+tpm2_ecc_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		 gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+				       char **retstr),
+		 const char *ciphertext, int ciphertext_len,
+		 char **decrypt, size_t *decrypt_len)
+{
+  ECDH_ZGen_In in;
+  ECDH_ZGen_Out out;
+  size_t len;
+  int ret;
+
+  /* This isn't really a decryption per se.  The ciphertext actually
+   * contains an EC Point which we must multiply by the private key number.
+   *
+   * The reason is to generate a diffe helman agreement on a shared
+   * point.  This shared point is then used to generate the per
+   * session encryption key.
+   */
+  if (ciphertext[0] != 0x04)
+    {
+      log_error ("Decryption Shared Point format is not uncompressed\n");
+      return GPG_ERR_ENCODING_PROBLEM;
+    }
+  if ((ciphertext_len & 0x01) != 1)
+    {
+      log_error ("Decryption Shared Point has incorrect length\n");
+      return GPG_ERR_ENCODING_PROBLEM;
+    }
+  len = ciphertext_len >> 1;
+
+  in.keyHandle = key;
+  memcpy(in.inPoint.point.x.t.buffer, ciphertext + 1, len);
+  in.inPoint.point.x.t.size = len;
+  memcpy(in.inPoint.point.y.t.buffer, ciphertext + 1 + len, len);
+  in.inPoint.point.y.t.size = len;
+
+  ret = tpm2_exec_with_auth(ctrl, tssc, pin_cb, TPM_CC_ECDH_ZGen,
+			    "TPM2_ECDH_ZGen", &out, &in);
+  if (ret)
+    return ret;
+
+  *decrypt_len = out.outPoint.point.x.t.size + out.outPoint.point.y.t.size + 1;
+  *decrypt = xtrymalloc(*decrypt_len);
+  (*decrypt)[0] = 0x04;
+  memcpy(*decrypt + 1, out.outPoint.point.x.t.buffer,
+	 out.outPoint.point.x.t.size);
+  memcpy(*decrypt + 1 + out.outPoint.point.x.t.size,
+	 out.outPoint.point.y.t.buffer,
+	 out.outPoint.point.y.t.size);
+
+  return 0;
+}
+
+int
+tpm2_rsa_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		 gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+				       char **retstr),
+		 const char *ciphertext, int ciphertext_len,
+		 char **decrypt, size_t *decrypt_len)
+{
+  RSA_Decrypt_In in;
+  RSA_Decrypt_Out out;
+  int ret;
+
+  in.keyHandle = key;
+  in.inScheme.scheme = TPM_ALG_RSAES;
+  in.cipherText.t.size = ciphertext_len;
+  memcpy (in.cipherText.t.buffer, ciphertext, ciphertext_len);
+  in.label.t.size = 0;
+
+  ret = tpm2_exec_with_auth(ctrl, tssc, pin_cb, TPM_CC_RSA_Decrypt,
+			    "TPM2_RSA_Decrypt", &out, &in);
+  if (ret)
+    return ret;
+
+  *decrypt_len = out.message.t.size;
+  *decrypt = xtrymalloc(out.message.t.size);
+  memcpy (*decrypt, out.message.t.buffer, out.message.t.size);
+
+  return 0;
+}
diff --git a/tpm2d/tpm2.h b/tpm2d/tpm2.h
new file mode 100644
index 000000000..38ee8a81c
--- /dev/null
+++ b/tpm2d/tpm2.h
@@ -0,0 +1,39 @@
+#ifndef _TPM2_H
+#define _TPM2_H
+
+#include "../common/util.h"
+
+#define TSSINCLUDE(x) < TSS_INCLUDE/x >
+#include TSSINCLUDE(tss.h)
+
+#define TSS2_LIB "libtss.so.0"
+#define TPM2_PARENT 0x81000001
+
+int tpm2_start(TSS_CONTEXT **tssc);
+void tpm2_end(TSS_CONTEXT *tssc);
+void tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h);
+int tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
+                  TPM_HANDLE *key, TPMI_ALG_PUBLIC *type);
+int tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+	      gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+				    char **retstr),
+	      TPMI_ALG_PUBLIC type,
+              const unsigned char *digest, size_t digestlen,
+              unsigned char **r_sig, size_t *r_siglen);
+int tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc,
+		    gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+					 char **retstr),
+		    unsigned char **shadow_info, size_t *shadow_len,
+		    gcry_sexp_t s_skey);
+int tpm2_rsa_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		     gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+					   char **retstr),
+		     const char *ciphertext, int ciphertext_len,
+		     char **decrypt, size_t *decrypt_len);
+int tpm2_ecc_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		     gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+					 char **retstr),
+		     const char *ciphertext, int ciphertext_len,
+		     char **decrypt, size_t *decrypt_len);
+
+#endif
diff --git a/tpm2d/tpm2daemon.c b/tpm2d/tpm2daemon.c
new file mode 100644
index 000000000..6761aa9ef
--- /dev/null
+++ b/tpm2d/tpm2daemon.c
@@ -0,0 +1,1434 @@
+/* tpm2daemon.c  -  The GnuPG tpm2 Daemon
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2009 Free Software Foundation, Inc.
+ * Copyright (C) 2001-2002, 2004-2005, 2007-2014 Werner Koch
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <time.h>
+#include <fcntl.h>
+#ifndef HAVE_W32_SYSTEM
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif /*HAVE_W32_SYSTEM*/
+#include <unistd.h>
+#include <signal.h>
+#include <npth.h>
+
+#define GNUPG_COMMON_NEED_AFLOCAL
+#include "tpm2daemon.h"
+#include <gcrypt.h>
+
+#include <assuan.h> /* malloc hooks */
+
+#include "../common/i18n.h"
+#include "../common/sysutils.h"
+#include "../common/gc-opt-flags.h"
+#include "../common/asshelp.h"
+#include "../common/exechelp.h"
+#include "../common/init.h"
+
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+
+enum cmd_and_opt_values
+{ aNull = 0,
+  oCsh            = 'c',
+  oQuiet          = 'q',
+  oSh             = 's',
+  oVerbose        = 'v',
+
+  oNoVerbose = 500,
+  aGPGConfList,
+  aGPGConfTest,
+  oOptions,
+  oDebug,
+  oDebugAll,
+  oDebugLevel,
+  oDebugWait,
+  oDebugAllowCoreDump,
+  oDebugCCIDDriver,
+  oDebugLogTid,
+  oDebugAssuanLogCats,
+  oNoGreeting,
+  oNoOptions,
+  oHomedir,
+  oNoDetach,
+  oNoGrab,
+  oLogFile,
+  oServer,
+  oMultiServer,
+  oDaemon,
+  oBatch,
+  oReaderPort,
+  oCardTimeout,
+  octapiDriver,
+  opcscDriver,
+  oDisableCCID,
+  oDisableOpenSC,
+  oDisablePinpad,
+  oAllowAdmin,
+  oDenyAdmin,
+  oDisableApplication,
+  oEnablePinpadVarlen,
+  oListenBacklog
+};
+
+
+
+static ARGPARSE_OPTS opts[] = {
+  ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
+  ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@"),
+
+  ARGPARSE_group (301, N_("@Options:\n ")),
+
+  ARGPARSE_s_n (oServer,"server", N_("run in server mode (foreground)")),
+  ARGPARSE_s_n (oMultiServer, "multi-server",
+                N_("run in multi server mode (foreground)")),
+  ARGPARSE_s_n (oDaemon, "daemon", N_("run in daemon mode (background)")),
+  ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
+  ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
+  ARGPARSE_s_n (oSh,    "sh", N_("sh-style command output")),
+  ARGPARSE_s_n (oCsh,   "csh", N_("csh-style command output")),
+  ARGPARSE_s_s (oOptions, "options", N_("|FILE|read options from FILE")),
+  ARGPARSE_s_s (oDebug, "debug", "@"),
+  ARGPARSE_s_n (oDebugAll, "debug-all", "@"),
+  ARGPARSE_s_s (oDebugLevel, "debug-level" ,
+                N_("|LEVEL|set the debugging level to LEVEL")),
+  ARGPARSE_s_i (oDebugWait, "debug-wait", "@"),
+  ARGPARSE_s_n (oDebugAllowCoreDump, "debug-allow-core-dump", "@"),
+  ARGPARSE_s_n (oDebugCCIDDriver, "debug-ccid-driver", "@"),
+  ARGPARSE_s_n (oDebugLogTid, "debug-log-tid", "@"),
+  ARGPARSE_p_u (oDebugAssuanLogCats, "debug-assuan-log-cats", "@"),
+  ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
+  ARGPARSE_s_s (oLogFile,  "log-file", N_("|FILE|write a log to FILE")),
+  ARGPARSE_s_s (oReaderPort, "reader-port",
+                N_("|N|connect to reader at port N")),
+  ARGPARSE_s_s (octapiDriver, "ctapi-driver",
+                N_("|NAME|use NAME as ct-API driver")),
+  ARGPARSE_s_s (opcscDriver, "pcsc-driver",
+                N_("|NAME|use NAME as PC/SC driver")),
+  ARGPARSE_s_n (oDisableCCID, "disable-ccid",
+#ifdef HAVE_LIBUSB
+                                    N_("do not use the internal CCID driver")
+#else
+                                    "@"
+#endif
+                /* end --disable-ccid */),
+  ARGPARSE_s_u (oCardTimeout, "card-timeout",
+                N_("|N|disconnect the card after N seconds of inactivity")),
+
+  ARGPARSE_s_n (oDisablePinpad, "disable-pinpad",
+                N_("do not use a reader's pinpad")),
+  ARGPARSE_ignore (300, "disable-keypad"),
+
+  ARGPARSE_s_n (oAllowAdmin, "allow-admin", "@"),
+  ARGPARSE_s_n (oDenyAdmin, "deny-admin",
+                N_("deny the use of admin card commands")),
+  ARGPARSE_s_s (oDisableApplication, "disable-application", "@"),
+  ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen",
+                N_("use variable length input for pinpad")),
+  ARGPARSE_s_s (oHomedir,    "homedir",      "@"),
+  ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"),
+
+  ARGPARSE_end ()
+};
+
+
+/* The list of supported debug flags.  */
+static struct debug_flags_s debug_flags [] =
+  {
+    { DBG_MPI_VALUE    , "mpi"     },
+    { DBG_CRYPTO_VALUE , "crypto"  },
+    { DBG_MEMORY_VALUE , "memory"  },
+    { DBG_CACHE_VALUE  , "cache"   },
+    { DBG_MEMSTAT_VALUE, "memstat" },
+    { DBG_HASHING_VALUE, "hashing" },
+    { DBG_IPC_VALUE    , "ipc"     },
+    { DBG_CARD_IO_VALUE, "cardio"  },
+    { DBG_READER_VALUE , "reader"  },
+    { 0, NULL }
+  };
+
+
+/* The card driver we use by default for PC/SC.  */
+#if defined(HAVE_W32_SYSTEM) || defined(__CYGWIN__)
+#define DEFAULT_PCSC_DRIVER "winscard.dll"
+#elif defined(__APPLE__)
+#define DEFAULT_PCSC_DRIVER "/System/Library/Frameworks/PCSC.framework/PCSC"
+#elif defined(__GLIBC__)
+#define DEFAULT_PCSC_DRIVER "libpcsclite.so.1"
+#else
+#define DEFAULT_PCSC_DRIVER "libpcsclite.so"
+#endif
+
+/* The timer tick used to check card removal.
+
+   We poll every 500ms to let the user immediately know a status
+   change.
+
+   For a card reader with an interrupt endpoint, this timer is not
+   used with the internal CCID driver.
+
+   This is not too good for power saving but given that there is no
+   easy way to block on card status changes it is the best we can do.
+   For PC/SC we could in theory use an extra thread to wait for status
+   changes but that requires a native thread because there is no way
+   to make the underlying PC/SC card change function block using a Npth
+   mechanism.  Given that a native thread could only be used under W32
+   we don't do that at all.  */
+#define TIMERTICK_INTERVAL_SEC     (0)
+#define TIMERTICK_INTERVAL_USEC    (500000)
+
+/* Flag to indicate that a shutdown was requested. */
+static int shutdown_pending;
+
+/* It is possible that we are currently running under setuid permissions */
+static int maybe_setuid = 1;
+
+/* Flag telling whether we are running as a pipe server.  */
+static int pipe_server;
+
+/* Name of the communication socket */
+static char *socket_name;
+/* Name of the redirected socket or NULL.  */
+static char *redir_socket_name;
+
+/* We need to keep track of the server's nonces (these are dummies for
+   POSIX systems). */
+static assuan_sock_nonce_t socket_nonce;
+
+/* Value for the listen() backlog argument.  Change at runtime with
+ * --listen-backlog.  */
+static int listen_backlog = 64;
+
+#ifdef HAVE_W32_SYSTEM
+static HANDLE the_event;
+#else
+/* PID to notify update of usb devices.  */
+static pid_t main_thread_pid;
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+/* FD to notify changes.  */
+static int notify_fd;
+#endif
+
+static char *create_socket_name (char *standard_name);
+static gnupg_fd_t create_server_socket (const char *name,
+                                        char **r_redir_name,
+                                        assuan_sock_nonce_t *nonce);
+
+static void *start_connection_thread (void *arg);
+static void handle_connections (int listen_fd);
+
+/* Pth wrapper function definitions. */
+ASSUAN_SYSTEM_NPTH_IMPL;
+
+static int active_connections;
+
+
+static char *
+make_libversion (const char *libname, const char *(*getfnc)(const char*))
+{
+  const char *s;
+  char *result;
+
+  if (maybe_setuid)
+    {
+      gcry_control (GCRYCTL_INIT_SECMEM, 0, 0);  /* Drop setuid. */
+      maybe_setuid = 0;
+    }
+  s = getfnc (NULL);
+  result = xmalloc (strlen (libname) + 1 + strlen (s) + 1);
+  strcpy (stpcpy (stpcpy (result, libname), " "), s);
+  return result;
+}
+
+
+static const char *
+my_strusage (int level)
+{
+  static char *ver_gcry;
+  const char *p;
+
+  switch (level)
+    {
+    case 11: p = "@TPM2DAEMON@ (@GNUPG@)";
+      break;
+    case 13: p = VERSION; break;
+    case 17: p = PRINTABLE_OS_NAME; break;
+    case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
+
+    case 20:
+      if (!ver_gcry)
+        ver_gcry = make_libversion ("libgcrypt", gcry_check_version);
+      p = ver_gcry;
+      break;
+    case 1:
+    case 40: p =  _("Usage: @TPM2DAEMON@ [options] (-h for help)");
+      break;
+    case 41: p =  _("Syntax: tpm2daemon [options] [command [args]]\n"
+                    "TPM2 daemon for @GNUPG@\n");
+    break;
+
+    default: p = NULL;
+    }
+  return p;
+}
+
+
+static int
+tid_log_callback (unsigned long *rvalue)
+{
+  int len = sizeof (*rvalue);
+  npth_t thread;
+
+  thread = npth_self ();
+  if (sizeof (thread) < len)
+    len = sizeof (thread);
+  memcpy (rvalue, &thread, len);
+
+  return 2; /* Use use hex representation.  */
+}
+
+
+/* Setup the debugging.  With a LEVEL of NULL only the active debug
+   flags are propagated to the subsystems.  With LEVEL set, a specific
+   set of debug flags is set; thus overriding all flags already
+   set. */
+static void
+set_debug (const char *level)
+{
+  int numok = (level && digitp (level));
+  int numlvl = numok? atoi (level) : 0;
+
+  if (!level)
+    ;
+  else if (!strcmp (level, "none") || (numok && numlvl < 1))
+    opt.debug = 0;
+  else if (!strcmp (level, "basic") || (numok && numlvl <= 2))
+    opt.debug = DBG_IPC_VALUE;
+  else if (!strcmp (level, "advanced") || (numok && numlvl <= 5))
+    opt.debug = DBG_IPC_VALUE;
+  else if (!strcmp (level, "expert") || (numok && numlvl <= 8))
+    opt.debug = (DBG_IPC_VALUE|DBG_CACHE_VALUE|DBG_CARD_IO_VALUE);
+  else if (!strcmp (level, "guru") || numok)
+    {
+      opt.debug = ~0;
+      /* Unless the "guru" string has been used we don't want to allow
+         hashing debugging.  The rationale is that people tend to
+         select the highest debug value and would then clutter their
+         disk with debug files which may reveal confidential data.  */
+      if (numok)
+        opt.debug &= ~(DBG_HASHING_VALUE);
+    }
+  else
+    {
+      log_error (_("invalid debug-level '%s' given\n"), level);
+      tpm2d_exit(2);
+    }
+
+
+  if (opt.debug && !opt.verbose)
+    opt.verbose = 1;
+  if (opt.debug && opt.quiet)
+    opt.quiet = 0;
+
+  if (opt.debug & DBG_MPI_VALUE)
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2);
+  if (opt.debug & DBG_CRYPTO_VALUE )
+    gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1);
+  gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose);
+
+  if (opt.debug)
+    parse_debug_flag (NULL, &opt.debug, debug_flags);
+}
+
+
+
+static void
+cleanup (void)
+{
+  if (socket_name && *socket_name)
+    {
+      char *name;
+
+      name = redir_socket_name? redir_socket_name : socket_name;
+
+      gnupg_remove (name);
+      *socket_name = 0;
+    }
+}
+
+
+
+int
+main (int argc, char **argv )
+{
+  ARGPARSE_ARGS pargs;
+  int orig_argc;
+  char **orig_argv;
+  FILE *configfp = NULL;
+  char *configname = NULL;
+  const char *shell;
+  unsigned int configlineno;
+  int parse_debug = 0;
+  const char *debug_level = NULL;
+  int default_config =1;
+  int greeting = 0;
+  int nogreeting = 0;
+  int multi_server = 0;
+  int is_daemon = 0;
+  int nodetach = 0;
+  int csh_style = 0;
+  char *logfile = NULL;
+  int debug_wait = 0;
+  int gpgconf_list = 0;
+  const char *config_filename = NULL;
+  int allow_coredump = 0;
+  struct assuan_malloc_hooks malloc_hooks;
+  int res;
+  npth_t pipecon_handler;
+
+  early_system_init ();
+  set_strusage (my_strusage);
+  gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+  /* Please note that we may running SUID(ROOT), so be very CAREFUL
+     when adding any stuff between here and the call to INIT_SECMEM()
+     somewhere after the option parsing */
+  log_set_prefix ("tpm2daemon", GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_PID);
+
+  /* Make sure that our subsystems are ready.  */
+  i18n_init ();
+  init_common_subsystems (&argc, &argv);
+
+  malloc_hooks.malloc = gcry_malloc;
+  malloc_hooks.realloc = gcry_realloc;
+  malloc_hooks.free = gcry_free;
+  assuan_set_malloc_hooks (&malloc_hooks);
+  assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
+  assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH);
+  assuan_sock_init ();
+  setup_libassuan_logging (&opt.debug, NULL);
+
+  setup_libgcrypt_logging ();
+  gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);
+
+  disable_core_dumps ();
+
+  /* Set default options. */
+  opt.allow_admin = 1;
+  opt.pcsc_driver = DEFAULT_PCSC_DRIVER;
+
+  shell = getenv ("SHELL");
+  if (shell && strlen (shell) >= 3 && !strcmp (shell+strlen (shell)-3, "csh") )
+    csh_style = 1;
+
+  /* Check whether we have a config file on the commandline */
+  orig_argc = argc;
+  orig_argv = argv;
+  pargs.argc = &argc;
+  pargs.argv = &argv;
+  pargs.flags= 1|(1<<6);  /* do not remove the args, ignore version */
+  while (arg_parse( &pargs, opts))
+    {
+      if (pargs.r_opt == oDebug || pargs.r_opt == oDebugAll)
+        parse_debug++;
+      else if (pargs.r_opt == oOptions)
+        { /* yes there is one, so we do not try the default one, but
+             read the option file when it is encountered at the
+             commandline */
+          default_config = 0;
+        }
+        else if (pargs.r_opt == oNoOptions)
+          default_config = 0; /* --no-options */
+        else if (pargs.r_opt == oHomedir)
+          gnupg_set_homedir (pargs.r.ret_str);
+    }
+
+  /* initialize the secure memory. */
+  gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+  maybe_setuid = 0;
+
+  /*
+     Now we are working under our real uid
+  */
+
+
+  if (default_config)
+    configname = make_filename (gnupg_homedir (), TPM2DAEMON_NAME EXTSEP_S "conf",
+                                NULL );
+
+
+  argc = orig_argc;
+  argv = orig_argv;
+  pargs.argc = &argc;
+  pargs.argv = &argv;
+  pargs.flags=  1;  /* do not remove the args */
+ next_pass:
+  if (configname)
+    {
+      configlineno = 0;
+      configfp = fopen (configname, "r");
+      if (!configfp)
+        {
+          if (default_config)
+            {
+              if( parse_debug )
+                log_info (_("Note: no default option file '%s'\n"),
+                          configname );
+            }
+          else
+            {
+              log_error (_("option file '%s': %s\n"),
+                         configname, strerror(errno) );
+              exit(2);
+            }
+          xfree (configname);
+          configname = NULL;
+        }
+      if (parse_debug && configname )
+        log_info (_("reading options from '%s'\n"), configname );
+      default_config = 0;
+    }
+
+  while (optfile_parse( configfp, configname, &configlineno, &pargs, opts) )
+    {
+      switch (pargs.r_opt)
+        {
+        case aGPGConfList: gpgconf_list = 1; break;
+        case aGPGConfTest: gpgconf_list = 2; break;
+        case oQuiet: opt.quiet = 1; break;
+        case oVerbose: opt.verbose++; break;
+        case oBatch: opt.batch=1; break;
+
+        case oDebug:
+          if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags))
+            {
+              pargs.r_opt = ARGPARSE_INVALID_ARG;
+              pargs.err = ARGPARSE_PRINT_ERROR;
+            }
+          break;
+        case oDebugAll: opt.debug = ~0; break;
+        case oDebugLevel: debug_level = pargs.r.ret_str; break;
+        case oDebugWait: debug_wait = pargs.r.ret_int; break;
+        case oDebugAllowCoreDump:
+          enable_core_dumps ();
+          allow_coredump = 1;
+          break;
+        case oDebugCCIDDriver:
+          break;
+        case oDebugLogTid:
+          log_set_pid_suffix_cb (tid_log_callback);
+          break;
+        case oDebugAssuanLogCats:
+          set_libassuan_log_cats (pargs.r.ret_ulong);
+          break;
+
+        case oOptions:
+          /* config files may not be nested (silently ignore them) */
+          if (!configfp)
+            {
+                xfree(configname);
+                configname = xstrdup(pargs.r.ret_str);
+                goto next_pass;
+            }
+          break;
+        case oNoGreeting: nogreeting = 1; break;
+        case oNoVerbose: opt.verbose = 0; break;
+        case oNoOptions: break; /* no-options */
+        case oHomedir: gnupg_set_homedir (pargs.r.ret_str); break;
+        case oNoDetach: nodetach = 1; break;
+        case oLogFile: logfile = pargs.r.ret_str; break;
+        case oCsh: csh_style = 1; break;
+        case oSh: csh_style = 0; break;
+        case oServer: pipe_server = 1; break;
+        case oMultiServer: pipe_server = 1; multi_server = 1; break;
+        case oDaemon: is_daemon = 1; break;
+
+        case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
+        case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
+        case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
+        case oDisableCCID: opt.disable_ccid = 1; break;
+        case oDisableOpenSC: break;
+
+        case oDisablePinpad: opt.disable_pinpad = 1; break;
+
+        case oAllowAdmin: /* Dummy because allow is now the default.  */
+          break;
+        case oDenyAdmin: opt.allow_admin = 0; break;
+
+        case oCardTimeout: opt.card_timeout = pargs.r.ret_ulong; break;
+
+        case oDisableApplication:
+          add_to_strlist (&opt.disabled_applications, pargs.r.ret_str);
+          break;
+
+        case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break;
+
+        case oListenBacklog:
+          listen_backlog = pargs.r.ret_int;
+          break;
+
+        default:
+          pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
+          break;
+        }
+    }
+  if (configfp)
+    {
+      fclose( configfp );
+      configfp = NULL;
+      /* Keep a copy of the config name for use by --gpgconf-list. */
+      config_filename = configname;
+      configname = NULL;
+      goto next_pass;
+    }
+  xfree (configname);
+  configname = NULL;
+  if (log_get_errorcount(0))
+    exit(2);
+  if (nogreeting )
+    greeting = 0;
+
+  if (greeting)
+    {
+      es_fprintf (es_stderr, "%s %s; %s\n",
+                  strusage(11), strusage(13), strusage(14) );
+      es_fprintf (es_stderr, "%s\n", strusage(15) );
+    }
+#ifdef IS_DEVELOPMENT_VERSION
+  log_info ("NOTE: this is a development version!\n");
+#endif
+
+  /* Print a warning if an argument looks like an option.  */
+  if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
+    {
+      int i;
+
+      for (i=0; i < argc; i++)
+        if (argv[i][0] == '-' && argv[i][1] == '-')
+          log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
+    }
+
+  if (atexit (cleanup))
+    {
+      log_error ("atexit failed\n");
+      cleanup ();
+      exit (1);
+    }
+
+  set_debug (debug_level);
+
+#if 0
+  if (initialize_module_command ())
+    {
+      log_error ("initialization failed\n");
+      cleanup ();
+      exit (1);
+    }
+#endif
+
+  if (gpgconf_list == 2)
+    tpm2d_exit (0);
+  if (gpgconf_list)
+    {
+      /* List options and default values in the GPG Conf format.  */
+      char *filename = NULL;
+      char *filename_esc;
+
+      if (config_filename)
+        filename = xstrdup (config_filename);
+      else
+        filename = make_filename (gnupg_homedir (),
+                                  TPM2DAEMON_NAME EXTSEP_S "conf", NULL);
+      filename_esc = percent_escape (filename, NULL);
+
+      es_printf ("%s-%s.conf:%lu:\"%s\n",
+                 GPGCONF_NAME, TPM2DAEMON_NAME,
+                 GC_OPT_FLAG_DEFAULT, filename_esc);
+      xfree (filename_esc);
+      xfree (filename);
+
+      es_printf ("verbose:%lu:\n"
+                 "quiet:%lu:\n"
+                 "debug-level:%lu:\"none:\n"
+                 "log-file:%lu:\n",
+                 GC_OPT_FLAG_NONE,
+                 GC_OPT_FLAG_NONE,
+                 GC_OPT_FLAG_DEFAULT,
+                 GC_OPT_FLAG_NONE );
+
+      es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("ctapi-driver:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("pcsc-driver:%lu:\"%s:\n",
+              GC_OPT_FLAG_DEFAULT, DEFAULT_PCSC_DRIVER );
+#ifdef HAVE_LIBUSB
+      es_printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE );
+#endif
+      es_printf ("deny-admin:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE );
+      es_printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0);
+      es_printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE );
+
+      tpm2d_exit (0);
+    }
+
+  /* Now start with logging to a file if this is desired.  */
+  if (logfile)
+    {
+      log_set_file (logfile);
+      log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID);
+    }
+
+  if (debug_wait && pipe_server)
+    {
+      log_debug ("waiting for debugger - my pid is %u .....\n",
+                 (unsigned int)getpid());
+      gnupg_sleep (debug_wait);
+      log_debug ("... okay\n");
+    }
+
+  if (pipe_server)
+    {
+      /* This is the simple pipe based server */
+      ctrl_t ctrl;
+      npth_attr_t tattr;
+      int fd = -1;
+
+#ifndef HAVE_W32_SYSTEM
+      {
+        struct sigaction sa;
+
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
+#endif
+
+      npth_init ();
+      gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+      /* If --debug-allow-core-dump has been given we also need to
+         switch the working directory to a place where we can actually
+         write. */
+      if (allow_coredump)
+        {
+          if (chdir("/tmp"))
+            log_debug ("chdir to '/tmp' failed: %s\n", strerror (errno));
+          else
+            log_debug ("changed working directory to '/tmp'\n");
+        }
+
+      /* In multi server mode we need to listen on an additional
+         socket.  Create that socket now before starting the handler
+         for the pipe connection.  This allows that handler to send
+         back the name of that socket. */
+      if (multi_server)
+        {
+          socket_name = create_socket_name (TPM2DAEMON_SOCK_NAME);
+          fd = FD2INT(create_server_socket (socket_name,
+                                            &redir_socket_name, &socket_nonce));
+        }
+
+      res = npth_attr_init (&tattr);
+      if (res)
+        {
+          log_error ("error allocating thread attributes: %s\n",
+                     strerror (res));
+          tpm2d_exit (2);
+        }
+      npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+      ctrl = xtrycalloc (1, sizeof *ctrl);
+      if ( !ctrl )
+        {
+          log_error ("error allocating connection control data: %s\n",
+                     strerror (errno) );
+          tpm2d_exit (2);
+        }
+      ctrl->thread_startup.fd = GNUPG_INVALID_FD;
+      res = npth_create (&pipecon_handler, &tattr, start_connection_thread, ctrl);
+      if (res)
+        {
+          log_error ("error spawning pipe connection handler: %s\n",
+                     strerror (res) );
+          xfree (ctrl);
+          tpm2d_exit (2);
+        }
+      npth_setname_np (pipecon_handler, "pipe-connection");
+      npth_attr_destroy (&tattr);
+
+      /* We run handle_connection to wait for the shutdown signal and
+         to run the ticker stuff.  */
+      handle_connections (fd);
+      if (fd != -1)
+        close (fd);
+    }
+  else if (!is_daemon)
+    {
+      log_info (_("please use the option '--daemon'"
+                  " to run the program in the background\n"));
+    }
+  else
+    { /* Regular server mode */
+      int fd;
+#ifndef HAVE_W32_SYSTEM
+      pid_t pid;
+      int i;
+#endif
+
+      /* Create the socket.  */
+      socket_name = create_socket_name (TPM2DAEMON_SOCK_NAME);
+      fd = FD2INT (create_server_socket (socket_name,
+                                         &redir_socket_name, &socket_nonce));
+
+
+      fflush (NULL);
+#ifdef HAVE_W32_SYSTEM
+      (void)csh_style;
+      (void)nodetach;
+#else
+      pid = fork ();
+      if (pid == (pid_t)-1)
+        {
+          log_fatal ("fork failed: %s\n", strerror (errno) );
+          exit (1);
+        }
+      else if (pid)
+        { /* we are the parent */
+          char *infostr;
+
+          close (fd);
+
+          /* create the info string: <name>:<pid>:<protocol_version> */
+          if (gpgrt_asprintf (&infostr, "TPM2DAEMON_INFO=%s:%lu:1",
+                              socket_name, (ulong) pid) < 0)
+            {
+              log_error ("out of core\n");
+              kill (pid, SIGTERM);
+              exit (1);
+            }
+          *socket_name = 0; /* don't let cleanup() remove the socket -
+                               the child should do this from now on */
+          if (argc)
+            { /* run the program given on the commandline */
+              if (putenv (infostr))
+                {
+                  log_error ("failed to set environment: %s\n",
+                             strerror (errno) );
+                  kill (pid, SIGTERM );
+                  exit (1);
+                }
+              execvp (argv[0], argv);
+              log_error ("failed to run the command: %s\n", strerror (errno));
+              kill (pid, SIGTERM);
+              exit (1);
+            }
+          else
+            {
+              /* Print the environment string, so that the caller can use
+                 shell's eval to set it */
+              if (csh_style)
+                {
+                  *strchr (infostr, '=') = ' ';
+                  es_printf ( "setenv %s;\n", infostr);
+                }
+              else
+                {
+                  es_printf ( "%s; export TPM2DAEMON_INFO;\n", infostr);
+                }
+              xfree (infostr);
+              exit (0);
+            }
+          /* NOTREACHED */
+        } /* end parent */
+
+      /* This is the child. */
+
+      npth_init ();
+      gpgrt_set_syscall_clamp (npth_unprotect, npth_protect);
+
+      /* Detach from tty and put process into a new session. */
+      if (!nodetach )
+        {
+          /* Close stdin, stdout and stderr unless it is the log stream. */
+          for (i=0; i <= 2; i++)
+            {
+              if (!log_test_fd (i) && i != fd )
+                {
+                  if ( !close (i)
+                       && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1)
+                    {
+                      log_error ("failed to open '%s': %s\n",
+                                 "/dev/null", strerror (errno));
+                      cleanup ();
+                      exit (1);
+                    }
+                }
+            }
+
+          if (setsid() == -1)
+            {
+              log_error ("setsid() failed: %s\n", strerror(errno) );
+              cleanup ();
+              exit (1);
+            }
+        }
+
+      {
+        struct sigaction sa;
+
+        sa.sa_handler = SIG_IGN;
+        sigemptyset (&sa.sa_mask);
+        sa.sa_flags = 0;
+        sigaction (SIGPIPE, &sa, NULL);
+      }
+
+#endif /*!HAVE_W32_SYSTEM*/
+
+      if (gnupg_chdir (gnupg_daemon_rootdir ()))
+        {
+          log_error ("chdir to '%s' failed: %s\n",
+                     gnupg_daemon_rootdir (), strerror (errno));
+          exit (1);
+        }
+
+      handle_connections (fd);
+
+      close (fd);
+    }
+
+  return 0;
+}
+
+void
+tpm2d_exit (int rc)
+{
+#if 0
+#warning no update_random_seed_file
+  update_random_seed_file();
+#endif
+#if 0
+  /* at this time a bit annoying */
+  if (opt.debug & DBG_MEMSTAT_VALUE)
+    {
+      gcry_control( GCRYCTL_DUMP_MEMORY_STATS );
+      gcry_control( GCRYCTL_DUMP_RANDOM_STATS );
+    }
+  if (opt.debug)
+    gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
+#endif
+  gcry_control (GCRYCTL_TERM_SECMEM );
+  rc = rc? rc : log_get_errorcount(0)? 2 : 0;
+  exit (rc);
+}
+
+
+static void
+tpm2d_init_default_ctrl (ctrl_t ctrl)
+{
+  (void)ctrl;
+}
+
+static void
+tpm2d_deinit_default_ctrl (ctrl_t ctrl)
+{
+  if (!ctrl)
+    return;
+  xfree (ctrl->in_data.value);
+  ctrl->in_data.value = NULL;
+  ctrl->in_data.valuelen = 0;
+}
+
+
+/* Return the name of the socket to be used to connect to this
+   process.  If no socket is available, return NULL. */
+const char *
+tpm2d_get_socket_name ()
+{
+  if (socket_name && *socket_name)
+    return socket_name;
+  return NULL;
+}
+
+
+#ifndef HAVE_W32_SYSTEM
+static void
+handle_signal (int signo)
+{
+  switch (signo)
+    {
+    case SIGHUP:
+      log_info ("SIGHUP received - "
+                "re-reading configuration and resetting cards\n");
+/*       reread_configuration (); */
+      break;
+
+    case SIGUSR1:
+      log_info ("SIGUSR1 received - printing internal information:\n");
+      /* Fixme: We need to see how to integrate pth dumping into our
+         logging system.  */
+      /* pth_ctrl (PTH_CTRL_DUMPSTATE, log_get_stream ()); */
+#if 0
+      app_dump_state ();
+#endif
+      break;
+
+    case SIGUSR2:
+      log_info ("SIGUSR2 received - no action defined\n");
+      break;
+
+    case SIGCONT:
+      /* Nothing.  */
+      log_debug ("SIGCONT received - breaking select\n");
+      break;
+
+    case SIGTERM:
+      if (!shutdown_pending)
+        log_info ("SIGTERM received - shutting down ...\n");
+      else
+        log_info ("SIGTERM received - still %i running threads\n",
+                  active_connections);
+      shutdown_pending++;
+      if (shutdown_pending > 2)
+        {
+          log_info ("shutdown forced\n");
+          log_info ("%s %s stopped\n", strusage(11), strusage(13) );
+          cleanup ();
+          tpm2d_exit (0);
+        }
+      break;
+
+    case SIGINT:
+      log_info ("SIGINT received - immediate shutdown\n");
+      log_info( "%s %s stopped\n", strusage(11), strusage(13));
+      cleanup ();
+      tpm2d_exit (0);
+      break;
+
+    default:
+      log_info ("signal %d received - no action defined\n", signo);
+    }
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+/* Create a name for the socket.  We check for valid characters as
+   well as against a maximum allowed length for a unix domain socket
+   is done.  The function terminates the process in case of an error.
+   Retunrs: Pointer to an allcoated string with the absolute name of
+   the socket used.  */
+static char *
+create_socket_name (char *standard_name)
+{
+  char *name;
+
+  name = make_filename (gnupg_socketdir (), standard_name, NULL);
+  if (strchr (name, PATHSEP_C))
+    {
+      log_error (("'%s' are not allowed in the socket name\n"), PATHSEP_S);
+      tpm2d_exit (2);
+    }
+  return name;
+}
+
+
+
+/* Create a Unix domain socket with NAME.  Returns the file descriptor
+   or terminates the process in case of an error.  If the socket has
+   been redirected the name of the real socket is stored as a malloced
+   string at R_REDIR_NAME. */
+static gnupg_fd_t
+create_server_socket (const char *name, char **r_redir_name,
+                      assuan_sock_nonce_t *nonce)
+{
+  struct sockaddr *addr;
+  struct sockaddr_un *unaddr;
+  socklen_t len;
+  gnupg_fd_t fd;
+  int rc;
+
+  xfree (*r_redir_name);
+  *r_redir_name = NULL;
+
+  fd = assuan_sock_new (AF_UNIX, SOCK_STREAM, 0);
+  if (fd == GNUPG_INVALID_FD)
+    {
+      log_error (_("can't create socket: %s\n"), strerror (errno));
+      tpm2d_exit (2);
+    }
+
+  unaddr = xmalloc (sizeof (*unaddr));
+  addr = (struct sockaddr*)unaddr;
+
+  {
+    int redirected;
+
+    if (assuan_sock_set_sockaddr_un (name, addr, &redirected))
+      {
+        if (errno == ENAMETOOLONG)
+          log_error (_("socket name '%s' is too long\n"), name);
+        else
+          log_error ("error preparing socket '%s': %s\n",
+                     name, gpg_strerror (gpg_error_from_syserror ()));
+        tpm2d_exit (2);
+      }
+    if (redirected)
+      {
+        *r_redir_name = xstrdup (unaddr->sun_path);
+        if (opt.verbose)
+          log_info ("redirecting socket '%s' to '%s'\n", name, *r_redir_name);
+      }
+  }
+
+  len = SUN_LEN (unaddr);
+
+  rc = assuan_sock_bind (fd, addr, len);
+  if (rc == -1 && errno == EADDRINUSE)
+    {
+      gnupg_remove (unaddr->sun_path);
+      rc = assuan_sock_bind (fd, addr, len);
+    }
+  if (rc != -1
+      && (rc=assuan_sock_get_nonce (addr, len, nonce)))
+    log_error (_("error getting nonce for the socket\n"));
+ if (rc == -1)
+    {
+      log_error (_("error binding socket to '%s': %s\n"),
+                 unaddr->sun_path,
+                 gpg_strerror (gpg_error_from_syserror ()));
+      assuan_sock_close (fd);
+      tpm2d_exit (2);
+    }
+
+  if (gnupg_chmod (unaddr->sun_path, "-rwx"))
+    log_error (_("can't set permissions of '%s': %s\n"),
+               unaddr->sun_path, strerror (errno));
+
+  if (listen (FD2INT(fd), listen_backlog) == -1)
+    {
+      log_error ("listen(fd, %d) failed: %s\n",
+                 listen_backlog, gpg_strerror (gpg_error_from_syserror ()));
+      assuan_sock_close (fd);
+      tpm2d_exit (2);
+    }
+
+  if (opt.verbose)
+    log_info (_("listening on socket '%s'\n"), unaddr->sun_path);
+
+  return fd;
+}
+
+
+
+/* This is the standard connection thread's main function.  */
+static void *
+start_connection_thread (void *arg)
+{
+  ctrl_t ctrl = arg;
+
+  if (ctrl->thread_startup.fd != GNUPG_INVALID_FD
+      && assuan_sock_check_nonce (ctrl->thread_startup.fd, &socket_nonce))
+    {
+      log_info (_("error reading nonce on fd %d: %s\n"),
+                FD2INT(ctrl->thread_startup.fd), strerror (errno));
+      assuan_sock_close (ctrl->thread_startup.fd);
+      xfree (ctrl);
+      return NULL;
+    }
+
+  active_connections++;
+
+  tpm2d_init_default_ctrl (ctrl);
+  if (opt.verbose)
+    log_info (_("handler for fd %d started\n"),
+              FD2INT(ctrl->thread_startup.fd));
+
+  /* If this is a pipe server, we request a shutdown if the command
+     handler asked for it.  With the next ticker event and given that
+     no other connections are running the shutdown will then
+     happen.  */
+  if (tpm2d_command_handler (ctrl, FD2INT(ctrl->thread_startup.fd))
+      && pipe_server)
+    shutdown_pending = 1;
+
+  if (opt.verbose)
+    log_info (_("handler for fd %d terminated\n"),
+              FD2INT (ctrl->thread_startup.fd));
+
+  tpm2d_deinit_default_ctrl (ctrl);
+  xfree (ctrl);
+
+  if (--active_connections == 0)
+    tpm2d_kick_the_loop ();
+
+  return NULL;
+}
+
+
+void
+tpm2d_kick_the_loop (void)
+{
+#ifdef HAVE_W32_SYSTEM
+  int ret;
+
+  /* Kick the select loop.  */
+  ret = SetEvent (the_event);
+  if (ret == 0)
+    log_error ("SetEvent for tpm2d_kick_the_loop failed: %s\n",
+               w32_strerror (-1));
+#elif defined(HAVE_PSELECT_NO_EINTR)
+  write (notify_fd, "", 1);
+#else
+  int ret;
+
+  ret = kill (main_thread_pid, SIGCONT);
+  if (ret < 0)
+    log_error ("SetEvent for tpm2d_kick_the_loop failed: %s\n",
+               gpg_strerror (gpg_error_from_syserror ()));
+#endif
+}
+
+/* Connection handler loop.  Wait for connection requests and spawn a
+   thread after accepting a connection.  LISTEN_FD is allowed to be -1
+   in which case this code will only do regular timeouts and handle
+   signals. */
+static void
+handle_connections (int listen_fd)
+{
+  npth_attr_t tattr;
+  struct sockaddr_un paddr;
+  socklen_t plen;
+  fd_set fdset, read_fdset;
+  int nfd;
+  int ret;
+  int fd;
+  struct timespec timeout;
+  struct timespec *t;
+  int saved_errno;
+#ifdef HAVE_W32_SYSTEM
+  HANDLE events[2];
+  unsigned int events_set;
+#else
+  int signo;
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+  int pipe_fd[2];
+
+  ret = gnupg_create_pipe (pipe_fd);
+  if (ret)
+    {
+      log_error ("pipe creation failed: %s\n", gpg_strerror (ret));
+      return;
+    }
+  notify_fd = pipe_fd[1];
+#endif
+
+  ret = npth_attr_init(&tattr);
+  if (ret)
+    {
+      log_error ("npth_attr_init failed: %s\n", strerror (ret));
+      return;
+    }
+
+  npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
+
+#ifdef HAVE_W32_SYSTEM
+  {
+    HANDLE h, h2;
+    SECURITY_ATTRIBUTES sa = { sizeof (SECURITY_ATTRIBUTES), NULL, TRUE};
+
+    events[0] = the_event = INVALID_HANDLE_VALUE;
+    events[1] = INVALID_HANDLE_VALUE;
+    h = CreateEvent (&sa, TRUE, FALSE, NULL);
+    if (!h)
+      log_error ("can't create tpm2d event: %s\n", w32_strerror (-1) );
+    else if (!DuplicateHandle (GetCurrentProcess(), h,
+                               GetCurrentProcess(), &h2,
+                               EVENT_MODIFY_STATE|SYNCHRONIZE, TRUE, 0))
+      {
+        log_error ("setting synchronize for tpm2d_kick_the_loop failed: %s\n",
+                   w32_strerror (-1) );
+        CloseHandle (h);
+      }
+    else
+      {
+        CloseHandle (h);
+        events[0] = the_event = h2;
+      }
+  }
+#else
+  npth_sigev_init ();
+  npth_sigev_add (SIGHUP);
+  npth_sigev_add (SIGUSR1);
+  npth_sigev_add (SIGUSR2);
+  npth_sigev_add (SIGINT);
+  npth_sigev_add (SIGCONT);
+  npth_sigev_add (SIGTERM);
+  npth_sigev_fini ();
+  main_thread_pid = getpid ();
+#endif
+
+  FD_ZERO (&fdset);
+  nfd = 0;
+  if (listen_fd != -1)
+    {
+      FD_SET (listen_fd, &fdset);
+      nfd = listen_fd;
+    }
+
+  for (;;)
+    {
+      int periodical_check;
+      int max_fd = nfd;
+
+      if (shutdown_pending)
+        {
+          if (active_connections == 0)
+            break; /* ready */
+
+          /* Do not accept anymore connections but wait for existing
+             connections to terminate. We do this by clearing out all
+             file descriptors to wait for, so that the select will be
+             used to just wait on a signal or timeout event. */
+          FD_ZERO (&fdset);
+          listen_fd = -1;
+        }
+
+      periodical_check = 0;
+
+      timeout.tv_sec = TIMERTICK_INTERVAL_SEC;
+      timeout.tv_nsec = TIMERTICK_INTERVAL_USEC * 1000;
+
+      if (shutdown_pending || periodical_check)
+        t = &timeout;
+      else
+        t = NULL;
+
+      /* POSIX says that fd_set should be implemented as a structure,
+         thus a simple assignment is fine to copy the entire set.  */
+      read_fdset = fdset;
+
+#ifdef HAVE_PSELECT_NO_EINTR
+      FD_SET (pipe_fd[0], &read_fdset);
+      if (max_fd < pipe_fd[0])
+        max_fd = pipe_fd[0];
+#endif
+
+#ifndef HAVE_W32_SYSTEM
+      ret = npth_pselect (max_fd+1, &read_fdset, NULL, NULL, t,
+                          npth_sigev_sigmask ());
+      saved_errno = errno;
+
+      while (npth_sigev_get_pending(&signo))
+        handle_signal (signo);
+#else
+      ret = npth_eselect (nfd+1, &read_fdset, NULL, NULL, t,
+                          events, &events_set);
+      saved_errno = errno;
+      if (events_set & 1)
+        continue;
+#endif
+
+      if (ret == -1 && saved_errno != EINTR)
+        {
+          log_error (_("npth_pselect failed: %s - waiting 1s\n"),
+                     strerror (saved_errno));
+          npth_sleep (1);
+          continue;
+        }
+
+      if (ret <= 0)
+        /* Timeout.  Will be handled when calculating the next timeout.  */
+        continue;
+
+#ifdef HAVE_PSELECT_NO_EINTR
+      if (FD_ISSET (pipe_fd[0], &read_fdset))
+        {
+          char buf[256];
+
+          read (pipe_fd[0], buf, sizeof buf);
+        }
+#endif
+
+      if (listen_fd != -1 && FD_ISSET (listen_fd, &read_fdset))
+        {
+          ctrl_t ctrl;
+
+          plen = sizeof paddr;
+          fd = npth_accept (listen_fd, (struct sockaddr *)&paddr, &plen);
+          if (fd == -1)
+            {
+              log_error ("accept failed: %s\n", strerror (errno));
+            }
+          else if ( !(ctrl = xtrycalloc (1, sizeof *ctrl)) )
+            {
+              log_error ("error allocating connection control data: %s\n",
+                         strerror (errno) );
+              close (fd);
+            }
+          else
+            {
+              char threadname[50];
+              npth_t thread;
+
+              snprintf (threadname, sizeof threadname, "conn fd=%d", fd);
+              ctrl->thread_startup.fd = INT2FD (fd);
+              ret = npth_create (&thread, &tattr, start_connection_thread, ctrl);
+              if (ret)
+                {
+                  log_error ("error spawning connection handler: %s\n",
+                             strerror (ret));
+                  xfree (ctrl);
+                  close (fd);
+                }
+              else
+                npth_setname_np (thread, threadname);
+            }
+        }
+    }
+
+#ifdef HAVE_W32_SYSTEM
+  if (the_event != INVALID_HANDLE_VALUE)
+    CloseHandle (the_event);
+#endif
+#ifdef HAVE_PSELECT_NO_EINTR
+  close (pipe_fd[0]);
+  close (pipe_fd[1]);
+#endif
+  cleanup ();
+  log_info (_("%s %s stopped\n"), strusage(11), strusage(13));
+  npth_attr_destroy (&tattr);
+}
+
+/* Return the number of active connections. */
+int
+get_active_connection_count (void)
+{
+  return active_connections;
+}
diff --git a/tpm2d/tpm2daemon.h b/tpm2d/tpm2daemon.h
new file mode 100644
index 000000000..5e07d9d93
--- /dev/null
+++ b/tpm2d/tpm2daemon.h
@@ -0,0 +1,130 @@
+/* scdaemon.h - Global definitions for the SCdaemon
+ *	Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 this program; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#ifndef TPM2DAEMON_H
+#define TPM2DAEMON_H
+
+#ifdef GPG_ERR_SOURCE_DEFAULT
+#error GPG_ERR_SOURCE_DEFAULT already defined
+#endif
+/* FIXME: assuan doesn't have a define for TPM2D */
+#define GPG_ERR_SOURCE_DEFAULT  GPG_ERR_SOURCE_UNKNOWN
+#include <gpg-error.h>
+
+#include <time.h>
+#include <gcrypt.h>
+#include "../common/util.h"
+#include "../common/sysutils.h"
+
+/* To convey some special hash algorithms we use algorithm numbers
+   reserved for application use. */
+#ifndef GCRY_MODULE_ID_USER
+#define GCRY_MODULE_ID_USER 1024
+#endif
+#define MD_USER_TLS_MD5SHA1 (GCRY_MODULE_ID_USER+1)
+
+/* Maximum length of a digest.  */
+#define MAX_DIGEST_LEN 64
+
+
+
+/* A large struct name "opt" to keep global flags. */
+struct
+{
+  unsigned int debug; /* Debug flags (DBG_foo_VALUE). */
+  int verbose;        /* Verbosity level. */
+  int quiet;          /* Be as quiet as possible. */
+  int dry_run;        /* Don't change any persistent data. */
+  int batch;          /* Batch mode. */
+  const char *ctapi_driver; /* Library to access the ctAPI. */
+  const char *pcsc_driver;  /* Library to access the PC/SC system. */
+  const char *reader_port;  /* NULL or reder port to use. */
+  int disable_ccid;    /* Disable the use of the internal CCID driver. */
+  int disable_pinpad;  /* Do not use a pinpad. */
+  int enable_pinpad_varlen;  /* Use variable length input for pinpad. */
+  int allow_admin;     /* Allow the use of admin commands for certain
+                          cards. */
+  strlist_t disabled_applications;  /* Card applications we do not
+                                       want to use. */
+  unsigned long card_timeout; /* Disconnect after N seconds of inactivity.  */
+} opt;
+
+
+#define DBG_MPI_VALUE	  2	/* debug mpi details */
+#define DBG_CRYPTO_VALUE  4	/* debug low level crypto */
+#define DBG_MEMORY_VALUE  32	/* debug memory allocation stuff */
+#define DBG_CACHE_VALUE   64	/* debug the caching */
+#define DBG_MEMSTAT_VALUE 128	/* show memory statistics */
+#define DBG_HASHING_VALUE 512	/* debug hashing operations */
+#define DBG_IPC_VALUE     1024
+#define DBG_CARD_IO_VALUE 2048
+#define DBG_READER_VALUE  4096  /* Trace reader related functions.  */
+
+#define DBG_CRYPTO  (opt.debug & DBG_CRYPTO_VALUE)
+#define DBG_MEMORY  (opt.debug & DBG_MEMORY_VALUE)
+#define DBG_CACHE   (opt.debug & DBG_CACHE_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_IPC     (opt.debug & DBG_IPC_VALUE)
+#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
+#define DBG_READER  (opt.debug & DBG_READER_VALUE)
+
+struct server_local_s;
+
+struct server_control_s
+{
+  /* Private data used to fire up the connection thread.  We use this
+     structure do avoid an extra allocation for just a few bytes. */
+  struct {
+    gnupg_fd_t fd;
+  } thread_startup;
+
+  /* Local data of the server; used only in command.c. */
+  struct server_local_s *server_local;
+
+  /* The application context used with this connection or NULL if none
+     associated.  Note that this is shared with the other connections:
+     All connections accessing the same reader are using the same
+     application context. */
+  struct assuan_context_s *ctx;
+
+  /* Helper to store the value we are going to sign */
+  struct
+  {
+    unsigned char *value;
+    int valuelen;
+  } in_data;
+};
+
+typedef struct app_ctx_s *app_t;
+
+/*-- scdaemon.c --*/
+void tpm2d_exit (int rc);
+const char *scd_get_socket_name (void);
+
+/*-- command.c --*/
+gpg_error_t initialize_module_command (void);
+int  tpm2d_command_handler (ctrl_t, int);
+void send_status_info (ctrl_t ctrl, const char *keyword, ...)
+     GPGRT_ATTR_SENTINEL(1);
+void send_status_direct (ctrl_t ctrl, const char *keyword, const char *args);
+void send_client_notifications (app_t app, int removal);
+void tpm2d_kick_the_loop (void);
+int get_active_connection_count (void);
+
+#endif /*TPM2DAEMON_H*/
-- 
2.26.2




More information about the Gnupg-devel mailing list