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(); + } +}