hidgd: add TPM functions to give correct registration

Use the TPM to obtain a static EC key at 81000101 as the public point
and signature for registration.  We assume the DER certificate is a
signed version of this.  Currently, I'm just using a self signed
certificate which the webauthn demo site seems to accept.

https://webauthn.org/

This commit of code passes registration, but obviously not login
because signing is currently unimplemented.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/Makefile.am b/Makefile.am
index bc3e28b..bd1b881 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,9 +4,12 @@
 
 fido_SOURCES=fido.c
 
-hidgd_SOURCES=hidgd.c u2f.h u2f_hid.h
-
 AM_CFLAGS=-Wall -Werror
 
+hidgd_SOURCES=hidgd.c u2f.h u2f_hid.h tpm.c hidgd-tpm.h
+hidgd_CFLAGS=-I@TSSINCLUDE@ ${CRYPTO_CFLAGS} ${AM_CFLAGS}
+hidgd_LDADD=${CRYPTO_LIBS}
+
+
 $(builddir)/%.1: $(srcdir)/%.1.in $(top_builddir)/%
 	$(HELP2MAN) --no-info -i $< -o $@ $(top_builddir)/$*
diff --git a/configure.ac b/configure.ac
index 9d1512a..2d46509 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,8 +3,22 @@
 
 AC_CHECK_PROGS([HELP2MAN], [help2man])
 
+AC_LANG(C)
 AC_PROG_CC_STDC
+AC_PROG_CC_C_O
 AC_USE_SYSTEM_EXTENSIONS
 AC_SYS_LARGEFILE
 
+PKG_CHECK_MODULES([CRYPTO], [libcrypto]);
+
+AC_SEARCH_LIBS([TSS_Create], [tss ibmtss], [], [
+	AC_MSG_ERROR([Unable to find the TSS2 library])
+])
+
+CPPFLAGS="$CPPFLAGS -DTPM_POSIX"
+AC_CHECK_HEADER([/usr/include/tss2/tss.h],[TSSINCLUDE=/usr/include/tss2],
+	AC_CHECK_HEADER([/usr/include/ibmtss/tss.h],[TSSINCLUDE=/usr/include/ibmtss],
+	   AC_MSG_ERROR([No TSS2 include directory found])))
+AC_SUBST([TSSINCLUDE])
+
 AC_OUTPUT([Makefile])
diff --git a/hidgd-tpm.h b/hidgd-tpm.h
new file mode 100644
index 0000000..50337ee
--- /dev/null
+++ b/hidgd-tpm.h
@@ -0,0 +1,10 @@
+#ifndef _HIDGD_TPM_H
+#define _HIDGD_TPM_H
+
+#include "u2f.h"
+
+void tpm_get_public_point(uint32_t handle, U2F_EC_POINT *pub);
+int tpm_fill_register_sig(uint32_t parent, U2F_REGISTER_REQ *req,
+			  U2F_REGISTER_RESP *resp, uint8_t *sig);
+
+#endif
diff --git a/hidgd.1.in b/hidgd.1.in
index 1299cee..751fc76 100644
--- a/hidgd.1.in
+++ b/hidgd.1.in
@@ -6,3 +6,11 @@
 Handles the hidg end of a FIDO2 device.  Note that the certificate
 file is simply placed straight into the register reply and therefore
 must be correctly DER encoded.
+
+[examples]
+
+Create a master parent key and place it at 81000101
+
+openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve -out parent_key.key
+create_tpm2_key -w parent_key.key parent_key.tpm
+load_tpm2_key parent_key.tpm 8100101
diff --git a/hidgd.c b/hidgd.c
index 44b5b52..daa2f76 100644
--- a/hidgd.c
+++ b/hidgd.c
@@ -19,10 +19,13 @@
 
 #include "u2f.h"
 #include "u2f_hid.h"
+#include "hidgd-tpm.h"
 
 static int dev;
 static int certd;
 
