From 0a1a5ce899a765deb710b30941b02ef890c5cbc3 Mon Sep 17 00:00:00 2001 From: pedro martelletto Date: Tue, 22 Dec 2020 12:24:17 +0100 Subject: [PATCH] experimental nfc support on linux joint work w/ Ludvig Michaelsson, who did a lot of the heavy lifting. thank you! --- README.adoc | 2 + src/CMakeLists.txt | 2 +- src/dev.c | 27 ++ src/extern.h | 11 +- src/fido/param.h | 1 + src/iso7816.c | 3 +- src/iso7816.h | 2 +- src/netlink.c | 757 +++++++++++++++++++++++++++++++++++++++++++++ src/netlink.h | 29 ++ src/nfc_linux.c | 602 +++++++++++++++++++++++++++++++++++ src/u2f.c | 10 +- 11 files changed, 1437 insertions(+), 9 deletions(-) create mode 100644 src/netlink.c create mode 100644 src/netlink.h create mode 100644 src/nfc_linux.c diff --git a/README.adoc b/README.adoc index 9da64d3c..79b49175 100644 --- a/README.adoc +++ b/README.adoc @@ -23,6 +23,8 @@ file for the full license text. *libfido2* is known to work on Linux, MacOS, Windows, OpenBSD, and FreeBSD. +On Linux, *experimental* NFC support is available in git HEAD. + === Documentation Documentation is available in troff and HTML formats. An diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d46494bb..92a450d0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -44,7 +44,7 @@ elseif(WIN32) elseif(APPLE) list(APPEND FIDO_SOURCES hid_osx.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - list(APPEND FIDO_SOURCES hid_linux.c hid_unix.c) + list(APPEND FIDO_SOURCES hid_linux.c hid_unix.c netlink.c nfc_linux.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD") list(APPEND FIDO_SOURCES hid_netbsd.c hid_unix.c) elseif(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") diff --git a/src/dev.c b/src/dev.c index 105296ec..9c1941b1 100644 --- a/src/dev.c +++ b/src/dev.c @@ -271,6 +271,11 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK) return (FIDO_ERR_INTERNAL); +#ifdef __linux__ + if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK) + return (FIDO_ERR_INTERNAL); +#endif + for (curr = manifest_funcs; curr != NULL; curr = curr->next) { curr_olen = 0; m_func = curr->manifest_func; @@ -297,6 +302,28 @@ fido_dev_open_with_info(fido_dev_t *dev) int fido_dev_open(fido_dev_t *dev, const char *path) { +#ifdef __linux__ + /* + * this is a hack to get existing applications up and running with nfc; + * it will *NOT* be part of a libfido2 release. to support nfc in your + * application, please change it to use fido_dev_open_with_info(). + */ + if (strncmp(path, "/sys", strlen("/sys")) == 0 && strlen(path) > 4 && + path[strlen(path) - 4] == 'n' && path[strlen(path) - 3] == 'f' && + path[strlen(path) - 2] == 'c') { + dev->io_own = true; + dev->io = (fido_dev_io_t) { + fido_nfc_open, + fido_nfc_close, + fido_nfc_read, + fido_nfc_write, + }; + dev->transport = (fido_dev_transport_t) { + fido_nfc_rx, + fido_nfc_tx, + }; + } +#endif return (fido_dev_open_wait(dev, path, -1)); } diff --git a/src/extern.h b/src/extern.h index 39b7d8e6..3ca8762b 100644 --- a/src/extern.h +++ b/src/extern.h @@ -95,6 +95,14 @@ int fido_hid_unix_wait(int, int); size_t fido_hid_report_in_len(void *); size_t fido_hid_report_out_len(void *); +/* nfc i/o */ +void *fido_nfc_open(const char *); +void fido_nfc_close(void *); +int fido_nfc_read(void *, unsigned char *, size_t, int); +int fido_nfc_write(void *, const unsigned char *, size_t); +int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int); +int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t); + /* generic i/o */ int fido_rx_cbor_status(fido_dev_t *, int); int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int); @@ -150,8 +158,9 @@ int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *, int fido_get_signed_hash(int, fido_blob_t *, const fido_blob_t *, const fido_blob_t *); -/* hid device manifest */ +/* device manifest functions */ int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *); +int fido_nfc_manifest(fido_dev_info_t *, size_t, size_t *); /* device manifest registration */ typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *); diff --git a/src/fido/param.h b/src/fido/param.h index 14ee74e4..b080ac94 100644 --- a/src/fido/param.h +++ b/src/fido/param.h @@ -43,6 +43,7 @@ #define U2F_AUTH_CHECK 0x07 /* ISO7816-4 status words. */ +#define SW1_MORE_DATA 0x61 #define SW_CONDITIONS_NOT_SATISFIED 0x6985 #define SW_WRONG_DATA 0x6a80 #define SW_NO_ERROR 0x9000 diff --git a/src/iso7816.c b/src/iso7816.c index 4fe6329a..8f2b0e95 100644 --- a/src/iso7816.c +++ b/src/iso7816.c @@ -8,7 +8,7 @@ #include "fido.h" iso7816_apdu_t * -iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len) +iso7816_new(uint8_t cla, uint8_t ins, uint8_t p1, uint16_t payload_len) { iso7816_apdu_t *apdu; size_t alloc_len; @@ -21,6 +21,7 @@ iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len) apdu->alloc_len = alloc_len; apdu->payload_len = payload_len; apdu->payload_ptr = apdu->payload; + apdu->header.cla = cla; apdu->header.ins = ins; apdu->header.p1 = p1; apdu->header.lc2 = (uint8_t)((payload_len >> 8) & 0xff); diff --git a/src/iso7816.h b/src/iso7816.h index 563243fa..5f5363a6 100644 --- a/src/iso7816.h +++ b/src/iso7816.h @@ -38,7 +38,7 @@ struct iso7816_apdu { const unsigned char *iso7816_ptr(const iso7816_apdu_t *); int iso7816_add(iso7816_apdu_t *, const void *, size_t); -iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint16_t); +iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint8_t, uint16_t); size_t iso7816_len(const iso7816_apdu_t *); void iso7816_free(iso7816_apdu_t **); diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 00000000..6eeb5412 --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2020 Yubico AB. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "fido.h" +#include "netlink.h" + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +/* XXX avoid signed NLA_ALIGNTO */ +#undef NLA_HDRLEN +#define NLA_HDRLEN NLMSG_ALIGN(sizeof(struct nlattr)) + +typedef struct nlmsgbuf { + size_t siz; /* alloc size */ + size_t len; /* of payload */ + unsigned char *ptr; /* in payload */ + union { + struct nlmsghdr nlmsg; + char buf[NLMSG_HDRLEN]; /* align */ + } u; + unsigned char payload[]; +} nlmsgbuf_t; + +typedef struct genlmsgbuf { + union { + struct genlmsghdr genl; + char buf[GENL_HDRLEN]; /* align */ + } u; +} genlmsgbuf_t; + +typedef struct nlamsgbuf { + size_t siz; /* alloc size */ + size_t len; /* of payload */ + unsigned char *ptr; /* in payload */ + union { + struct nlattr nla; + char buf[NLA_HDRLEN]; /* align */ + } u; + unsigned char payload[]; +} nlamsgbuf_t; + +typedef struct nl_family { + uint16_t id; + uint32_t mcastgrp; +} nl_family_t; + +typedef struct nl_poll { + uint32_t dev; + unsigned int eventcnt; +} nl_poll_t; + +struct fido_nl { + int fd; + uint16_t nfc_type; + uint32_t nfc_mcastgrp; + struct sockaddr_nl saddr; +}; + +static const void * +nlmsg_ptr(const nlmsgbuf_t *m) +{ + return (&m->u.nlmsg); +} + +static size_t +nlmsg_len(const nlmsgbuf_t *m) +{ + return (m->u.nlmsg.nlmsg_len); +} + +static uint16_t +nlmsg_type(const nlmsgbuf_t *m) +{ + return (m->u.nlmsg.nlmsg_type); +} + +static nlmsgbuf_t * +nlmsg_new(uint16_t type, uint16_t flags, size_t len) +{ + nlmsgbuf_t *m; + size_t siz; + + if (len > SIZE_MAX - sizeof(*m) || + (siz = sizeof(*m) + len) > UINT16_MAX || + (m = calloc(1, siz)) == NULL) + return (NULL); + + m->siz = siz; + m->len = len; + m->ptr = m->payload; + m->u.nlmsg.nlmsg_type = type; + m->u.nlmsg.nlmsg_flags = NLM_F_REQUEST | flags; + m->u.nlmsg.nlmsg_len = NLMSG_HDRLEN; + + return (m); +} + +static nlamsgbuf_t * +nla_from_buf(const unsigned char **ptr, size_t *len) +{ + nlamsgbuf_t h, *a; + size_t nlalen, skip; + + if (*len < sizeof(h.u)) + return (NULL); + + memset(&h, 0, sizeof(h)); + memcpy(&h.u, *ptr, sizeof(h.u)); + + if ((nlalen = h.u.nla.nla_len) < sizeof(h.u) || nlalen > *len || + nlalen - sizeof(h.u) > UINT16_MAX || + nlalen > SIZE_MAX - sizeof(*a) || + (skip = NLMSG_ALIGN(nlalen)) > *len || + (a = calloc(1, sizeof(*a) + nlalen - sizeof(h.u))) == NULL) + return (NULL); + + memcpy(&a->u, *ptr, nlalen); + a->siz = sizeof(*a) + nlalen - sizeof(h.u); + a->ptr = a->payload; + a->len = nlalen - sizeof(h.u); + *ptr += skip; + *len -= skip; + + return (a); +} + +static nlamsgbuf_t * +nla_getattr(nlamsgbuf_t *a) +{ + return (nla_from_buf((void *)&a->ptr, &a->len)); +} + +static uint16_t +nla_type(const nlamsgbuf_t *a) +{ + return (a->u.nla.nla_type); +} + +static nlamsgbuf_t * +nlmsg_getattr(nlmsgbuf_t *m) +{ + return (nla_from_buf((void *)&m->ptr, &m->len)); +} + +static int +nla_read(nlamsgbuf_t *a, void *buf, size_t cnt) +{ + if (cnt > a->u.nla.nla_len || + fido_buf_read((void *)&a->ptr, &a->len, buf, cnt) < 0) + return (-1); + + a->u.nla.nla_len = (uint16_t)(a->u.nla.nla_len - cnt); + + return (0); +} + +static nlmsgbuf_t * +nlmsg_from_buf(const unsigned char **ptr, size_t *len) +{ + nlmsgbuf_t h, *m; + size_t msglen, skip; + + if (*len < sizeof(h.u)) + return (NULL); + + memset(&h, 0, sizeof(h)); + memcpy(&h.u, *ptr, sizeof(h.u)); + + if ((msglen = h.u.nlmsg.nlmsg_len) < sizeof(h.u) || msglen > *len || + msglen - sizeof(h.u) > UINT16_MAX || + (skip = NLMSG_ALIGN(msglen)) > *len || + (m = nlmsg_new(0, 0, msglen - sizeof(h.u))) == NULL) + return (NULL); + + memcpy(&m->u, *ptr, msglen); + *ptr += skip; + *len -= skip; + + return (m); +} + +static int +nlmsg_read(nlmsgbuf_t *m, void *buf, size_t cnt) +{ + if (cnt > m->u.nlmsg.nlmsg_len || + fido_buf_read((void *)&m->ptr, &m->len, buf, cnt) < 0) + return (-1); + + m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len - cnt); + + return (0); +} + +static int +nlmsg_write(nlmsgbuf_t *m, const void *buf, size_t cnt) +{ + if (cnt > UINT32_MAX - m->u.nlmsg.nlmsg_len || + fido_buf_write(&m->ptr, &m->len, buf, cnt) < 0) + return (-1); + + m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len + cnt); + + return (0); +} + +static int +nlmsg_set_genl(nlmsgbuf_t *m, uint8_t cmd) +{ + genlmsgbuf_t g; + + memset(&g, 0, sizeof(g)); + g.u.genl.cmd = cmd; + g.u.genl.version = NFC_GENL_VERSION; + + return (nlmsg_write(m, &g, sizeof(g))); +} + +static int +nlmsg_get_genl(nlmsgbuf_t *m, uint8_t cmd) +{ + genlmsgbuf_t g; + + memset(&g, 0, sizeof(g)); + + if (nlmsg_read(m, &g, sizeof(g)) < 0 || g.u.genl.cmd != cmd) + return (-1); + + return (0); +} + +static int +nlmsg_get_status(nlmsgbuf_t *m) +{ + int status; + + if (nlmsg_read(m, &status, sizeof(status)) < 0) + return (-1); + if (status < 0) + status = -status; + + return (status); +} + +static int +nlmsg_setattr(nlmsgbuf_t *m, uint16_t type, const void *ptr, size_t len) +{ + int r; + char *padding; + size_t skip; + nlamsgbuf_t a; + + if ((skip = NLMSG_ALIGN(len)) > UINT16_MAX - sizeof(a.u) || + skip < len || (padding = calloc(1, skip - len)) == NULL) + return (-1); + + memset(&a, 0, sizeof(a)); + a.u.nla.nla_type = type; + a.u.nla.nla_len = (uint16_t)(len + sizeof(a.u)); + r = nlmsg_write(m, &a.u, sizeof(a.u)) < 0 || + nlmsg_write(m, ptr, len) < 0 || + nlmsg_write(m, padding, skip - len) < 0 ? -1 : 0; + + free(padding); + + return (r); +} + +static int +nlmsg_set_u16(nlmsgbuf_t *m, uint16_t type, uint16_t val) +{ + return (nlmsg_setattr(m, type, &val, sizeof(val))); +} + +static int +nlmsg_set_u32(nlmsgbuf_t *m, uint16_t type, uint32_t val) +{ + return (nlmsg_setattr(m, type, &val, sizeof(val))); +} + +static int +nlmsg_set_str(nlmsgbuf_t *m, uint16_t type, const char *val) +{ + return (nlmsg_setattr(m, type, val, strlen(val) + 1)); +} + +static int +nla_get_u16(nlamsgbuf_t *a, uint16_t *v) +{ + return (nla_read(a, v, sizeof(*v))); +} + +static int +nla_get_u32(nlamsgbuf_t *a, uint32_t *v) +{ + return (nla_read(a, v, sizeof(*v))); +} + +static char * +nla_get_str(nlamsgbuf_t *a) +{ + size_t n; + char *s = NULL; + + if ((n = a->len) < 1 || a->ptr[n - 1] != '\0' || + (s = calloc(1, n)) == NULL || nla_read(a, s, n) < 0) { + free(s); + return (NULL); + } + s[n - 1] = '\0'; + + return (s); +} + +static int +nlmsg_tx(int fd, const nlmsgbuf_t *m) +{ + ssize_t r; + + if ((r = write(fd, nlmsg_ptr(m), nlmsg_len(m))) < 0 || + (size_t)r != nlmsg_len(m)) { + fido_log_debug("%s: write", __func__); + return (-1); + } + fido_log_debug("%s: buf=%p, len=%zu", __func__, nlmsg_ptr(m), + nlmsg_len(m)); + fido_log_xxd(nlmsg_ptr(m), nlmsg_len(m)); + + return (0); +} + +static ssize_t +nlmsg_rx(int fd, unsigned char *ptr, size_t len, int ms) +{ + ssize_t r; + + if (len > SSIZE_MAX) { + fido_log_debug("%s: len", __func__); + return (-1); + } + if (fido_hid_unix_wait(fd, ms) < 0) { + fido_log_debug("%s: fido_hid_unix_wait", __func__); + return (-1); + } + if ((r = read(fd, ptr, len)) < 0) { + fido_log_debug("%s: read", __func__); + return (-1); + } + fido_log_debug("%s: buf=%p, len=%zu", __func__, (void *)ptr, (size_t)r); + fido_log_xxd(ptr, (size_t)r); + + return (r); +} + +static int +nlmsg_iter(nlmsgbuf_t *m, void *arg, int (*parser)(nlamsgbuf_t *, void *)) +{ + nlamsgbuf_t *a; + int r; + + while ((a = nlmsg_getattr(m)) != NULL) { + r = parser(a, arg); + free(a); + if (r < 0) { + fido_log_debug("%s: parser", __func__); + return (-1); + } + } + + return (0); +} + +static int +nla_iter(nlamsgbuf_t *g, void *arg, int (*parser)(nlamsgbuf_t *, void *)) +{ + nlamsgbuf_t *a; + int r; + + while ((a = nla_getattr(g)) != NULL) { + r = parser(a, arg); + free(a); + if (r < 0) { + fido_log_debug("%s: parser", __func__); + return (-1); + } + } + + return (0); +} + +static int +nl_parse_reply(const uint8_t *blob, size_t blob_len, uint16_t msg_type, + uint8_t genl_cmd, void *arg, int (*parser)(nlamsgbuf_t *, void *)) +{ + nlmsgbuf_t *m; + int r; + + while (blob_len) { + if ((m = nlmsg_from_buf(&blob, &blob_len)) == NULL) { + fido_log_debug("%s: nlmsg", __func__); + return (-1); + } + if (nlmsg_type(m) == NLMSG_ERROR) { + r = nlmsg_get_status(m); + free(m); + return (r); + } + if (nlmsg_type(m) != msg_type || + nlmsg_get_genl(m, genl_cmd) < 0) { + fido_log_debug("%s: skipping", __func__); + free(m); + continue; + } + if (parser != NULL && nlmsg_iter(m, arg, parser) < 0) { + fido_log_debug("%s: nlmsg_iter", __func__); + free(m); + return (-1); + } + free(m); + } + + return (0); +} + +static int +parse_mcastgrp(nlamsgbuf_t *a, void *arg) +{ + nl_family_t *family = arg; + char *name; + + switch (nla_type(a)) { + case CTRL_ATTR_MCAST_GRP_NAME: + if ((name = nla_get_str(a)) == NULL || + strcmp(name, NFC_GENL_MCAST_EVENT_NAME) != 0) { + free(name); + return (-1); /* XXX skip? */ + } + free(name); + return (0); + case CTRL_ATTR_MCAST_GRP_ID: + if (family->mcastgrp) + break; + if (nla_get_u32(a, &family->mcastgrp) < 0) { + fido_log_debug("%s: group", __func__); + return (-1); + } + return (0); + } + + fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); + + return (0); +} + +static int +parse_mcastgrps(nlamsgbuf_t *a, void *arg) +{ + return (nla_iter(a, arg, parse_mcastgrp)); +} + +static int +parse_family(nlamsgbuf_t *a, void *arg) +{ + nl_family_t *family = arg; + + switch (nla_type(a)) { + case CTRL_ATTR_FAMILY_ID: + if (family->id) + break; + if (nla_get_u16(a, &family->id) < 0) { + fido_log_debug("%s: id", __func__); + return (-1); + } + return (0); + case CTRL_ATTR_MCAST_GROUPS: + return (nla_iter(a, family, parse_mcastgrps)); + } + + fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); + + return (0); +} + +static int +nl_get_nfc_family(int fd, uint16_t *type, uint32_t *mcastgrp) +{ + nlmsgbuf_t *m; + uint8_t reply[512]; + nl_family_t family; + ssize_t r; + int ok; + + if ((m = nlmsg_new(GENL_ID_CTRL, 0, 64)) == NULL || + nlmsg_set_genl(m, CTRL_CMD_GETFAMILY) < 0 || + nlmsg_set_u16(m, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0 || + nlmsg_set_str(m, CTRL_ATTR_FAMILY_NAME, NFC_GENL_NAME) < 0 || + nlmsg_tx(fd, m) < 0) { + free(m); + return (-1); + } + free(m); + memset(&family, 0, sizeof(family)); + if ((r = nlmsg_rx(fd, reply, sizeof(reply), -1)) < 0) { + fido_log_debug("%s: nlmsg_rx", __func__); + return (-1); + } + if ((ok = nl_parse_reply(reply, (size_t)r, GENL_ID_CTRL, + CTRL_CMD_NEWFAMILY, &family, parse_family)) != 0) { + fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); + return (-1); + } + if (family.id == 0 || family.mcastgrp == 0) { + fido_log_debug("%s: missing attr", __func__); + return (-1); + } + *type = family.id; + *mcastgrp = family.mcastgrp; + + return (0); +} + +static int +parse_target(nlamsgbuf_t *a, void *arg) +{ + uint32_t *target = arg; + + if (nla_type(a) != NFC_ATTR_TARGET_INDEX) { + fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); + return (0); + } + if (nla_get_u32(a, target) < 0) { + fido_log_debug("%s: target", __func__); + return (-1); + } + + return (0); +} + +int +fido_nl_power_nfc(fido_nl_t *nl, uint32_t dev) +{ + nlmsgbuf_t *m; + uint8_t reply[512]; + ssize_t r; + int ok; + + if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL || + nlmsg_set_genl(m, NFC_CMD_DEV_UP) < 0 || + nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 || + nlmsg_tx(nl->fd, m) < 0) { + free(m); + return (-1); + } + free(m); + if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) { + fido_log_debug("%s: nlmsg_rx", __func__); + return (-1); + } + if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, + NFC_CMD_DEV_UP, NULL, NULL)) != 0 && ok != EALREADY) { + fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); + return (-1); + } + + return (0); +} + +static int +nl_nfc_poll(fido_nl_t *nl, uint32_t dev) +{ + nlmsgbuf_t *m; + uint8_t reply[512]; + ssize_t r; + int ok; + + if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL || + nlmsg_set_genl(m, NFC_CMD_START_POLL) < 0 || + nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 || + nlmsg_set_u32(m, NFC_ATTR_PROTOCOLS, NFC_PROTO_ISO14443_MASK) < 0 || + nlmsg_tx(nl->fd, m) < 0) { + free(m); + return (-1); + } + free(m); + if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) { + fido_log_debug("%s: nlmsg_rx", __func__); + return (-1); + } + if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, + NFC_CMD_START_POLL, NULL, NULL)) != 0) { + fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); + return (-1); + } + + return (0); +} + +static int +nl_dump_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target, int ms) +{ + nlmsgbuf_t *m; + uint8_t reply[512]; + ssize_t r; + int ok; + + if ((m = nlmsg_new(nl->nfc_type, NLM_F_DUMP, 64)) == NULL || + nlmsg_set_genl(m, NFC_CMD_GET_TARGET) < 0 || + nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 || + nlmsg_tx(nl->fd, m) < 0) { + free(m); + return (-1); + } + free(m); + if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), ms)) < 0) { + fido_log_debug("%s: nlmsg_rx", __func__); + return (-1); + } + if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, + NFC_CMD_GET_TARGET, target, parse_target)) != 0) { + fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); + return (-1); + } + + return (0); +} + +static int +parse_nfc_event(nlamsgbuf_t *a, void *arg) +{ + nl_poll_t *ctx = arg; + uint32_t dev; + + if (nla_type(a) != NFC_ATTR_DEVICE_INDEX) { + fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a)); + return (0); + } + if (nla_get_u32(a, &dev) < 0) { + fido_log_debug("%s: dev", __func__); + return (-1); + } + if (dev == ctx->dev) + ctx->eventcnt++; + else + fido_log_debug("%s: ignoring dev 0x%x", __func__, dev); + + return (0); +} + +int +fido_nl_get_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target) +{ + uint8_t reply[512]; + nl_poll_t ctx; + ssize_t r; + int ok; + + if (nl_nfc_poll(nl, dev) < 0) { + fido_log_debug("%s: nl_nfc_poll", __func__); + return (-1); + } + if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) { + fido_log_debug("%s: setsockopt add", __func__); + return (-1); + } + r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1); + if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) { + fido_log_debug("%s: setsockopt drop", __func__); + return (-1); + } + if (r < 0) { + fido_log_debug("%s: nlmsg_rx", __func__); + return (-1); + } + memset(&ctx, 0, sizeof(ctx)); + ctx.dev = dev; + if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type, + NFC_EVENT_TARGETS_FOUND, &ctx, parse_nfc_event)) != 0) { + fido_log_debug("%s: nl_parse_reply: %d", __func__, ok); + return (-1); + } + if (ctx.eventcnt == 0) { + fido_log_debug("%s: dev 0x%x not observed", __func__, dev); + return (-1); + } + if (nl_dump_nfc_target(nl, dev, target, -1) < 0) { + fido_log_debug("%s: nl_dump_nfc_target", __func__); + return (-1); + } + + return (0); +} + +void +fido_nl_free(fido_nl_t **nlp) +{ + fido_nl_t *nl; + + if (nlp == NULL || (nl = *nlp) == NULL) + return; + if (nl->fd != -1) + close(nl->fd); + + free(nl); + *nlp = NULL; +} + +fido_nl_t * +fido_nl_new(void) +{ + fido_nl_t *nl; + int ok = -1; + + if ((nl = calloc(1, sizeof(*nl))) == NULL) + return (NULL); + if ((nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, + NETLINK_GENERIC)) == -1) { + fido_log_debug("%s: socket", __func__); + goto fail; + } + nl->saddr.nl_family = AF_NETLINK; + if (bind(nl->fd, (struct sockaddr *)&nl->saddr, + sizeof(nl->saddr)) == -1) { + fido_log_debug("%s: bind", __func__); + goto fail; + } + if (nl_get_nfc_family(nl->fd, &nl->nfc_type, &nl->nfc_mcastgrp) < 0) { + fido_log_debug("%s: nl_get_nfc_family", __func__); + goto fail; + } + + ok = 0; +fail: + if (ok < 0) + fido_nl_free(&nl); + + return (nl); +} diff --git a/src/netlink.h b/src/netlink.h new file mode 100644 index 00000000..f6ec9b71 --- /dev/null +++ b/src/netlink.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 Yubico AB. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#ifndef _FIDO_NETLINK_H +#define _FIDO_NETLINK_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +struct fido_nl; +typedef struct fido_nl fido_nl_t; + +fido_nl_t *fido_nl_new(void); +void fido_nl_free(struct fido_nl **); +int fido_nl_power_nfc(struct fido_nl *, uint32_t); +int fido_nl_get_nfc_target(struct fido_nl *, uint32_t , uint32_t *); + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_FIDO_NETLINK_H */ diff --git a/src/nfc_linux.c b/src/nfc_linux.c new file mode 100644 index 00000000..adda3ce8 --- /dev/null +++ b/src/nfc_linux.c @@ -0,0 +1,602 @@ +/* + * Copyright (c) 2020 Yubico AB. All rights reserved. + * Use of this source code is governed by a BSD-style + * license that can be found in the LICENSE file. + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "fido.h" +#include "fido/param.h" +#include "netlink.h" +#include "iso7816.h" + +#define TX_CHUNK_SIZE 240 + +static const uint8_t aid[] = { 0xa0, 0x00, 0x00, 0x06, 0x47, 0x2f, 0x00, 0x01 }; +static const uint8_t v_u2f[] = { 'U', '2', 'F', '_', 'V', '2' }; +static const uint8_t v_fido[] = { 'F', 'I', 'D', 'O', '_', '2', '_', '0' }; + +struct nfc_linux { + int fd; + uint32_t dev; + uint32_t target; + struct fido_nl *nl; +}; + +static int +tx_short_apdu(fido_dev_t *d, const iso7816_header_t *h, const uint8_t *payload, + uint8_t payload_len, uint8_t cla_flags) +{ + uint8_t apdu[5 + UINT8_MAX + 1]; + uint8_t sw[2]; + size_t apdu_len; + int ok = -1; + + memset(&apdu, 0, sizeof(apdu)); + apdu[0] = h->cla | cla_flags; + apdu[1] = h->ins; + apdu[2] = h->p1; + apdu[3] = h->p2; + apdu[4] = payload_len; + memcpy(&apdu[5], payload, payload_len); + apdu_len = (size_t)(5 + payload_len + 1); + + if (d->io.write(d->io_handle, apdu, apdu_len) < 0) { + fido_log_debug("%s: write", __func__); + goto fail; + } + + if (cla_flags & 0x10) { + if (d->io.read(d->io_handle, sw, sizeof(sw), -1) != 2) { + fido_log_debug("%s: read", __func__); + goto fail; + } + if ((sw[0] << 8 | sw[1]) != SW_NO_ERROR) { + fido_log_debug("%s: unexpected sw", __func__); + goto fail; + } + } + + ok = 0; +fail: + explicit_bzero(apdu, sizeof(apdu)); + + return (ok); +} + +static int +nfc_do_tx(fido_dev_t *d, const uint8_t *apdu_ptr, size_t apdu_len) +{ + iso7816_header_t h; + + if (fido_buf_read(&apdu_ptr, &apdu_len, &h, sizeof(h)) < 0) { + fido_log_debug("%s: header", __func__); + return (-1); + } + if (apdu_len < 2) { + fido_log_debug("%s: apdu_len %zu", __func__, apdu_len); + return (-1); + } + + apdu_len -= 2; /* trim le1 le2 */ + + while (apdu_len > TX_CHUNK_SIZE) { + if (tx_short_apdu(d, &h, apdu_ptr, TX_CHUNK_SIZE, 0x10) < 0) { + fido_log_debug("%s: chain", __func__); + return (-1); + } + apdu_ptr += TX_CHUNK_SIZE; + apdu_len -= TX_CHUNK_SIZE; + } + + if (tx_short_apdu(d, &h, apdu_ptr, (uint8_t)apdu_len, 0) < 0) { + fido_log_debug("%s: tx_short_apdu", __func__); + return (-1); + } + + return (0); +} + +int +fido_nfc_tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count) +{ + iso7816_apdu_t *apdu = NULL; + const uint8_t *ptr; + size_t len; + int ok = -1; + + switch (cmd) { + case CTAP_CMD_INIT: /* select */ + if ((apdu = iso7816_new(0, 0xa4, 0x04, sizeof(aid))) == NULL || + iso7816_add(apdu, aid, sizeof(aid)) < 0) { + fido_log_debug("%s: iso7816", __func__); + goto fail; + } + break; + case CTAP_CMD_CBOR: /* wrap cbor */ + if (count > UINT16_MAX || (apdu = iso7816_new(0x80, 0x10, 0x80, + (uint16_t)count)) == NULL || + iso7816_add(apdu, buf, count) < 0) { + fido_log_debug("%s: iso7816", __func__); + goto fail; + } + break; + case CTAP_CMD_MSG: /* already an apdu */ + break; + default: + fido_log_debug("%s: cmd=%02x", __func__, cmd); + goto fail; + } + + if (apdu != NULL) { + ptr = iso7816_ptr(apdu); + len = iso7816_len(apdu); + } else { + ptr = buf; + len = count; + } + + if (nfc_do_tx(d, ptr, len) < 0) { + fido_log_debug("%s: nfc_do_tx", __func__); + goto fail; + } + + ok = 0; +fail: + iso7816_free(&apdu); + + return (ok); +} + +static int +rx_init(fido_dev_t *d, unsigned char *buf, size_t count, int ms) +{ + fido_ctap_info_t *attr = (fido_ctap_info_t *)buf; + uint8_t f[64]; + int n; + + if (count != sizeof(*attr)) { + fido_log_debug("%s: count=%zu", __func__, count); + return (-1); + } + + memset(attr, 0, sizeof(*attr)); + + if ((n = d->io.read(d->io_handle, f, sizeof(f), ms)) < 2 || + (f[n - 2] << 8 | f[n - 1]) != SW_NO_ERROR) { + fido_log_debug("%s: read", __func__); + return (-1); + } + + n -= 2; + + if (n == sizeof(v_u2f) && memcmp(f, v_u2f, sizeof(v_u2f)) == 0) + attr->flags = FIDO_CAP_CBOR; + else if (n == sizeof(v_fido) && memcmp(f, v_fido, sizeof(v_fido)) == 0) + attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG; + else { + fido_log_debug("%s: unknown version string", __func__); + return (-1); + } + + memcpy(&attr->nonce, &d->nonce, sizeof(attr->nonce)); /* XXX */ + + return ((int)count); +} + +static int +tx_get_response(fido_dev_t *d, uint8_t count) +{ + uint8_t apdu[5]; + + memset(apdu, 0, sizeof(apdu)); + apdu[1] = 0xc0; /* GET_RESPONSE */ + apdu[4] = count; + + if (d->io.write(d->io_handle, apdu, sizeof(apdu)) < 0) { + fido_log_debug("%s: write", __func__); + return (-1); + } + + return (0); +} + +static int +rx_apdu(fido_dev_t *d, uint16_t *sw, unsigned char **buf, size_t *count, int ms) +{ + uint8_t f[256 + 2]; + int n, ok = -1; + + if ((n = d->io.read(d->io_handle, f, sizeof(f), ms)) < 2) { + fido_log_debug("%s: read", __func__); + goto fail; + } + + if (fido_buf_write(buf, count, f, (size_t)(n - 2)) < 0) { + fido_log_debug("%s: fido_buf_write", __func__); + goto fail; + } + + memcpy(sw, f + n - 2, 2); + + ok = 0; +fail: + explicit_bzero(f, sizeof(f)); + + return (ok); +} + +static int +rx_msg(fido_dev_t *d, unsigned char *buf, size_t count, int ms) +{ + uint16_t sw; + const size_t bufsiz = count; + + if (rx_apdu(d, &sw, &buf, &count, ms) < 0) { + fido_log_debug("%s: preamble", __func__); + return (-1); + } + + while ((sw & 0xff) == SW1_MORE_DATA) + if (tx_get_response(d, (uint8_t)(sw >> 8)) < 0 || + rx_apdu(d, &sw, &buf, &count, ms) < 0) { + fido_log_debug("%s: chain", __func__); + return (-1); + } + + if (fido_buf_write(&buf, &count, &sw, sizeof(sw)) < 0) { + fido_log_debug("%s: sw", __func__); + return (-1); + } + + if (bufsiz - count > INT_MAX) { + fido_log_debug("%s: bufsiz", __func__); + return (-1); + } + + return ((int)(bufsiz - count)); +} + +static int +rx_cbor(fido_dev_t *d, unsigned char *buf, size_t count, int ms) +{ + int r; + + if ((r = rx_msg(d, buf, count, ms)) < 2) + return (-1); + + return (r - 2); +} + +int +fido_nfc_rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms) +{ + switch (cmd) { + case CTAP_CMD_INIT: + return (rx_init(d, buf, count, ms)); + case CTAP_CMD_CBOR: + return (rx_cbor(d, buf, count, ms)); + case CTAP_CMD_MSG: + return (rx_msg(d, buf, count, ms)); + default: + fido_log_debug("%s: cmd=%02x", __func__, cmd); + return (-1); + } +} + +static char * +get_parent_attr(struct udev_device *dev, const char *subsystem, + const char *devtype, const char *attr) +{ + struct udev_device *parent; + const char *value; + + if ((parent = udev_device_get_parent_with_subsystem_devtype(dev, + subsystem, devtype)) == NULL || (value = + udev_device_get_sysattr_value(parent, attr)) == NULL) + return (NULL); + + return (strdup(value)); +} + +static char * +get_usb_attr(struct udev_device *dev, const char *attr) +{ + return (get_parent_attr(dev, "usb", "usb_device", attr)); +} + +static int +to_int(const char *str, int base) +{ + char *ep; + long long ll; + + ll = strtoll(str, &ep, base); + if (str == ep || *ep != '\0') + return (-1); + else if (ll == LLONG_MIN && errno == ERANGE) + return (-1); + else if (ll == LLONG_MAX && errno == ERANGE) + return (-1); + else if (ll < 0 || ll > INT_MAX) + return (-1); + + return ((int)ll); +} + +static int +copy_info(fido_dev_info_t *di, struct udev *udev, + struct udev_list_entry *udev_entry) +{ + const char *name; + char *str; + struct udev_device *dev = NULL; + int id, ok = -1; + + memset(di, 0, sizeof(*di)); + + if ((name = udev_list_entry_get_name(udev_entry)) == NULL || + (dev = udev_device_new_from_syspath(udev, name)) == NULL) + goto fail; + + if ((di->path = strdup(name)) == NULL || + (di->manufacturer = get_usb_attr(dev, "manufacturer")) == NULL || + (di->product = get_usb_attr(dev, "product")) == NULL) + goto fail; + + /* XXX assumes USB for vendor/product info */ + if ((str = get_usb_attr(dev, "idVendor")) != NULL && + (id = to_int(str, 16)) > 0 && id <= INT16_MAX) + di->vendor_id = (int16_t)id; + free(str); + + if ((str = get_usb_attr(dev, "idProduct")) != NULL && + (id = to_int(str, 16)) > 0 && id <= INT16_MAX) + di->product_id = (int16_t)id; + free(str); + + ok = 0; +fail: + if (dev != NULL) + udev_device_unref(dev); + + if (ok < 0) { + free(di->path); + free(di->manufacturer); + free(di->product); + explicit_bzero(di, sizeof(*di)); + } + + return (ok); +} + +static int +sysnum_from_syspath(const char *path) +{ + struct udev *udev = NULL; + struct udev_device *dev = NULL; + const char *str; + int idx; + + if ((udev = udev_new()) == NULL || + (dev = udev_device_new_from_syspath(udev, path)) == NULL || + (str = udev_device_get_sysnum(dev)) == NULL) + idx = -1; + else + idx = to_int(str, 10); + + if (dev != NULL) + udev_device_unref(dev); + if (udev != NULL) + udev_unref(udev); + + return (idx); +} + +int +fido_nfc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) +{ + struct udev *udev = NULL; + struct udev_enumerate *udev_enum = NULL; + struct udev_list_entry *udev_list; + struct udev_list_entry *udev_entry; + int r = FIDO_ERR_INTERNAL; + + *olen = 0; + + if (ilen == 0) + return (FIDO_OK); + + if (devlist == NULL) + return (FIDO_ERR_INVALID_ARGUMENT); + + if ((udev = udev_new()) == NULL || + (udev_enum = udev_enumerate_new(udev)) == NULL) + goto fail; + + if (udev_enumerate_add_match_subsystem(udev_enum, "nfc") < 0 || + udev_enumerate_scan_devices(udev_enum) < 0) + goto fail; + + if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) { + r = FIDO_OK; /* zero nfc devices */ + goto fail; + } + + udev_list_entry_foreach(udev_entry, udev_list) { + if (copy_info(&devlist[*olen], udev, udev_entry) == 0) { + devlist[*olen].io = (fido_dev_io_t) { + fido_nfc_open, + fido_nfc_close, + fido_nfc_read, + fido_nfc_write, + }; + devlist[*olen].transport = (fido_dev_transport_t) { + fido_nfc_rx, + fido_nfc_tx, + }; + if (++(*olen) == ilen) + break; + } + } + + r = FIDO_OK; +fail: + if (udev_enum != NULL) + udev_enumerate_unref(udev_enum); + if (udev != NULL) + udev_unref(udev); + + return (r); +} + +static int +nfc_target_connect(struct nfc_linux *ctx) +{ + struct sockaddr_nfc sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_family = AF_NFC; + sa.dev_idx = ctx->dev; + sa.target_idx = ctx->target; + sa.nfc_protocol = NFC_PROTO_ISO14443; + + if ((ctx->fd = socket(AF_NFC, SOCK_SEQPACKET | SOCK_CLOEXEC, + NFC_SOCKPROTO_RAW)) == -1 || + connect(ctx->fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { + fido_log_debug("%s: connect", __func__); + if (ctx->fd != -1) { + close(ctx->fd); + ctx->fd = -1; + } + return (-1); + } + + return (0); +} + +static void +nfc_free(struct nfc_linux **ctx_p) +{ + struct nfc_linux *ctx; + + if (ctx_p == NULL || (ctx = *ctx_p) == NULL) + return; + if (ctx->fd != -1) + close(ctx->fd); + if (ctx->nl != NULL) + fido_nl_free(&ctx->nl); + + free(ctx); + *ctx_p = NULL; +} + +static struct nfc_linux * +nfc_new(uint32_t dev) +{ + struct nfc_linux *ctx; + + if ((ctx = calloc(1, sizeof(*ctx))) == NULL || + (ctx->nl = fido_nl_new()) == NULL) { + nfc_free(&ctx); + return (NULL); + } + + ctx->fd = -1; + ctx->dev = dev; + + return (ctx); +} + +void * +fido_nfc_open(const char *path) +{ + struct nfc_linux *ctx = NULL; + int idx; + + if ((idx = sysnum_from_syspath(path)) < 0 || + (ctx = nfc_new((uint32_t)idx)) == NULL) { + fido_log_debug("%s: nfc_new", __func__); + goto fail; + } + if (fido_nl_power_nfc(ctx->nl, ctx->dev) < 0 || + fido_nl_get_nfc_target(ctx->nl, ctx->dev, &ctx->target) < 0 || + nfc_target_connect(ctx) < 0) { + fido_log_debug("%s: netlink", __func__); + goto fail; + } + + return (ctx); +fail: + nfc_free(&ctx); + return (NULL); +} + +void +fido_nfc_close(void *handle) +{ + struct nfc_linux *ctx = handle; + + nfc_free(&ctx); +} + +int +fido_nfc_read(void *handle, unsigned char *buf, size_t len, int ms) +{ + struct nfc_linux *ctx = handle; + struct iovec iov[2]; + uint8_t preamble; + ssize_t r; + + memset(&iov, 0, sizeof(iov)); + iov[0].iov_base = &preamble; + iov[0].iov_len = sizeof(preamble); + iov[1].iov_base = buf; + iov[1].iov_len = len; + + if (fido_hid_unix_wait(ctx->fd, ms) < 0) { + fido_log_debug("%s: fido_hid_unix_wait", __func__); + return (-1); + } + if ((r = readv(ctx->fd, iov, nitems(iov))) < 1 || preamble != 0x00) { + fido_log_debug("%s: readv", __func__); + return (-1); + } + + r--; + fido_log_debug("%s: buf=%p, r=%zu", __func__, (void *)buf, (size_t)r); + fido_log_xxd(buf, (size_t)r); + + return ((int)r); +} + +int +fido_nfc_write(void *handle, const unsigned char *buf, size_t len) +{ + struct nfc_linux *ctx = handle; + ssize_t r; + + fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void*)buf, len); + fido_log_xxd(buf, len); + + if (len > INT_MAX) { + fido_log_debug("%s: len", __func__); + return (-1); + } + if ((r = write(ctx->fd, buf, len)) < 0 || (size_t)r != len) { + fido_log_debug("%s: write", __func__); + return (-1); + } + + return ((int)r); +} diff --git a/src/u2f.c b/src/u2f.c index 3c6ea827..40d48f50 100644 --- a/src/u2f.c +++ b/src/u2f.c @@ -140,7 +140,7 @@ send_dummy_register(fido_dev_t *dev, int ms) memset(&challenge, 0xff, sizeof(challenge)); memset(&application, 0xff, sizeof(application)); - if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 * + if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * SHA256_DIGEST_LENGTH)) == NULL || iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || iso7816_add(apdu, &application, sizeof(application)) < 0) { @@ -205,7 +205,7 @@ key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id, key_id_len = (uint8_t)key_id->len; - if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 * + if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 * SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL || iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 || iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || @@ -313,7 +313,7 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id, key_id_len = (uint8_t)key_id->len; - if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 * + if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 * SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL || iso7816_add(apdu, cdh->ptr, cdh->len) < 0 || iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 || @@ -622,7 +622,7 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms) return (FIDO_ERR_INTERNAL); } - if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 * + if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * SHA256_DIGEST_LENGTH)) == NULL || iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 || iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) { @@ -792,7 +792,7 @@ u2f_get_touch_begin(fido_dev_t *dev) return (FIDO_ERR_INTERNAL); } - if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 * + if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 * SHA256_DIGEST_LENGTH)) == NULL || iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 || iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {