crypto: make certificate key an openssl one and split out crypto processing

The certificate key is really only used for an attestation and doesn't
necessarily need TPM protection, so make it an openssl key for now
(potentially later adding the engine).

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/Makefile.am b/Makefile.am
index bd1b881..5e92c3f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,7 +6,7 @@
 
 AM_CFLAGS=-Wall -Werror
 
-hidgd_SOURCES=hidgd.c u2f.h u2f_hid.h tpm.c hidgd-tpm.h
+hidgd_SOURCES=hidgd.c u2f.h u2f_hid.h tpm.c hidgd-tpm.h crypto.c
 hidgd_CFLAGS=-I@TSSINCLUDE@ ${CRYPTO_CFLAGS} ${AM_CFLAGS}
 hidgd_LDADD=${CRYPTO_LIBS}
 
diff --git a/crypto.c b/crypto.c
new file mode 100644
index 0000000..f1b6abc
--- /dev/null
+++ b/crypto.c
@@ -0,0 +1,70 @@
+/*
+ * Crypto Code for hid gadget driver
+ *
+ * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+#include <stdio.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include "u2f.h"
+#include "hidgd.h"
+
+void *crypto_load_key(const char *file)
+{
+	BIO *b = NULL;
+        EVP_PKEY *pkey;
+
+        b = BIO_new_file(file, "r");
+        if (b == NULL) {
+		ERR_print_errors_fp(stderr);
+                return NULL;
+        }
+
+	pkey = PEM_read_bio_PrivateKey(b, NULL, PEM_def_callback, NULL);
+        if (pkey == NULL)
+		ERR_print_errors_fp(stderr);
+
+        BIO_free(b);
+
+        return pkey;
+}
+
+int crypto_fill_register_sig(uint32_t parent, U2F_REGISTER_REQ *req,
+			     U2F_REGISTER_RESP *resp, uint8_t *sig,
+			     void *key)
+{
+	EVP_MD_CTX *ctx;
+	EVP_PKEY *pkey = key;
+	uint8_t prefix[1];
+	size_t len;
+
+	/* conventional prefix containing zero byte */
+	prefix[0] = 0x00;
+
+	ctx = EVP_MD_CTX_new();
+	if (!ctx)
+		goto error;
+
+	if (EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey) != 1)
+		goto error;
+
+	EVP_DigestSignUpdate(ctx, prefix, sizeof(prefix));
+	EVP_DigestSignUpdate(ctx, req->appId, sizeof(req->appId));
+	EVP_DigestSignUpdate(ctx, req->chal, sizeof(req->chal));
+	EVP_DigestSignUpdate(ctx, resp->keyHandleCertSig, resp->keyHandleLen);
+	EVP_DigestSignUpdate(ctx, &resp->pubKey, sizeof(resp->pubKey));
+
+	if (EVP_DigestSignFinal(ctx, sig, &len) != 1)
+		goto error;
+
+	return len;
+ error:
+	ERR_print_errors_fp(stderr);
+	return 0;
+}
diff --git a/hidgd-tpm.h b/hidgd-tpm.h
deleted file mode 100644
index 50337ee..0000000
--- a/hidgd-tpm.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#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 751fc76..6d388b5 100644
--- a/hidgd.1.in
+++ b/hidgd.1.in
@@ -12,5 +12,7 @@
 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
+
+create_tpm2_key --restricted -w parent_key.key parent_key.tpm
+
 load_tpm2_key parent_key.tpm 8100101
diff --git a/hidgd.c b/hidgd.c
index 7f4621c..c4314cd 100644
--- a/hidgd.c
+++ b/hidgd.c
@@ -19,10 +19,11 @@
 
 #include "u2f.h"
 #include "u2f_hid.h"
-#include "hidgd-tpm.h"
+#include "hidgd.h"
 
 static int dev;
 static int certd;
+static void *key;
 
 static uint32_t parent = 0x81000101;
 
