[PATCH v3 0/5] Add TPM2 support to gnupg 2.3

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
This is a set of patches adding TPM support to gnupg-2.3

The architecture of the patches is that they build if the TSS
libraries are present, but all of the TPM specific code and libraries
is sequestered in tpm2daemon.  If this daemon isn't present, gpg will
just run normally except it won't understand TPM keys and the
'keytotpm' command won't work.  The reason for this design is so that
distributions can package tpm2daemon separately for gnupg with TPM
support.

These patches also add a testing infrastructure which uses a software
TPM to run the tpm2daemon through its usual operations.

Changes from v2 are

* add a check for a leading 0 in the rsa signature
* refactor the tpm2 support to make for easy TSS porting.  Initial code
  is for the IBM TSS
* add tests
* add experimental support for the Intel TSS.  This is the last patch and
  can be dropped without affecting any functionality.

For those who want to try it out, I've created a git tree with these
patches in at

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/gnupg.git

The master-tpm-daemon branch is current 2.3 with the TPM patches

James

---

James Bottomley (5):
  tpm2d: Add tpm2daemon code
  agent: Add new shadow key type and functions to call tpm2daemon
  g10: add new command keytotpm to convert a private key to TPM format
  tpm2d: add tests for the tpm2daemon
  Add Support for the Intel TSS

 Makefile.am                         |    7 +-
 agent/Makefile.am                   |    5 +
 agent/agent.h                       |   50 ++
 agent/call-daemon.c                 |    3 +-
 agent/call-tpm2d.c                  |  248 ++++++
 agent/command.c                     |   57 ++
 agent/divert-tpm2.c                 |  144 +++
 agent/gpg-agent.c                   |    4 +
 agent/keyformat.txt                 |   12 +-
 agent/pkdecrypt.c                   |    8 +-
 agent/pksign.c                      |   16 +-
 am/cmacros.am                       |    3 +
 common/homedir.c                    |    7 +
 common/mapstrings.c                 |    1 +
 common/util.h                       |    1 +
 configure.ac                        |   76 ++
 g10/call-agent.c                    |   22 +
 g10/call-agent.h                    |    3 +
 g10/keyedit.c                       |   45 +-
 tests/Makefile.am                   |    3 +
 tests/tpm2dtests/Makefile.am        |   79 ++
 tests/tpm2dtests/all-tests.scm      |   81 ++
 tests/tpm2dtests/defs.scm           |  473 ++++++++++
 tests/tpm2dtests/ecc.scm            |   23 +
 tests/tpm2dtests/longpassphrase.scm |   36 +
 tests/tpm2dtests/rsa.scm            |   13 +
 tests/tpm2dtests/run-tests.scm      |   43 +
 tests/tpm2dtests/setup.scm          |   48 +
 tests/tpm2dtests/shell.scm          |   51 ++
 tests/tpm2dtests/unimportable.scm   |   28 +
 tools/gpgconf-comp.c                |   62 +-
 tools/gpgconf.h                     |    3 +
 tpm2d/Makefile.am                   |   18 +
 tpm2d/command.c                     |  508 +++++++++++
 tpm2d/ibm-tss.h                     |  378 ++++++++
 tpm2d/intel-tss.h                   |  667 ++++++++++++++
 tpm2d/tpm2.c                        |  985 ++++++++++++++++++++
 tpm2d/tpm2.h                        |   38 +
 tpm2d/tpm2daemon.c                  | 1289 +++++++++++++++++++++++++++
 tpm2d/tpm2daemon.h                  |  104 +++
 40 files changed, 5629 insertions(+), 13 deletions(-)
 create mode 100644 agent/call-tpm2d.c
 create mode 100644 agent/divert-tpm2.c
 create mode 100644 tests/tpm2dtests/Makefile.am
 create mode 100644 tests/tpm2dtests/all-tests.scm
 create mode 100644 tests/tpm2dtests/defs.scm
 create mode 100644 tests/tpm2dtests/ecc.scm
 create mode 100644 tests/tpm2dtests/longpassphrase.scm
 create mode 100644 tests/tpm2dtests/rsa.scm
 create mode 100644 tests/tpm2dtests/run-tests.scm
 create mode 100644 tests/tpm2dtests/setup.scm
 create mode 100644 tests/tpm2dtests/shell.scm
 create mode 100644 tests/tpm2dtests/unimportable.scm
 create mode 100644 tpm2d/Makefile.am
 create mode 100644 tpm2d/command.c
 create mode 100644 tpm2d/ibm-tss.h
 create mode 100644 tpm2d/intel-tss.h
 create mode 100644 tpm2d/tpm2.c
 create mode 100644 tpm2d/tpm2.h
 create mode 100644 tpm2d/tpm2daemon.c
 create mode 100644 tpm2d/tpm2daemon.h

--
2.26.2


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 2/5] agent: Add new shadow key type and functions to call tpm2daemon

GnuPG - Dev mailing list
A new shadow key type: "tpm2-v1" is introduced signalling that the
shadowed key is handled by the tpm2daemon.  A function to identify
this type is introduced and diversions to the tpm2daemon functions are
conditioned on this function for pkign and pkdecrypt where the same
diversions to scd are currently done.  The (info) field of the
shadowed key stores the actual TPM key.  The TPM key is encrypted so
only the physical TPM it was created on can read it (so no special
protection is required for the info filed), but if the (info) field
becomes corrupt or damaged, the key will be lost (unlike the token
case, where the key is actually moved inside the token).

Note, this commit adds handling for existing TPM format shadow keys,
but there is still no way to create them.

Signed-off-by: James Bottomley <[hidden email]>
---
 agent/Makefile.am   |   5 +
 agent/agent.h       |  50 +++++++++
 agent/call-daemon.c |   3 +-
 agent/call-tpm2d.c  | 248 ++++++++++++++++++++++++++++++++++++++++++++
 agent/command.c     |   5 +
 agent/divert-tpm2.c | 144 +++++++++++++++++++++++++
 agent/gpg-agent.c   |   4 +
 agent/keyformat.txt |  12 ++-
 agent/pkdecrypt.c   |   8 +-
 agent/pksign.c      |  16 ++-
 10 files changed, 485 insertions(+), 10 deletions(-)
 create mode 100644 agent/call-tpm2d.c
 create mode 100644 agent/divert-tpm2.c

diff --git a/agent/Makefile.am b/agent/Makefile.am
index 2688ba967..036cdc357 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -57,6 +57,11 @@ gpg_agent_SOURCES = \
  call-daemon.c \
  learncard.c
 
+if HAVE_LIBTSS
+gpg_agent_SOURCES +=  divert-tpm2.c \
+ call-tpm2d.c
+endif
+
 common_libs = $(libcommon)
 commonpth_libs = $(libcommonpth)
 if HAVE_W32CE_SYSTEM
diff --git a/agent/agent.h b/agent/agent.h
index 4d29ce9c9..fcd74abaf 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -59,6 +59,7 @@
 enum daemon_type
   {
    DAEMON_SCD,
+   DAEMON_TPM2D,
    DAEMON_MAX_TYPE
   };
 
@@ -459,6 +460,7 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
                                         const unsigned char *grip,
                                         gcry_sexp_t *result);
 int agent_pk_get_algo (gcry_sexp_t s_key);
+int agent_is_tpm2_key(gcry_sexp_t s_key);
 int agent_key_available (const unsigned char *grip);
 gpg_error_t agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
                                       int *r_keytype,
@@ -577,6 +579,44 @@ gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name,
                                const char *fpr, int flag);
 void agent_reload_trustlist (void);
 
+/*-- divert-tpm2.c --*/
+#ifdef HAVE_LIBTSS
+int divert_tpm2_pksign (ctrl_t ctrl, const char *desc_text,
+                        const unsigned char *digest, size_t digestlen, int algo,
+                        const unsigned char *shadow_info, unsigned char **r_sig,
+                        size_t *r_siglen);
+int divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
+                           const unsigned char *cipher,
+                           const unsigned char *shadow_info,
+                           char **r_buf, size_t *r_len, int *r_padding);
+int divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip,
+                          gcry_sexp_t s_skey);
+#else
+static inline int divert_tpm2_pksign (ctrl_t ctrl, const char *desc_text,
+      const unsigned char *digest,
+      size_t digestlen, int algo,
+      const unsigned char *shadow_info,
+      unsigned char **r_sig,
+      size_t *r_siglen)
+{
+  return -EINVAL;
+}
+static inline int divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
+ const unsigned char *cipher,
+ const unsigned char *shadow_info,
+ char **r_buf, size_t *r_len,
+ int *r_padding)
+{
+  return -EINVAL;
+}
+static inline int divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip,
+ gcry_sexp_t s_skey)
+{
+  return -EINVAL;
+}
+#endif
+
+
 
 /*-- divert-scd.c --*/
 int divert_pksign (ctrl_t ctrl, const char *desc_text,
@@ -606,6 +646,16 @@ void agent_daemon_check_aliveness (void);
 void agent_reset_daemon (ctrl_t ctrl);
 void agent_kill_daemon (enum daemon_type type);
 
+/*-- call-tpm2d.c --*/
+int agent_tpm2d_writekey (ctrl_t ctrl, unsigned char **shadow_info,
+  gcry_sexp_t s_skey);
+int agent_tpm2d_pksign (ctrl_t ctrl, const unsigned char *digest,
+ size_t digestlen, const unsigned char *shadow_info,
+ unsigned char **r_sig, size_t *r_siglen);
+int agent_tpm2d_pkdecrypt (ctrl_t ctrl, const unsigned char *cipher,
+   size_t cipherlen, const unsigned char *shadow_info,
+   char **r_buf, size_t *r_len);
+
 /*-- call-scd.c --*/
 int agent_card_learn (ctrl_t ctrl,
                       void (*kpinfo_cb)(void*, const char *),
diff --git a/agent/call-daemon.c b/agent/call-daemon.c
index 5147f1557..f0df67f1d 100644
--- a/agent/call-daemon.c
+++ b/agent/call-daemon.c
@@ -45,7 +45,8 @@
  * same order as given by the daemon_type enum.  */
 static const int daemon_modules[DAEMON_MAX_TYPE] =
   {
-   GNUPG_MODULE_NAME_SCDAEMON
+    GNUPG_MODULE_NAME_SCDAEMON,
+    GNUPG_MODULE_NAME_TPM2DAEMON,
   };
 
 /* Definition of module local data of the CTRL structure.  */
diff --git a/agent/call-tpm2d.c b/agent/call-tpm2d.c
new file mode 100644
index 000000000..6fae5d85a
--- /dev/null
+++ b/agent/call-tpm2d.c
@@ -0,0 +1,248 @@
+#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 "agent.h"
+#include <assuan.h>
+#include "../common/strlist.h"
+#include "../common/sexp-parse.h"
+#include "../common/i18n.h"
+
+static int
+start_tpm2d (ctrl_t ctrl)
+{
+  return daemon_start (DAEMON_TPM2D, ctrl);
+}
+
+static int
+unlock_tpm2d (ctrl_t ctrl, gpg_error_t err)
+{
+  return daemon_unlock (DAEMON_TPM2D, ctrl, err);
+}
+
+static assuan_context_t
+daemon_ctx (ctrl_t ctrl)
+{
+  return daemon_type_ctx (DAEMON_TPM2D, ctrl);
+}
+
+struct inq_parm_s {
+  assuan_context_t ctx;
+  gpg_error_t (*getpin_cb)(ctrl_t, const char *, char **);
+  ctrl_t ctrl;
+  /* The next fields are used by inq_keydata.  */
+  const unsigned char *keydata;
+  size_t keydatalen;
+  /* following only used by inq_extra */
+  const unsigned char *extra;
+  size_t extralen;
+  char *pin;
+};
+
+static gpg_error_t
+inq_needpin (void *opaque, const char *line)
+{
+  struct inq_parm_s *parm = opaque;
+  char *pin = NULL;
+  gpg_error_t rc;
+  const char *s;
+
+  if ((s = has_leading_keyword (line, "NEEDPIN")))
+    {
+      rc = parm->getpin_cb (parm->ctrl, s, &pin);
+      if (!rc)
+        rc = assuan_send_data (parm->ctx, pin, strlen(pin));
+      parm->pin = pin;
+    }
+  else
+    {
+      log_error ("unsupported inquiry '%s'\n", line);
+      rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+    }
+
+  return rc;
+}
+
+static gpg_error_t
+inq_keydata (void *opaque, const char *line)
+{
+  struct inq_parm_s *parm = opaque;
+
+  if (has_leading_keyword (line, "KEYDATA"))
+    return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen);
+  else
+    return inq_needpin (opaque, line);
+}
+
+static gpg_error_t
+inq_extra (void *opaque, const char *line)
+{
+  struct inq_parm_s *parm = opaque;
+
+  if (has_leading_keyword (line, "EXTRA"))
+    return assuan_send_data (parm->ctx, parm->extra, parm->extralen);
+  else
+    return inq_keydata (opaque, line);
+}
+
+int
+agent_tpm2d_writekey (ctrl_t ctrl, unsigned char **shadow_info,
+      gcry_sexp_t s_skey)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  size_t n;
+  unsigned char *kbuf;
+  membuf_t data;
+  struct inq_parm_s inqparm;
+  size_t len;
+
+  rc = start_tpm2d (ctrl);
+  if (rc)
+    return rc;
+
+  /* note: returned data is TPM protected so no need for a sensitive context */
+  init_membuf(&data, 4096);
+
+  inqparm.ctx = daemon_ctx (ctrl);
+  inqparm.getpin_cb = agent_ask_new_passphrase;
+  inqparm.ctrl = ctrl;
+  inqparm.pin = NULL;
+
+  n = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0);
+  kbuf = xtrymalloc (n);
+  gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, kbuf, n);
+  inqparm.keydata = kbuf;
+  inqparm.keydatalen = n;
+  snprintf(line, sizeof(line), "IMPORT");
+
+  rc = assuan_transact (daemon_ctx (ctrl), line,
+ put_membuf_cb, &data,
+ inq_keydata, &inqparm,
+ NULL, NULL);
+  xfree (kbuf);
+  xfree (inqparm.pin);
+  if (rc)
+    {
+      xfree (get_membuf (&data, &len));
+      return unlock_tpm2d (ctrl, rc);
+    }
+
+  *shadow_info = get_membuf (&data, &len);
+
+  return unlock_tpm2d (ctrl, 0);
+}
+
+static gpg_error_t
+pin_cb (ctrl_t ctrl, const char *prompt, char **passphrase)
+{
+  *passphrase = agent_get_cache (ctrl, ctrl->keygrip, CACHE_MODE_USER);
+  if (*passphrase)
+    return 0;
+  return agent_get_passphrase(ctrl, passphrase,
+      _("Please enter your passphrase, so that the "
+ "secret key can be unlocked for this session"),
+      prompt, NULL, 0,
+      ctrl->keygrip, CACHE_MODE_USER, NULL);
+}
+
+int
+agent_tpm2d_pksign (ctrl_t ctrl, const unsigned char *digest,
+    size_t digestlen, const unsigned char *shadow_info,
+    unsigned char **r_sig, size_t *r_siglen)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  membuf_t data;
+  struct inq_parm_s inqparm;
+
+  rc = start_tpm2d (ctrl);
+  if (rc)
+    return rc;
+
+  init_membuf (&data, 1024);
+
+  inqparm.ctx = daemon_ctx (ctrl);
+  inqparm.getpin_cb = pin_cb;
+  inqparm.ctrl = ctrl;
+  inqparm.keydata = shadow_info;
+  inqparm.keydatalen = gcry_sexp_canon_len (shadow_info, 0, NULL, NULL);
+  inqparm.extra = digest;
+  inqparm.extralen = digestlen;
+  inqparm.pin = NULL;
+
+  snprintf(line, sizeof(line), "PKSIGN");
+
+  rc = assuan_transact (daemon_ctx (ctrl), line,
+ put_membuf_cb, &data,
+ inq_extra, &inqparm,
+ NULL, NULL);
+  if (!rc)
+    agent_put_cache (ctrl, ctrl->keygrip, CACHE_MODE_USER, inqparm.pin, 0);
+
+  xfree (inqparm.pin);
+
+  if (rc)
+    {
+      size_t len;
+      xfree (get_membuf (&data, &len));
+      return unlock_tpm2d (ctrl, rc);
+    }
+
+  *r_sig = get_membuf (&data, r_siglen);
+
+  return unlock_tpm2d (ctrl, 0);
+}
+
+int
+agent_tpm2d_pkdecrypt (ctrl_t ctrl, const unsigned char *cipher,
+       size_t cipherlen, const unsigned char *shadow_info,
+       char **r_buf, size_t *r_len)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  membuf_t data;
+  struct inq_parm_s inqparm;
+
+  rc = start_tpm2d (ctrl);
+  if (rc)
+    return rc;
+
+  init_membuf (&data, 1024);
+
+  inqparm.ctx = daemon_ctx (ctrl);
+  inqparm.getpin_cb = pin_cb;
+  inqparm.ctrl = ctrl;
+  inqparm.keydata = shadow_info;
+  inqparm.keydatalen = gcry_sexp_canon_len (shadow_info, 0, NULL, NULL);
+  inqparm.extra = cipher;
+  inqparm.extralen = cipherlen;
+  inqparm.pin = NULL;
+
+  snprintf(line, sizeof(line), "PKDECRYPT");
+
+  rc = assuan_transact (daemon_ctx (ctrl), line,
+ put_membuf_cb, &data,
+ inq_extra, &inqparm,
+ NULL, NULL);
+  if (!rc)
+    agent_put_cache (ctrl, ctrl->keygrip, CACHE_MODE_USER, inqparm.pin, 0);
+
+  xfree (inqparm.pin);
+
+  if (rc)
+    {
+      size_t len;
+      xfree (get_membuf (&data, &len));
+      return unlock_tpm2d (ctrl, rc);
+    }
+
+  *r_buf = get_membuf (&data, r_len);
+
+  return unlock_tpm2d (ctrl, 0);
+}
diff --git a/agent/command.c b/agent/command.c
index 8384560cd..87446a233 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1314,6 +1314,11 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
           if (err)
             goto leave;
         }