+static const uint32_t parent = 0x81000101;
+
 static struct option long_options[] = {
 	{"help", 0, 0, 'h'},
 	{"version", 0, 0, 'v'},
@@ -198,6 +201,7 @@
 	printf("chal[0] = %d, appId[0] = %d\n", req->chal[0], req->appId[0]);
 	memset(buf, 0, sizeof(buf));
 	resp->registerId = U2F_REGISTER_ID;
+	tpm_get_public_point(parent, &resp->pubKey);
 	resp->keyHandleLen = sizeof(keystr); /* include trailing 0 */
 	strcpy((char *)resp->keyHandleCertSig, keystr);
 	ptr = &resp->keyHandleCertSig[resp->keyHandleLen];
@@ -209,8 +213,10 @@
 		process_error(cid, ERR_INVALID_CMD);
 		return;
 	}
+	ptr += len;
+	ptr += tpm_fill_register_sig(parent, req, resp, ptr);
 
-	send_payload(buf, sizeof(U2F_REGISTER_RESP), cid, U2F_SW_NO_ERROR);
+	send_payload(buf, ptr - buf, cid, U2F_SW_NO_ERROR);
 }
 
 static void process_authenticate(uint32_t cid, uint8_t ctap[HID_MAX_PAYLOAD])
diff --git a/tpm.c b/tpm.c
new file mode 100644
index 0000000..05a4a26
--- /dev/null
+++ b/tpm.c
@@ -0,0 +1,180 @@
+/*
+ * TPM Code for hid gadget driver
+ *
+ * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <tss.h>
+#include <tssresponsecode.h>
+#include <tsscryptoh.h>
+
+#include <openssl/ecdsa.h>
+
+#include "hidgd-tpm.h"
+
+static char *dir = NULL;
+static TSS_CONTEXT *tssContext;
+
+static void tpm2_error(TPM_RC rc, const char *reason)
+{
+	const char *msg, *submsg, *num;
+
+	fprintf(stderr, "%s failed with %d\n", reason, rc);
+	TSS_ResponseCode_toString(&msg, &submsg, &num, rc);
+	fprintf(stderr, "%s%s%s\n", msg, submsg, num);
+}
+
+static void tpm2_rm_keyfile(TPM_HANDLE key)
+{
+        char keyfile[1024];
+
+        snprintf(keyfile, sizeof(keyfile), "%s/h%08x.bin", dir, key);
+        unlink(keyfile);
+        snprintf(keyfile, sizeof(keyfile), "%s/hp%08x.bin", dir, key);
+        unlink(keyfile);
+}
+
+static void tpm2_delete(void)
+{
+	if (rmdir(dir) < 0) {
+		fprintf(stderr, "Unlinking %s", dir);
+		perror(":");
+	}
+	TSS_Delete(tssContext);
+	dir = NULL;
+	tssContext = NULL;
+}
+
+static TPM_RC tpm2_create(void)
+{
+	char *prefix = getenv("XDG_RUNTIME_DIR");
+	char *template;
+	TPM_RC rc;
+
+	if (!prefix)
+		prefix = "/tmp";
+
+	rc = TSS_Create(&tssContext);
+	if (rc) {
+		tpm2_error(rc, "TSS_Create");
+		return rc;
+	}
+
+	if (!dir) {
+		int len;
+
+		len = snprintf(NULL, 0, "%s/tss2.XXXXXX", prefix);
+		template = malloc(len + 1);
+		snprintf(template, len + 1, "%s/tss2.XXXXXX", prefix);
+
+		dir = mkdtemp(template);
+	}
+
+	printf("DIR IS %s\n", dir);
+	rc = TSS_SetProperty(tssContext, TPM_DATA_DIR, dir);
+	if (rc) {
+		tpm2_error(rc, "TSS_SetProperty");
+		return rc;
+	}
+	return TPM_RC_SUCCESS;
+}
+
+void tpm_get_public_point(uint32_t handle, U2F_EC_POINT *pub)
+{
+	ReadPublic_In in;
+	ReadPublic_Out out;
+	TPM_RC rc;
+	TPMS_ECC_POINT *pt;
+
+	rc = tpm2_create();
+	if (rc)
+		return;
+
+	in.objectHandle = handle;
+
+	rc = TSS_Execute(tssContext,
+			 (RESPONSE_PARAMETERS *)&out,
+			 (COMMAND_PARAMETERS *)&in,
+			 NULL,
+			 TPM_CC_ReadPublic,
+			 TPM_RH_NULL, NULL, 0);
+	if (rc) {
+		tpm2_error(rc, "TPM2_ReadPublic");
+		return;
+	}
+	pt = &out.outPublic.publicArea.unique.ecc;
+	pub->pointFormat = U2F_POINT_UNCOMPRESSED;
+	printf("PUBLIC POINTS  %d,%d\n", pt->x.t.size, pt->y.t.size);
+	memcpy(pub->x, pt->x.t.buffer, pt->x.t.size);
+	memcpy(pub->y, pt->y.t.buffer, pt->y.t.size);
+	printf("DONE\n");
+}
+
+int tpm_fill_register_sig(uint32_t parent, U2F_REGISTER_REQ *req,
+			  U2F_REGISTER_RESP *resp, uint8_t *sig)
+{
+	TPMT_HA digest;
+	TPM_RC rc;
+	Sign_In in;
+	Sign_Out out;
+	uint8_t prefix[1];
+	ECDSA_SIG *osig;
+	BIGNUM *r,*s;
+	int len;
+
+	/* conventional prefix containing zero byte */
+	prefix[0] = 0x00;
+
+	digest.hashAlg = TPM_ALG_SHA256;
+
+	TSS_Hash_Generate(&digest,
+			  sizeof(prefix), prefix,
+			  sizeof(req->appId), req->appId,
+			  sizeof(req->chal), req->chal,
+			  resp->keyHandleLen, resp->keyHandleCertSig,
+			  sizeof(resp->pubKey), &resp->pubKey,
+			  0, NULL);
+
+	in.inScheme.details.ecdsa.hashAlg = digest.hashAlg;
+	in.keyHandle = parent;
+	in.inScheme.scheme = TPM_ALG_ECDSA;
+	in.digest.t.size = TSS_GetDigestSize(digest.hashAlg);
+	memcpy(in.digest.t.buffer, digest.digest.tssmax, in.digest.t.size);
+	in.validation.tag = TPM_ST_HASHCHECK;
+	in.validation.hierarchy = TPM_RH_NULL;
+	in.validation.digest.t.size = 0;
+
+	rc = TSS_Execute(tssContext,
+                         (RESPONSE_PARAMETERS *)&out,
+                         (COMMAND_PARAMETERS *)&in,
+                         NULL,
+                         TPM_CC_Sign,
+			 TPM_RS_PW, NULL, 0,
+                         TPM_RH_NULL, NULL, 0);
+        if (rc) {
+                tpm2_error(rc, "TPM2_Sign");
+		return 0;
+        }
+
+	osig = ECDSA_SIG_new();
+	r = BN_bin2bn(out.signature.signature.ecdsa.signatureR.t.buffer,
+                      out.signature.signature.ecdsa.signatureR.t.size,
+                      NULL);
+        s = BN_bin2bn(out.signature.signature.ecdsa.signatureS.t.buffer,
+                      out.signature.signature.ecdsa.signatureS.t.size,
+                      NULL);
+	ECDSA_SIG_set0(osig, r, s);
+	len = i2d_ECDSA_SIG(osig, &sig);
+	ECDSA_SIG_free(osig);
+
+	tpm2_rm_keyfile(parent);
+	tpm2_delete();
+
+	return len;
+}