[git] GnuPG - branch, tpm-work, created. gnupg-2.2.5-126-gfb0470a

by Werner Koch cvs at cvs.gnupg.org
Fri Mar 9 10:42:51 CET 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, tpm-work has been created
        at  fb0470a9f5834b77fba2bf5e1b41f56d063e556f (commit)

- Log -----------------------------------------------------------------
commit fb0470a9f5834b77fba2bf5e1b41f56d063e556f
Author: Werner Koch <wk at gnupg.org>
Date:   Fri Mar 9 10:36:14 2018 +0100

    agent: Minor cleanup of the TPM patches.
    
    * configure.ac (AC_CHECK_HEADERS): Add tss2/tss.h.
    * agent/divert-tpm2.c: Print an error if that file is not available.
    * agent/Makefile.am (gpg_agent_SOURCES): Add tpm.h
    * agent/command.c (do_one_keyinfo): Replace xstrdup by xtrystrdup.
    * agent/protect.c (agent_get_shadow_info_type): Check error of
    xtrystrdup.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/Makefile.am b/agent/Makefile.am
index 290ef12..4fe74f5 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -52,7 +52,7 @@ gpg_agent_SOURCES = \
 	trustlist.c \
 	divert-scd.c \
 	divert-tpm2.c \
-	tpm2.c \
+	tpm2.c tpm2.h \
 	cvt-openpgp.c cvt-openpgp.h \
 	call-scd.c \
 	learncard.c
diff --git a/agent/command.c b/agent/command.c
index c439aa5..a46e288 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1104,7 +1104,8 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
   char hexgrip[40+1];
   char *fpr = NULL;
   int keytype;
-  unsigned char *shadow_info = NULL, *shadow_info_type = NULL;
+  unsigned char *shadow_info = NULL;
+  unsigned char *shadow_info_type = NULL;
   char *serialno = NULL;
   char *idstr = NULL;
   const char *keytypestr;
@@ -1194,7 +1195,12 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
         }
       else if (strcmp (shadow_info_type, "tpm2-v1") == 0)
         {
-          serialno = xstrdup("TPM-Protected");
+          serialno = xtrystrdup("TPM-Protected");
+          if (!serialno)
+            {
+              err = gpg_error_from_syserror ();
+              goto leave;
+            }
           idstr = NULL;
         }
       else
diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c
index deb655a..84935de 100644
--- a/agent/divert-tpm2.c
+++ b/agent/divert-tpm2.c
@@ -8,6 +8,12 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
+/* FIXME: Until we have a proper checking in configure we give a hint
+ * on what to do  */
+#ifndef HAVE_TSS2_TSS_H
+# error Please install the libtss2 dev package first
+#endif
+
 #include "agent.h"
 #include "../common/i18n.h"
 #include "../common/sexp-parse.h"
diff --git a/agent/protect.c b/agent/protect.c
index 0920667..09c7d61 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1559,11 +1559,12 @@ agent_shadow_key_type (const unsigned char *pubkey,
 
   /* Calculate required length by taking in account: the "shadowed-"
      prefix, the "shadowed", shadow type as well as some parenthesis */
+  /* FIXME: We should use membuf functions here.  */
   n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
   *result = xtrymalloc (n);
   p = (char*)*result;
   if (!p)
-      return out_of_core ();
+    return out_of_core ();
   p = stpcpy (p, "(20:shadowed-private-key");
   /* (10:public-key ...)*/
   memcpy (p, pubkey+14, point - (pubkey+14));
@@ -1643,12 +1644,15 @@ agent_get_shadow_info_type (const unsigned char *shadowkey,
   n = snext (&s);
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP);
-  if (shadow_type) {
-    char *buf = xtrymalloc(n+1);
-    memcpy(buf, s, n);
-    buf[n] = '\0';
-    *shadow_type = buf;
-  }
+  if (shadow_type)
+    {
+      char *buf = xtrymalloc(n+1);
+      if (!buf)
+        return gpg_error_from_syserror ();
+      memcpy (buf, s, n);
+      buf[n] = '\0';
+      *shadow_type = buf;
+    }
 
   if (smatch (&s, n, "t1-v1") || smatch(&s, n, "tpm2-v1"))
     {
diff --git a/configure.ac b/configure.ac
index 7522b69..9dd9230 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1301,7 +1301,7 @@ AC_HEADER_STDC
 AC_CHECK_HEADERS([string.h unistd.h langinfo.h termio.h locale.h getopt.h \
                   pty.h utmp.h pwd.h inttypes.h signal.h sys/select.h     \
                   stdint.h signal.h util.h libutil.h termios.h \
-                  ucred.h sys/ucred.h sys/sysmacros.h sys/mkdev.h])
+                  ucred.h sys/ucred.h sys/sysmacros.h sys/mkdev.h tss2/tss.h])
 
 AC_HEADER_TIME
 

commit 72ece35fb713eaf88d425f8b3468bec0d2b887e1
Author: James Bottomley <James.Bottomley at HansenPartnership.com>
Date:   Mon Mar 5 11:18:15 2018 -0800

    tpm2: add handling for elliptic curve keys
    
    * agent/divert-tpm2.c: Support ECC.
    
    --
    This adds handling for the way gnupg does elliptic keys, namely ECDSA
    for signatures and using ECDH with an ephemeral key to generate an
    encrypted message.  The main problem is that the TPM2 usually has a
    very small list of built in curves and it won't handle any others.
    Thanks to TCG mandates, all TPM2 systems in the USA should come with
    NIST P-256, but do not come with the Bernstien curve 25519, so the
    only way to use the TPM2 to protect an elliptic curve key is first to
    create it with a compatible algorithm.
    
    Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>