+      else if (strcmp (shadow_info_type, "tpm2-v1") == 0)
+        {
+          serialno = xstrdup("TPM-Protected");
+          idstr = NULL;
+        }
       else
         {
           log_error ("unrecognised shadow key type %s\n", shadow_info_type);
diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c
new file mode 100644
index 000000000..c37cea2e0
--- /dev/null
+++ b/agent/divert-tpm2.c
@@ -0,0 +1,144 @@
+#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 "agent.h"
+#include "../common/i18n.h"
+#include "../common/sexp-parse.h"
+
+int
+divert_tpm2_pksign (ctrl_t ctrl, const char *desc_text,
+                    const unsigned char *digest, size_t digestlen, int algo,
+                    const unsigned char *shadow_info, unsigned char **r_sig,
+                    size_t *r_siglen)
+{
+  return agent_tpm2d_pksign(ctrl, digest, digestlen,
+    shadow_info, r_sig, r_siglen);
+}
+
+
+static gpg_error_t
+agent_write_tpm2_shadow_key (ctrl_t ctrl, const unsigned char *grip,
+     unsigned char *shadow_info)
+{
+  gpg_error_t err;
+  unsigned char *shdkey;
+  unsigned char *pkbuf;
+  size_t len;
+  gcry_sexp_t s_pkey;
+
+  err = agent_public_key_from_file (ctrl, grip, &s_pkey);
+  len = gcry_sexp_sprint(s_pkey, GCRYSEXP_FMT_CANON, NULL, 0);
+  pkbuf = xtrymalloc (len);
+  gcry_sexp_sprint (s_pkey, GCRYSEXP_FMT_CANON, pkbuf, len);
+  gcry_sexp_release (s_pkey);
+
+  err = agent_shadow_key_type (pkbuf, shadow_info, "tpm2-v1", &shdkey);
+  xfree (pkbuf);
+  if (err)
+    {
+      log_error ("shadowing the key failed: %s\n", gpg_strerror (err));
+      return err;
+    }
+
+  len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL);
+  err = agent_write_private_key (grip, shdkey, len, 1 /*force*/, NULL, NULL, NULL);
+  xfree (shdkey);
+  if (err)
+    log_error ("error writing key: %s\n", gpg_strerror (err));
+
+  return err;
+}
+
+int
+divert_tpm2_writekey (ctrl_t ctrl, const unsigned char *grip,
+                      gcry_sexp_t s_skey)
+{
+  int ret;
+  /* shadow_info is always shielded so no special handling required */
+  unsigned char *shadow_info;
+
+  ret = agent_tpm2d_writekey(ctrl, &shadow_info, s_skey);
+  if (!ret) {
+    ret = agent_write_tpm2_shadow_key (ctrl, grip, shadow_info);
+    xfree (shadow_info);
+  }
+  return ret;
+}
+
+int
+divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
+                       const unsigned char *cipher,
+                       const unsigned char *shadow_info,
+                       char **r_buf, size_t *r_len, int *r_padding)
+{
+  const unsigned char *s;
+  size_t n;
+
+  *r_padding = -1;
+
+  (void)desc_text;
+
+  s = cipher;
+  if (*s != '(')
+    return gpg_error (GPG_ERR_INV_SEXP);
+  s++;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  if (!smatch (&s, n, "enc-val"))
+    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+  if (*s != '(')
+    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+  s++;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  if (smatch (&s, n, "rsa"))
+    {
+      *r_padding = 0;
+      if (*s != '(')
+        return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+      s++;
+      n = snext (&s);
+      if (!n)
+        return gpg_error (GPG_ERR_INV_SEXP);
+      if (!smatch (&s, n, "a"))
+        return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+      n = snext (&s);
+    }
+  else if (smatch (&s, n, "ecdh"))
+    {
+      if (*s != '(')
+        return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+      s++;
+      n = snext (&s);
+      if (!n)
+        return gpg_error (GPG_ERR_INV_SEXP);
+      if (smatch (&s, n, "s"))
+        {
+          n = snext (&s);
+          s += n;
+          if (*s++ != ')')
+            return gpg_error (GPG_ERR_INV_SEXP);
+          if (*s++ != '(')
+            return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+          n = snext (&s);
+          if (!n)
+            return gpg_error (GPG_ERR_INV_SEXP);
+        }
+      if (!smatch (&s, n, "e"))
+        return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+      n = snext (&s);
+    }
+  else
+    return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+
+  return agent_tpm2d_pkdecrypt (ctrl, s, n, shadow_info, r_buf, r_len);
+}
diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c
index b3a0c230c..5fdb94f17 100644
--- a/agent/gpg-agent.c
+++ b/agent/gpg-agent.c
@@ -102,6 +102,7 @@ enum cmd_and_opt_values
   oLCmessages,
   oXauthority,
   oScdaemonProgram,
+  oTpm2daemonProgram,
   oDefCacheTTL,
   oDefCacheTTLSSH,
   oMaxCacheTTL,
@@ -199,6 +200,8 @@ static gpgrt_opt_t opts[] = {
                 /* */             N_("do not use the SCdaemon") ),
   ARGPARSE_s_s (oScdaemonProgram, "scdaemon-program",
                 /* */             N_("|PGM|use PGM as the SCdaemon program") ),
+  ARGPARSE_s_s (oTpm2daemonProgram, "tpm2daemon-program",
+ /* */             N_("|PGM|use PGM as the tpm2daemon program") ),
   ARGPARSE_s_n (oDisableCheckOwnSocket, "disable-check-own-socket", "@"),
 
   ARGPARSE_s_s (oExtraSocket, "extra-socket",
@@ -905,6 +908,7 @@ parse_rereadable_options (gpgrt_argparse_t *pargs, int reread)
       opt.pinentry_invisible_char = xtrystrdup (pargs->r.ret_str); break;
       break;
     case oPinentryTimeout: opt.pinentry_timeout = pargs->r.ret_ulong; break;
+    case oTpm2daemonProgram: opt.daemon_program[DAEMON_TPM2D] = pargs->r.ret_str; break;
     case oScdaemonProgram: opt.daemon_program[DAEMON_SCD] = pargs->r.ret_str; break;
     case oDisableScdaemon: opt.disable_daemon[DAEMON_SCD] = 1; break;
     case oDisableCheckOwnSocket: disable_check_own_socket = 1; break;
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index 88c3a2d36..3467f3bc5 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -312,8 +312,9 @@ to keys stored on a token:
    (comment whatever)
 )
 
-The currently used protocol is "t1-v1" (token info version 1).  The
-second list with the information has this layout:
+The currently used protocols are "t1-v1" (token info version 1) and
+"tpm2-v1" (TPM format key information).  The second list with the
+information has this layout for "t1-v1":
 
 (card_serial_number id_string_of_key fixed_pin_length)
 
@@ -322,6 +323,13 @@ the PIN; a value of 0 indicates that this information is not
 available.  The rationale for this field is that some pinpad equipped
 readers don't allow passing a variable length PIN.
 
+This is the (info) layout for "tpm2-v1":
+
+(parent tpm_private_string tpm_public_string)
+
+Although this precise format is encapsulated inside the tpm2daemon
+itself and nothing in gpg ever uses this.
+
 More items may be added to the list.
 
 ** OpenPGP Private Key Transfer Format
diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c
index da370bb0a..0bd989d62 100644
--- a/agent/pkdecrypt.c
+++ b/agent/pkdecrypt.c
@@ -88,8 +88,12 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
           goto leave;
         }
 
-      err = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext,
-                              shadow_info, &buf, &len, r_padding);
+      if (agent_is_tpm2_key (s_skey))
+ err = divert_tpm2_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info,
+    &buf, &len, r_padding);
+      else
+      err = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext,
+     shadow_info, &buf, &len, r_padding);
       if (err)
         {
           log_error ("smartcard decryption failed: %s\n", gpg_strerror (err));
diff --git a/agent/pksign.c b/agent/pksign.c
index ca9a35292..00b31ee45 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -397,11 +397,17 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         if (desc_text)
           agent_modify_description (desc_text, NULL, s_pkey, &desc2);
 
-        err = divert_pksign (ctrl, desc2? desc2 : desc_text,
-                             ctrl->keygrip,
-                             data, datalen,
-                             ctrl->digest.algo,
-                             shadow_info, &buf, &len);
+ if (agent_is_tpm2_key (s_skey))
+  err = divert_tpm2_pksign (ctrl, desc2? desc2 : desc_text,
+    data, datalen,
+    ctrl->digest.algo,
+    shadow_info, &buf, &len);
+ else
+  err = divert_pksign (ctrl, desc2? desc2 : desc_text,
+       ctrl->keygrip,
+       data, datalen,
+       ctrl->digest.algo,
+       shadow_info, &buf, &len);
         xfree (desc2);
       }
       if (err)
--
2.26.2


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 3/5] g10: add new command keytotpm to convert a private key to TPM format

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
The plumbing is done in two parts: the agent is modified to understand
a KEYTOTPM assuan command taking the key grip as an argument.  This
simply obtains the key s expression and calls the existing writeky
diversion to the tpm2daemon.  The daemon reponds with the TPM
conversion of the key and that key is then stored in the keyfile as a
shadowed-private-key with "tpm2-v1" type.

To effect the conversion, all the user does from gpg --edit-key is
select which private key they wish to move (or move the primary if no
key is selected) and type keytotpm.  The conversion to TPM form is
instantaneous and once converted, the actual key cannot be recovered,
meaning that if you want your gpg key to move to a new laptop you must
keep an unconverted backup copy in a safe location.

When you do a list command, all TPM keys show up as

     card-no: TPM-Protected

The key is stored encrypted to the TPM2 storage seed and since each
TPM has a unique seed, only the single TPM contained in your laptop
can now read the key.  This means you cannot simply copy the shadowed
key file over to a new laptop, you must copy over the backup copy and
then convert it to TPM form on the new laptop.

To decomission your laptop, execute a tssclear command which
regenerates the storage seed and effectively shreds all keys.  Note
when you have done this *every* TPM2 shadowed private key becomes
unreadable by any TPM and all are effectively destroyed.

Signed-off-by: James Bottomley <[hidden email]>
---
 agent/command.c  | 52 ++++++++++++++++++++++++++++++++++++++++++++++++
 g10/call-agent.c | 22 ++++++++++++++++++++
 g10/call-agent.h |  3 +++
 g10/keyedit.c    | 45 ++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 121 insertions(+), 1 deletion(-)

diff --git a/agent/command.c b/agent/command.c
index 87446a233..095f38ba3 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -3113,6 +3113,57 @@ cmd_put_secret (assuan_context_t ctx, char *line)
 }
 
 
+
+static const char hlp_keytotpm[] =
+  "KEYTOTPM <hexstring_with_keygrip>\n"
+  "\n";
+static gpg_error_t
+cmd_keytotpm (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err = 0;
+  unsigned char grip[20];
+  gcry_sexp_t s_skey;
+  unsigned char *shadow_info = NULL;
+
+  if (ctrl->restricted)
+    return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN));
+
+  err = parse_keygrip (ctx, line, grip);
+  if (err)
+    goto leave;
+
+  if (agent_key_available (grip))
+    {
+      err =gpg_error (GPG_ERR_NO_SECKEY);
+      goto leave;
+    }
+
+  err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip,
+                             &shadow_info, CACHE_MODE_IGNORE, NULL,
+                             &s_skey, NULL);
+  if (err)
+    {
+      xfree (shadow_info);
+      goto leave;
+    }
+  if (shadow_info)
+    {
+      /* Key is on a TPM or smartcard already.  */
+      xfree (shadow_info);
+      gcry_sexp_release (s_skey);
+      err = gpg_error (GPG_ERR_UNUSABLE_SECKEY);
+      goto leave;
+    }
+
+  err = divert_tpm2_writekey (ctrl, grip, s_skey);
+  gcry_sexp_release (s_skey);
+
+ leave:
+  return leave_cmd (ctx, err);
+}
+
+
 
 static const char hlp_getval[] =
   "GETVAL <key>\n"
@@ -3812,6 +3863,7 @@ register_commands (assuan_context_t ctx)
     { "RELOADAGENT",    cmd_reloadagent,hlp_reloadagent },
     { "GETINFO",        cmd_getinfo,   hlp_getinfo },
     { "KEYTOCARD",      cmd_keytocard, hlp_keytocard },
+    { "KEYTOTPM", cmd_keytotpm, hlp_keytotpm },
     { NULL }
   };
   int i, rc;
