diff --git a/agent/Makefile.am b/agent/Makefile.am
index ce29462b2..39e69cdd0 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -1,109 +1,110 @@
# Copyright (C) 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
#
# This file is part of GnuPG.
#
# GnuPG is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# GnuPG is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see .
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = gpg-agent
libexec_PROGRAMS = gpg-protect-tool
if !HAVE_W32CE_SYSTEM
# fixme: Do no use simple-pwquery for preset-passphrase.
libexec_PROGRAMS += gpg-preset-passphrase
endif
noinst_PROGRAMS = $(TESTS)
EXTRA_DIST = ChangeLog-2011 gpg-agent-w32info.rc all-tests.scm
AM_CPPFLAGS =
include $(top_srcdir)/am/cmacros.am
if HAVE_W32_SYSTEM
resource_objs += gpg-agent-w32info.o
endif
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
gpg_agent_SOURCES = \
gpg-agent.c agent.h \
command.c command-ssh.c \
call-pinentry.c \
cache.c \
trans.c \
findkey.c \
pksign.c \
pkdecrypt.c \
genkey.c \
protect.c \
trustlist.c \
divert-scd.c \
+ tpm2.c \
cvt-openpgp.c cvt-openpgp.h \
call-scd.c \
learncard.c
common_libs = $(libcommon)
commonpth_libs = $(libcommonpth)
if HAVE_W32CE_SYSTEM
pwquery_libs =
else
pwquery_libs = ../common/libsimple-pwquery.a
endif
gpg_agent_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \
$(INCICONV)
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 = \
protect-tool.c \
protect.c cvt-openpgp.c
gpg_protect_tool_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) \
$(INCICONV)
gpg_protect_tool_LDADD = $(common_libs) $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) \
$(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV)
gpg_preset_passphrase_SOURCES = \
preset-passphrase.c
# Needs $(NETLIBS) for libsimple-pwquery.la.
gpg_preset_passphrase_LDADD = \
$(pwquery_libs) $(common_libs) $(LIBASSUAN_LIBS) \
$(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV)
# Make sure that all libs are build before we use them. This is
# important for things like make -j2.
$(PROGRAMS): $(common_libs) $(commonpth_libs) $(pwquery_libs)
#
# Module tests
#
TESTS = t-protect
t_common_ldadd = $(common_libs) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \
$(LIBINTL) $(LIBICONV) $(NETLIBS)
t_protect_SOURCES = t-protect.c protect.c
t_protect_LDADD = $(t_common_ldadd)
diff --git a/agent/tpm2.c b/agent/tpm2.c
new file mode 100644
index 000000000..734f0fe74
--- /dev/null
+++ b/agent/tpm2.c
@@ -0,0 +1,784 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../common/i18n.h"
+#include "../common/sexp-parse.h"
+
+#include
+#include
+#include
+#include
+#include
+
+/* 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 000000000..2e168032f
--- /dev/null
+++ b/agent/tpm2.h
@@ -0,0 +1,22 @@
+#ifndef _TPM2_H
+#define _TPM2_H
+
+#include
+
+#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