diff --git a/agent/divert-tpm2.c b/agent/divert-tpm2.c
index dc3110d..deb655a 100644
--- a/agent/divert-tpm2.c
+++ b/agent/divert-tpm2.c
@@ -22,15 +22,16 @@ divert_tpm2_pksign (ctrl_t ctrl, const char *desc_text,
 {
   TSS_CONTEXT *tssc;
   TPM_HANDLE key;
+  TPMI_ALG_PUBLIC type;
   int ret;
 
   ret = tpm2_start(&tssc);
   if (ret)
     return ret;
-  ret = tpm2_load_key(tssc, shadow_info, &key);
+  ret = tpm2_load_key(tssc, shadow_info, &key, &type);
   if (ret)
     goto out;
-  ret = tpm2_sign(ctrl, tssc, key, digest, digestlen, r_sig, r_siglen);
+  ret = tpm2_sign(ctrl, tssc, key, type, digest, digestlen, r_sig, r_siglen);
 
   tpm2_flush_handle(tssc, key);
 
@@ -130,11 +131,12 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
 {
   TSS_CONTEXT *tssc;
   TPM_HANDLE key;
+  TPMI_ALG_PUBLIC type;
   int ret;
   const unsigned char *s;
   size_t n;
 
-  *r_padding = 0;
+  *r_padding = -1;
 
   (void)desc_text;
 
@@ -155,6 +157,7 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
     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++;
@@ -165,6 +168,30 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
         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);
 
@@ -173,10 +200,14 @@ divert_tpm2_pkdecrypt (ctrl_t ctrl, const char *desc_text,
   ret = tpm2_start(&tssc);
   if (ret)
     return ret;
-  ret = tpm2_load_key(tssc, shadow_info, &key);
+  ret = tpm2_load_key(tssc, shadow_info, &key, &type);
   if (ret)
     goto out;
-  ret = tpm2_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
+
+  if (type == TPM_ALG_RSA)
+    ret = tpm2_rsa_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
+  else if (type == TPM_ALG_ECC)
+    ret = tpm2_ecc_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
 
   tpm2_flush_handle(tssc, key);
 
diff --git a/agent/tpm2.c b/agent/tpm2.c
index 734f0fe..ff57560 100644
--- a/agent/tpm2.c
+++ b/agent/tpm2.c
@@ -313,7 +313,7 @@ parse_tpm2_shadow_info (const unsigned char *shadow_info,
 
 int
 tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
-              TPM_HANDLE *key)
+              TPM_HANDLE *key, TPMI_ALG_PUBLIC *type)
 {
   uint32_t parent;
   Load_In in;
@@ -339,6 +339,8 @@ tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
   size = pub_len;
   pTPM2B_PUBLIC_Unmarshal(&in.inPublic, &buf, &size, FALSE);
 
+  *type = in.inPublic.publicArea.type;
+
   rc = pTSS_Execute(tssc,
                     (RESPONSE_PARAMETERS *)&out,
                     (COMMAND_PARAMETERS *)&in,
@@ -358,7 +360,8 @@ tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
 
 int
 tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
-          const unsigned char *digest, size_t digestlen,
+	  TPMI_ALG_PUBLIC type,
+	  const unsigned char *digest, size_t digestlen,
           unsigned char **r_sig, size_t *r_siglen)
 {
   Sign_In in;
@@ -367,7 +370,6 @@ tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
 
   /* The TPM insists on knowing the digest type, so
    * calculate that from the size */
-  in.inScheme.scheme = TPM_ALG_RSASSA;
   switch (digestlen) {
   case 20:
     in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
@@ -394,22 +396,181 @@ tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
   in.validation.hierarchy = TPM_RH_NULL;
   in.validation.digest.t.size = 0;
 
+  if (type == TPM_ALG_RSA)
+    in.inScheme.scheme = TPM_ALG_RSASSA;
+  else if (type == TPM_ALG_ECC)
+    in.inScheme.scheme = TPM_ALG_ECDSA;
+  else
+    return GPG_ERR_PUBKEY_ALGO;
+
+
   ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_Sign, "TPM2_Sign", &out, &in);
   if (ret)
     return ret;
 
-  *r_siglen = out.signature.signature.rsassa.sig.t.size;
+  if (type == TPM_ALG_RSA)
+    *r_siglen = out.signature.signature.rsassa.sig.t.size;
+  else if (type == TPM_ALG_ECC)
+    *r_siglen = out.signature.signature.ecdsa.signatureR.t.size
+      + out.signature.signature.ecdsa.signatureS.t.size;
+
   *r_sig = xtrymalloc(*r_siglen);
   if (!r_sig)
     return GPG_ERR_ENOMEM;
 
-  memcpy(*r_sig, out.signature.signature.rsassa.sig.t.buffer, *r_siglen);
+  if (type == TPM_ALG_RSA)
+    {
+      memcpy(*r_sig, out.signature.signature.rsassa.sig.t.buffer, *r_siglen);
+    }
+  else if (type == TPM_ALG_ECC)
+    {
+      memcpy(*r_sig, out.signature.signature.ecdsa.signatureR.t.buffer,
+	     out.signature.signature.ecdsa.signatureR.t.size);
+      memcpy(*r_sig + out.signature.signature.ecdsa.signatureR.t.size,
+	     out.signature.signature.ecdsa.signatureS.t.buffer,
+	     out.signature.signature.ecdsa.signatureS.t.size);
+    }
 
   return 0;
 }
 
 static int
-sexp_to_tpm2_sensitive(TPMT_SENSITIVE *s, gcry_sexp_t key)
+sexp_to_tpm2_sensitive_ecc(TPMT_SENSITIVE *s, gcry_sexp_t key)
+{
+  gcry_mpi_t d;
+  gcry_sexp_t l;
+  int rc = -1;
+  size_t len;
+
+  s->sensitiveType = TPM_ALG_ECC;
+  s->seedValue.b.size = 0;
+
+  l = gcry_sexp_find_token (key, "d", 0);
+  if (!l)
+    return rc;
+  d = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof(s->sensitive.ecc.t.buffer);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, s->sensitive.ecc.t.buffer, len, &len, d);
+  s->sensitive.ecc.t.size = len;
+  gcry_mpi_release (d);
+
+  return rc;
+}
+
+/* try to match the libgcrypt curve names to known TPM parameters.
+ *
+ * As of 2018 the TCG defined curves are only NIST
+ * (192,224,256,384,521) Barreto-Naehring (256,638) and the Chinese
+ * SM2 (256), which means only the NIST ones overlap with libgcrypt */
+static struct {
+  const char *name;
+  TPMI_ECC_CURVE c;
+} tpm2_curves[] = {
+  { "NIST P-192", TPM_ECC_NIST_P192 },
+  { "prime192v1", TPM_ECC_NIST_P192 },
+  { "secp192r1", TPM_ECC_NIST_P192 },
+  { "nistp192", TPM_ECC_NIST_P192 },
+  { "NIST P-224", TPM_ECC_NIST_P224 },
+  { "secp224r1", TPM_ECC_NIST_P224 },
+  { "nistp224", TPM_ECC_NIST_P224 },
+  { "NIST P-256", TPM_ECC_NIST_P256 },
+  { "prime256v1", TPM_ECC_NIST_P256 },
+  { "secp256r1", TPM_ECC_NIST_P256 },
+  { "nistp256", TPM_ECC_NIST_P256 },
+  { "NIST P-384", TPM_ECC_NIST_P384 },
+  { "secp384r1", TPM_ECC_NIST_P384 },
+  { "nistp384", TPM_ECC_NIST_P384 },
+  { "NIST P-521", TPM_ECC_NIST_P521 },
+  { "secp521r1", TPM_ECC_NIST_P521 },
+  { "nistp521", TPM_ECC_NIST_P521 },
+};
+
+static int
+tpm2_ecc_curve (const char *curve_name, TPMI_ECC_CURVE *c)
+{
+  int i;
+
+  for (i = 0; i < DIM (tpm2_curves); i++)
+    if (strcmp (tpm2_curves[i].name, curve_name) == 0)
+      break;
+  if (i == DIM (tpm2_curves)) {
+    log_error ("curve %s does not match any available TPM curves\n", curve_name);
+    return GPG_ERR_UNKNOWN_CURVE;
+  }
+
+  *c = tpm2_curves[i].c;
+
+  return 0;
+}
+
+static int
+sexp_to_tpm2_public_ecc(TPMT_PUBLIC *p, gcry_sexp_t key)
+{
+  const char *q;
+  gcry_sexp_t l;
+  int rc = GPG_ERR_BAD_PUBKEY;
+  size_t len;
+  TPMI_ECC_CURVE curve;
+  char *curve_name;
+
+  l = gcry_sexp_find_token (key, "curve", 0);
+  if (!l)
+    return rc;
+  curve_name = gcry_sexp_nth_string (l, 1);
+  if (!curve_name)
+    goto out;
+  rc = tpm2_ecc_curve (curve_name, &curve);
+  gcry_free (curve_name);
+  if (rc)
+    goto out;
+  gcry_sexp_release(l);
+
+  l = gcry_sexp_find_token (key, "q", 0);
+  if (!l)
+    return rc;
+  q = gcry_sexp_nth_data (l, 1, &len);
+  /* This is a point representation, the first byte tells you what
+   * type.  The only format we understand is uncompressed (0x04)
+   * which has layout 0x04 | x | y */
+  if (q[0] != 0x04)
+    {
+      log_error ("Point format for q is not uncompressed\n");
+      goto out;
+    }
+  q++;
+  len--;
+  /* now should have to equal sized big endian point numbers */
+  if ((len & 0x01) == 1)
+    {
+      log_error ("Point format for q has incorrect length\n");
+      goto out;
+    }
+
+  len >>= 1;
+
+  p->type = TPM_ALG_ECC;
+  p->nameAlg = TPM_ALG_SHA256;
+  p->objectAttributes.val = TPMA_OBJECT_NODA |
+    TPMA_OBJECT_SIGN |
+    TPMA_OBJECT_DECRYPT |
+    TPMA_OBJECT_USERWITHAUTH;
+  p->authPolicy.t.size = 0;
+  p->parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
+  p->parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+  p->parameters.eccDetail.curveID = curve;
+  p->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+  memcpy(p->unique.ecc.x.t.buffer, q, len);
+  p->unique.ecc.x.t.size = len;
+  memcpy(p->unique.ecc.y.t.buffer, q + len, len);
+  p->unique.ecc.y.t.size = len;
+ out:
+  gcry_sexp_release (l);
+  return rc;
+}
+
+static int
+sexp_to_tpm2_sensitive_rsa(TPMT_SENSITIVE *s, gcry_sexp_t key)
 {
   gcry_mpi_t p;
   gcry_sexp_t l;
@@ -433,7 +594,7 @@ sexp_to_tpm2_sensitive(TPMT_SENSITIVE *s, gcry_sexp_t key)
 }
 
 static int
-sexp_to_tpm2_public(TPMT_PUBLIC *p, gcry_sexp_t key)
+sexp_to_tpm2_public_rsa(TPMT_PUBLIC *p, gcry_sexp_t key)
 {
   gcry_mpi_t n, e;
   gcry_sexp_t l;
@@ -506,12 +667,18 @@ sexp_to_tpm2(TPMT_PUBLIC *p, TPMT_SENSITIVE *s, gcry_sexp_t s_skey)
     return rc;
 
   l2 = gcry_sexp_find_token (l1, "rsa", 0);
-  if (!l2)
-    goto out;
-
-  rc = sexp_to_tpm2_public(p, l2);
-  if (!rc)
-    rc = sexp_to_tpm2_sensitive(s, l2);
+  if (l2) {
+    rc = sexp_to_tpm2_public_rsa (p, l2);
+    if (!rc)
+      rc = sexp_to_tpm2_sensitive_rsa (s, l2);
+  } else {
+    l2 = gcry_sexp_find_token (l1, "ecc", 0);
+    if (!l2)
+      goto out;
+    rc = sexp_to_tpm2_public_ecc (p, l2);
+    if (!rc)
+      rc = sexp_to_tpm2_sensitive_ecc (s, l2);
+  }
 
   gcry_sexp_release(l2);
 
@@ -757,9 +924,61 @@ tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc, char *pub, int *pub_len,
 }
 
 int
-tpm2_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
-             const char *ciphertext, int ciphertext_len,
-             char **decrypt, size_t *decrypt_len)
+tpm2_ecc_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		 const char *ciphertext, int ciphertext_len,
+		 char **decrypt, size_t *decrypt_len)
+{
+  ECDH_ZGen_In in;
+  ECDH_ZGen_Out out;
+  size_t len;
+  int ret;
+
+  /* This isn't really a decryption per se.  The ciphertext actually
+   * contains an EC Point which we must multiply by the private key number.
+   *
+   * The reason is to generate a diffe helman agreement on a shared
+   * point.  This shared point is then used to generate the per
+   * session encryption key.
+   */
+  if (ciphertext[0] != 0x04)
+    {
+      log_error ("Decryption Shared Point format is not uncompressed\n");
+      return GPG_ERR_ENCODING_PROBLEM;
+    }
+  if ((ciphertext_len & 0x01) != 1)
+    {
+      log_error ("Decryption Shared Point has incorrect length\n");
+      return GPG_ERR_ENCODING_PROBLEM;
+    }
+  len = ciphertext_len >> 1;
+
+  in.keyHandle = key;
+  memcpy(in.inPoint.point.x.t.buffer, ciphertext + 1, len);
+  in.inPoint.point.x.t.size = len;
+  memcpy(in.inPoint.point.y.t.buffer, ciphertext + 1 + len, len);
+  in.inPoint.point.y.t.size = len;
+
+  ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_ECDH_ZGen, "TPM2_ECDH_ZGen",
+			    &out, &in);
+  if (ret)
+    return ret;
+
+  *decrypt_len = out.outPoint.point.x.t.size + out.outPoint.point.y.t.size + 1;
+  *decrypt = xtrymalloc(*decrypt_len);
+  (*decrypt)[0] = 0x04;
+  memcpy(*decrypt + 1, out.outPoint.point.x.t.buffer,
+	 out.outPoint.point.x.t.size);
+  memcpy(*decrypt + 1 + out.outPoint.point.x.t.size,
+	 out.outPoint.point.y.t.buffer,
+	 out.outPoint.point.y.t.size);
+
+  return 0;
+}
+
+int
+tpm2_rsa_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		 const char *ciphertext, int ciphertext_len,
+		 char **decrypt, size_t *decrypt_len)
 {
   RSA_Decrypt_In in;
   RSA_Decrypt_Out out;
diff --git a/agent/tpm2.h b/agent/tpm2.h
index 2e16803..7a63aab 100644
--- a/agent/tpm2.h
+++ b/agent/tpm2.h
@@ -10,13 +10,18 @@ int tpm2_start(TSS_CONTEXT **tssc);
 void tpm2_end(TSS_CONTEXT *tssc);
 void tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h);
 int tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