diff --git a/g10/call-agent.c b/g10/call-agent.c
index a553ef67a..fb80489b2 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -1060,6 +1060,28 @@ agent_scd_apdu (const char *hexapdu, unsigned int *r_sw)
   return err;
 }
 
+int
+agent_keytotpm (ctrl_t ctrl, const char *hexgrip)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s parm;
+
+  snprintf(line, DIM(line), "KEYTOTPM %s\n", hexgrip);
+
+  rc = start_agent (ctrl, 0);
+  if (rc)
+    return rc;
+  parm.ctx = agent_ctx;
+  parm.ctrl = ctrl;
+
+  rc = assuan_transact (agent_ctx, line, NULL, NULL, default_inq_cb, &parm,
+ NULL, NULL);
+  if (rc)
+    log_log (GPGRT_LOGLVL_ERROR, _("error from TPM: %s\n"), gpg_strerror (rc));
+  return rc;
+}
+
 
 /* Used by:
  *  card_store_subkey
diff --git a/g10/call-agent.h b/g10/call-agent.h
index 4a66af2aa..efea7ec4a 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -126,6 +126,9 @@ gpg_error_t agent_scd_getattr_one (const char *name, char **r_value);
 /* Update INFO with the attribute NAME. */
 int agent_scd_getattr (const char *name, struct agent_card_info_s *info);
 
+/* send the KEYTOTPM command */
+int agent_keytotpm (ctrl_t ctrl, const char *hexgrip);
+
 /* Send the KEYTOCARD command. */
 int agent_keytocard (const char *hexgrip, int keyno, int force,
                      const char *serialno, const char *timestamp);
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 596662dda..8740f5cf7 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -1247,7 +1247,7 @@ enum cmdids
 #endif /*!NO_TRUST_MODELS*/
   cmdSHOWPREF,
   cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
-  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD,
+  cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdKEYTOTPM, cmdBKUPTOCARD,
   cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP
 };
 
@@ -1298,6 +1298,8 @@ static struct
     N_("add a key to a smartcard")},
   { "keytocard", cmdKEYTOCARD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
     N_("move a key to a smartcard")},
+  { "keytotpm", cmdKEYTOTPM, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
+    N_("convert a key to TPM form using the local TPM")},
   { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK,
     N_("move a backup key to a smartcard")},
 #endif /*ENABLE_CARD_SUPPORT */
@@ -1796,6 +1798,47 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
     }
   break;
 
+ case cmdKEYTOTPM:
+  /* FIXME need to store the key and not commit until later */
+  {
+    KBNODE node = NULL;
+    switch (count_selected_keys (keyblock))
+      {
+      case 0:
+ if (cpr_get_answer_is_yes
+                    ("keyedit.keytocard.use_primary",
+                     /* TRANSLATORS: Please take care: This is about
+                        moving the key and not about removing it.  */
+                     _("Really move the primary key? (y/N) ")))
+  node = keyblock;
+ break;
+      case 1:
+ for (node = keyblock; node; node = node->next)
+  {
+    if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ && node->flag & NODFLG_SELKEY)
+      break;
+  }
+ break;
+      default:
+ tty_printf (_("You must select exactly one key.\n"));
+ break;
+      }
+    if (node)
+      {
+ PKT_public_key *xxpk = node->pkt->pkt.public_key;
+ char *hexgrip;
+
+ hexkeygrip_from_pk (xxpk, &hexgrip);
+ if (!agent_keytotpm (ctrl, hexgrip))
+  {
+    redisplay = 1;
+  }
+ xfree (hexgrip);
+      }
+  }
+  break;
+
  case cmdKEYTOCARD:
   {
     KBNODE node = NULL;
--
2.26.2


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 4/5] tpm2d: add tests for the tpm2daemon

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
Add a set of tests that exercise tpm2daemon handling of keys and
verify compatibility with non-tpm based keys.

Running this test infrastructure requires a tpm emulator, which is
tested for during configuration.  If an emulator is not found, the
tests won't be run since they require the presence of a TPM (although
the TPM handling code will still be built).

Signed-off-by: James Bottomley <[hidden email]>
---
 configure.ac                        |  13 +
 tests/Makefile.am                   |   3 +
 tests/tpm2dtests/Makefile.am        |  79 +++++
 tests/tpm2dtests/all-tests.scm      |  81 +++++
 tests/tpm2dtests/defs.scm           | 473 ++++++++++++++++++++++++++++
 tests/tpm2dtests/ecc.scm            |  23 ++
 tests/tpm2dtests/longpassphrase.scm |  36 +++
 tests/tpm2dtests/rsa.scm            |  13 +
 tests/tpm2dtests/run-tests.scm      |  43 +++
 tests/tpm2dtests/setup.scm          |  48 +++
 tests/tpm2dtests/shell.scm          |  51 +++
 tests/tpm2dtests/unimportable.scm   |  28 ++
 12 files changed, 891 insertions(+)
 create mode 100644 tests/tpm2dtests/Makefile.am
 create mode 100644 tests/tpm2dtests/all-tests.scm
 create mode 100644 tests/tpm2dtests/defs.scm
 create mode 100644 tests/tpm2dtests/ecc.scm
 create mode 100644 tests/tpm2dtests/longpassphrase.scm
 create mode 100644 tests/tpm2dtests/rsa.scm
 create mode 100644 tests/tpm2dtests/run-tests.scm
 create mode 100644 tests/tpm2dtests/setup.scm
 create mode 100644 tests/tpm2dtests/shell.scm
 create mode 100644 tests/tpm2dtests/unimportable.scm

diff --git a/configure.ac b/configure.ac
index 841115af0..a7e891e5e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1609,12 +1609,17 @@ if test "$have_libtss" = yes; then
     LIBTSS_LIBS=$LIBS
     AC_DEFINE(HAVE_LIBTSS, 1, [Defined if we have TPM2 support library])
     AC_SUBST(TSS_INCLUDE)
+    # look for a TPM emulator for testing
+    AC_PATH_PROG(TPMSERVER, tpm_server,,/bin:/usr/bin:/usr/lib/ibmtss:/usr/libexec/ibmtss)
+    AC_PATH_PROG(SWTPM, swtpm,,/bin:/usr/bin:/usr/lib/ibmtss:/usr/libexec/ibmtss)
+    AC_PATH_PROG(SWTPM_IOCTL, swtpm_ioctl,,/bin:/usr/bin:/usr/lib/ibmtss:/usr/libexec/ibmtss)
 fi
 LIBS="$_save_libs"
 CFLAGS="$_save_cflags"
 AC_SUBST(LIBTSS_LIBS)
 AC_SUBST(LIBTSS_CFLAGS)
 AM_CONDITIONAL(HAVE_LIBTSS, test "$have_libtss" = yes)
+AM_CONDITIONAL(TEST_LIBTSS, test -n "$TPMSERVER" -o -n "$SWTPM")
 AC_SUBST(HAVE_LIBTSS)
 
 #
@@ -2091,6 +2096,7 @@ doc/Makefile
 tests/Makefile
 tests/gpgscm/Makefile
 tests/openpgp/Makefile
+tests/tpm2dtests/Makefile
 tests/migrations/Makefile
 tests/gpgsm/Makefile
 tests/gpgme/Makefile
@@ -2141,6 +2147,13 @@ echo "
         Tor support:         $show_tor_support
         TPM support:         $have_libtss
 "
+if test "$have_libtss" != no -a -z "$TPMSERVER" -a -z "$SWTPM"; then
+cat <<G10EOF
+        Warning: TPM support is compiled in but no software TPM
+                 for testing was discovered. TPM tests are disabled
+
+G10EOF
+fi
 if test "x${gpg_config_script_warn}" != x; then
 cat <<G10EOF
         Warning: Mismatches between the target platform and the
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 65978ae3f..7697a43bf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -19,6 +19,9 @@
 ## Process this file with automake to produce Makefile.in
 
 SUBDIRS = gpgscm openpgp migrations gpgsm gpgme pkits .
+if TEST_LIBTSS
+SUBDIRS += tpm2dtests
+endif
 
 GPGSM = ../sm/gpgsm
 
