aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2019-03-19 15:05:05 -0700
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2019-03-19 15:05:05 -0700
commitfb684edc6c49564007dc011fab80ca202e7cef58 (patch)
tree3bea4842b18f40bd58c6d9bbe3e58ef3537f867a
parent6964deda1643da8d8fd73068baf1eb177e03fc54 (diff)
downloadfido2-ctap-gadget-fb684edc6c49564007dc011fab80ca202e7cef58.tar.gz
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>
-rw-r--r--Makefile.am7
-rw-r--r--configure.ac14
-rw-r--r--hidgd-tpm.h10
-rw-r--r--hidgd.1.in8
-rw-r--r--hidgd.c8
-rw-r--r--tpm.c180
6 files changed, 224 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am
index bc3e28b..bd1b881 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4,9 +4,12 @@ man1_MANS=hidgd.1
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 @@ AM_INIT_AUTOMAKE([foreign 1.6])
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 @@ hidgd - hid gadget daemon
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 @@ static void process_register(uint32_t cid, uint8_t ctap[HID_MAX_PAYLOAD])
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 @@ static void process_register(uint32_t cid, uint8_t ctap[HID_MAX_PAYLOAD])
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;
+}