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