diff --git a/tests/tpm2dtests/Makefile.am b/tests/tpm2dtests/Makefile.am
new file mode 100644
index 000000000..36be90efd
--- /dev/null
+++ b/tests/tpm2dtests/Makefile.am
@@ -0,0 +1,79 @@
+# Makefile.am - For tests/openpgp
+# Copyright (C) 1998, 1999, 2000, 2001, 2003,
+#               2010 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/>.
+# Process this file with automake to create Makefile.in
+
+
+# Programs required before we can run these tests.
+required_pgms = ../../g10/gpg$(EXEEXT) ../../agent/gpg-agent$(EXEEXT) \
+                ../../tools/gpg-connect-agent$(EXEEXT) \
+ ../gpgscm/gpgscm$(EXEEXT) ../openpgp/fake-pinentry$(EXEEXT)
+
+AM_CPPFLAGS = -I$(top_srcdir)/common
+include $(top_srcdir)/am/cmacros.am
+
+AM_CFLAGS =
+
+TESTS_ENVIRONMENT = LC_ALL=C \
+ EXEEXT=$(EXEEXT) \
+ PATH="../gpgscm:$(PATH)" \
+ abs_top_srcdir="$(abs_top_srcdir)" \
+ objdir="$(abs_top_builddir)" \
+ TPMSERVER="$(TPMSERVER)" \
+ SWTPM="$(SWTPM)" \
+ SWTPM_IOCTL="$(SWTPM_IOCTL)" \
+ GPGSCM_PATH="$(abs_top_srcdir)/tests/gpgscm"
+
+XTESTS = \
+ rsa.scm \
+ ecc.scm \
+ longpassphrase.scm \
+ unimportable.scm
+
+# XXX: Currently, one cannot override automake's 'check' target.  As a
+# workaround, we avoid defining 'TESTS', thus automake will not emit
+# the 'check' target.  For extra robustness, we merely define a
+# dependency on 'xcheck', so this hack should also work even if
+# automake would emit the 'check' target, as adding dependencies to
+# targets is okay.
+check: xcheck
+
+.PHONY: xcheck
+xcheck: tpm_server_found
+ $(TESTS_ENVIRONMENT) $(abs_top_builddir)/tests/gpgscm/gpgscm \
+  $(abs_srcdir)/run-tests.scm $(TESTFLAGS) $(TESTS)
+
+tpm_server_found:
+ @if [ -z "$(TPMSERVER)" -a -z "$(SWTPM)" -a -z "$(FORCE)" ]; then echo "ERROR: No Software TPM has been found, cannot run TPM tests.  Set FORCE=1 to force using the physical TPM"; exit 1; fi
+
+EXTRA_DIST = defs.scm shell.scm all-tests.scm
+
+CLEANFILES = gpg.conf gpg-agent.conf S.gpg-agent \
+     pubring.gpg pubring.gpg~ pubring.kbx pubring.kbx~ \
+     secring.gpg pubring.pkr secring.skr \
+     gnupg-test.stop random_seed gpg-agent.log tofu.db \
+     passphrases sshcontrol S.gpg-agent.ssh report.xml \
+     msg.txt
+
+clean-local:
+ -rm -rf private-keys-v1.d openpgp-revocs.d
+
+
+# We need to depend on a couple of programs so that the tests don't
+# start before all programs are built.
+all-local: $(required_pgms)
diff --git a/tests/tpm2dtests/all-tests.scm b/tests/tpm2dtests/all-tests.scm
new file mode 100644
index 000000000..bf7a981ca
--- /dev/null
+++ b/tests/tpm2dtests/all-tests.scm
@@ -0,0 +1,81 @@
+;; Copyright (C) 2017 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(export all-tests
+ ;; Parse the Makefile.am to find all tests.
+
+ (load (with-path "makefile.scm"))
+
+ (define (expander filename port key)
+   (parse-makefile port key))
+
+ (define (parse filename key)
+   (parse-makefile-expand filename expander key))
+
+ (define setup
+   (make-environment-cache
+    (test::scm
+     #f
+     (path-join "tests" "openpgp" "setup.scm")
+     (in-srcdir "tests" "openpgp" "setup.scm"))))
+
+ (define (qualify path variant)
+   (string-append "<" variant ">" path))
+
+ (define (setup* variant)
+   (make-environment-cache
+    (test::scm
+     #f
+     (qualify (path-join "tests" "openpgp" "setup.scm") variant)
+     (in-srcdir "tests" "openpgp" "setup.scm")
+     (string-append "--" variant))))
+
+ (define setup-use-keyring (setup* "use-keyring"))
+ (define setup-use-keyboxd (setup* "use-keyboxd"))
+
+ (define all-tests
+   (parse-makefile-expand "Makefile"
+  (lambda (filename port key) (parse-makefile port key))
+  "XTESTS"))
+
+ (define tests
+   (map (lambda (name)
+  (test::scm setup
+     (qualify (path-join "tests" "tpm2dtests" name) "standard")
+     (in-srcdir "tests" "tpm2dtests" name))) all-tests))
+
+ (when *run-all-tests*
+       (set! tests
+     (append
+      tests
+              ;; The second pass uses the keyboxd
+      (map (lambda (name)
+     (test::scm setup-use-keyboxd
+ (qualify (path-join "tests" "tpm2dtests" name)
+ "keyboxd")
+ (in-srcdir "tests" "tpm2dtests" name)
+ "--use-keyboxd")) all-tests)
+              ;; The third pass uses the legact pubring.gpg
+      (map (lambda (name)
+     (test::scm setup-use-keyring
+ (qualify (path-join "tests" "tpm2dtests" name)
+ "keyring")
+ (in-srcdir "tests" "tpm2dtests" name)
+ "--use-keyring")) all-tests)
+              )))
+
+ tests)
diff --git a/tests/tpm2dtests/defs.scm b/tests/tpm2dtests/defs.scm
new file mode 100644
index 000000000..2a0910945
--- /dev/null
+++ b/tests/tpm2dtests/defs.scm
@@ -0,0 +1,473 @@
+;; Common definitions for the OpenPGP test scripts.
+;;
+;; Copyright (C) 2016, 2017 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(let ((verbose (string->number (getenv "verbose"))))
+  (if (number? verbose)
+      (*set-verbose!* verbose)))
+
+(define (qualify executable)
+  (string-append executable (getenv "EXEEXT")))
+
+(define (getenv' key default)
+  (let ((value (getenv key)))
+    (if (string=? "" value)
+ default
+ value)))
+
+(define (percent-decode s)
+  (define (decode c)
+    (if (and (> (length c) 2) (char=? #\% (car c)))
+ (integer->char (string->number (string #\# #\x (cadr c) (caddr c))))
+ #f))
+  (let loop ((i 0) (c (string->list s)) (r (make-string (string-length s))))
+    (if (null? c)
+ (substring r 0 i)
+ (let ((decoded (decode c)))
+  (string-set! r i (if decoded decoded (car c)))
+  (loop (+ 1 i) (if decoded (cdddr c) (cdr c)) r)))))
+(assert (equal? (percent-decode "") ""))
+(assert (equal? (percent-decode "%61") "a"))
+(assert (equal? (percent-decode "foob%61r") "foobar"))
+
+(define (percent-encode s)
+  (define (encode c)
+    `(#\% ,@(string->list (number->string (char->integer c) 16))))
+  (let loop ((acc '()) (cs (reverse (string->list s))))
+    (if (null? cs)
+ (list->string acc)
+ (case (car cs)
+  ((#\: #\%)
+   (loop (append (encode (car cs)) acc) (cdr cs)))
+  (else
+   (loop (cons (car cs) acc) (cdr cs)))))))
+(assert (equal? (percent-encode "") ""))
+(assert (equal? (percent-encode "%61") "%2561"))
+(assert (equal? (percent-encode "foob%61r") "foob%2561r"))
+
+(define tools
+  '((gpgv "GPGV" "g10/gpgv")
+    (gpg-connect-agent "GPG_CONNECT_AGENT" "tools/gpg-connect-agent")
+    (gpgconf "GPGCONF" "tools/gpgconf")
+    (gpg-preset-passphrase "GPG_PRESET_PASSPHRASE"
+   "agent/gpg-preset-passphrase")
+    (gpgtar "GPGTAR" "tools/gpgtar")
+    (gpg-zip "GPGZIP" "tools/gpg-zip")
+    (pinentry "PINENTRY" "tests/openpgp/fake-pinentry")
+    (tpm2daemon "TPM2DAEMON" "tpm2d/tpm2daemon")))
+
+(define bin-prefix (getenv "BIN_PREFIX"))
+(define installed? (not (string=? "" bin-prefix)))
+(define with-valgrind? (not (string=? (getenv "with_valgrind") "")))
+
+(define (tool-hardcoded which)
+  (let ((t (assoc which tools)))
+    (getenv' (cadr t)
+     (qualify (if installed?
+  (string-append bin-prefix "/" (basename (caddr t)))
+  (string-append (getenv "objdir") "/" (caddr t)))))))
+
+;; You can splice VALGRIND into your argument vector to run programs
+;; under valgrind.  For example, to run valgrind on gpg, you may want
+;; to redefine gpg:
+;;
+;; (set! gpg `(,@valgrind ,@gpg))
+;;
+(define valgrind
+  '("/usr/bin/valgrind" -q --leak-check=no --track-origins=yes
+                        --error-exitcode=154 --exit-on-first-error=yes))
+
+(unless installed?
+ (setenv "GNUPG_BUILDDIR" (getenv "objdir") #t))
+
+(define (gpg-conf . args)
+  (gpg-conf' "" args))
+(define (gpg-conf' input args)
+  (let ((s (call-popen `(,(tool-hardcoded 'gpgconf)
+ ,@(if installed? '()
+       (list '--build-prefix (getenv "objdir")))
+ ,@args) input)))
+    (map (lambda (line) (map percent-decode (string-split line #\:)))
+ (string-split-newlines s))))
+(define :gc:c:name car)
+(define :gc:c:description cadr)
+(define :gc:c:pgmname caddr)
+(define (:gc:o:name x)             (list-ref x 0))
+(define (:gc:o:flags x)            (string->number (list-ref x 1)))
+(define (:gc:o:level x)            (string->number (list-ref x 2)))
+(define (:gc:o:description x)      (list-ref x 3))
+(define (:gc:o:type x)             (string->number (list-ref x 4)))
+(define (:gc:o:alternate-type x)   (string->number (list-ref x 5)))
+(define (:gc:o:argument-name x)    (list-ref x 6))
+(define (:gc:o:default-value x)    (list-ref x 7))
+(define (:gc:o:default-argument x) (list-ref x 8))
+(define (:gc:o:value x)            (if (< (length x) 10) "" (list-ref x 9)))
+
+(define (gpg-config component key)
+  (package
+   (define (value)
+     (let* ((conf (assoc key (gpg-conf '--list-options component)))
+    (type (:gc:o:type conf))
+    (value (:gc:o:value conf)))
+       (case type
+ ((0 2 3) (string->number value))
+ ((1 32) (substring value 1 (string-length value))))))
+   (define (update value)
+     (let ((value' (cond
+    ((string? value) (string-append "\"" value))
+    ((number? value) (number->string value))
+    (else (throw "Unsupported value" value)))))
+       (gpg-conf' (string-append key ":0:" (percent-encode value'))
+  `(--change-options ,component))))
+   (define (clear)
+     (gpg-conf' (string-append key ":16:")
+ `(--change-options ,component)))))
+
+(define gpg-components (apply gpg-conf '(--list-components)))
+
+(define (tool which)
+  (case which
+    ((gpg gpg-agent scdaemon gpgsm dirmngr)
+     (:gc:c:pgmname (assoc (symbol->string which) gpg-components)))
+    (else
+     (tool-hardcoded which))))
+
+(define (gpg-has-option? option)
+  (string-contains? (call-popen `(,(tool 'gpg) --dump-options) "")
+    option))
+
+(define have-opt-always-trust
+  (catch #f
+ (with-ephemeral-home-directory (lambda ()) (lambda ())
+   (call-check `(,(tool 'gpg) --gpgconf-test --always-trust)))
+ #t))
+
+(define GPG `(,(tool 'gpg) --no-permission-warning
+      ,@(if have-opt-always-trust '(--always-trust) '())))
+(define GPGV `(,(tool 'gpgv)))
+(define PINENTRY (tool 'pinentry))
+(define TPM2DAEMON (tool 'tpm2daemon))
+
+(define (tr:gpg input args)
+  (tr:spawn input `(,@GPG --output **out** ,@args **in**)))
+
+(define (pipe:gpg args)
+  (pipe:spawn `(,@GPG --output - ,@args)))
+
+(define (gpg-with-colons args)
+  (let ((s (call-popen `(,@GPG --with-colons ,@args) "")))
+    (map (lambda (line) (string-split line #\:))
+ (string-split-newlines s))))
+
+(define (secinfo name)
+  (assoc "sec" (gpg-with-colons `(--list-secret-key ,name))))
+(define (ssbinfo name)
+  (assoc "ssb" (gpg-with-colons `(--list-secret-key ,name))))
+(define (fingerprint name)
+  (:fpr (assoc "fpr" (gpg-with-colons `(--list-secret-key ,name)))))
+;; convenient accessors for sec
+(define (:cardinfo x) (list-ref x 14))
+;; Convenient accessors for the colon output of pub.
+(define (:type x)   (string->symbol (list-ref x 0)))
+(define (:length x) (string->number (list-ref x 2)))
+(define (:alg x) (string->number (list-ref x 3)))
+(define (:expire x) (list-ref x 6))
+(define (:fpr x) (list-ref x 9))
+(define (:cap x) (list-ref x 11))
+
+(define (have-public-key? key)
+  (catch #f
+ (pair? (filter (lambda (l) (and (equal? 'fpr (:type l))
+ (equal? key::fpr (:fpr l))))
+ (gpg-with-colons `(--list-keys ,key::fpr))))))
+
+(define (have-secret-key? key)
+  (catch #f
+ (pair? (filter (lambda (l) (and (equal? 'fpr (:type l))
+ (equal? key::fpr (:fpr l))))
+ (gpg-with-colons `(--list-secret-keys ,key::fpr))))))
+
+(define (have-secret-key-file? key)
+  (file-exists? (path-join (getenv "GNUPGHOME") "private-keys-v1.d"
+   (string-append key::grip ".key"))))
+
+(define (get-config what)
+  (string-split (caddar (gpg-with-colons `(--list-config ,what))) #\;))
+
+(define all-pubkey-algos (delay (get-config "pubkeyname")))
+(define all-hash-algos (delay (get-config "digestname")))
+(define all-cipher-algos (delay (get-config "ciphername")))
+(define all-compression-algos (delay (get-config "compressname")))
+
+(define (have-pubkey-algo? x)
+  (not (not (member x (force all-pubkey-algos)))))
+(define (have-hash-algo? x)
+  (not (not (member x (force all-hash-algos)))))
+(define (have-cipher-algo? x)
+  (not (not (member x (force all-cipher-algos)))))
+(define (have-compression-algo? x)
+  (not (not (member x (force all-compression-algos)))))
+
+(define (gpg-pipe args0 args1 errfd)
+  (lambda (source sink)
+    (let* ((p (pipe))
+   (task0 (spawn-process-fd `(,@GPG ,@args0)
+   source (:write-end p) errfd))
+   (_ (close (:write-end p)))
+   (task1 (spawn-process-fd `(,@GPG ,@args1)
+   (:read-end p) sink errfd)))
+      (close (:read-end p))
+      (wait-processes (list GPG GPG) (list task0 task1) #t))))
+
+;;
+;; Do we have a software tpm
+;;
+(define have-swtpm? (not (and (string=? "" (getenv "TPMSERVER"))
+      (string=? "" (getenv "SWTPM")))))
+(setenv "GPG_AGENT_INFO" "" #t)
+(setenv "GNUPGHOME" (getcwd) #t)
+(if have-swtpm?
+    (setenv "TPM_INTERFACE_TYPE" "socsim" #t))
+(define GNUPGHOME (getcwd))
+
+;;
+;; GnuPG helper.
+;;
+
+;; Call GPG to obtain the hash sums.  Either specify an input file in
+;; ARGS, or an string in INPUT.  Returns a list of (<algo>
+;; "<hashsum>") lists.
+(define (gpg-hash-string args input)
+  (map
+   (lambda (line)
+     (let ((p (string-split line #\:)))
+       (list (string->number (cadr p)) (caddr p))))
+   (string-split-newlines
+    (call-popen `(,@GPG --with-colons ,@args) input))))
+
+;; Dearmor a file.
+(define (dearmor source-name sink-name)
+  (pipe:do
+   (pipe:open source-name (logior O_RDONLY O_BINARY))
+   (pipe:spawn `(,@GPG --dearmor))
+   (pipe:write-to sink-name (logior O_WRONLY O_CREAT O_BINARY) #o600)))
+
+(define (gpg-dump-packets source-name sink-name)
+  (pipe:do
+   (pipe:open source-name (logior O_RDONLY O_BINARY))
+   (pipe:spawn `(,@GPG --list-packets))
+   (pipe:write-to sink-name (logior O_WRONLY O_CREAT O_BINARY) #o600)))
+
+;;
+;; Support for test environment creation and teardown.
+;;
+
+(define (make-test-data filename size)
+  (call-with-binary-output-file
+   filename
+   (lambda (port)
+     (display (make-random-string size) port))))
+
+(define (create-file name . lines)
+  (catch #f (unlink name))
+  (letfd ((fd (open name (logior O_WRONLY O_CREAT O_BINARY) #o600)))
+    (let ((port (fdopen fd "wb")))
+      (for-each (lambda (line) (display line port) (newline port))
+ lines))))
+
+(define (create-gpghome)
+  (log "Creating test environment...")
+
+  (srandom (getpid))
+  (make-test-data "random_seed" 600)
+
+  (log "Creating configuration files")
+
+  (if (flag "--use-keyring" *args*)
+      (create-file "pubring.gpg"))
+
+  (create-file "gpg.conf"
+               ;;"log-file socket:///tmp/S.wklog"
+               ;;"verbose"
+       "no-greeting"
+       "no-secmem-warning"
+       "no-permission-warning"
+       "batch"
+               "no-auto-key-retrieve"
+               "no-auto-key-locate"
+       "allow-weak-digest-algos"
+               "ignore-mdc-error"
+       (if have-opt-always-trust
+   "no-auto-check-trustdb" "#no-auto-check-trustdb")
+       (string-append "agent-program "
+      (tool 'gpg-agent)
+      "|--debug-quick-random\n")
+       (if (flag "--use-keyboxd" *args*)
+   "use-keyboxd" "#use-keyboxd")
+       )
+  (create-file "gpg-agent.conf"
+       "allow-preset-passphrase"
+       "debug-all"
+       "log-file gpg-agent.log"
+       "no-grab"
+       "enable-ssh-support"
+               "s2k-count 65536"
+       (string-append "pinentry-program " (tool 'pinentry))
+       (string-append "tpm2daemon-program " (tool 'tpm2daemon))
+       "disable-scdaemon")
+  (create-file "msg.txt"
+       "This is a test of TPM signing and encryption"
+       "With two lines of text"))
+
+;; Initialize the test environment, install appropriate configuration
+;; and start the agent, without any keys.
+(define (setup-environment)
+  (create-gpghome)
+  (start-agent)
+  (start-tpm))
+
+(define (setup-environment-no-atexit)
+  (create-gpghome)
+  (start-agent #t))
+
+;; Initialize the test environment, install appropriate configuration
+;; and start the agent, with the keys from the legacy test suite.
+(define (setup-legacy-environment)
+  (create-gpghome)
+  (if (member "--unpack-tarball" *args*)
+      (begin
+ (call-check `(,(tool 'gpgtar) --extract --directory=. ,(cadr *args*)))
+ (start-agent))
+      (begin
+ (start-agent)
+ (create-legacy-gpghome)))
+  (preset-passphrases))
+
+;; start the tpm server
+(define (start-tpm)
+  (if have-swtpm?
+      (begin (define pid (call-check `(,(in-srcdir "tests" "tpm2dtests" "start_sw_tpm.sh"))))
+     (if (not (null? pid))
+ (atexit (lambda ()
+   (call-check `("/bin/kill" ,pid))))))))
+
+;; Create the socket dir and start the agent.
+(define (start-agent . args)
+  (log "Starting gpg-agent...")
+  (let ((gnupghome (getenv "GNUPGHOME")))
+    (if (null? args)
+ (atexit (lambda ()
+  (with-home-directory gnupghome (stop-agent))))))
+  (catch (log "Warning: Creating socket directory failed:" (car *error*))
+ (gpg-conf '--create-socketdir))
+  (call-check `(,(tool 'gpg-connect-agent) --verbose
+ ,(string-append "--agent-program=" (tool 'gpg-agent)
+ "|--debug-quick-random")
+ /bye)))
+
+;; Stop the agent and other daemons and remove the socket dir.
+(define (stop-agent)
+  (log "Stopping gpg-agent...")
+  (gpg-conf '--kill 'all)
+  (catch (log "Warning: Removing socket directory failed.")
+ (gpg-conf '--remove-socketdir)))
+
+;; Get the trust level for KEYID.  Any remaining arguments are simply
+;; passed to GPG.
+;;
+;; This function only supports keys with a single user id.
+(define (gettrust keyid . args)
+  (let ((trust
+  (list-ref (assoc "pub" (gpg-with-colons
+   `(,@args
+      --list-keys ,keyid))) 1)))
+    (unless (and (= 1 (string-length trust))
+ (member (string-ref trust 0) (string->list "oidreqnmfuws-")))
+    (fail "Bad trust value:" trust))
+    trust))
+
+;; Check that KEYID's trust level matches EXPECTED-TRUST.  Any
+;; remaining arguments are simply passed to GPG.
+;;
+;; This function only supports keys with a single user id.
+(define (checktrust keyid expected-trust . args)
+  (let ((trust (apply gettrust `(,keyid ,@args))))
+    (unless (string=? trust expected-trust)
+    (fail keyid ": Expected trust to be" expected-trust
+  "but got" trust))))
+
+(define (keytotpm name select)
+  (let ((result (call-with-io `(,@GPG --command-fd=0 --edit-key ,name ,select keytotpm)  "y\n")))
+    (if (= 0 (:retcode result))
+ (:stdout result)
+ (throw "keytotpm failed"
+       (:stderr result)))))
+
+
+(define (quick-gen name algo)
+  (info "creating TPM " algo " key")
+  (call-check `(,@GPG --quick-generate-key ,name ,algo))
+  (keytotpm name "key 0")
+  (unless (string=? (:cardinfo (secinfo name)) "TPM-Protected")
+      (throw "key is not in the TPM")))
+
+(define (quick-add name algo)
+  (info "adding TPM encryption " algo " key")
+  (call-check `(,@GPG --quick-add-key ,(fingerprint name) ,algo "encr"))
+  (keytotpm name "key 1")
+  (unless (string=? (:cardinfo (ssbinfo name)) "TPM-Protected")
+      (throw "Added key is not in the TPM")))
+
+(define (check-sig name)
+  (info "checking TPM signing")
+  (call-check `(,@GPG --default-key ,name --sign msg.txt))
+  (call-check `(,@GPG --verify msg.txt.gpg))
+  (unlink "msg.txt.gpg"))
+
+(define (check-encrypt name)
+  (info "Checking TPM decryption")
+  (call-check `(,@GPG --recipient ,name --encrypt msg.txt))
+  (call-check `(,@GPG --output msg.out.txt --decrypt msg.txt.gpg))
+  (unless (file=? "msg.txt" "msg.out.txt")
+  (throw "File did not decrypt to the same message"))
+  (unlink "msg.out.txt")
+  (unlink "msg.txt.gpg"))
+
+;;
+;; Tests are very simple: create primary key in TPM add encryption key
+;; in TPM (verifies TPM primary can certify secondary), sign a message
+;; with primary key and check signature encrypt a message with
+;; encryption key and check signature
+;;
+(define (test-tpm name algo)
+  (quick-gen name algo)
+  (quick-add name algo)
+  (check-sig name)
+  (check-encrypt name))
+
+;;
+;; Enable checking with valgrind if the envvar "with_valgrind" is set
+;;
+(when with-valgrind?
+  (set! gpg `(,@valgrind ,@gpg)))
+
+
+;;(set! *args* (append *args* (list "--use-keyboxd")))
+
+
+;; end
diff --git a/tests/tpm2dtests/ecc.scm b/tests/tpm2dtests/ecc.scm
new file mode 100644
index 000000000..8b28cad23
--- /dev/null
+++ b/tests/tpm2dtests/ecc.scm
@@ -0,0 +1,23 @@
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2021 [hidden email]
+;;
+;; SPDX-License-Identifier: GPL-3.0-or-later
+;;
+(load (in-srcdir "tests" "tpm2dtests" "defs.scm"))
+
+(setup-environment)
+(setenv "PINENTRY_USER_DATA" "ecckey" #t)
+
+;;
+;; try checking signature and encryption on supported elliptic
+;; curve keys.  Note this list must be allowable by the swtpm
+;; used for the test, which is why it's so small
+;;
+(define key-list '("nistp256" "nistp384"))
+
+(for-each
+ (lambda (algo)
+   (define name algo "<" algo "@example.com>")
+   (test-tpm name algo))
+ key-list)
diff --git a/tests/tpm2dtests/longpassphrase.scm b/tests/tpm2dtests/longpassphrase.scm
new file mode 100644
index 000000000..6e72dc317
--- /dev/null
+++ b/tests/tpm2dtests/longpassphrase.scm
@@ -0,0 +1,36 @@
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2021 [hidden email]
+;;
+;; SPDX-License-Identifier: GPL-3.0-or-later
+;;
+(load (in-srcdir "tests" "tpm2dtests" "defs.scm"))
+
+(setup-environment)
+
+;;
+;; Check that a key with a long passphrase can be created and check
+;; the passphrase can be truncated and still work
+;;
+(define name "ecc <[hidden email]>")
+(define name1 "ecc1 <[hidden email]>")
+(define algo "nistp256")
+
+(setenv "PINENTRY_USER_DATA" "this is a password longer than the TPM max of the name algorithm (i.e. 32)" #t)
+(quick-gen name algo)
+
+(setenv "PINENTRY_USER_DATA" "this is a password longer than the TPM max of the name" #t)
+(check-sig name)
+
+;; exactly the TPM limit (sha256 hash name algorithm: 32)
+(setenv "PINENTRY_USER_DATA" "12345678901234567890123456789012" #t)
+(quick-gen name1 algo)
+
+(info "checking TPM signing failure with truncated passphrase")
+;; passphrase one character shorter, should fail with bad passphrase
+(setenv "PINENTRY_USER_DATA" "1234567890123456789012345678901" #t)
+(let ((result (call-with-io `(,@GPG --default-key ,name1 --sign msg.txt) "")))
+  (if (= 0 (:retcode result))
+      (throw "Signing Key succeeded with wrong passphrase")
+      (unless (string-contains? (:stderr result) "Bad passphrase")
+      (throw "Unexpected signing error:" (:stderr result)))))
diff --git a/tests/tpm2dtests/rsa.scm b/tests/tpm2dtests/rsa.scm
new file mode 100644
index 000000000..aaae35276
--- /dev/null
+++ b/tests/tpm2dtests/rsa.scm
@@ -0,0 +1,13 @@
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2021 [hidden email]
+;;
+;; SPDX-License-Identifier: GPL-3.0-or-later
+;;
+(load (in-srcdir "tests" "tpm2dtests" "defs.scm"))
+
+(setup-environment)
+
+(setenv "PINENTRY_USER_DATA" "rsakey" #t)
+
+(test-tpm "rsa <[hidden email]>" "rsa2048")
diff --git a/tests/tpm2dtests/run-tests.scm b/tests/tpm2dtests/run-tests.scm
new file mode 100644
index 000000000..fdf1859a8
--- /dev/null
+++ b/tests/tpm2dtests/run-tests.scm
@@ -0,0 +1,43 @@
+;; Test-suite runner.
+;;
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(if (string=? "" (getenv "abs_top_srcdir"))
+    (begin
+      (echo "Environment variable 'abs_top_srcdir' not set.  Please point it to"
+    "tests/tpm2dtests.")
+      (exit 2)))
+
+;; Set objdir so that the tests can locate built programs.
+(setenv "objdir" (getcwd) #f)
+
+(define setup
+  (make-environment-cache (test::scm
+   #f
+   (path-join "tests" "tpm2dtests" "setup.scm")
+   (in-srcdir "tests" "tpm2dtests" "setup.scm"))))
+
+(define tests (filter (lambda (arg) (not (string-prefix? arg "--"))) *args*))
+
+(run-tests (if (null? tests)
+      (load-tests "tests" "tpm2dtests")
+      (map (lambda (name)
+      (test::scm setup
+ (path-join "tests" "tpm2dtests" name)
+ (in-srcdir "tests" "tpm2dtests" name)
+ "--use-keyring")) tests)))
diff --git a/tests/tpm2dtests/setup.scm b/tests/tpm2dtests/setup.scm
new file mode 100644
index 000000000..df917e2e4
--- /dev/null
+++ b/tests/tpm2dtests/setup.scm
@@ -0,0 +1,48 @@
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (in-srcdir "tests" "tpm2dtests" "defs.scm"))
+
+(define cache (flag "--create-tarball" *args*))
+(unless (and cache (= 1 (length cache)))
+ (fail "Usage: setup.scm --create-tarball <file> [--use-keyring]"))
+
+(when (> (*verbose*) 0)
+      (define (pad symbol length)
+ (let loop ((cs (string->list (symbol->string symbol)))
+   (result (make-string length #\space))
+   (i 0))
+  (if (null? cs)
+      result
+      (begin
+ (string-set! result i (car cs))
+ (loop (cdr cs) result (+ 1 i))))))
+      (log " I am going to use these tools:\n"
+   "==============================")
+      (for-each
+       (lambda (t)
+ (log (pad t 25) (tool t)))
+       '(gpgconf gpg gpg-agent scdaemon gpgsm dirmngr gpg-connect-agent
+ gpg-preset-passphrase gpgtar pinentry)))
+
+;;(setenv "GNUPGHOME" (getcwd) #t)
+(create-gpghome)
+(start-agent)
+(stop-agent)
+(call-check `(,(tool 'gpgtar) --create --output ,(car cache) "."))
diff --git a/tests/tpm2dtests/shell.scm b/tests/tpm2dtests/shell.scm
new file mode 100644
index 000000000..a0d32ec68
--- /dev/null
+++ b/tests/tpm2dtests/shell.scm
@@ -0,0 +1,51 @@
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2016 g10 Code GmbH
+;;
+;; 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 <http://www.gnu.org/licenses/>.
+
+(load (in-srcdir "tests" "tpm2dtests" "defs.scm"))
+
+;; This is not a test, but can be used to inspect the test
+;; environment.  Simply execute
+;;
+;;   make -Ctests/tpm2dtests check TESTS=shell.scm
+;;
+;; to run it.
+
+(setup-environment)
+
+(if (prompt-yes-no? "Drop 'batch' from gpg.conf" #t)
+    (apply create-file
+   (cons "gpg.conf"
+ (filter (lambda (line) (not (equal? "batch" line)))
+ (string-split-newlines
+  (call-with-input-file "gpg.conf" read-all)))))
+    (begin
+      (echo "Note that gpg.conf includes 'batch'.  If you want to use gpg")
+      (echo "interactively you should drop that.")))
+
+;; Add paths to tools to PATH.
+(setenv "PATH" (pathsep-join
+ (append (map (lambda (t) (dirname (tool t)))
+     '(gpg gpg-agent scdaemon gpgsm dirmngr gpgconf tpm2daemon))
+ (pathsep-split (getenv "PATH"))))
+ #t)
+
+(echo "\nEnjoy your test environment. "
+      "Type 'exit' to exit it, it will be cleaned up after you.\n")
+
+(interactive-shell)
diff --git a/tests/tpm2dtests/unimportable.scm b/tests/tpm2dtests/unimportable.scm
new file mode 100644
index 000000000..be84c13c6
--- /dev/null
+++ b/tests/tpm2dtests/unimportable.scm
@@ -0,0 +1,28 @@
+#!/usr/bin/env gpgscm
+
+;; Copyright (C) 2021 [hidden email]
+;;
+;; SPDX-License-Identifier: GPL-3.0-or-later
+;;
+(load (in-srcdir "tests" "tpm2dtests" "defs.scm"))
+
+(setup-environment)
+(setenv "PINENTRY_USER_DATA" "this is a password" #t)
+
+;;
+;; Tries to import a selection of keys with no TPM representation
+;; and verifies it fails.  There are many unimportable keys, so
+;; save time by only choosing one EC and one RSA one
+;;
+(define key-list '("ed25519" "rsa4096"))
+
+(for-each
+ (lambda(algo)
+   (info "Checking failure to import" algo)
+   (define name algo "<ecc" algo "@example.com>")
+   (call-check `(,@GPG --quick-generate-key ,name ,algo))
+   (let ((result (call-with-io `(,@GPG --command-fd=0 --edit-key ,name "key 0" keytotpm)  "y\n")))
+     (if (= 0 (:retcode result))
+ (throw "Importing Key succeeded")
+ (:stderr result))))
+ key-list)
--
2.26.2


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel
Reply | Threaded
Open this post in threaded view
|

[PATCH v3 5/5] Add Support for the Intel TSS

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
The Intel TSS is somewhat of a moving target, so this wraps support
for this TSS into tpm2daemon.  Unfortunately this wrapper uses some
APIs that are only present in a relatively recent Intel TSS, so it
looks like it will only work with version 2.4.0 or higher.

Signed-off-by: James Bottomley <[hidden email]>
---
 configure.ac      |  29 +-
 tpm2d/intel-tss.h | 667 ++++++++++++++++++++++++++++++++++++++++++++++
 tpm2d/tpm2.h      |   4 +
 3 files changed, 694 insertions(+), 6 deletions(-)
 create mode 100644 tpm2d/intel-tss.h

diff --git a/configure.ac b/configure.ac
index a7e891e5e..72abbc032 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1596,8 +1596,9 @@ AC_SUBST(W32SOCKLIBS)
 _save_libs="$LIBS"
 _save_cflags="$CFLAGS"
 LIBS=""
-AC_SEARCH_LIBS([TSS_Create], [tss ibmtss],have_libtss=yes,)
-if test "$have_libtss" = yes; then
+AC_SEARCH_LIBS([TSS_Create], [tss ibmtss],have_libtss=IBM,
+    AC_SEARCH_LIBS([Esys_Initialize], [tss2-esys],have_libtss=Intel))
+if test "$have_libtss" = IBM; then
     LIBTSS_CFLAGS="-DTPM_POSIX"
     CFLAGS="$CFLAGS ${LIBTSS_CFLAGS}"
     AC_CHECK_HEADER([tss2/tss.h],[AC_DEFINE(TSS_INCLUDE,tss2, [tss2 include location])], [
@@ -1607,18 +1608,34 @@ if test "$have_libtss" = yes; then
  ])
     ])
     LIBTSS_LIBS=$LIBS
-    AC_DEFINE(HAVE_LIBTSS, 1, [Defined if we have TPM2 support library])
     AC_SUBST(TSS_INCLUDE)
+elif test "$have_libtss" = Intel; then
+    ##
+    # Intel TSS has an API issue: Esys_TR_GetTpmHandle wasn't introduced
+    # until version 2.4.0.
+    #
+    # Note: the missing API is fairly serious and is also easily backportable
+    # so keep the check below as is intead of going by library version number.
+    ##
+    AC_CHECK_LIB(tss2-esys, Esys_TR_GetTpmHandle, [], [
+ AC_MSG_WARN([Need Esys_TR_GetTpmHandle API (usually requires Intel TSS 2.4.0 or later, disabling TPM support)])
+ have_libtss=no
+    ])
+    LIBTSS_LIBS="$LIBS -ltss2-mu -ltss2-rc -ltss2-tctildr"
+    AC_DEFINE(HAVE_INTEL_TSS, 1, [Defined if we have the Intel TSS])
+fi
+LIBS="$_save_libs"
+CFLAGS="$_save_cflags"
+if test "$have_libtss" != no; then
+    AC_DEFINE(HAVE_LIBTSS, 1, [Defined if we have TPM2 support library])
     # look for a TPM emulator for testing
     AC_PATH_PROG(TPMSERVER, tpm_server,,/bin:/usr/bin:/usr/lib/ibmtss:/usr/libexec/ibmtss)
     AC_PATH_PROG(SWTPM, swtpm,,/bin:/usr/bin:/usr/lib/ibmtss:/usr/libexec/ibmtss)
     AC_PATH_PROG(SWTPM_IOCTL, swtpm_ioctl,,/bin:/usr/bin:/usr/lib/ibmtss:/usr/libexec/ibmtss)
 fi
-LIBS="$_save_libs"
-CFLAGS="$_save_cflags"
 AC_SUBST(LIBTSS_LIBS)
 AC_SUBST(LIBTSS_CFLAGS)
-AM_CONDITIONAL(HAVE_LIBTSS, test "$have_libtss" = yes)
+AM_CONDITIONAL(HAVE_LIBTSS, test "$have_libtss" != no)
 AM_CONDITIONAL(TEST_LIBTSS, test -n "$TPMSERVER" -o -n "$SWTPM")
 AC_SUBST(HAVE_LIBTSS)
 
diff --git a/tpm2d/intel-tss.h b/tpm2d/intel-tss.h
new file mode 100644
index 000000000..6e0d0a040
--- /dev/null
+++ b/tpm2d/intel-tss.h
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2021 James Bottomley <[hidden email]>
+ *
+ * Some portions of the TSS routines are
+ * (c) Copyright IBM Corporation 2015 - 2019
+ */
+#ifndef _TPM2_INTEL_TSS_H
+#define _TPM2_INTEL_TSS_H
+
+#include <tss2/tss2_esys.h>
+#include <tss2/tss2_mu.h>
+#include <tss2/tss2_rc.h>
+#include <tss2/tss2_tcti.h>
+#include <tss2/tss2_tctildr.h>
+
+#define EXT_TPM_RH_OWNER TPM2_RH_OWNER
+#define EXT_TPM_RH_PLATFORM TPM2_RH_PLATFORM
+#define EXT_TPM_RH_ENDORSEMENT TPM2_RH_ENDORSEMENT
+#define EXT_TPM_RH_NULL TPM2_RH_NULL
+#define INT_TPM_RH_NULL ESYS_TR_RH_NULL
+
+#define TSS_CONTEXT ESYS_CONTEXT
+
+#define MAX_RESPONSE_SIZE TPM2_MAX_RESPONSE_SIZE
+#define MAX_RSA_KEY_BYTES TPM2_MAX_RSA_KEY_BYTES
+#define MAX_ECC_CURVES TPM2_MAX_ECC_CURVES
+#define MAX_ECC_KEY_BYTES TPM2_MAX_ECC_KEY_BYTES
+#define MAX_SYM_DATA TPM2_MAX_SYM_DATA
+
+#define AES_128_BLOCK_SIZE_BYTES 16
+
+/*
+ * The TCG defines all begin TPM_ but for some unknown reason Intel
+ * ignored this and all its defines begin TPM2_
+ */
+
+#define TPM_RC_SUCCESS TPM2_RC_SUCCESS
+#define TPM_RC_SYMMETRIC TPM2_RC_SYMMETRIC
+#define TPM_RC_ASYMMETRIC TPM2_RC_ASYMMETRIC
+#define TPM_RC_CURVE TPM2_RC_CURVE
+#define TPM_RC_KEY_SIZE TPM2_RC_KEY_SIZE
+#define TPM_RC_KEY TPM2_RC_KEY
+#define TPM_RC_VALUE TPM2_RC_VALUE
+#define TPM_RC_POLICY TPM2_RC_POLICY
+#define TPM_RC_FAILURE TPM2_RC_FAILURE
+#define TPM_RC_AUTH_FAIL TPM2_RC_AUTH_FAIL
+#define TPM_RC_BAD_AUTH TPM2_RC_BAD_AUTH
+
+#define RC_VER1 TPM2_RC_VER1
+#define RC_FMT1 TPM2_RC_FMT1
+
+#define TPM_EO_EQ TPM2_EO_EQ
+#define TPM_EO_NEQ TPM2_EO_NEQ
+#define TPM_EO_SIGNED_GT TPM2_EO_SIGNED_GT
+#define TPM_EO_UNSIGNED_GT TPM2_EO_UNSIGNED_GT
+#define TPM_EO_SIGNED_LT TPM2_EO_SIGNED_LT
+#define TPM_EO_UNSIGNED_LT TPM2_EO_UNSIGNED_LT
+#define TPM_EO_SIGNED_GE TPM2_EO_SIGNED_GE
+#define TPM_EO_UNSIGNED_GE TPM2_EO_UNSIGNED_GE
+#define TPM_EO_SIGNED_LE TPM2_EO_SIGNED_LE
+#define TPM_EO_UNSIGNED_LE TPM2_EO_UNSIGNED_LE
+#define TPM_EO_BITSET TPM2_EO_BITSET
+#define TPM_EO_BITCLEAR TPM2_EO_BITCLEAR
+
+#define TPM_CC_PolicyPCR TPM2_CC_PolicyPCR
+#define TPM_CC_PolicyAuthValue TPM2_CC_PolicyAuthValue
+#define TPM_CC_PolicyCounterTimer TPM2_CC_PolicyCounterTimer
+
+#define TPM_ST_HASHCHECK TPM2_ST_HASHCHECK
+
+#define TPM_RH_OWNER ESYS_TR_RH_OWNER
+#define TPM_RH_PLATFORM ESYS_TR_RH_PLATFORM
+#define TPM_RH_ENDORSEMENT ESYS_TR_RH_ENDORSEMENT
+#define TPM_RH_NULL ESYS_TR_NONE
+#define TPM_RS_PW ESYS_TR_PASSWORD
+
+#define TPM_HT_PERMANENT TPM2_HT_PERMANENT
+#define TPM_HT_TRANSIENT TPM2_HT_TRANSIENT
+#define TPM_HT_PERSISTENT TPM2_HT_PERSISTENT
+
+#define TPM_HANDLE ESYS_TR
+#define TPM_RC TPM2_RC
+#define TPM_CC TPM2_CC
+
+#define TPM_ALG_ID TPM2_ALG_ID
+#define TPM_SE TPM2_SE
+#define TPM_SE_HMAC TPM2_SE_HMAC
+#define TPM_SE_POLICY TPM2_SE_POLICY
+#define TPM_CAP TPM2_CAP
+#define TPM_CAP_ECC_CURVES TPM2_CAP_ECC_CURVES
+#define TPM_EO TPM2_EO
+
+#define TPM_ECC_NONE TPM2_ECC_NONE
+#define TPM_ECC_NIST_P192 TPM2_ECC_NIST_P192
+#define TPM_ECC_NIST_P224 TPM2_ECC_NIST_P224
+#define TPM_ECC_NIST_P256 TPM2_ECC_NIST_P256
+#define TPM_ECC_NIST_P384 TPM2_ECC_NIST_P384
+#define TPM_ECC_NIST_P521 TPM2_ECC_NIST_P521
+#define TPM_ECC_BN_P256 TPM2_ECC_BN_P256
+#define TPM_ECC_BN_P638 TPM2_ECC_BN_P638
+#define TPM_ECC_SM2_P256 TPM2_ECC_SM2_P256
+
+#define TPM_ALG_NULL TPM2_ALG_NULL
+#define TPM_ALG_SHA1 TPM2_ALG_SHA1
+#define TPM_ALG_SHA256 TPM2_ALG_SHA256
+#define TPM_ALG_SHA384 TPM2_ALG_SHA384
+#define TPM_ALG_SHA512 TPM2_ALG_SHA512
+#define TPM_ALG_AES TPM2_ALG_AES
+#define TPM_ALG_CFB TPM2_ALG_CFB
+#define TPM_ALG_RSA TPM2_ALG_RSA
+#define TPM_ALG_RSASSA TPM2_ALG_RSASSA
+#define TPM_ALG_ECC TPM2_ALG_ECC
+#define TPM_ALG_KEYEDHASH TPM2_ALG_KEYEDHASH
+#define TPM_ALG_RSAES TPM2_ALG_RSAES
+#define TPM_ALG_OAEP TPM2_ALG_OAEP
+#define TPM_ALG_ECDSA TPM2_ALG_ECDSA
+
+/* the odd TPMA_OBJECT_  type is wrong too */
+
+#define TPMA_OBJECT_SIGN TPMA_OBJECT_SIGN_ENCRYPT
+
+/* Intel and IBM have slightly different names for all the 2B structures */
+
+#define NAME_2B TPM2B_NAME
+#define DATA_2B TPM2B_DATA
+#define PRIVATE_2B TPM2B_PRIVATE
+#define ENCRYPTED_SECRET_2B TPM2B_ENCRYPTED_SECRET
+#define KEY_2B TPM2B_KEY
+#define TPM2B_KEY TPM2B_DATA
+#define DIGEST_2B TPM2B_DIGEST
+#define ECC_PARAMETER_2B TPM2B_ECC_PARAMETER
+#define SENSITIVE_DATA_2B TPM2B_SENSITIVE_DATA
+#define PUBLIC_KEY_RSA_2B TPM2B_PUBLIC_KEY_RSA
+
+#define FALSE 0
+#define TRUE 1
+
+typedef struct {
+ uint16_t size;
+ BYTE buffer[];
+} TPM2B;
+
+#define TSS_CONVERT_MARSHAL(TYPE, PTR) \
+static inline TPM_RC \
+TSS_##TYPE##_Marshal(const TYPE *source, UINT16 *written, \
+     BYTE **buffer, INT32 *size) \
+{ \
+  size_t offset = 0; \
+  TPM_RC rc; \
+ \
+  rc = Tss2_MU_##TYPE##_Marshal(PTR source, *buffer, *size, &offset); \
+ \
+  *buffer += offset; \
+  *size -= offset; \
+  *written = offset; \
+ \
+  return rc; \
+}
+#define TSS_CONVERT_UNMARSHAL(TYPE, ARG) \
+static inline TPM_RC \
+TYPE##_Unmarshal##ARG(TYPE *dest, \
+ BYTE **buffer, INT32 *size) \
+{ \
+  size_t offset = 0; \
+  TPM_RC rc; \
+ \
+  memset(dest, 0, sizeof(TYPE)); \
+  rc = Tss2_MU_##TYPE##_Unmarshal(*buffer, *size, &offset, dest); \
+ \
+  *buffer += offset; \
+  *size -= offset; \
+ \
+  return rc; \
+}
+
+TSS_CONVERT_MARSHAL(TPMT_PUBLIC, )
+TSS_CONVERT_MARSHAL(UINT16, *)
+TSS_CONVERT_MARSHAL(TPMT_SENSITIVE, )
+TSS_CONVERT_MARSHAL(TPM2B_ECC_POINT, )
+TSS_CONVERT_MARSHAL(TPM2B_DIGEST, )
+TSS_CONVERT_MARSHAL(TPM2B_PUBLIC, )
+TSS_CONVERT_MARSHAL(TPM2B_PRIVATE, )
+
+TSS_CONVERT_UNMARSHAL(TPML_PCR_SELECTION, )
+TSS_CONVERT_UNMARSHAL(TPM2B_PRIVATE, )
+TSS_CONVERT_UNMARSHAL(TPM2B_PUBLIC, X)
+TSS_CONVERT_UNMARSHAL(TPM2B_ENCRYPTED_SECRET, )
+TSS_CONVERT_UNMARSHAL(UINT16, )
+TSS_CONVERT_UNMARSHAL(UINT32, )
+
+#define ARRAY_SIZE(A) (sizeof(A)/sizeof(A[0]))
+
+#define TPM2B_PUBLIC_Unmarshal(A, B, C, D) TPM2B_PUBLIC_UnmarshalX(A, B, C)
+#define TPM_EO_Unmarshal UINT16_Unmarshal
+#define TPM_CC_Unmarshal UINT32_Unmarshal
+
+#define VAL(X) X
+#define VAL_2B(X, MEMBER) X.MEMBER
+#define VAL_2B_P(X, MEMBER) X->MEMBER
+
+static const struct {
+  TPM_ALG_ID alg;
+  int gcrypt_algo;
+  int size;
+} TSS_Hashes[] = {
+  { TPM_ALG_SHA1,   GCRY_MD_SHA1,     20 },
+  { TPM_ALG_SHA256, GCRY_MD_SHA256,   32 },
+  { TPM_ALG_SHA384, GCRY_MD_SHA3_384, 48 },
+  { TPM_ALG_SHA512, GCRY_MD_SHA3_512, 64 }
+};
+
+static inline void
+intel_auth_helper(TSS_CONTEXT *tssContext, TPM_HANDLE auth, const char *authVal)
+{
+  TPM2B_AUTH authVal2B;
+
+  if (authVal)
+    {
+      authVal2B.size = strlen(authVal);
+      memcpy(authVal2B.buffer, authVal, authVal2B.size);
+    }
+  else
+    {
+      authVal2B.size = 0;
+    }
+  Esys_TR_SetAuth(tssContext, auth, &authVal2B);
+}
+
+static inline void
+intel_sess_helper(TSS_CONTEXT *tssContext, TPM_HANDLE auth, TPMA_SESSION flags)
+{
+  Esys_TRSess_SetAttributes(tssContext, auth, flags,
+    TPMA_SESSION_CONTINUESESSION | flags);
+}
+
+static inline TPM_HANDLE
+intel_handle(TPM_HANDLE h)
+{
+  if (h == 0)
+    return ESYS_TR_NONE;
+  return h;
+}
+
+static inline void
+TSS_Delete(TSS_CONTEXT *tssContext)
+{
+  TSS2_TCTI_CONTEXT *tcti_ctx;
+  TPM_RC rc;
+
+  rc = Esys_GetTcti(tssContext, &tcti_ctx);
+  Esys_Finalize(&tssContext);
+  if (rc == TPM_RC_SUCCESS)
+    Tss2_TctiLdr_Finalize(&tcti_ctx);
+}
+
+static inline TPM_RC
+TSS_Create(TSS_CONTEXT **tssContext)
+{
+  TPM_RC rc;
+  TSS2_TCTI_CONTEXT *tcti_ctx = NULL;
+  char *intType;
+  char *tctildr = NULL;
+
+  intType = getenv("TPM_INTERFACE_TYPE");
+  /*
+   * FIXME: This should be way more sophisticated, but it's
+   * enough to get the simulator tests running
+   */
+  if (intType)
+    {
+      if (strcmp("socsim", intType) == 0) {
+ tctildr = "mssim";
+      }
+      else if (strcmp("dev", intType) == 0)
+ {
+  tctildr = "device";
+ }
+      else
+ {
+  fprintf(stderr, "Unknown TPM_INTERFACE_TYPE %s\n", intType);
+ }
+    }
+
+  rc = Tss2_TctiLdr_Initialize(tctildr, &tcti_ctx);
+  if (rc)
+    return rc;
+
+  rc =  Esys_Initialize(tssContext, tcti_ctx, NULL);
+
+  return rc;
+}
+
+static inline int
+TSS_GetDigestSize(TPM_ALG_ID alg) {
+  int i;
+
+  for (i = 0; i < ARRAY_SIZE(TSS_Hashes); i++)
+    if (TSS_Hashes[i].alg == alg)
+      return TSS_Hashes[i].size;
+  return -1;
+}
+
+static inline int
+TSS_Hash_GetMd(int *algo, TPM_ALG_ID alg) {
+  int i;
+
+  for (i = 0; i < ARRAY_SIZE(TSS_Hashes); i++)
+    if (TSS_Hashes[i].alg == alg)
+      {
+ *algo = TSS_Hashes[i].gcrypt_algo;
+ return 0;
+      }
+  return TPM_RC_FAILURE;
+}
+
+/* copied with modifications from the IBM TSS tsscrypto.c */
+static inline TPM_RC
+TSS_Hash_Generate(TPMT_HA *digest, ...)
+{
+  TPM_RC rc = 0;
+  int length;
+  uint8_t *buffer;
+  int algo;
+  gcry_md_hd_t md;
+  va_list ap;
+
+  va_start(ap, digest);
+
+  rc = TSS_Hash_GetMd(&algo, digest->hashAlg);
+  if (rc)
+    {
+      fprintf(stderr, "TSS_HASH_GENERATE: Unknown hash %d\n",
+      digest->hashAlg);
+      goto out;
+    }
+
+  rc = gcry_md_open (&md, algo, 0);
+  if (rc != 0)
+    {
+      fprintf(stderr, "TSS_Hash_Generate: EVP_MD_CTX_create failed\n");
+      rc = TPM_RC_FAILURE;
+      goto out;
+    }
+
+  rc = TPM_RC_FAILURE;
+  for (;;)
+    {
+      length = va_arg(ap, int); /* first vararg is the length */
+      buffer = va_arg(ap, unsigned char *); /* second vararg is the array */
+      if (buffer == NULL) /* loop until a NULL buffer terminates */
+ break;
+      if (length < 0)
+ {
+  fprintf(stderr, "TSS_Hash_Generate: Length is negative\n");
+  goto out_free;
+ }
+      if (length != 0)
+ gcry_md_write (md, buffer, length);
+    }
+
+  memcpy (&digest->digest, gcry_md_read (md, algo),
+  TSS_GetDigestSize(digest->hashAlg));
+  rc = TPM_RC_SUCCESS;
+ out_free:
+  gcry_md_close (md);
+ out:
+  va_end(ap);
+  return rc;
+}
+
+static inline TPM_RC
+TSS_TPM2B_Create(TPM2B *target, uint8_t *buffer, uint16_t size,
+ uint16_t targetSize)
+{
+  if (size > targetSize)
+    return TSS2_MU_RC_INSUFFICIENT_BUFFER;
+  target->size = size;
+  if (size)
+    memmove(target->buffer, buffer, size);
+  return TPM_RC_SUCCESS;
+}
+
+static inline void
+tpm2_error(TPM_RC rc, const char *reason)
+{
+  const char *msg;
+
+  fprintf(stderr, "%s failed with %d\n", reason, rc);
+  msg = Tss2_RC_Decode(rc);
+  fprintf(stderr, "%s\n", msg);
+}
+
+static inline int
+TSS_start (TSS_CONTEXT **tssc)
+{
+  TPM_RC rc;
+
+  rc = TSS_Create (tssc);
+  if (rc)
+    {
+      tpm2_error(rc, "TSS_Create");
+      return GPG_ERR_CARD;
+    }
+
+  return 0;
+}
+
+static inline TPM_RC
+tpm2_Import(TSS_CONTEXT *tssContext, TPM_HANDLE parentHandle,
+    DATA_2B *encryptionKey, TPM2B_PUBLIC *objectPublic,
+    PRIVATE_2B *duplicate, ENCRYPTED_SECRET_2B *inSymSeed,
+    TPMT_SYM_DEF_OBJECT *symmetricAlg, PRIVATE_2B *outPrivate,
+    TPM_HANDLE auth, const char *authVal)
+{
+  PRIVATE_2B *out;
+  TPM_RC rc;
+
+  intel_auth_helper(tssContext, parentHandle, authVal);
+  intel_sess_helper(tssContext, auth, TPMA_SESSION_DECRYPT);
+  rc = Esys_Import(tssContext, parentHandle, auth, ESYS_TR_NONE,
+   ESYS_TR_NONE, encryptionKey, objectPublic,
+   duplicate, inSymSeed, symmetricAlg, &out);
+  if (rc)
+    return rc;
+
+  *outPrivate = *out;
+  free(out);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_Create(TSS_CONTEXT *tssContext, TPM_HANDLE parentHandle,
+    TPM2B_SENSITIVE_CREATE *inSensitive, TPM2B_PUBLIC *inPublic,
+    PRIVATE_2B *outPrivate, TPM2B_PUBLIC *outPublic,
+    TPM_HANDLE auth, const char *authVal)
+{
+  TPM_RC rc;
+  PRIVATE_2B *opriv;
+  TPM2B_PUBLIC *opub;
+  DATA_2B outsideInfo;
+  TPML_PCR_SELECTION creationPCR;
+
+  outsideInfo.size = 0;
+  creationPCR.count = 0;
+
+  intel_auth_helper(tssContext, parentHandle, authVal);
+  intel_sess_helper(tssContext, auth, TPMA_SESSION_DECRYPT);
+  rc = Esys_Create(tssContext, parentHandle, auth,
+   ESYS_TR_NONE, ESYS_TR_NONE, inSensitive,
+   inPublic, &outsideInfo, &creationPCR, &opriv,
+   &opub, NULL, NULL, NULL);
+
+  if (rc)
+    return rc;
+
+  *outPublic = *opub;
+  free(opub);
+  *outPrivate = *opriv;
+  free(opriv);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_ReadPublic(TSS_CONTEXT *tssContext, TPM_HANDLE objectHandle,
+ TPMT_PUBLIC *pub, TPM_HANDLE auth)
+{
+  TPM2B_PUBLIC *out;
+  TPM_RC rc;
+
+  if (auth != TPM_RH_NULL)
+    intel_sess_helper(tssContext, auth, TPMA_SESSION_ENCRYPT);
+
+  rc = Esys_ReadPublic(tssContext, objectHandle, auth, ESYS_TR_NONE,
+     ESYS_TR_NONE, &out, NULL, NULL);
+  if (rc)
+    return rc;
+
+  if (pub)
+    *pub = out->publicArea;
+  free(out);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_RSA_Decrypt(TSS_CONTEXT *tssContext, TPM_HANDLE keyHandle,
+ PUBLIC_KEY_RSA_2B *cipherText, TPMT_RSA_DECRYPT *inScheme,
+ PUBLIC_KEY_RSA_2B *message,
+ TPM_HANDLE auth, const char *authVal, int flags)
+{
+  PUBLIC_KEY_RSA_2B *out;
+  DATA_2B label;
+  TPM_RC rc;
+
+  label.size = 0;
+
+  intel_auth_helper(tssContext, keyHandle, authVal);
+  intel_sess_helper(tssContext, auth, flags);
+  rc = Esys_RSA_Decrypt(tssContext, keyHandle, auth, ESYS_TR_NONE,
+ ESYS_TR_NONE, cipherText,
+ inScheme, &label, &out);
+
+  if (rc)
+    return rc;
+
+  *message = *out;
+  free(out);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_Sign(TSS_CONTEXT *tssContext, TPM_HANDLE keyHandle, DIGEST_2B *digest,
+  TPMT_SIG_SCHEME *inScheme, TPMT_SIGNATURE *signature,
+  TPM_HANDLE auth, const char *authVal)
+{
+  TPM_RC rc;
+  TPMT_TK_HASHCHECK validation;
+  TPMT_SIGNATURE *out;
+
+  validation.tag = TPM_ST_HASHCHECK;
+  validation.hierarchy = EXT_TPM_RH_NULL;
+  validation.digest.size = 0;
+
+  intel_auth_helper(tssContext, keyHandle, authVal);
+  intel_sess_helper(tssContext, auth, 0);
+  rc = Esys_Sign(tssContext, keyHandle, auth, ESYS_TR_NONE,
+ ESYS_TR_NONE, digest, inScheme, &validation, &out);
+
+  if (rc)
+    return rc;
+
+  *signature = *out;
+  free(out);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_ECDH_ZGen(TSS_CONTEXT *tssContext, TPM_HANDLE keyHandle,
+       TPM2B_ECC_POINT *inPoint, TPM2B_ECC_POINT *outPoint,
+       TPM_HANDLE auth, const char *authVal)
+{
+  TPM2B_ECC_POINT *out;
+  TPM_RC rc;
+
+  intel_auth_helper(tssContext, keyHandle, authVal);
+  intel_sess_helper(tssContext, auth, TPMA_SESSION_ENCRYPT);
+  rc = Esys_ECDH_ZGen(tssContext, keyHandle, auth, ESYS_TR_NONE,
+      ESYS_TR_NONE, inPoint, &out);
+
+  if (rc)
+    return rc;
+
+  *outPoint = *out;
+  free(out);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_CreatePrimary(TSS_CONTEXT *tssContext, TPM_HANDLE primaryHandle,
+   TPM2B_SENSITIVE_CREATE *inSensitive,
+   TPM2B_PUBLIC *inPublic, TPM_HANDLE *objectHandle)
+{
+  TPM2B_DATA outsideInfo;
+  TPML_PCR_SELECTION creationPcr;
+  TPM_RC rc;
+
+  /* FIXME will generate wrong value for NULL hierarchy */
+  primaryHandle = intel_handle(primaryHandle);
+
+  outsideInfo.size = 0;
+  creationPcr.count = 0;
+
+  rc = Esys_CreatePrimary(tssContext, primaryHandle, ESYS_TR_PASSWORD, ESYS_TR_NONE,
+  ESYS_TR_NONE, inSensitive, inPublic,
+  &outsideInfo, &creationPcr, objectHandle,
+  NULL, NULL, NULL, NULL);
+
+  return rc;
+}
+
+static inline TPM_RC
+tpm2_FlushContext(TSS_CONTEXT *tssContext, TPM_HANDLE flushHandle)
+{
+  return Esys_FlushContext(tssContext, flushHandle);
+}
+
+static inline TPM_RC
+tpm2_StartAuthSession(TSS_CONTEXT *tssContext, TPM_HANDLE tpmKey,
+      TPM_HANDLE bind, TPM_SE sessionType,
+      TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash,
+      TPM_HANDLE *sessionHandle,
+      const char *bindPassword)
+{
+  bind = intel_handle(bind);
+  tpmKey = intel_handle(tpmKey);
+  if (bind != ESYS_TR_NONE)
+    intel_auth_helper(tssContext, bind, bindPassword);
+
+  return Esys_StartAuthSession(tssContext, tpmKey, bind, ESYS_TR_NONE,
+       ESYS_TR_NONE, ESYS_TR_NONE, NULL,
+       sessionType, symmetric, authHash,
+       sessionHandle);
+}
+
+static inline TPM_RC
+tpm2_Load(TSS_CONTEXT *tssContext, TPM_HANDLE parentHandle,
+  PRIVATE_2B *inPrivate, TPM2B_PUBLIC *inPublic,
+  TPM_HANDLE *objectHandle,
+  TPM_HANDLE auth, const char *authVal)
+{
+  intel_auth_helper(tssContext, parentHandle, authVal);
+  intel_sess_helper(tssContext, auth, 0);
+  return Esys_Load(tssContext, parentHandle, auth, ESYS_TR_NONE,
+   ESYS_TR_NONE, inPrivate, inPublic, objectHandle);
+}
+
+static inline TPM_HANDLE
+tpm2_handle_ext(TSS_CONTEXT *tssContext, TPM_HANDLE esysh)
+{
+  TPM2_HANDLE realh = 0;
+
+  Esys_TR_GetTpmHandle(tssContext, esysh, &realh);
+
+  return realh;
+}
+
+static inline TPM_HANDLE
+tpm2_handle_int(TSS_CONTEXT *tssContext, TPM_HANDLE realh)
+{
+  TPM_HANDLE esysh = 0;
+
+  /* ***ing thing doesn't transform permanent handles */
+  if ((realh >> 24) == TPM_HT_PERMANENT)
+    {
+      switch (realh)
+ {
+ case TPM2_RH_OWNER:
+  return TPM_RH_OWNER;
+ case TPM2_RH_PLATFORM:
+  return TPM_RH_PLATFORM;
+ case TPM2_RH_ENDORSEMENT:
+  return TPM_RH_ENDORSEMENT;
+ case TPM2_RH_NULL:
+  return ESYS_TR_RH_NULL;
+ default:
+  return 0;
+ }
+    }
+
+  Esys_TR_FromTPMPublic(tssContext, realh, ESYS_TR_NONE,
+ ESYS_TR_NONE, ESYS_TR_NONE, &esysh);
+
+  return esysh;
+}
+
+static inline int
+tpm2_handle_mso(TSS_CONTEXT *tssContext, TPM_HANDLE esysh, UINT32 mso)
+{
+  return (tpm2_handle_ext(tssContext, esysh) >> 24) == mso;
+}
+
+#endif
diff --git a/tpm2d/tpm2.h b/tpm2d/tpm2.h
index a2d3745ea..0926d8014 100644
--- a/tpm2d/tpm2.h
+++ b/tpm2d/tpm2.h
@@ -2,7 +2,11 @@
 #define _TPM2_H
 
 #include "../common/util.h"
+#ifdef HAVE_INTEL_TSS
+#include "intel-tss.h"
+#else
 #include "ibm-tss.h"
+#endif
 
 int tpm2_start (TSS_CONTEXT **tssc);
 void tpm2_end (TSS_CONTEXT *tssc);
--
2.26.2


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
Hi James,

> This is a set of patches adding TPM support to gnupg-2.3

Thanks for the patches.  I was already considering when to add your old
patches.  So these reworked patches really came in time for a new beta.
Thanks.

>   tpm2d: Add tpm2daemon code
>   agent: Add new shadow key type and functions to call tpm2daemon
>   g10: add new command keytotpm to convert a private key to TPM format
>   tpm2d: add tests for the tpm2daemon

I applied all these patches with a few minor changes.  However, I have
not yet tested anything, just made sure that it builds fine.

The tests duplicate quite some some code but I guess we better live with
this until we could rework the test framework.  header blurbs are
missing but there is an SPDX line thus this should be okay.


>   Add Support for the Intel TSS

I am not sure about this one and whether this needs to be applied right
now.  My installed libtss-dev version is the 2 years old 1045-1.2.

The files in tpm2d/ are missing the usual header blurb.  I assume they
are all meant to be GPL-3.  I attach a patch adding them.  Would you
mind to sign this off and send a fixed patch?  In fact I am not sure were
you use the code too and thus a different license version might be
desired.

The whole gpg with TPM thing sounds interesting.  I took quite a while
to add this to master since you first showed be it at some FOSDEM.
Sorry.


Shalom-Salam,

   Werner
   
--
Die Gedanken sind frei.  Ausnahmen regelt ein Bundesgesetz.

From 3caa08023fb57eb3260c3de59867ec2281e0592f Mon Sep 17 00:00:00 2001
From: Werner Koch <[hidden email]>
Date: Wed, 10 Mar 2021 14:48:10 +0100
Subject: [PATCH GnuPG] tpmd2: Add copyright blurbs.

--
---
 AUTHORS            |  1 +
 tpm2d/Makefile.am  | 19 +++++++++++++++++++
 tpm2d/command.c    |  1 +
 tpm2d/ibm-tss.h    | 19 +++++++++++++++++--
 tpm2d/tpm2.c       | 20 ++++++++++++++++++++
 tpm2d/tpm2.h       | 24 ++++++++++++++++++++++--
 tpm2d/tpm2daemon.c |  1 +
 tpm2d/tpm2daemon.h |  6 ++++--
 8 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 6980407e5..71644c967 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -33,6 +33,7 @@ List of Copyright holders
   Copyright (C) 2000 Dimitrios Souflis
   Copyright (C) 2008,2009,2010,2012-2016 William Ahern
   Copyright (C) 2017 Bundesamt für Sicherheit in der Informationstechnik
+  Copyright (C) 2021 James Bottomley <[hidden email]>
 
 
 Authors with a FSF copyright assignment
diff --git a/tpm2d/Makefile.am b/tpm2d/Makefile.am
index 56a9ab7c9..bf5acd452 100644
--- a/tpm2d/Makefile.am
+++ b/tpm2d/Makefile.am
@@ -1,3 +1,22 @@
+# Makfile.am - Makefile for tpm2d
+# Copyright (C) 2021 James Bottomley <[hidden email]>
+#
+# 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/>.
+# SPDX-License-Identifier: GPL-3.0-or-later
+
 AM_CPPFLAGS =
 
 include $(top_srcdir)/am/cmacros.am
diff --git a/tpm2d/command.c b/tpm2d/command.c
index 619bb56e6..351781111 100644
--- a/tpm2d/command.c
+++ b/tpm2d/command.c
@@ -16,6 +16,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 #include <config.h>
diff --git a/tpm2d/ibm-tss.h b/tpm2d/ibm-tss.h
index 2a4961359..4ca49a6cf 100644
--- a/tpm2d/ibm-tss.h
+++ b/tpm2d/ibm-tss.h
@@ -1,8 +1,23 @@
-/*
+/* ibm-tss.h -  Supporting TPM routines for the IBM TSS
  * Copyright (C) 2021 James Bottomley <[hidden email]>
  *
- * Supporting TPM routines for the IBM TSS
+ * 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/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
+
 #ifndef _TPM2_IBM_TSS_H
 #define _TPM2_IBM_TSS_H
 
diff --git a/tpm2d/tpm2.c b/tpm2d/tpm2.c
index 2bd3dc177..3e908ddb1 100644
--- a/tpm2d/tpm2.c
+++ b/tpm2d/tpm2.c
@@ -1,3 +1,23 @@
+/* tpm2.c - Supporting TPM routines for the IBM TSS
+ * Copyright (C) 2021 James Bottomley <[hidden email]>
+ *
+ * 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/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
diff --git a/tpm2d/tpm2.h b/tpm2d/tpm2.h
index a2d3745ea..f2fa89ed9 100644
--- a/tpm2d/tpm2.h
+++ b/tpm2d/tpm2.h
@@ -1,5 +1,25 @@
-#ifndef _TPM2_H
-#define _TPM2_H
+/* tpm2.h - Definitions for supporting TPM routines for the IBM TSS
+ * Copyright (C) 2021 James Bottomley <[hidden email]>
+ *
+ * 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/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#ifndef _GNUPG_TPM2_H
+#define _GNUPG_TPM2_H
 
 #include "../common/util.h"
 #include "ibm-tss.h"
diff --git a/tpm2d/tpm2daemon.c b/tpm2d/tpm2daemon.c
index 4ec6d7959..261896cc2 100644
--- a/tpm2d/tpm2daemon.c
+++ b/tpm2d/tpm2daemon.c
@@ -16,6 +16,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 #include <config.h>
diff --git a/tpm2d/tpm2daemon.h b/tpm2d/tpm2daemon.h
index 24d56a8dc..095978a5b 100644
--- a/tpm2d/tpm2daemon.h
+++ b/tpm2d/tpm2daemon.h
@@ -1,5 +1,6 @@
-/* tpm2daemon.h - Global definitions for the SCdaemon
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+/* tpm2daemon.h - Global definitions for the TPM2D
+ * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2021 James Bottomley <[hidden email]>
  *
  * This file is part of GnuPG.
  *
@@ -15,6 +16,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
 #ifndef TPM2DAEMON_H
--
2.20.1


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (233 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
On Wed, 2021-03-10 at 15:06 +0100, Werner Koch wrote:
> Hi James,
>
> > This is a set of patches adding TPM support to gnupg-2.3
>
> Thanks for the patches.  I was already considering when to add your
> old patches.  So these reworked patches really came in time for a new
> beta. Thanks.

You're welcome.

> >   tpm2d: Add tpm2daemon code
> >   agent: Add new shadow key type and functions to call tpm2daemon
> >   g10: add new command keytotpm to convert a private key to TPM
> > format
> >   tpm2d: add tests for the tpm2daemon
>
> I applied all these patches with a few minor changes.  However, I
> have not yet tested anything, just made sure that it builds fine.

Unfortunately debian doesn't package a software TPM ... I don't know
why, most other distributions do.  I have one here in deb format:

https://build.opensuse.org/package/show/home:jejb1:TPM/swtpm2

> The tests duplicate quite some some code but I guess we better live
> with this until we could rework the test framework.  header blurbs
> are missing but there is an SPDX line thus this should be okay.

Yes, there's also doc missing, but I thought we could add that after
the fact if you agree to the keytotpm command.  It's basically just
that to convert an existing key to TPM format.  After that everything
should just work (except once the key is converted it can't be
unconverted and it will stop operating if you lose your TPM or clear
it).

>
> >   Add Support for the Intel TSS
>
> I am not sure about this one and whether this needs to be applied
> right now.  My installed libtss-dev version is the 2 years old 1045-
> 1.2.

It doesn't need to be applied immediately.  Your libtss-dev is an IBM
version number and the above patches, without this one, should work
with every IBM TSS however old.

For the Intel TSS on debian you need libtss2-dev from the tpm2-tss
source package.  The version on stable is too old (2.1.0) but the
version in testing will work (3.0.3).

> The files in tpm2d/ are missing the usual header blurb.  I assume
> they are all meant to be GPL-3.

Yes, that was the intention ... I always forget header files, sorry.

>   I attach a patch adding them.  Would you mind to sign this off and
> send a fixed patch?  In fact I am not sure were you use the code too
> and thus a different license version might be desired.

I copied the code with modifications from a different project which is
under LGPL:

https://git.kernel.org/pub/scm/linux/kernel/git/jejb/openssl_tpm2_engine.git/

But I own copyright in all the base files I've added to your repo, so
I'm happy for them to remain under GPLv3 going forward.  Since they had
to be modified to support gcrypt, I don't think there's much direct
reuse outside of the GPLv3 licence.

I am contemplating helping gnutls add TPM2 support using the same
framework, but their crypto system will require different modifications
of the base files.

> The whole gpg with TPM thing sounds interesting.  I took quite a
> while to add this to master since you first showed be it at some
> FOSDEM.

> Sorry.

Well, stuff takes a while, thanks for adding it.  Since it was always
targetted at 2.3, there's no real delay anyway.

James


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (235 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
On Wed, 10 Mar 2021 10:04, James Bottomley said:

> Unfortunately debian doesn't package a software TPM ... I don't know
> why, most other distributions do.  I have one here in deb format:
>
> https://build.opensuse.org/package/show/home:jejb1:TPM/swtpm2

Thanks.

> For the Intel TSS on debian you need libtss2-dev from the tpm2-tss
> source package.  The version on stable is too old (2.1.0) but the
> version in testing will work (3.0.3).

I deinstalled the IBM stack and the Intel code works with the opther patch.

> But I own copyright in all the base files I've added to your repo, so
> I'm happy for them to remain under GPLv3 going forward.  Since they had
> to be modified to support gcrypt, I don't think there's much direct
> reuse outside of the GPLv3 licence.

If there is ever a need we can easily chnage back to LGPL for tehse
parts.  I pushed my proposed patch with a link to your mail.

We plan to do Debian packages; do you have any advise which stack we
should prefer?  I guess IBM, becuase that one is tried first in
configure.

> Well, stuff takes a while, thanks for adding it.  Since it was always
> targetted at 2.3, there's no real delay anyway.

Now with TPM support in place, do you think that we could now go after
the passpharse caching code which states:

/* The encryption context.  This is the only place where the
   encryption key for all cached entries is available.  It would be nice
   to keep this (or just the key) in some hardware device, for example
   a TPM.  Libgcrypt could be extended to provide such a service.
   With the current scheme it is easy to retrieve the cached entries
   if access to Libgcrypt's memory is available.  The encryption
   merely avoids grepping for clear texts in the memory.  Nevertheless
   the encryption provides the necessary infrastructure to make it
   more secure.  */
static gcry_cipher_hd_t encryption_handle;

It would be sufficent if we could limit the time this symmetric
encryption key is exposed in memory to a minimum by encrypting the key
with the tpm.  Any ideas how to best integrate this?


And a last thing: It would be supercool if you could do a short writeup
on how to use the system in practise; for example as an article in our
blob.  Just if you can find some spare time (good joke, I know).


Salam-Shalom,

   Werner


--
Die Gedanken sind frei.  Ausnahmen regelt ein Bundesgesetz.

_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (233 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
On Thu, 2021-03-11 at 09:19 +0100, Werner Koch wrote:

> On Wed, 10 Mar 2021 10:04, James Bottomley said:
>
> > Unfortunately debian doesn't package a software TPM ... I don't
> > know why, most other distributions do.  I have one here in deb
> > format:
> >
> > https://build.opensuse.org/package/show/home:jejb1:TPM/swtpm2
>
> Thanks.
>
> > For the Intel TSS on debian you need libtss2-dev from the tpm2-tss
> > source package.  The version on stable is too old (2.1.0) but the
> > version in testing will work (3.0.3).
>
> I deinstalled the IBM stack and the Intel code works with the opther
> patch.
>
> > But I own copyright in all the base files I've added to your repo,
> > so I'm happy for them to remain under GPLv3 going forward.  Since
> > they had to be modified to support gcrypt, I don't think there's
> > much direct reuse outside of the GPLv3 licence.
>
> If there is ever a need we can easily chnage back to LGPL for tehse
> parts.  I pushed my proposed patch with a link to your mail.
Yes, that looks fine.

> We plan to do Debian packages; do you have any advise which stack we
> should prefer?  I guess IBM, becuase that one is tried first in
> configure.

The Intel TSS is very new.  I've actually been using the IBM TSS
version of the patches with gnupg-2.2 for several years, so it's fairly
well tested.  I thought once I had a testing infrastructure it would be
easy to verify the Intel TSS patch, which I did in my local
environment.  However, when I pushed the Intel TSS build to the
openSUSE build servers, it exploded on pretty much every non-current
distro because of API mutations.  I think the configure.ac check I
added catches all the problems, but I wouldn't necessarily bet the farm
on it.

So the bottom line is the most reliable build is with the IBM TSS
although the Intel TSS in debian testing seems to work fine as well.

> > Well, stuff takes a while, thanks for adding it.  Since it was
> > always targetted at 2.3, there's no real delay anyway.
>
> Now with TPM support in place, do you think that we could now go
> after the passpharse caching code which states:
>
> /* The encryption context.  This is the only place where the
>    encryption key for all cached entries is available.  It would be
> nice
>    to keep this (or just the key) in some hardware device, for
> example
>    a TPM.  Libgcrypt could be extended to provide such a service.
>    With the current scheme it is easy to retrieve the cached entries
>    if access to Libgcrypt's memory is available.  The encryption
>    merely avoids grepping for clear texts in the
> memory.  Nevertheless
>    the encryption provides the necessary infrastructure to make it
>    more secure.  */
> static gcry_cipher_hd_t encryption_handle;
>
> It would be sufficent if we could limit the time this symmetric
> encryption key is exposed in memory to a minimum by encrypting the
> key with the tpm.  Any ideas how to best integrate this?
It's certainly possible.  The TPM API you're after is called the
sealing one.  What happens is that the TPM can encrypt a blob of data
up to 128 bytes with a TPM internal key and give it back to you when
you call TPM2_Unseal().  For safety, since the password cache isn't
expected to survive a reboot, the parent of the seal operation should
be the NULL seed, so the sealed data becomes unextractable after a TPM
restart (the NULL seed changes on every restart).  You could seal the
password when it's added to the cache and unseal it just before use.
The safety you get is that an attacker who steals memory pages can't
unseal the password unless they also can access the TPM on your laptop.
However, if the threat model is an attacker who can steal memory pages
in real time, they can likely snoop the unseal operation as well and
extract the password that way.

I can investigate coding this if you like.  Unfortunately the Intel TSS
has yet another bug around NULL seed handling, which we'll then need a
configure.ac test for:

https://github.com/intel/tpm2-tss/issues/1993

> And a last thing: It would be supercool if you could do a short
> writeup on how to use the system in practise; for example as an
> article in our blob.  Just if you can find some spare time (good
> joke, I know).

Adding documentation is on my list of things to do, so a blog entry
shouldn't be too much of a stretch.

Regards,

James


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (235 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
On Thu, 2021-03-11 at 09:19 +0100, Werner Koch wrote:
[...]
> And a last thing: It would be supercool if you could do a short
> writeup on how to use the system in practise; for example as an
> article in our blob.  Just if you can find some spare time (good
> joke, I know).

I sent the doc update as a separate patch

How about this for the blog entry:

https://blog.hansenpartnership.com/?p=1223&shareadraft=baba1223_604b8ffac6d44

It's written in wordpress so it should simply cut and paste into your
blog.

James



_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (235 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 4/5] tpm2d: add tests for the tpm2daemon

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
Hi,

On Tue, Mar 09, 2021 at 01:50:31PM -0800, James Bottomley via
Gnupg-devel wrote:
>Running this test infrastructure requires a tpm emulator, which is
>tested for during configuration.

I tried to run the tests. I do have an emulator (tpm_server) which is
correctly found at configure time, but the tests failed because the
script `start_sw_tpm.sh` is not found in the tests/tpm2dtests directory.

There’s no trace of such a script in the patchset (apart from a mention
in defs.scm), is it supposed to be auto-generated somehow? Or manually
provided by whoever is calling `make check`?

- Damien

_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (235 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 4/5] tpm2d: add tests for the tpm2daemon

GnuPG - Dev mailing list
On Mon, 2021-03-15 at 13:56 +0000, Damien Goutte-Gattat via Gnupg-devel
wrote:

> Hi,
>
> On Tue, Mar 09, 2021 at 01:50:31PM -0800, James Bottomley via
> Gnupg-devel wrote:
> > Running this test infrastructure requires a tpm emulator, which is
> > tested for during configuration.
>
> I tried to run the tests. I do have an emulator (tpm_server) which
> is correctly found at configure time, but the tests failed because
> the script `start_sw_tpm.sh` is not found in the tests/tpm2dtests
> directory.
>
> There’s no trace of such a script in the patchset (apart from a
> mention in defs.scm), is it supposed to be auto-generated somehow? Or
> manually provided by whoever is calling `make check`?
No it was supposed to be part of the testing commit.  I think I just
forgot to do git add ... and, of course, it still works for me because
I have the script.  I'll send it in a followup patch.

James


_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (235 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH v3 0/5] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
In reply to this post by GnuPG - Dev mailing list
On Thu, 11 Mar 2021 08:51, James Bottomley said:

> The Intel TSS is very new.  I've actually been using the IBM TSS
> version of the patches with gnupg-2.2 for several years, so it's fairly
> well tested.  I thought once I had a testing infrastructure it would be

Okay, stuff for packaging folks ;-)

> It's certainly possible.  The TPM API you're after is called the
> sealing one.  What happens is that the TPM can encrypt a blob of data
> up to 128 bytes with a TPM internal key and give it back to you when
> you call TPM2_Unseal().  For safety, since the password cache isn't
> expected to survive a reboot, the parent of the seal operation should
> be the NULL seed, so the sealed data becomes unextractable after a TPM
> restart (the NULL seed changes on every restart).  You could seal the
> password when it's added to the cache and unseal it just before use.
> The safety you get is that an attacker who steals memory pages can't
> unseal the password unless they also can access the TPM on your laptop.
> However, if the threat model is an attacker who can steal memory pages
> in real time, they can likely snoop the unseal operation as well and
> extract the password that way.
Thanks for explaining.  I see when we can add this.


Shalom-Salam,

   Werner

--
Die Gedanken sind frei.  Ausnahmen regelt ein Bundesgesetz.

_______________________________________________
Gnupg-devel mailing list
[hidden email]
http://lists.gnupg.org/mailman/listinfo/gnupg-devel

signature.asc (233 bytes) Download Attachment