@@ -35,7 +36,7 @@
 
 static void usage(char *argv0, FILE *f)
 {
-	fprintf(f, "Usage: %s [options] <hidg device> <certificate file>\n\n"
+	fprintf(f, "Usage: %s [options] <hidg device> <certificate file> <key file>\n\n"
 		"Options:\n"
 		"\t-h, --help                print this help message\n"
 		"\t-v, --version             print package version\n"
@@ -172,8 +173,6 @@
 	write(dev, buf, sizeof(buf));
 }
 
-const char keystr[] = "This is a key handle";
-
 static void process_register(uint32_t cid, uint8_t ctap[HID_MAX_PAYLOAD])
 {
 	uint8_t buf[HID_MAX_PAYLOAD];
@@ -203,9 +202,9 @@
 	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);
+	resp->keyHandleLen = tpm_get_public_point(parent, &resp->pubKey,
+						  resp->keyHandleCertSig);
+
 	ptr = &resp->keyHandleCertSig[resp->keyHandleLen];
 	/* place the DER encoded cert into the buffer */
 	lseek(certd, 0, SEEK_SET);
@@ -216,7 +215,7 @@
 		return;
 	}
 	ptr += len;
-	ptr += tpm_fill_register_sig(parent, req, resp, ptr);
+	ptr += crypto_fill_register_sig(parent, req, resp, ptr, key);
 
 	send_payload(buf, ptr - buf, cid, U2F_SW_NO_ERROR);
 }
@@ -230,12 +229,15 @@
 	int len;
 
 	len = get_apdu(&ptr);
+	(void)len;
+	/*
 	if (len > sizeof(U2F_AUTHENTICATE_REQ)) {
 		fprintf(stderr, "Wrong AUTHENTICATE REQ len %d > %ld\n",
 			len, sizeof(U2F_AUTHENTICATE_REQ));
 		process_error(cid, ERR_INVALID_CMD);
 		return;
 	}
+	*/
 	/*
 	 * standard seems to require this but Mozilla doesn't transmit it
 	len = get_apdu(&ptr);
@@ -247,18 +249,8 @@
 	}
 	*/
 	req = (U2F_AUTHENTICATE_REQ *)ptr;
