aboutsummaryrefslogtreecommitdiffstats
path: root/tpm2d/tpm2.c
diff options
context:
space:
mode:
Diffstat (limited to 'tpm2d/tpm2.c')
-rw-r--r--tpm2d/tpm2.c987
1 files changed, 987 insertions, 0 deletions
diff --git a/tpm2d/tpm2.c b/tpm2d/tpm2.c
new file mode 100644
index 000000000..2bd3dc177
--- /dev/null
+++ b/tpm2d/tpm2.c
@@ -0,0 +1,987 @@
+#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 "tpm2.h"
+
+#include "../common/i18n.h"
+#include "../common/sexp-parse.h"
+
+int
+tpm2_start (TSS_CONTEXT **tssc)
+{
+ return TSS_start(tssc);
+}
+
+void
+tpm2_end (TSS_CONTEXT *tssc)
+{
+ TSS_Delete (tssc);
+}
+
+static TPM_HANDLE
+tpm2_get_parent (TSS_CONTEXT *tssc, TPM_HANDLE p)
+{
+ TPM_RC rc;
+ TPM2B_SENSITIVE_CREATE inSensitive;
+ TPM2B_PUBLIC inPublic;
+ TPM_HANDLE objectHandle;
+
+ p = tpm2_handle_int(tssc, p);
+ if (tpm2_handle_mso(tssc, p, TPM_HT_PERSISTENT))
+ return p; /* should only be permanent */
+
+ /* assume no hierarchy auth */
+ VAL_2B (inSensitive.sensitive.userAuth, size) = 0;
+ /* no sensitive date for storage keys */
+ VAL_2B (inSensitive.sensitive.data, size) = 0;
+
+ /* public parameters for a P-256 EC key */
+ inPublic.publicArea.type = TPM_ALG_ECC;
+ inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+ VAL (inPublic.publicArea.objectAttributes) =
+ TPMA_OBJECT_NODA |
+ TPMA_OBJECT_SENSITIVEDATAORIGIN |
+ TPMA_OBJECT_USERWITHAUTH |
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_RESTRICTED |
+ TPMA_OBJECT_FIXEDPARENT |
+ TPMA_OBJECT_FIXEDTPM;
+
+ inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
+ inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
+ inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
+ inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+ inPublic.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
+ inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+
+ VAL_2B (inPublic.publicArea.unique.ecc.x, size) = 0;
+ VAL_2B (inPublic.publicArea.unique.ecc.y, size) = 0;
+ VAL_2B (inPublic.publicArea.authPolicy, size) = 0;
+
+ rc = tpm2_CreatePrimary (tssc, p, &inSensitive, &inPublic, &objectHandle);
+ if (rc)
+ {
+ tpm2_error (rc, "TSS_CreatePrimary");
+ return 0;
+ }
+ return objectHandle;
+}
+
+void
+tpm2_flush_handle (TSS_CONTEXT *tssc, TPM_HANDLE h)
+{
+ /* only flush volatile handles */
+ if (tpm2_handle_mso(tssc, h, TPM_HT_PERSISTENT))
+ return;
+
+ tpm2_FlushContext(tssc, h);
+}
+
+static int
+tpm2_get_hmac_handle (TSS_CONTEXT *tssc, TPM_HANDLE *handle,
+ TPM_HANDLE salt_key)
+{
+ TPM_RC rc;
+ TPMT_SYM_DEF symmetric;
+
+ symmetric.algorithm = TPM_ALG_AES;
+ symmetric.keyBits.aes = 128;
+ symmetric.mode.aes = TPM_ALG_CFB;
+
+ rc = tpm2_StartAuthSession(tssc, salt_key, TPM_RH_NULL, TPM_SE_HMAC,
+ &symmetric, TPM_ALG_SHA256, handle, NULL);
+ if (rc)
+ {
+ tpm2_error (rc, "TPM2_StartAuthSession");
+ return GPG_ERR_CARD;
+ }
+
+ return 0;
+}
+
+static int
+tpm2_pre_auth (ctrl_t ctrl, TSS_CONTEXT *tssc,
+ gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+ char **retstr),
+ TPM_HANDLE *ah, char **auth)
+{
+ TPM_RC rc;
+ int len;
+
+ rc = pin_cb (ctrl, _("TPM Key Passphrase"), auth);
+ if (rc)
+ return rc;
+
+ len = strlen(*auth);
+ /*
+ * TPMs can't accept a longer passphrase than the name algorithm.
+ * We hard code the name algorithm to SHA256 so the max passphrase
+ * length is 32
+ */
+ if (len > 32)
+ {
+ log_error ("Truncating Passphrase to TPM allowed 32\n");
+ (*auth)[32] = '\0';
+ }
+
+ rc = tpm2_get_hmac_handle (tssc, ah, TPM_RH_NULL);
+
+ return rc;
+}
+
+static int
+tpm2_post_auth (TSS_CONTEXT *tssc, TPM_RC rc, TPM_HANDLE ah,
+ char **auth, const char *cmd_str)
+{
+ gcry_free (*auth);
+ *auth = NULL;
+ 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 unsigned char *
+make_tpm2_shadow_info (uint32_t parent, const char *pub, int pub_len,
+ const char *priv, int priv_len, size_t *len)
+{
+ gcry_sexp_t s_exp;
+ 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);
+ if (!info)
+ goto out;
+ gcry_sexp_sprint (s_exp, GCRYSEXP_FMT_CANON, info, *len);
+
+ out:
+ gcry_sexp_release (s_exp);
+ return (unsigned char *)info;
+}
+
+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, TPMI_ALG_PUBLIC *type)
+{
+ uint32_t parent;
+ TPM_HANDLE parentHandle;
+ PRIVATE_2B inPrivate;
+ TPM2B_PUBLIC inPublic;
+ 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;
+
+ parentHandle = tpm2_get_parent (tssc, parent);
+
+ buf = (BYTE *)priv;
+ size = priv_len;
+ TPM2B_PRIVATE_Unmarshal ((TPM2B_PRIVATE *)&inPrivate, &buf, &size);
+
+ buf = (BYTE *)pub;
+ size = pub_len;
+ TPM2B_PUBLIC_Unmarshal (&inPublic, &buf, &size, FALSE);
+
+ *type = inPublic.publicArea.type;
+
+ rc = tpm2_Load (tssc, parentHandle, &inPrivate, &inPublic, key,
+ TPM_RS_PW, NULL);
+
+ tpm2_flush_handle (tssc, parentHandle);
+
+ if (rc != TPM_RC_SUCCESS)
+ {
+ tpm2_error (rc, "TPM2_Load");
+ return GPG_ERR_CARD;
+ }
+
+ return 0;
+}
+
+int
+tpm2_sign (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+ gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+ char **retstr),
+ TPMI_ALG_PUBLIC type,
+ const unsigned char *digest, size_t digestlen,
+ unsigned char **r_sig, size_t *r_siglen)
+{
+ int ret;
+ DIGEST_2B digest2b;
+ TPMT_SIG_SCHEME inScheme;
+ TPMT_SIGNATURE signature;
+ TPM_HANDLE ah;
+ char *auth;
+
+ /* The TPM insists on knowing the digest type, so
+ * calculate that from the size */
+ switch (digestlen)
+ {
+ case 20:
+ inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
+ break;
+ case 32:
+ inScheme.details.rsassa.hashAlg = TPM_ALG_SHA256;
+ break;
+ case 48:
+ inScheme.details.rsassa.hashAlg = TPM_ALG_SHA384;
+ break;
+#ifdef TPM_ALG_SHA512
+ case 64:
+ 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;
+ }
+ digest2b.size = digestlen;
+ memcpy (digest2b.buffer, digest, digestlen);
+
+ if (type == TPM_ALG_RSA)
+ inScheme.scheme = TPM_ALG_RSASSA;
+ else if (type == TPM_ALG_ECC)
+ inScheme.scheme = TPM_ALG_ECDSA;
+ else
+ return GPG_ERR_PUBKEY_ALGO;
+
+ ret = tpm2_pre_auth (ctrl, tssc, pin_cb, &ah, &auth);
+ if (ret)
+ return ret;
+ ret = tpm2_Sign (tssc, key, &digest2b, &inScheme, &signature, ah, auth);
+ ret = tpm2_post_auth (tssc, ret, ah, &auth, "TPM2_Sign");
+ if (ret)
+ return ret;
+
+ if (type == TPM_ALG_RSA)
+ *r_siglen = VAL_2B (signature.signature.rsassa.sig, size);
+ else if (type == TPM_ALG_ECC)
+ *r_siglen = VAL_2B (signature.signature.ecdsa.signatureR, size)
+ + VAL_2B (signature.signature.ecdsa.signatureS, size);
+
+ *r_sig = xtrymalloc (*r_siglen);
+ if (!r_sig)
+ return GPG_ERR_ENOMEM;
+
+ if (type == TPM_ALG_RSA)
+ {
+ memcpy (*r_sig, VAL_2B (signature.signature.rsassa.sig, buffer),
+ *r_siglen);
+ }
+ else if (type == TPM_ALG_ECC)
+ {
+ memcpy (*r_sig, VAL_2B (signature.signature.ecdsa.signatureR, buffer),
+ VAL_2B (signature.signature.ecdsa.signatureR, size));
+ memcpy (*r_sig + VAL_2B (signature.signature.ecdsa.signatureR, size),
+ VAL_2B (signature.signature.ecdsa.signatureS, buffer),
+ VAL_2B (signature.signature.ecdsa.signatureS, size));
+ }
+
+ return 0;
+}
+
+static int
+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;
+ VAL_2B (s->seedValue, 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 (VAL_2B (s->sensitive.ecc, buffer));
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, VAL_2B (s->sensitive.ecc, buffer),
+ len, &len, d);
+ VAL_2B (s->sensitive.ecc, 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;
+ VAL (p->objectAttributes) = TPMA_OBJECT_NODA |
+ TPMA_OBJECT_SIGN |
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_USERWITHAUTH;
+ VAL_2B (p->authPolicy, 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 (VAL_2B (p->unique.ecc.x, buffer), q, len);
+ VAL_2B (p->unique.ecc.x, size) = len;
+ memcpy (VAL_2B (p->unique.ecc.y, buffer), q + len, len);
+ VAL_2B (p->unique.ecc.y, 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;
+ int rc = -1;
+ size_t len;
+
+ s->sensitiveType = TPM_ALG_RSA;
+ VAL_2B (s->seedValue, 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 (VAL_2B (s->sensitive.rsa, buffer));
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, VAL_2B (s->sensitive.rsa, buffer),
+ len, &len, p);
+ VAL_2B (s->sensitive.rsa, size) = len;
+ gcry_mpi_release (p);
+
+ return rc;
+}
+
+static int
+sexp_to_tpm2_public_rsa (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;
+ VAL (p->objectAttributes) = TPMA_OBJECT_NODA |
+ TPMA_OBJECT_DECRYPT |
+ TPMA_OBJECT_SIGN |
+ TPMA_OBJECT_USERWITHAUTH;
+ VAL_2B (p->authPolicy, 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 (VAL_2B (p->unique.rsa, buffer));
+ p->parameters.rsaDetail.keyBits = gcry_mpi_get_nbits (n);
+ rc = gcry_mpi_print (GCRYMPI_FMT_USG, VAL_2B (p->unique.rsa, buffer),
+ len, &len, n);
+ VAL_2B (p->unique.rsa, 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)
+ {
+ 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);
+
+ out:
+ gcry_sexp_release (l1);
+ return rc;
+}
+
+/* copied from TPM implementation code */
+static TPM_RC
+tpm2_ObjectPublic_GetName (NAME_2B *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 = TSS_TPMT_PUBLIC_Marshal (tpmtPublic, &written, &buffer1, &size);
+ }
+ /* hash the public area */
+ if (rc == 0)
+ {
+ sizeInBytes = TSS_GetDigestSize (tpmtPublic->nameAlg);
+ digest.hashAlg = tpmtPublic->nameAlg; /* Name digest algorithm */
+ /* generate the TPMT_HA */
+ rc = TSS_Hash_Generate (&digest, written, buffer, 0, NULL);
+ }
+ if (rc == 0)
+ {
+ TPMI_ALG_HASH nameAlgNbo;
+
+ /* copy the digest */
+ memcpy (name->name + sizeof (TPMI_ALG_HASH),
+ (uint8_t *)&digest.digest, sizeInBytes);
+ /* copy the hash algorithm */
+ nameAlgNbo = htons (tpmtPublic->nameAlg);
+ memcpy (name->name, (uint8_t *)&nameAlgNbo, sizeof (TPMI_ALG_HASH));
+ /* set the size */
+ name->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,
+ NAME_2B *name,
+ TPM_ALG_ID nalg,
+ TPMT_SYM_DEF_OBJECT *symdef,
+ DATA_2B *innerkey,
+ PRIVATE_2B *p)
+{
+ BYTE *buf = p->buffer;
+
+ p->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 = TSS_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->size += bsize;
+ s2b = (TPM2B *)buf;
+
+ /* marshal the digest size */
+ buf = (BYTE *)&digest->size;
+ bsize = hlen;
+ size = 2;
+ TSS_UINT16_Marshal (&bsize, &written, &buf, &size);
+
+ /* marshal the unencrypted sensitive in place */
+ size = sizeof (*s);
+ bsize = 0;
+ buf = s2b->buffer;
+ TSS_TPMT_SENSITIVE_Marshal (s, &bsize, &buf, &size);
+ buf = (BYTE *)&s2b->size;
+ size = 2;
+ TSS_UINT16_Marshal (&bsize, &written, &buf, &size);
+
+ bsize = bsize + sizeof (s2b->size);
+ p->size += bsize;
+
+ /* compute hash of unencrypted marshalled sensitive and
+ * write to the digest buffer */
+ hash.hashAlg = nalg;
+ TSS_Hash_Generate (&hash, bsize, s2b,
+ name->size, name->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->buffer, innerkey->size);
+ /* encrypt the hash and sensitive in-place */
+ gcry_cipher_encrypt (hd, p->buffer, p->size, NULL, 0);
+ gcry_cipher_close (hd);
+
+ }
+ else if (symdef->algorithm == TPM_ALG_NULL)
+ {
+ /* Code is for debugging only, should never be used in production */
+ TPM2B *s2b = (TPM2B *)buf;
+ int32_t size = sizeof (*s);
+ UINT16 bsize = 0, written = 0;
+
+ log_error ("Secret key sent to TPM unencrypted\n");
+ buf = s2b->buffer;
+
+ /* marshal the unencrypted sensitive in place */
+ TSS_TPMT_SENSITIVE_Marshal (s, &bsize, &buf, &size);
+ buf = (BYTE *)&s2b->size;
+ size = 2;
+ TSS_UINT16_Marshal (&bsize, &written, &buf, &size);
+
+ p->size += bsize + sizeof (s2b->size);
+ }
+ else
+ {
+ log_error ("Unknown symmetric algorithm\n");
+ return TPM_RC_SYMMETRIC;
+ }
+
+ return TPM_RC_SUCCESS;
+}
+
+int
+tpm2_import_key (ctrl_t ctrl, TSS_CONTEXT *tssc,
+ gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+ char **retstr),
+ unsigned char **shadow_info, size_t *shadow_len,
+ gcry_sexp_t s_skey, unsigned long parent)
+{
+ TPM_HANDLE parentHandle;
+ DATA_2B encryptionKey;
+ TPM2B_PUBLIC objectPublic;
+ PRIVATE_2B duplicate;
+ ENCRYPTED_SECRET_2B inSymSeed;
+ TPMT_SYM_DEF_OBJECT symmetricAlg;
+ PRIVATE_2B outPrivate;
+ NAME_2B name;
+ const int aes_key_bits = 128;
+ const int aes_key_bytes = aes_key_bits/8;
+
+ TPMT_SENSITIVE s;
+ TPM_HANDLE ah;
+ TPM_RC rc;
+
+ uint32_t size;
+ uint16_t len;
+ BYTE *buffer;
+ int ret;
+ char *passphrase;
+
+ char pub[sizeof (TPM2B_PUBLIC)];
+ int pub_len;
+ char priv[sizeof (TPM2B_PRIVATE)];
+ int priv_len;
+
+ if (parent == 0)
+ parent = EXT_TPM_RH_OWNER;
+
+ ret = sexp_to_tpm2 (&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 = pin_cb (ctrl, _("Please enter the TPM Authorization passphrase for the key."), &passphrase);
+ if (ret)
+ return ret;
+ len = strlen(passphrase);
+ if (len > TSS_GetDigestSize(objectPublic.publicArea.nameAlg))
+ {
+ len = TSS_GetDigestSize(objectPublic.publicArea.nameAlg);
+ log_error ("Truncating Passphrase to TPM allowed %d\n", len);
+ }
+ VAL_2B (s.authValue, size) = len;
+ memcpy (VAL_2B (s.authValue, buffer), passphrase, len);
+
+ /* 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 */
+ parentHandle = tpm2_get_parent (tssc, parent);
+ tpm2_ObjectPublic_GetName (&name, &objectPublic.publicArea);
+ gcry_randomize (encryptionKey.buffer,
+ aes_key_bytes, GCRY_STRONG_RANDOM);
+ encryptionKey.size = aes_key_bytes;
+
+ /* set random symSeed */
+ inSymSeed.size = 0;
+ symmetricAlg.algorithm = TPM_ALG_AES;
+ symmetricAlg.keyBits.aes = aes_key_bits;
+ symmetricAlg.mode.aes = TPM_ALG_CFB;
+
+ tpm2_SensitiveToDuplicate (&s, &name, objectPublic.publicArea.nameAlg,
+ &symmetricAlg, &encryptionKey, &duplicate);
+
+ /* 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, parentHandle);
+ if (rc)
+ {
+ tpm2_flush_handle (tssc, parentHandle);
+ return GPG_ERR_CARD;
+ }
+
+ rc = tpm2_Import (tssc, parentHandle, &encryptionKey, &objectPublic,
+ &duplicate, &inSymSeed, &symmetricAlg, &outPrivate,
+ ah, NULL);
+ tpm2_flush_handle (tssc, parentHandle);
+ if (rc)
+ {
+ tpm2_error (rc, "TPM2_Import");
+ /* failure means auth handle is not flushed */
+ tpm2_flush_handle (tssc, ah);
+
+ if ((rc & 0xbf) == TPM_RC_VALUE)
+ {
+ log_error ("TPM cannot import RSA key: wrong size");
+ return GPG_ERR_UNSUPPORTED_ALGORITHM;
+ }
+ else if ((rc & 0xbf) == TPM_RC_CURVE)
+ {
+ log_error ("TPM cannot import requested curve");
+ return GPG_ERR_UNKNOWN_CURVE;
+ }
+ return GPG_ERR_CARD;
+ }
+
+ size = sizeof (pub);
+ buffer = pub;
+ len = 0;
+ TSS_TPM2B_PUBLIC_Marshal (&objectPublic,
+ &len, &buffer, &size);
+ pub_len = len;
+
+ size = sizeof (priv);
+ buffer = priv;
+ len = 0;
+ TSS_TPM2B_PRIVATE_Marshal ((TPM2B_PRIVATE *)&outPrivate,
+ &len, &buffer, &size);
+ priv_len = len;
+
+ *shadow_info = make_tpm2_shadow_info (parent, pub, pub_len,
+ priv, priv_len, shadow_len);
+ return rc;
+}
+
+int
+tpm2_ecc_decrypt (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+ gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+ char **retstr),
+ const char *ciphertext, int ciphertext_len,
+ char **decrypt, size_t *decrypt_len)
+{
+ TPM2B_ECC_POINT inPoint;
+ TPM2B_ECC_POINT outPoint;
+ TPM_HANDLE ah;
+ char *auth;
+ 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;
+
+ memcpy (VAL_2B (inPoint.point.x, buffer), ciphertext + 1, len);
+ VAL_2B (inPoint.point.x, size) = len;
+ memcpy (VAL_2B (inPoint.point.y, buffer), ciphertext + 1 + len, len);
+ VAL_2B (inPoint.point.y, size) = len;
+
+ ret = tpm2_pre_auth (ctrl, tssc, pin_cb, &ah, &auth);
+ if (ret)
+ return ret;
+ ret = tpm2_ECDH_ZGen (tssc, key, &inPoint, &outPoint, ah, auth);
+ ret = tpm2_post_auth (tssc, ret, ah, &auth, "TPM2_ECDH_ZGen");
+ if (ret)
+ return ret;
+
+ *decrypt_len = VAL_2B (outPoint.point.x, size) +
+ VAL_2B (outPoint.point.y, size) + 1;
+ *decrypt = xtrymalloc (*decrypt_len);
+ (*decrypt)[0] = 0x04;
+ memcpy (*decrypt + 1, VAL_2B (outPoint.point.x, buffer),
+ VAL_2B (outPoint.point.x, size));
+ memcpy (*decrypt + 1 + VAL_2B (outPoint.point.x, size),
+ VAL_2B (outPoint.point.y, buffer),
+ VAL_2B (outPoint.point.y, size));
+
+ return 0;
+}
+
+int
+tpm2_rsa_decrypt (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
+ gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
+ char **retstr),
+ const char *ciphertext, int ciphertext_len,
+ char **decrypt, size_t *decrypt_len)
+{
+ int ret;
+ PUBLIC_KEY_RSA_2B cipherText;
+ TPMT_RSA_DECRYPT inScheme;
+ PUBLIC_KEY_RSA_2B message;
+ TPM_HANDLE ah;
+ char *auth;
+
+ inScheme.scheme = TPM_ALG_RSAES;
+ /*
+ * apparent gcrypt error: occasionally rsa ciphertext will
+ * be one byte too long and have a leading zero
+ */
+ if ((ciphertext_len & 1) == 1 && ciphertext[0] == 0)
+ {
+ log_info ("Fixing Wrong Ciphertext size %d\n", ciphertext_len);
+ ciphertext_len--;
+ ciphertext++;
+ }
+ cipherText.size = ciphertext_len;
+ memcpy (cipherText.buffer, ciphertext, ciphertext_len);
+
+ ret = tpm2_pre_auth (ctrl, tssc, pin_cb, &ah, &auth);
+ if (ret)
+ return ret;
+ ret = tpm2_RSA_Decrypt (tssc, key, &cipherText, &inScheme, &message,
+ ah, auth, TPMA_SESSION_ENCRYPT);
+ ret = tpm2_post_auth (tssc, ret, ah, &auth, "TPM2_RSA_Decrypt");
+ if (ret)
+ return ret;
+
+ *decrypt_len = message.size;
+ *decrypt = xtrymalloc (message.size);
+ memcpy (*decrypt, message.buffer, message.size);
+
+ return 0;
+}