-                  TPM_HANDLE *key);
+                  TPM_HANDLE *key, TPMI_ALG_PUBLIC *type);
 int tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+	      TPMI_ALG_PUBLIC type,
               const unsigned char *digest, size_t digestlen,
               unsigned char **r_sig, size_t *r_siglen);
 int tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc, char *pub, int *pub_len,
                     char *priv, int *priv_len, gcry_sexp_t s_skey);
-int tpm2_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
-                 const char *ciphertext, int ciphertext_len,
-                 char **decrypt, size_t *decrypt_len);
+int tpm2_rsa_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		     const char *ciphertext, int ciphertext_len,
+		     char **decrypt, size_t *decrypt_len);
+int tpm2_ecc_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+		     const char *ciphertext, int ciphertext_len,
+		     char **decrypt, size_t *decrypt_len);
+
 #endif

commit c4c7b7d7ba6b9d52bb7884b4bb4f84b7dd96340b
Author: James Bottomley <James.Bottomley at HansenPartnership.com>
Date:   Mon Mar 5 11:16:40 2018 -0800

    g10: add ability to transfer a private key to the tpm
    
    * g10/keyedit.c (cmdKEYTOTPM): New enum value.
    (cmds): New command "keytotpm".
    (keyedit_menu): Implement cmdKEYTOTPM.
    
    --
    
    Exactly like the gpg --edit-key command keytosc, keytotpm has been
    added which immedately converts the private key file to TPM shadowed
    form.  Once this is done, the key cannot be recovered and may only be
    used via the TPM of the computer system on which the conversion was
    done.  If that system is ever lost, or its TPM cleared, the shadowed
    key becomes unusable.
    
    Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>

diff --git a/g10/call-agent.c b/g10/call-agent.c
index b1f589b..8b224f7 100644
--- a/g10/call-agent.c
+++ b/g10/call-agent.c
@@ -778,6 +778,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;
+}
+
 
 int
 agent_keytocard (const char *hexgrip, int keyno, int force,
diff --git a/g10/call-agent.h b/g10/call-agent.h
index 53775c5..ba4c398 100644
--- a/g10/call-agent.h
+++ b/g10/call-agent.h
@@ -91,6 +91,9 @@ gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw);
 /* 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 2c33a29..038c318 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -1241,7 +1241,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
 };
 
@@ -1292,6 +1292,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 */
@@ -1789,6 +1791,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;

commit 1a4a4a8f5fa575df64ab130b03e666920f7093ee
Author: James Bottomley <James.Bottomley at HansenPartnership.com>
Date:   Mon Mar 5 11:15:29 2018 -0800

    agent: plumb in TPM handling
    
    * agent/divert-tpm2.c: New.
    * Makefile.am (gpg_agent_SOURCES): Add it.
    * agent/command.c (do_one_keyinfo): Fake serialno for TPM.
    (cmd_keytotpm): New.
    (register_commands): Register KEYTOTPM command.
    * agent/pkdecrypt.c (agent_pkdecrypt): Divert to TPM.
    * agent/pksign.c (agent_pksign_do): Divert to TPM.
    
    --
    This code installs diversions for pksign and pkdecrypt to do the
    operations via the TPM if a TPM shadowed key is present.  It also adds
    an extra assuan command KEYTOTPM which moves an existing private key
    to a TPM shadowed key.
    
    The way TPM shadowing works is that the public and private key parts
    are fed in to the TPM command TPM2_Import.  The output of this command
    is a TPM specific public and private key data where the private key
    data is symmetrically encrypted using a TPM internal key.  If this
    physical TPM is ever lost or cleared, that TPM internal key will
    likewise be lost and nothing will ever be able to read the private
    key.  Once the import is done, the shadow information for the key is
    updated to be a three part list consisting of the parent key (hard
    coded to 81000001 which is the Microsoft preferred RSA incarnation of
    the storage seed) and the public and private TPM data blobs.
    
    Now when a TPM shadowed key is used, the data blobs must be loaded
    into the TPM with TPM2_Load before any operation can be performed.
    
    Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
    
    - Added ChangeLog entries
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/Makefile.am b/agent/Makefile.am
index 39e69cd..290ef12 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -51,6 +51,7 @@ gpg_agent_SOURCES = \
 	protect.c \
 	trustlist.c \
 	divert-scd.c \
+	divert-tpm2.c \
 	tpm2.c \
 	cvt-openpgp.c cvt-openpgp.h \
 	call-scd.c \