-	if (req->keyHandleLen != sizeof(keystr)) {
-		fprintf(stderr, "Wrong keystr len %d != %ld\n",
-			req->keyHandleLen, sizeof(keystr));
-		process_error(cid, ERR_INVALID_CMD);
-		return;
-	}
-	if (strcmp((char *)req->keyHandle, keystr) != 0) {
-			fprintf(stderr, "Wrong keystr '%s' != '%s'\n",
-			req->keyHandle, keystr);
-		process_error(cid, ERR_INVALID_CMD);
-		return;
-	}
+	(void)*req;
+
 	memset(buf, 0, sizeof(buf));
 	resp->flags = U2F_AUTH_FLAG_TUP; /* pretend we have user presence */
 	send_payload(buf, sizeof(U2F_AUTHENTICATE_RESP), cid,
@@ -353,7 +345,7 @@
 
 int main(int argc, char *argv[])
 {
-	const char *file, *cert;
+	const char *file, *cert, *keyfile;
 
 	for (;;) {
 		int c, option_index;
@@ -383,19 +375,20 @@
 		}
 	}
 
-	if (optind > argc - 2) {
+	if (optind > argc - 3) {
 		fprintf(stderr, "too few arguments\n");
 		usage(argv[0], stderr);
 		exit(1);
 	}
-	if (optind < argc -2) {
+	if (optind < argc - 3) {
 		fprintf(stderr, "too many arguments\n");
 		usage(argv[0], stderr);
 		exit(1);
 	}
 
-	cert = argv[argc - 1];
-	file = argv[argc - 2];
+	keyfile = argv[argc - 1];
+	cert = argv[argc - 2];
+	file = argv[argc - 3];
 
 	dev = open(file, O_RDWR);
 	if (dev < 0) {
@@ -411,6 +404,12 @@
 		exit(1);
 	}
 
+	key = crypto_load_key(keyfile);
+	if (key == NULL) {
+		fprintf(stderr, "Failed to open %s: ", keyfile);
+		exit(1);
+	}
+
 	for (;;) {
 		command_loop();
 	}
diff --git a/hidgd.h b/hidgd.h
new file mode 100644
index 0000000..e2c8ed2
--- /dev/null
+++ b/hidgd.h
@@ -0,0 +1,14 @@
+#ifndef _HIDGD_TPM_H
+#define _HIDGD_TPM_H
+
+#include "u2f.h"
+
+/* tpm.c */
+int tpm_get_public_point(uint32_t parent, U2F_EC_POINT *pub, uint8_t *handle);
+
+/* crypto.c */
+int crypto_fill_register_sig(uint32_t parent, U2F_REGISTER_REQ *req,
+			     U2F_REGISTER_RESP *resp, uint8_t *sig, void *key);
+void *crypto_load_key(const char *file);
+
+#endif
diff --git a/tpm.c b/tpm.c
index 05a4a26..936f2a3 100644
--- a/tpm.c
+++ b/tpm.c
@@ -13,10 +13,11 @@
 #include <tss.h>
 #include <tssresponsecode.h>
 #include <tsscryptoh.h>
+#include <tssmarshal.h>
 
 #include <openssl/ecdsa.h>
 
-#include "hidgd-tpm.h"
+#include "hidgd.h"
 
 static char *dir = NULL;
 static TSS_CONTEXT *tssContext;
@@ -30,6 +31,7 @@
 	fprintf(stderr, "%s%s%s\n", msg, submsg, num);
 }
 
+#if 0
 static void tpm2_rm_keyfile(TPM_HANDLE key)
 {
         char keyfile[1024];
@@ -39,6 +41,7 @@
         snprintf(keyfile, sizeof(keyfile), "%s/hp%08x.bin", dir, key);
         unlink(keyfile);
 }
+#endif
 
 static void tpm2_delete(void)
 {
@@ -85,96 +88,72 @@
 	return TPM_RC_SUCCESS;
 }
 
-void tpm_get_public_point(uint32_t handle, U2F_EC_POINT *pub)
+int tpm_get_public_point(uint32_t parent, U2F_EC_POINT *pub, uint8_t *handle)
 {
-	ReadPublic_In in;
-	ReadPublic_Out out;
+	Create_In in;
+	Create_Out out;
 	TPM_RC rc;
+	INT32 size;
+	uint16_t len;
 	TPMS_ECC_POINT *pt;
 
 	rc = tpm2_create();
 	if (rc)
-		return;
+		return 0;
 
-	in.objectHandle = handle;
+	in.inPublic.publicArea.type = TPM_ALG_ECC;
+	in.inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+	in.inPublic.publicArea.authPolicy.t.size = 0;
+	in.inPublic.publicArea.objectAttributes.val =
+		TPMA_OBJECT_SIGN |
+		TPMA_OBJECT_USERWITHAUTH |
+		TPMA_OBJECT_NODA |
+		TPMA_OBJECT_SENSITIVEDATAORIGIN;
+	in.inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
+        in.inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+        in.inPublic.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
+        in.inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+        in.inPublic.publicArea.unique.ecc.x.t.size = 0;
+        in.inPublic.publicArea.unique.ecc.y.t.size = 0;
+
+	in.inSensitive.sensitive.userAuth.b.size = 0;
+	in.inSensitive.sensitive.data.t.size = 0;
+	in.parentHandle = parent;
+	in.outsideInfo.t.size = 0;
+	in.creationPCR.count = 0;
 
 	rc = TSS_Execute(tssContext,
 			 (RESPONSE_PARAMETERS *)&out,
 			 (COMMAND_PARAMETERS *)&in,
 			 NULL,
-			 TPM_CC_ReadPublic,
+			 TPM_CC_Create,
+			 TPM_RS_PW, NULL, 0,
 			 TPM_RH_NULL, NULL, 0);
+	tpm2_delete();
 	if (rc) {
-		tpm2_error(rc, "TPM2_ReadPublic");
-		return;
+		tpm2_error(rc, "TPM2_Create");
+		return 0;
 	}
+
+	size = 255;		/* max by U2F standard */
+	len = 0;
+	rc = TSS_TPM2B_PUBLIC_Marshal(&out.outPublic, &len, &handle, &size);
+	if (rc) {
+		tpm2_error(rc, "PUBLIC_Marshal");
+		return 0;
+	}
+	rc = TSS_TPM2B_PRIVATE_Marshal(&out.outPrivate, &len, &handle, &size);
+	if (rc) {
+		tpm2_error(rc, "PRIVATE_Marshal");
+		return 0;
+	}
+
 	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;
 }