[PATCH v2 0/3] Add TPM2 support to gnupg 2.3

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

[PATCH v2 0/3] Add TPM2 support to gnupg 2.3

GnuPG - Dev mailing list
This patch series is based on the master branch with the two patches
Werner upported applied.  I'm not as familiar with this branch, so the
patch set is lightly tested, but I have got it to do key conversion,
signing and encryption successfully.  I may have missed some
subtleties of the new daemon structure, but the tpm2daemon is very
simple, so hopefully it doesn't matter.

This branch also contains the code to derive an EC primary each time,
so it will work out of the box on any TPM2 system without the need to
create a persistent RSA storage key at 81000001.

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 based on 2.3 and the
stable-2-2-tpm-daemon branch is currently based on 2.2.20

James

---

James Bottomley (3):
  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

 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         |   39 ++
 g10/call-agent.c     |   22 +
 g10/call-agent.h     |    3 +
 g10/keyedit.c        |   45 +-
 tools/gpgconf-comp.c |   62 +-
 tools/gpgconf.h      |    3 +
 tpm2d/Makefile.am    |   18 +
 tpm2d/command.c      |  508 +++++++++++++++++
 tpm2d/tpm2.c         | 1048 ++++++++++++++++++++++++++++++++++
 tpm2d/tpm2.h         |   36 ++
 tpm2d/tpm2daemon.c   | 1289 ++++++++++++++++++++++++++++++++++++++++++
 tpm2d/tpm2daemon.h   |  105 ++++
 27 files changed, 3731 insertions(+), 13 deletions(-)
 create mode 100644 agent/call-tpm2d.c
 create mode 100644 agent/divert-tpm2.c
 create mode 100644 tpm2d/Makefile.am
 create mode 100644 tpm2d/command.c
 create mode 100644 tpm2d/tpm2.c
 create mode 100644 tpm2d/tpm2.h
 create mode 100644 tpm2d/tpm2daemon.c
 create mode 100644 tpm2d/tpm2daemon.h

--
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 v2 2/3] 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 64d308838..d72d989d3 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 9c84f6a17..6b8c5fa14 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -59,6 +59,7 @@
 enum daemon_type
   {
    DAEMON_SCD,
+   DAEMON_TPM2D,
    DAEMON_MAX_TYPE
   };
 
@@ -456,6 +457,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,
@@ -573,6 +575,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,
@@ -602,6 +642,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 7a2bcbe27..2518a3c0e 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..66401b2bc
--- /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);
+}
+
+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 689507bbc..3388664e4 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1288,6 +1288,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..22fbfd808
--- /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);
+  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 ae7da0634..568fb0464 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 e2ca05c84..e130e28b1 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -307,8 +307,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)
 
@@ -317,6 +318,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 ec23daf83..03e8ab713 100644
--- a/agent/pkdecrypt.c
+++ b/agent/pkdecrypt.c
@@ -85,8 +85,12 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
           goto leave;
         }
 
-      rc = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext,
-                             shadow_info, &buf, &len, r_padding);
+      if (agent_is_tpm2_key (s_skey))
+ rc = divert_tpm2_pkdecrypt (ctrl, desc_text, ciphertext, shadow_info,
+    &buf, &len, r_padding);
+      else
+      rc = divert_pkdecrypt (ctrl, desc_text, ctrl->keygrip, ciphertext,
+     shadow_info, &buf, &len, r_padding);
       if (rc)
         {
           log_error ("smartcard decryption failed: %s\n", gpg_strerror (rc));
diff --git a/agent/pksign.c b/agent/pksign.c
index 0640b04ef..55aefa0fe 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -395,11 +395,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 v2 3/3] 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 3388664e4..cb49793fb 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -2912,6 +2912,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"
@@ -3613,6 +3664,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 5c6a4a66d..55fb5b1e9 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -1040,6 +1040,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 be5c777d4..7bf0b7823 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -125,6 +125,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 ac9f4688c..7cfc9ef38 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -1245,7 +1245,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
 };
 
@@ -1296,6 +1296,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 */
@@ -1794,6 +1796,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