diff --git a/agent/agent.h b/agent/agent.h
index e13ee1f..7a77eb6 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -417,6 +417,7 @@ gpg_error_t agent_public_key_from_file (ctrl_t ctrl,
                                         gcry_sexp_t *result);
 int agent_is_dsa_key (gcry_sexp_t s_key);
 int agent_is_eddsa_key (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,
@@ -533,6 +534,18 @@ gpg_error_t agent_marktrusted (ctrl_t ctrl, const char *name,
                                const char *fpr, int flag);
 void agent_reload_trustlist (void);
 
+/*-- divert-tpm2.c --*/
+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);
+
 
 /*-- divert-scd.c --*/
 int divert_pksign (ctrl_t ctrl, const char *desc_text,
diff --git a/agent/command.c b/agent/command.c
index 32c12d9..c439aa5 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1192,6 +1192,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);
@@ -2578,6 +2583,57 @@ cmd_keytocard (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"
   "\n"
@@ -3243,6 +3299,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/agent/divert-tpm2.c b/agent/divert-tpm2.c
new file mode 100644
index 0000000..dc3110d
--- /dev/null
+++ b/agent/divert-tpm2.c
@@ -0,0 +1,187 @@
+#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"
+
+#include "tpm2.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)
+{
+  TSS_CONTEXT *tssc;
+  TPM_HANDLE key;
+  int ret;
+
+  ret = tpm2_start(&tssc);
+  if (ret)
+    return ret;
+  ret = tpm2_load_key(tssc, shadow_info, &key);
+  if (ret)
+    goto out;
+  ret = tpm2_sign(ctrl, tssc, key, digest, digestlen, r_sig, r_siglen);
+
+  tpm2_flush_handle(tssc, key);
+
+ out:
+  tpm2_end(tssc);
+  return ret;
+}
+
+static unsigned char *
+make_tpm2_shadow_info (uint32_t parent, const char *pub, int pub_len,
+                       const char *priv, int priv_len)
+{
+  gcry_sexp_t s_exp;
+  size_t len;
+  char *info;
+
+  gcry_sexp_build(&s_exp, NULL, "(%u%b%b)", parent, pub_len, pub, priv_len, priv);
+
+  len = gcry_sexp_sprint(s_exp, GCRYSEXP_FMT_CANON, NULL, 0);
+  info = xtrymalloc(len);
+  gcry_sexp_sprint(s_exp, GCRYSEXP_FMT_CANON, info, len);
+
+  gcry_sexp_release(s_exp);
+
+  return (unsigned char *)info;
+}
+
+static gpg_error_t
+agent_write_tpm2_shadow_key (ctrl_t ctrl, const unsigned char *grip,
+                             int parent, char *pub,  int pub_len,
+                             char *priv, int priv_len)
+{
+  gpg_error_t err;
+  unsigned char *shadow_info;
+  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);
+
+  shadow_info = make_tpm2_shadow_info (parent, pub, pub_len, priv, priv_len);
+  if (!shadow_info) {
+    xfree (pkbuf);
+    return gpg_error_from_syserror ();
+  }
+
+  err = agent_shadow_key_type (pkbuf, shadow_info, "tpm2-v1", &shdkey);
+  xfree (shadow_info);
+  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*/);
+  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)
+{
+  TSS_CONTEXT *tssc;
+  int ret, pub_len, priv_len;
+  /* priv is always shielded so no special handling required */
+  char pub[sizeof(TPM2B_PUBLIC)], priv[sizeof(TPM2B_PRIVATE)];
+
+  ret = tpm2_start(&tssc);
+  if (ret)
+    return ret;
+  ret = tpm2_import_key (ctrl, tssc, pub, &pub_len, priv, &priv_len, s_skey);
+  if (ret)
+    goto out;
+  ret = agent_write_tpm2_shadow_key (ctrl, grip, TPM2_PARENT, pub, pub_len,
+                                     priv, priv_len);
+ out:
+  tpm2_end(tssc);
+  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)
+{
+  TSS_CONTEXT *tssc;
+  TPM_HANDLE key;
+  int ret;
+  const unsigned char *s;
+  size_t n;
+
+  *r_padding = 0;
+
+  (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"))
+    {
+      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
+    return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+
+  /* know we have RSA to decrypt at s,n */
+
+  ret = tpm2_start(&tssc);
+  if (ret)
+    return ret;
+  ret = tpm2_load_key(tssc, shadow_info, &key);
+  if (ret)
+    goto out;
+  ret = tpm2_decrypt(ctrl, tssc, key, s, n, r_buf, r_len);
+
+  tpm2_flush_handle(tssc, key);
+
+ out:
+  tpm2_end(tssc);
+  return ret;
+
+}
diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c
index 06a8e0b..6f766ca 100644
--- a/agent/pkdecrypt.c
+++ b/agent/pkdecrypt.c
@@ -86,8 +86,12 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
           goto leave;
         }
 
-      rc = divert_pkdecrypt (ctrl, desc_text, 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, 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 f54af08..dae2638 100644
--- a/agent/pksign.c
+++ b/agent/pksign.c
@@ -353,10 +353,16 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         if (desc_text)
           agent_modify_description (desc_text, NULL, s_skey, &desc2);
 
-        err = divert_pksign (ctrl, desc2? desc2 : desc_text,
-                             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,
+			       data, datalen,
+			       ctrl->digest.algo,
+			       shadow_info, &buf, &len);
         xfree (desc2);
       }
       if (err)

commit 144cceec7ca132d471cc6b7b200e0c59a23663d1
Author: James Bottomley <James.Bottomley at HansenPartnership.com>
Date:   Mon Mar 5 11:14:34 2018 -0800

    agent: add tpm specific functions
    
    * agent/tpm2.c: New.
    * agent/Makefile.am (gpg_agent_SOURCES): Add new file.
    (gpg_agent_LDFLAGS): Add DL_LIBS.
    * agent/tpm2.h: New.
    --
    
    This commit adds code to handle the three specific functions needed to
    make the agent TPM aware, namely the ability to load a key from shadow
    information, the ability to sign a digest with that key, the ability
    to decrypt with the key and the ability to import a key to the TPM.
    
    The TPM2 is a bit of an esoteric beast, so all TPM specific callouts
    are confined inside this code.  Additionaly, it requires the tss2
    library to function, so the code is designed such that if the library
    isn't present then all TPM functions simply fail.  This allows the
    code to be compiled with TPM support, but not require that the support
    library be present on the system.
    
    Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
    
    - Added ChangeLog entries.
    - Added DL_LIBS.
    - Removed one -Wdeclaration-after-statement case.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/Makefile.am b/agent/Makefile.am
index ce29462..39e69cd 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -51,6 +51,7 @@ gpg_agent_SOURCES = \
 	protect.c \
 	trustlist.c \
 	divert-scd.c \
+	tpm2.c \
 	cvt-openpgp.c cvt-openpgp.h \
 	call-scd.c \
 	learncard.c
@@ -70,7 +71,7 @@ gpg_agent_LDADD = $(commonpth_libs) \
                 $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
 	        $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \
 		$(resource_objs)
-gpg_agent_LDFLAGS = $(extra_bin_ldflags)
+gpg_agent_LDFLAGS = $(DL_LIBS) $(extra_bin_ldflags)
 gpg_agent_DEPENDENCIES = $(resource_objs)
 
 gpg_protect_tool_SOURCES = \
diff --git a/agent/tpm2.c b/agent/tpm2.c
new file mode 100644
index 0000000..734f0fe
--- /dev/null
+++ b/agent/tpm2.c
@@ -0,0 +1,784 @@
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <agent.h>
+#include <tpm2.h>
+
+#include "../common/i18n.h"
+#include "../common/sexp-parse.h"
+
+#include <tss2/tssutils.h>
+#include <tss2/tssresponsecode.h>
+#include <tss2/tssmarshal.h>
+#include <tss2/Unmarshal_fp.h>
+#include <tss2/tsscryptoh.h>
+
+/* List of tss2 functions we use.  This is macro jiggery-pokery:
+ * the F argument gives us the ability to run an arbitrary macro over
+ * the function list as for each function do macro F */
+#define _TSS2_LIST(F)                   \
+  F(TSS_Create);                        \
+  F(TSS_SetProperty);                   \
+  F(TSS_Execute);                       \
+  F(TSS_ResponseCode_toString);         \
+  F(TPM2B_PUBLIC_Unmarshal);            \
+  F(TPM2B_PRIVATE_Unmarshal);           \
+  F(TSS_TPM2B_PUBLIC_Marshal);          \
+  F(TSS_TPMT_PUBLIC_Marshal);           \
+  F(TSS_TPM2B_PRIVATE_Marshal);         \
+  F(TSS_UINT16_Marshal);                \
+  F(TSS_TPMT_SENSITIVE_Marshal);        \
+  F(TSS_SetProperty);                   \
+  F(TSS_GetDigestSize);                 \
+  F(TSS_Hash_Generate);                 \
+  F(TSS_Delete);
+
+/* create static declarations for the function pointers */
+#define _DL_DECLARE(func) \
+  static typeof(func) *p##func
+_TSS2_LIST(_DL_DECLARE);
+
+static const char *tpm2_dir;
+
+/* The TPM builds a small database of active files representing key
+ * parameters used for authentication and session encryption.  Make sure
+ * they're contained in a separate directory to avoid stepping on any
+ * other application uses of the TPM */
+static const char *
+tpm2_set_unique_tssdir(void)
+{
+  char *prefix = getenv("XDG_RUNTIME_DIR"), *template,
+    *dir;
+  int len = 0;
+
+  if (!prefix)
+    prefix = "/tmp";
+
+  len = snprintf(NULL, 0, "%s/tss2.XXXXXX", prefix);
+  if (len <= 0)
+    return NULL;
+  template = xtrymalloc(len + 1);
+  if (!template)
+    return NULL;
+
+  len++;
+  len = snprintf(template, len, "%s/tss2.XXXXXX", prefix);
+
+  dir = mkdtemp(template);
+
+  return dir;
+}
+
+/* now dynamically load the tss library (if it exists) and resolve the
+ * above symbols.  This allows us simply to return 0 for tpm2_init on
+ * systems where there is no TPM library */
+static int
+tpm2_init(void)
+{
+  static int inited = 0;
+  const char *sym;
+  void *dl;
+
+  if (inited)
+    return 0;
+
+  dl = dlopen(TSS2_LIB, RTLD_LAZY);
+
+  if (!dl)
+    {
+      log_error("opening of tss2 library failed %s\n", strerror(errno));
+      return GPG_ERR_CARD_NOT_PRESENT;
+    }
+
+  /* load each symbol pointer and check for existence */
+# define _DL_SYM(func)                          \
+    sym = #func;                                \
+    p##func = dlsym(dl, #func);                 \
+      if (p##func == NULL)                      \
+        goto out_symfail
+
+  _TSS2_LIST(_DL_SYM);
+
+  tpm2_dir = tpm2_set_unique_tssdir();
+  if (!tpm2_dir)
+    /* make this non fatal */
+    log_error("Failed to set unique TPM directory\n");
+  inited = 1;
+  return 0;
+
+ out_symfail:
+  log_error("Failed to find symbol %s in tss2 library\n", sym);
+  return GPG_ERR_CARD_NOT_PRESENT;
+}
+
+static void
+tpm2_error(TPM_RC rc, char *prefix)
+{
+  const char *msg, *submsg, *num;
+
+  pTSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+  log_error("%s gave TPM2 Error: %s%s%s", prefix, msg, submsg, num);
+}
+
+#define _TSS_CHECK(f)           \
+  rc = f;                       \
+  if (rc != TPM_RC_SUCCESS)     \
+    {                           \
+      tpm2_error(rc, #f);       \
+      return GPG_ERR_CARD;      \
+    }
+
+int
+tpm2_start(TSS_CONTEXT **tssc)
+{
+  TPM_RC rc;
+  int ret;
+
+  ret = tpm2_init();
+  if (ret)
+    return ret;
+
+  _TSS_CHECK(pTSS_Create(tssc));
+  _TSS_CHECK(pTSS_SetProperty(*tssc, TPM_DATA_DIR, tpm2_dir));
+  return 0;
+}
+
+void
+tpm2_end(TSS_CONTEXT *tssc)
+{
+  pTSS_Delete(tssc);
+}
+
+void
+tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h)
+{
+        FlushContext_In in;
+
+        if (!h)
+                return;
+
+        in.flushHandle = h;
+        pTSS_Execute(tssc, NULL,
+                     (COMMAND_PARAMETERS *)&in,
+                     NULL,
+                     TPM_CC_FlushContext,
+                     TPM_RH_NULL, NULL, 0);
+}
+
+static int
+tpm2_get_hmac_handle(TSS_CONTEXT *tssc, TPM_HANDLE *handle,
+                     TPM_HANDLE salt_key)
+{
+  TPM_RC rc;
+  StartAuthSession_In in;
+  StartAuthSession_Out out;
+  StartAuthSession_Extra extra;
+
+  memset(&in, 0, sizeof(in));
+  memset(&extra, 0 , sizeof(extra));
+  in.bind = TPM_RH_NULL;
+  in.sessionType = TPM_SE_HMAC;
+  in.authHash = TPM_ALG_SHA256;
+  in.tpmKey = TPM_RH_NULL;
+  in.symmetric.algorithm = TPM_ALG_AES;
+  in.symmetric.keyBits.aes = 128;
+  in.symmetric.mode.aes = TPM_ALG_CFB;
+  if (salt_key) {
+    ReadPublic_In rin;
+    ReadPublic_Out rout;
+
+    rin.objectHandle = salt_key;
+    rc = pTSS_Execute (tssc,
+                       (RESPONSE_PARAMETERS *)&rout,
+                       (COMMAND_PARAMETERS *)&rin,
+                       NULL,
+                       TPM_CC_ReadPublic,
+                       TPM_RH_NULL, NULL, 0);
+    if (rc) {
+      tpm2_error(rc, "TPM2_ReadPublic");
+      return GPG_ERR_CARD;
+    }
+
+    /* don't care what rout returns, the purpose of the operation was
+     * to get the public key parameters into the tss so it can
+     * construct the salt */
+    in.tpmKey = salt_key;
+  }
+  rc = pTSS_Execute(tssc,
+                    (RESPONSE_PARAMETERS *)&out,
+                    (COMMAND_PARAMETERS *)&in,
+                    (EXTRA_PARAMETERS *)&extra,
+                    TPM_CC_StartAuthSession,
+                    TPM_RH_NULL, NULL, 0);
+  if (rc) {
+    tpm2_error(rc, "TPM2_StartAuthSession");
+    return GPG_ERR_CARD;
+  }
+
+  *handle = out.sessionHandle;
+
+  return 0;
+}
+
+static int
+tpm2_exec_with_auth(ctrl_t ctrl, TSS_CONTEXT *tssc, int cmd, char *cmd_str,
+                    void *out, void *in)
+{
+  TPM_HANDLE ah;
+  struct pin_entry_info_s *pi;
+  TPM_RC rc;
+
+  pi = gcry_xmalloc_secure(sizeof(*pi) + MAX_PASSPHRASE_LEN + 10);
+  pi->max_length = MAX_PASSPHRASE_LEN;
+  pi->min_digits = 0;           /* want a real passphrase */
+  pi->max_digits = 16;
+  pi->max_tries = 3;
+  rc = agent_askpin(ctrl, NULL, "TPM Key Passphrase", NULL, pi, NULL, 0);
+  if (rc) {
+    gcry_free (pi);
+    return rc;
+  }
+
+  rc = tpm2_get_hmac_handle(tssc, &ah, 0);
+  if (rc)
+    return rc;
+
+  rc = pTSS_Execute(tssc, out, in, NULL,
+                    cmd,
+                    ah, pi->pin, 0,
+                    TPM_RH_NULL, NULL, 0);
+  gcry_free (pi);
+  if (rc) {
+    tpm2_error(rc, cmd_str);
+    tpm2_flush_handle(tssc, ah);
+    switch (rc & 0xFF) {
+    case TPM_RC_BAD_AUTH:
+    case TPM_RC_AUTH_FAIL:
+      return GPG_ERR_BAD_PASSPHRASE;
+    default:
+      return GPG_ERR_CARD;
+    }
+  }
+  return 0;
+}
+
+static gpg_error_t
+parse_tpm2_shadow_info (const unsigned char *shadow_info,
+                        uint32_t *parent,
+                        const char **pub, int *pub_len,
+                        const char **priv, int *priv_len)
+{
+  const unsigned char *s;
+  size_t n;
+  int i;
+
+  s = shadow_info;
+  if (*s != '(')
+    return gpg_error (GPG_ERR_INV_SEXP);
+  s++;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  *parent = 0;
+  for (i = 0; i < n; i++) {
+    *parent *= 10;
+    *parent += atoi_1(s+i);
+  }
+
+  s += n;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+
+  *pub_len = n;
+  *pub = s;
+
+  s += n;
+  n = snext (&s);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+
+  *priv_len = n;
+  *priv = s;
+
+  return 0;
+}
+
+int
+tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
+              TPM_HANDLE *key)
+{
+  uint32_t parent;
+  Load_In in;
+  Load_Out out;
+  const char *pub, *priv;
+  int ret, pub_len, priv_len;
+  TPM_RC rc;
+  BYTE *buf;
+  uint32_t size;
+
+  ret = parse_tpm2_shadow_info (shadow_info, &parent, &pub, &pub_len,
+                                &priv, &priv_len);
+  if (ret)
+    return ret;
+
+  in.parentHandle = parent;
+
+  buf = (BYTE *)priv;
+  size = priv_len;
+  pTPM2B_PRIVATE_Unmarshal(&in.inPrivate, &buf, &size);
+
+  buf = (BYTE *)pub;
+  size = pub_len;
+  pTPM2B_PUBLIC_Unmarshal(&in.inPublic, &buf, &size, FALSE);
+
+  rc = pTSS_Execute(tssc,
+                    (RESPONSE_PARAMETERS *)&out,
+                    (COMMAND_PARAMETERS *)&in,
+                    NULL,
+                    TPM_CC_Load,
+                    TPM_RS_PW, NULL, 0,
+                    TPM_RH_NULL, NULL, 0);
+  if (rc != TPM_RC_SUCCESS) {
+    tpm2_error(rc, "TPM2_Load");
+    return GPG_ERR_CARD;
+  }
+
+  *key = out.objectHandle;
+
+  return 0;
+}
+
+int
+tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+          const unsigned char *digest, size_t digestlen,
+          unsigned char **r_sig, size_t *r_siglen)
+{
+  Sign_In in;
+  Sign_Out out;
+  int ret;
+
+  /* The TPM insists on knowing the digest type, so
+   * calculate that from the size */
+  in.inScheme.scheme = TPM_ALG_RSASSA;
+  switch (digestlen) {
+  case 20:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
+    break;
+  case 32:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA256;
+    break;
+  case 48:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA384;
+    break;
+#ifdef TPM_ALG_SHA512
+  case 64:
+    in.inScheme.details.rsassa.hashAlg = TPM_ALG_SHA512;
+    break;
+#endif
+  default:
+    log_error("Unknown signature digest length, cannot deduce hash type for TPM\n");
+    return GPG_ERR_NO_SIGNATURE_SCHEME;
+  }
+  in.digest.t.size = digestlen;
+  memcpy(in.digest.t.buffer, digest, digestlen);
+  in.keyHandle = key;
+  in.validation.tag = TPM_ST_HASHCHECK;
+  in.validation.hierarchy = TPM_RH_NULL;
+  in.validation.digest.t.size = 0;
+
+  ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_Sign, "TPM2_Sign", &out, &in);
+  if (ret)
+    return ret;
+
+  *r_siglen = out.signature.signature.rsassa.sig.t.size;
+  *r_sig = xtrymalloc(*r_siglen);
+  if (!r_sig)
+    return GPG_ERR_ENOMEM;
+
+  memcpy(*r_sig, out.signature.signature.rsassa.sig.t.buffer, *r_siglen);
+
+  return 0;
+}
+
+static int
+sexp_to_tpm2_sensitive(TPMT_SENSITIVE *s, gcry_sexp_t key)
+{
+  gcry_mpi_t p;
+  gcry_sexp_t l;
+  int rc = -1;
+  size_t len;
+
+  s->sensitiveType = TPM_ALG_RSA;
+  s->seedValue.b.size = 0;
+
+  l = gcry_sexp_find_token (key, "p", 0);
+  if (!l)
+    return rc;
+  p = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof(s->sensitive.rsa.t.buffer);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, s->sensitive.rsa.t.buffer, len, &len, p);
+  s->sensitive.rsa.t.size = len;
+  gcry_mpi_release (p);
+
+  return rc;
+}
+
+static int
+sexp_to_tpm2_public(TPMT_PUBLIC *p, gcry_sexp_t key)
+{
+  gcry_mpi_t n, e;
+  gcry_sexp_t l;
+  int rc = -1, i;
+  size_t len;
+  /* longer than an int */
+  unsigned char ebuf[5];
+  uint32_t exp = 0;
+
+  p->type = TPM_ALG_RSA;
+  p->nameAlg = TPM_ALG_SHA256;
+  /* note: all our keys are decrypt only.  This is because
+   * we use the TPM2_RSA_Decrypt operation for both signing
+   * and decryption (see e_tpm2.c for details) */
+  p->objectAttributes.val = TPMA_OBJECT_NODA |
+    TPMA_OBJECT_DECRYPT |
+    TPMA_OBJECT_SIGN |
+    TPMA_OBJECT_USERWITHAUTH;
+  p->authPolicy.t.size = 0;
+  p->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
+  p->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+
+  l = gcry_sexp_find_token (key, "n", 0);
+  if (!l)
+    return rc;
+  n = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof(p->unique.rsa.t.buffer);
+  p->parameters.rsaDetail.keyBits = gcry_mpi_get_nbits (n);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, p->unique.rsa.t.buffer, len, &len, n);
+  p->unique.rsa.t.size = len;
+  gcry_mpi_release (n);
+  if (rc)
+    return rc;
+  rc = -1;
+  l = gcry_sexp_find_token (key, "e", 0);
+  if (!l)
+    return rc;
+  e = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
+  gcry_sexp_release (l);
+  len = sizeof (ebuf);
+  rc = gcry_mpi_print (GCRYMPI_FMT_USG, ebuf, len, &len, e);
+  gcry_mpi_release (e);
+  if (rc)
+    return rc;
+  if (len > 4)
+    return -1;
+
+  /* MPI are simply big endian integers, so convert to uint32 */
+  for (i = 0; i < len; i++) {
+    exp <<= 8;
+    exp += ebuf[i];
+  }
+  if (exp == 0x10001)
+    p->parameters.rsaDetail.exponent = 0;
+  else
+    p->parameters.rsaDetail.exponent = exp;
+  return 0;
+}
+
+static int
+sexp_to_tpm2(TPMT_PUBLIC *p, TPMT_SENSITIVE *s, gcry_sexp_t s_skey)
+{
+  gcry_sexp_t l1, l2;
+  int rc = -1;
+
+  /* find the value of (private-key */
+  l1 = gcry_sexp_nth (s_skey, 1);
+  if (!l1)
+    return rc;
+
+  l2 = gcry_sexp_find_token (l1, "rsa", 0);
+  if (!l2)
+    goto out;
+
+  rc = sexp_to_tpm2_public(p, l2);
+  if (!rc)
+    rc = sexp_to_tpm2_sensitive(s, l2);
+
+  gcry_sexp_release(l2);
+
+ out:
+  gcry_sexp_release(l1);
+  return rc;
+}
+
+/* copied from TPM implementation code */
+static TPM_RC
+tpm2_ObjectPublic_GetName(TPM2B_NAME *name,
+                          TPMT_PUBLIC *tpmtPublic)
+{
+  TPM_RC rc = 0;
+  uint16_t written = 0;
+  TPMT_HA digest;
+  uint32_t sizeInBytes;
+  uint8_t buffer[MAX_RESPONSE_SIZE];
+
+  /* marshal the TPMT_PUBLIC */
+  if (rc == 0) {
+    INT32 size = MAX_RESPONSE_SIZE;
+    uint8_t *buffer1 = buffer;
+    rc = pTSS_TPMT_PUBLIC_Marshal(tpmtPublic, &written, &buffer1, &size);
+  }
+  /* hash the public area */
+  if (rc == 0) {
+    sizeInBytes = pTSS_GetDigestSize(tpmtPublic->nameAlg);
+    digest.hashAlg = tpmtPublic->nameAlg;       /* Name digest algorithm */
+    /* generate the TPMT_HA */
+    rc = pTSS_Hash_Generate(&digest,
+                            written, buffer,
+                            0, NULL);
+  }
+  if (rc == 0) {
+    TPMI_ALG_HASH nameAlgNbo;
+
+    /* copy the digest */
+    memcpy(name->t.name + sizeof(TPMI_ALG_HASH), (uint8_t *)&digest.digest, sizeInBytes);
+    /* copy the hash algorithm */
+    nameAlgNbo = htons(tpmtPublic->nameAlg);
+    memcpy(name->t.name, (uint8_t *)&nameAlgNbo, sizeof(TPMI_ALG_HASH));
+    /* set the size */
+    name->t.size = sizeInBytes + sizeof(TPMI_ALG_HASH);
+  }
+  return rc;
+}
+
+/*
+ * Cut down version of Part 4 Supporting Routines 7.6.3.10
+ *
+ * Hard coded to symmetrically encrypt with aes128 as the inner
+ * wrapper and no outer wrapper but with a prototype that allows
+ * drop in replacement with a tss equivalent
+ */
+TPM_RC tpm2_SensitiveToDuplicate(TPMT_SENSITIVE *s,
+                                 TPM2B_NAME *name,
+                                 TPM_ALG_ID nalg,
+                                 TPMT_SYM_DEF_OBJECT *symdef,
+                                 TPM2B_DATA *innerkey,
+                                 TPM2B_PRIVATE *p)
+{
+  BYTE *buf = p->t.buffer;
+
+  p->t.size = 0;
+  memset(p, 0, sizeof(*p));
+
+  /* hard code AES CFB */
+  if (symdef->algorithm == TPM_ALG_AES
+      && symdef->mode.aes == TPM_ALG_CFB) {
+    TPMT_HA hash;
+    const int hlen = pTSS_GetDigestSize(nalg);
+    TPM2B *digest = (TPM2B *)buf;
+    TPM2B *s2b;
+    int32_t size;
+    unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
+    UINT16 bsize, written = 0;
+    gcry_cipher_hd_t hd;
+
+    /* WARNING: don't use the static null_iv trick here:
+     * the AES routines alter the passed in iv */
+    memset(null_iv, 0, sizeof(null_iv));
+
+    /* reserve space for hash before the encrypted sensitive */
+    bsize = sizeof(digest->size) + hlen;
+    buf += bsize;
+    p->t.size += bsize;
+    s2b = (TPM2B *)buf;
+
+    /* marshal the digest size */
+    buf = (BYTE *)&digest->size;
+    bsize = hlen;
+    size = 2;
+    pTSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+    /* marshal the unencrypted sensitive in place */
+    size = sizeof(*s);
+    bsize = 0;
+    buf = s2b->buffer;
+    pTSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+    buf = (BYTE *)&s2b->size;
+    size = 2;
+    pTSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+    bsize = bsize + sizeof(s2b->size);
+    p->t.size += bsize;
+
+    /* compute hash of unencrypted marshalled sensitive and
+     * write to the digest buffer */
+    hash.hashAlg = nalg;
+    pTSS_Hash_Generate(&hash, bsize, s2b,
+                       name->t.size, name->t.name,
+                       0, NULL);
+    memcpy(digest->buffer, &hash.digest, hlen);
+    gcry_cipher_open (&hd, GCRY_CIPHER_AES128,
+		      GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
+    gcry_cipher_setiv(hd, null_iv, sizeof(null_iv));
+    gcry_cipher_setkey(hd, innerkey->b.buffer, innerkey->b.size);
+    /* encrypt the hash and sensitive in-place */
+    gcry_cipher_encrypt(hd, p->t.buffer, p->t.size, NULL, 0);
+    gcry_cipher_close(hd);
+
+  } else if (symdef->algorithm == TPM_ALG_NULL) {
+    TPM2B *s2b = (TPM2B *)buf;
+    int32_t size = sizeof(*s);
+    UINT16 bsize = 0, written = 0;
+
+    buf = s2b->buffer;
+
+    /* marshal the unencrypted sensitive in place */
+    pTSS_TPMT_SENSITIVE_Marshal(s, &bsize, &buf, &size);
+    buf = (BYTE *)&s2b->size;
+    size = 2;
+    pTSS_UINT16_Marshal(&bsize, &written, &buf, &size);
+
+    p->b.size += bsize + sizeof(s2b->size);
+  } else {
+    log_error ("Unknown symmetric algorithm\n");
+    return TPM_RC_SYMMETRIC;
+  }
+
+  return TPM_RC_SUCCESS;
+}
+
+static void
+tpm2_encrypt_duplicate(Import_In *iin, TPMT_SENSITIVE *s)
+{
+  TPM2B_NAME name;
+  TPMT_PUBLIC *p = &iin->objectPublic.publicArea;
+  const int aes_key_bits = 128;
+  const int aes_key_bytes = aes_key_bits/8;
+
+  tpm2_ObjectPublic_GetName(&name, p);
+  gcry_randomize(iin->encryptionKey.t.buffer,
+                 aes_key_bytes, GCRY_STRONG_RANDOM);
+  iin->encryptionKey.t.size = aes_key_bytes;
+
+  /* set random iin.symSeed */
+  iin->inSymSeed.t.size = 0;
+  iin->symmetricAlg.algorithm = TPM_ALG_AES;
+  iin->symmetricAlg.keyBits.aes = aes_key_bits;
+  iin->symmetricAlg.mode.aes = TPM_ALG_CFB;
+
+  tpm2_SensitiveToDuplicate(s, &name, p->nameAlg, &iin->symmetricAlg,
+                            &iin->encryptionKey, &iin->duplicate);
+}
+
+int
+tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc, char *pub, int *pub_len,
+                char *priv, int *priv_len, gcry_sexp_t s_skey)
+{
+  Import_In iin;
+  Import_Out iout;
+  TPMT_SENSITIVE s;
+  TPM_HANDLE ah;
+  TPM_RC rc;
+
+  uint32_t size;
+  uint16_t len;
+  BYTE *buffer;
+  int ret;
+  char *passphrase;
+
+  iin.parentHandle = TPM2_PARENT;
+  ret = sexp_to_tpm2(&iin.objectPublic.publicArea, &s, s_skey);
+  if (ret) {
+    log_error("Failed to parse Key s-expression: key corrupt?\n");
+    return ret;
+  }
+
+  /* add an authorization password to the key which the TPM will check */
+
+  ret = agent_ask_new_passphrase (ctrl,  _("Please enter the TPM Authorization passphrase for the key."), &passphrase);
+  if (ret)
+    return ret;
+  s.authValue.b.size = strlen(passphrase);
+  memcpy(s.authValue.b.buffer, passphrase, s.authValue.b.size);
+
+  /* We're responsible for securing the data in transmission to the
+   * TPM here.  The TPM provides parameter encryption via a session,
+   * but only for the first parameter.  For TPM2_Import, the first
+   * parameter is a symmetric key used to encrypt the sensitive data,
+   * so we must populate this key with random value and encrypt the
+   * sensitive data with it */
+  tpm2_encrypt_duplicate(&iin, &s);
+
+  /* use salted parameter encryption to hide the key.  First we read
+   * the public parameters of the parent key and use them to agree an
+   * encryption for the first parameter */
+  rc = tpm2_get_hmac_handle(tssc, &ah, TPM2_PARENT);
+  if (rc)
+    return GPG_ERR_CARD;
+
+  rc = pTSS_Execute(tssc,
+                    (RESPONSE_PARAMETERS *)&iout,
+                    (COMMAND_PARAMETERS *)&iin,
+                    NULL,
+                    TPM_CC_Import,
+                    ah, NULL, TPMA_SESSION_DECRYPT,
+                    TPM_RH_NULL, NULL, 0);
+  if (rc) {
+    tpm2_error(rc, "TPM2_Import");
+    /* failure means auth handle is not flushed */
+    tpm2_flush_handle(tssc, ah);
+    return GPG_ERR_CARD;
+  }
+
+  size = sizeof(TPM2B_PUBLIC);
+  buffer = pub;
+  len = 0;
+  pTSS_TPM2B_PUBLIC_Marshal(&iin.objectPublic,
+                            &len, &buffer, &size);
+  *pub_len = len;
+
+  size = sizeof(TPM2B_PRIVATE);
+  buffer = priv;
+  len = 0;
+  pTSS_TPM2B_PRIVATE_Marshal(&iout.outPrivate,
+                             &len, &buffer, &size);
+  *priv_len = len;
+
+  return 0;
+}
+
+int
+tpm2_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+             const char *ciphertext, int ciphertext_len,
+             char **decrypt, size_t *decrypt_len)
+{
+  RSA_Decrypt_In in;
+  RSA_Decrypt_Out out;
+  int ret;
+
+  in.keyHandle = key;
+  in.inScheme.scheme = TPM_ALG_RSAES;
+  in.cipherText.t.size = ciphertext_len;
+  memcpy (in.cipherText.t.buffer, ciphertext, ciphertext_len);
+  in.label.t.size = 0;
+
+  ret = tpm2_exec_with_auth(ctrl, tssc, TPM_CC_RSA_Decrypt, "TPM2_RSA_Decrypt",
+                            &out, &in);
+  if (ret)
+    return ret;
+
+  *decrypt_len = out.message.t.size;
+  *decrypt = xtrymalloc(out.message.t.size);
+  memcpy (*decrypt, out.message.t.buffer, out.message.t.size);
+
+  return 0;
+}
diff --git a/agent/tpm2.h b/agent/tpm2.h
new file mode 100644
index 0000000..2e16803
--- /dev/null
+++ b/agent/tpm2.h
@@ -0,0 +1,22 @@
+#ifndef _TPM2_H
+#define _TPM2_H
+
+#include <tss2/tss.h>
+
+#define TSS2_LIB "libtss.so.0"
+#define TPM2_PARENT 0x81000001
+
+int tpm2_start(TSS_CONTEXT **tssc);
+void tpm2_end(TSS_CONTEXT *tssc);
+void tpm2_flush_handle(TSS_CONTEXT *tssc, TPM_HANDLE h);
+int tpm2_load_key(TSS_CONTEXT *tssc, const unsigned char *shadow_info,
+                  TPM_HANDLE *key);
+int tpm2_sign(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+              const unsigned char *digest, size_t digestlen,
+              unsigned char **r_sig, size_t *r_siglen);
+int tpm2_import_key(ctrl_t ctrl, TSS_CONTEXT *tssc, char *pub, int *pub_len,
+                    char *priv, int *priv_len, gcry_sexp_t s_skey);
+int tpm2_decrypt(ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+                 const char *ciphertext, int ciphertext_len,
+                 char **decrypt, size_t *decrypt_len);
+#endif

