diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2019-03-19 15:05:05 -0700 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2019-03-19 15:05:05 -0700 |
commit | fb684edc6c49564007dc011fab80ca202e7cef58 (patch) | |
tree | 3bea4842b18f40bd58c6d9bbe3e58ef3537f867a | |
parent | 6964deda1643da8d8fd73068baf1eb177e03fc54 (diff) | |
download | fido2-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.am | 7 | ||||
-rw-r--r-- | configure.ac | 14 | ||||
-rw-r--r-- | hidgd-tpm.h | 10 | ||||
-rw-r--r-- | hidgd.1.in | 8 | ||||
-rw-r--r-- | hidgd.c | 8 | ||||
-rw-r--r-- | tpm.c | 180 |
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 @@ -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 @@ -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]) @@ -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; +} |