hidgd: add a daemon for driving the hid gadget device

This is the daemon that simulates the FIDO2 authenticator protocol
CTAP2 over HID.

Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
diff --git a/.gitignore b/.gitignore
index 5be79fd..b737c51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@
 missing
 test-driver
 fido
+hidgd
diff --git a/Makefile.am b/Makefile.am
index e41caaf..8285148 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,5 @@
-bin_PROGRAMS=fido
+bin_PROGRAMS=fido hidgd
 
 fido_SOURCES=fido.c
+
+hidgd_SOURCES=hidgd.c u2f.h u2f_hid.h
diff --git a/hidgd.c b/hidgd.c
new file mode 100644
index 0000000..0463d61
--- /dev/null
+++ b/hidgd.c
@@ -0,0 +1,108 @@
+/*
+ * Hid gadget driver daemon for FIDO2
+ *
+ * Copyright (C) 2019 James.Bottomley@HansenPartnership.com
+ *
+ * SPDX-License-Identifier: GPL-2.0-only
+ */
+
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "u2f.h"
+#include "u2f_hid.h"
+
+static int dev;
+
+static void process_error(U2FHID_FRAME *frame, int err)
+{
+	char buf[HID_RPT_SIZE];
+	U2FHID_FRAME *reply = (U2FHID_FRAME *)buf;
+	int count;
+
+	memset(buf, 0, sizeof(buf));
+	reply->cid = frame->cid;
+	reply->init.cmd = U2FHID_ERROR;
+	reply->init.bcnth = 0;
+	reply->init.bcntl = sizeof(reply) + 1;
+	reply->init.data[0] = err;
+
+	count = write(dev, buf, sizeof(buf));
+	printf("wrote error frame %d\n", count);
+}
+
+static void process_init(U2FHID_FRAME *frame)
+{
+	char buf[HID_RPT_SIZE];
+	U2FHID_FRAME *reply = (U2FHID_FRAME *)buf;
+	U2FHID_INIT_REQ *req = (U2FHID_INIT_REQ *)frame->init.data;
+	U2FHID_INIT_RESP *resp = (U2FHID_INIT_RESP *)reply->init.data;
+	int count;
+
+	if (MSG_LEN(*frame) != sizeof(U2FHID_INIT_REQ)) {
+		fprintf(stderr, "INIT message wrong length %d != %d\n",
+			MSG_LEN(*frame),
+			sizeof(U2FHID_INIT_REQ));
+		process_error(frame, ERR_INVALID_LEN);
+		return;
+	}
+	if (frame->cid != CID_BROADCAST) {
+		fprintf(stderr, "INIT message to wrong cid %x\n", frame->cid);
+		process_error(frame, ERR_INVALID_CMD);
+		return;
+	}
+	memset(buf, 0, sizeof(buf));
+	reply->cid = CID_BROADCAST;
+	reply->init.cmd = U2FHID_INIT;
+	reply->init.bcnth = 0;
+	reply->init.bcntl = 17;
+	printf("setting reply size to %d\n", sizeof(*resp));
+	memcpy(resp->nonce, req->nonce, sizeof(req->nonce));
+	resp->cid = 1;
+	resp->versionInterface = U2FHID_IF_VERSION;
+
+	count = write(dev, buf, sizeof(buf));
+	printf("written %d bytes\n", count);
+}
+
+static void command_loop(void)
+{
+	int count;
+	uint8_t buf[HID_RPT_SIZE];
+	U2FHID_FRAME *frame = (U2FHID_FRAME *)buf;
+
+	count = read(dev, buf, sizeof(buf));
+	printf("received %d bytes\n", count);
+	if (frame->init.cmd == U2FHID_INIT) {
+		printf("Got INIT\n");
+		process_init(frame);
+	} else {
+		printf("Got unknown command 0x%x\n", FRAME_CMD(*frame));
+		process_error(frame, ERR_INVALID_CMD);
+	}
+}
+
+int main(int argc, const char *argv[])
+{
+	if (argc != 2) {
+		fprintf(stderr, "need hidg device\n");
+		exit(1);
+	}
+
+	dev = open(argv[1], O_RDWR);
+	if (dev < 0) {
+		perror("Failed to open HIDG");
+		exit(1);
+	}
+
+	for (;;) {
+		command_loop();
+	}
+}