commit 30c434eaf34e8facb3df0eebdd2de26f4fa604af
Author: James Bottomley <James.Bottomley at HansenPartnership.com>
Date:   Mon Mar 5 11:13:25 2018 -0800

    agent: expose shadow key type
    
    * agent/findkey.c (agent_key_info_from_file): Add new return arg
    r_shadow_info_type.
    * agent/protect.c (agent_shadow_key): Factor code out to ...
    (agent_shadow_key_type): new.  Add arg 'type'.
    (agent_get_shadow_info): Factor code out to ...
    (agent_get_shadow_info_type): new. Add arg 'shadow_type'.
    (agent_is_tpm2_key): New.
    (agent_get_shadow_type): New.
    * agent/command.c (do_one_keyinfo): Get and check the
    shadow_info_type.
    --
    
    For TPM support it is necessary to indroduce another type of shadow
    key, so allow other agent functions to extract the type so they can
    make the right decisions based on it.
    
    Signed-off-by: James Bottomley <James.Bottomley at HansenPartnership.com>
    
    Added ChangeLog entries.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/agent/agent.h b/agent/agent.h
index 0d5cf4f..e13ee1f 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -420,7 +420,8 @@ int agent_is_eddsa_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,
-                                      unsigned char **r_shadow_info);
+                                      unsigned char **r_shadow_info,
+                                      unsigned char **r_shadow_info_type);
 gpg_error_t agent_delete_key (ctrl_t ctrl, const char *desc_text,
                               const unsigned char *grip,
                               int force, int only_stubs);
@@ -503,8 +504,15 @@ unsigned char *make_shadow_info (const char *serialno, const char *idstring);
 int agent_shadow_key (const unsigned char *pubkey,
                       const unsigned char *shadow_info,
                       unsigned char **result);
+int agent_shadow_key_type (const unsigned char *pubkey,
+                           const unsigned char *shadow_info,
+                           const unsigned char *type,
+                           unsigned char **result);
 gpg_error_t agent_get_shadow_info (const unsigned char *shadowkey,
                                    unsigned char const **shadow_info);
+gpg_error_t agent_get_shadow_info_type (const unsigned char *shadowkey,
+                                        unsigned char const **shadow_info,
+                                        unsigned char **shadow_type);
 gpg_error_t parse_shadow_info (const unsigned char *shadow_info,
                                char **r_hexsn, char **r_idstr, int *r_pinlen);
 gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo,
diff --git a/agent/command.c b/agent/command.c
index e2486a5..32c12d9 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1104,7 +1104,7 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
   char hexgrip[40+1];
   char *fpr = NULL;
   int keytype;
-  unsigned char *shadow_info = NULL;
+  unsigned char *shadow_info = NULL, *shadow_info_type = NULL;
   char *serialno = NULL;
   char *idstr = NULL;
   const char *keytypestr;
@@ -1115,7 +1115,8 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
   char ttlbuf[20];
   char flagsbuf[5];
 
-  err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info);
+  err = agent_key_info_from_file (ctrl, grip, &keytype, &shadow_info,
+                                  &shadow_info_type);
   if (err)
     {
       if (in_ssh && gpg_err_code (err) == GPG_ERR_NOT_FOUND)
@@ -1185,9 +1186,18 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
 
   if (shadow_info)
     {
-      err = parse_shadow_info (shadow_info, &serialno, &idstr, NULL);
-      if (err)
-        goto leave;
+      if (strcmp (shadow_info_type, "t1-v1") == 0)
+        {
+          err = parse_shadow_info (shadow_info, &serialno, &idstr, NULL);
+          if (err)
+            goto leave;
+        }
+      else
+        {
+          log_error ("Unrecognised shadow key type %s\n", shadow_info_type);
+          err = GPG_ERR_BAD_KEY;
+          goto leave;
+        }
     }
 
   if (!data)
@@ -1222,6 +1232,7 @@ do_one_keyinfo (ctrl_t ctrl, const unsigned char *grip, assuan_context_t ctx,
 
  leave:
   xfree (fpr);
+  xfree (shadow_info_type);
   xfree (shadow_info);
   xfree (serialno);
   xfree (idstr);
diff --git a/agent/findkey.c b/agent/findkey.c
index e3e9a12..d6c600e 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -1359,7 +1359,8 @@ agent_key_available (const unsigned char *grip)
    S-expression.  */
 gpg_error_t
 agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
-                          int *r_keytype, unsigned char **r_shadow_info)
+                          int *r_keytype, unsigned char **r_shadow_info,
+                          unsigned char **r_shadow_info_type)
 {
   gpg_error_t err;
   unsigned char *buf;
@@ -1406,7 +1407,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip,
           const unsigned char *s;
           size_t n;
 
-          err = agent_get_shadow_info (buf, &s);
+          err = agent_get_shadow_info_type (buf, &s, r_shadow_info_type);
           if (!err)
             {
               n = gcry_sexp_canon_len (s, 0, NULL, NULL);
diff --git a/agent/protect.c b/agent/protect.c
index 16ae715..0920667 100644
--- a/agent/protect.c
+++ b/agent/protect.c
@@ -1499,9 +1499,10 @@ make_shadow_info (const char *serialno, const char *idstring)
   to. The input parameters are expected to be valid canonicalized
   S-expressions */
 int
-agent_shadow_key (const unsigned char *pubkey,
-                  const unsigned char *shadow_info,
-                  unsigned char **result)
+agent_shadow_key_type (const unsigned char *pubkey,
+                       const unsigned char *shadow_info,
+                       const unsigned char *type,
+                       unsigned char **result)
 {
   const unsigned char *s;
   const unsigned char *point;
@@ -1557,7 +1558,7 @@ agent_shadow_key (const unsigned char *pubkey,
   assert (depth == 1);
 
   /* Calculate required length by taking in account: the "shadowed-"
-     prefix, the "shadowed", "t1-v1" as well as some parenthesis */
+     prefix, the "shadowed", shadow type as well as some parenthesis */
   n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
   *result = xtrymalloc (n);
   p = (char*)*result;
@@ -1567,7 +1568,7 @@ agent_shadow_key (const unsigned char *pubkey,
   /* (10:public-key ...)*/
   memcpy (p, pubkey+14, point - (pubkey+14));
   p += point - (pubkey+14);
-  p = stpcpy (p, "(8:shadowed5:t1-v1");
+  p += sprintf (p, "(8:shadowed%d:%s", (int)strlen(type), type);
   memcpy (p, shadow_info, shadow_info_len);
   p += shadow_info_len;
   *p++ = ')';
@@ -1577,11 +1578,20 @@ agent_shadow_key (const unsigned char *pubkey,
   return 0;
 }
 
+int
+agent_shadow_key (const unsigned char *pubkey,
+                  const unsigned char *shadow_info,
+                  unsigned char **result)
+{
+  return agent_shadow_key_type (pubkey, shadow_info, "t1-v1", result);
+}
+
 /* Parse a canonical encoded shadowed key and return a pointer to the
-   inner list with the shadow_info */
+   inner list with the shadow_info and the shadow type */
 gpg_error_t
-agent_get_shadow_info (const unsigned char *shadowkey,
-                       unsigned char const **shadow_info)
+agent_get_shadow_info_type (const unsigned char *shadowkey,
+                            unsigned char const **shadow_info,
+                            unsigned char **shadow_type)
 {
   const unsigned char *s;
   size_t n;
@@ -1633,17 +1643,59 @@ agent_get_shadow_info (const unsigned char *shadowkey,
   n = snext (&s);
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP);
-  if (smatch (&s, n, "t1-v1"))
+  if (shadow_type) {
+    char *buf = xtrymalloc(n+1);
+    memcpy(buf, s, n);
+    buf[n] = '\0';
+    *shadow_type = buf;
+  }
+
+  if (smatch (&s, n, "t1-v1") || smatch(&s, n, "tpm2-v1"))
     {
       if (*s != '(')
         return gpg_error (GPG_ERR_INV_SEXP);
-      *shadow_info = s;
+      if (shadow_info)
+        *shadow_info = s;
     }
   else
     return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
   return 0;
 }
 
+gpg_error_t
+agent_get_shadow_info(const unsigned char *shadowkey,
+                      unsigned char const **shadow_info)
+{
+  return agent_get_shadow_info_type(shadowkey, shadow_info, NULL);
+}
+
+int
+agent_is_tpm2_key(gcry_sexp_t s_skey)
+{
+  unsigned char *buf;
+  unsigned char *type;
+  size_t len;
+  gpg_error_t err;
+
+  err = make_canon_sexp(s_skey, &buf, &len);
+  if (err)
+    return 0;
+
+  err = agent_get_shadow_info_type(buf, NULL, &type);
+  if (err)
+    return 0;
+
+  err = strcmp(type, "tpm2-v1") == 0;
+  xfree(type);
+  return err;
+}
+
+gpg_error_t
+agent_get_shadow_type(const unsigned char *shadowkey,
+                      unsigned char **shadow_type)
+{
+  return agent_get_shadow_info_type(shadowkey, NULL, shadow_type);
+}
 
 /* Parse the canonical encoded SHADOW_INFO S-expression.  On success
    the hex encoded serial number is returned as a malloced strings at

-----------------------------------------------------------------------


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list