summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/libfido2/LICENSE24
-rw-r--r--lib/libfido2/Makefile55
-rw-r--r--lib/libfido2/README.openbsd3
-rw-r--r--lib/libfido2/man/eddsa_pk.3122
-rw-r--r--lib/libfido2/man/es256_pk.3122
-rw-r--r--lib/libfido2/man/fido.343
-rw-r--r--lib/libfido2/man/fido2-assert.1220
-rw-r--r--lib/libfido2/man/fido2-cred.1238
-rw-r--r--lib/libfido2/man/fido2-token.1158
-rw-r--r--lib/libfido2/man/fido_assert.3190
-rw-r--r--lib/libfido2/man/fido_assert_allow_cred.347
-rw-r--r--lib/libfido2/man/fido_assert_set.3195
-rw-r--r--lib/libfido2/man/fido_assert_verify.379
-rw-r--r--lib/libfido2/man/fido_bio_dev.3120
-rw-r--r--lib/libfido2/man/fido_bio_enroll.395
-rw-r--r--lib/libfido2/man/fido_bio_info.381
-rw-r--r--lib/libfido2/man/fido_bio_template.3169
-rw-r--r--lib/libfido2/man/fido_cbor_info.3153
-rw-r--r--lib/libfido2/man/fido_cred.3157
-rw-r--r--lib/libfido2/man/fido_cred_exclude.360
-rw-r--r--lib/libfido2/man/fido_cred_set.3241
-rw-r--r--lib/libfido2/man/fido_cred_verify.364
-rw-r--r--lib/libfido2/man/fido_credman.3299
-rw-r--r--lib/libfido2/man/fido_dev_get_assert.376
-rw-r--r--lib/libfido2/man/fido_dev_info_manifest.3143
-rw-r--r--lib/libfido2/man/fido_dev_make_cred.377
-rw-r--r--lib/libfido2/man/fido_dev_open.3159
-rw-r--r--lib/libfido2/man/fido_dev_set_io_functions.395
-rw-r--r--lib/libfido2/man/fido_dev_set_pin.388
-rw-r--r--lib/libfido2/man/fido_strerr.327
-rw-r--r--lib/libfido2/man/rs256_pk.3122
-rw-r--r--lib/libfido2/shlib_version3
-rw-r--r--lib/libfido2/src/aes256.c98
-rw-r--r--lib/libfido2/src/assert.c1085
-rw-r--r--lib/libfido2/src/authkey.c98
-rw-r--r--lib/libfido2/src/bio.c844
-rw-r--r--lib/libfido2/src/blob.c102
-rw-r--r--lib/libfido2/src/blob.h28
-rw-r--r--lib/libfido2/src/buf.c34
-rw-r--r--lib/libfido2/src/cbor.c1514
-rw-r--r--lib/libfido2/src/cred.c1031
-rw-r--r--lib/libfido2/src/credman.c736
-rw-r--r--lib/libfido2/src/dev.c291
-rw-r--r--lib/libfido2/src/ecdh.c121
-rw-r--r--lib/libfido2/src/eddsa.c169
-rw-r--r--lib/libfido2/src/err.c122
-rw-r--r--lib/libfido2/src/es256.c433
-rw-r--r--lib/libfido2/src/export.llvm178
-rw-r--r--lib/libfido2/src/extern.h131
-rw-r--r--lib/libfido2/src/fido.h196
-rw-r--r--lib/libfido2/src/fido/bio.h95
-rw-r--r--lib/libfido2/src/fido/credman.h74
-rw-r--r--lib/libfido2/src/fido/eddsa.h40
-rw-r--r--lib/libfido2/src/fido/err.h69
-rw-r--r--lib/libfido2/src/fido/es256.h34
-rw-r--r--lib/libfido2/src/fido/param.h84
-rw-r--r--lib/libfido2/src/fido/rs256.h22
-rw-r--r--lib/libfido2/src/hid.c70
-rw-r--r--lib/libfido2/src/hid_openbsd.c277
-rw-r--r--lib/libfido2/src/info.c410
-rw-r--r--lib/libfido2/src/io.c256
-rw-r--r--lib/libfido2/src/iso7816.c70
-rw-r--r--lib/libfido2/src/iso7816.h38
-rw-r--r--lib/libfido2/src/log.c63
-rw-r--r--lib/libfido2/src/packed.h22
-rw-r--r--lib/libfido2/src/pin.c428
-rw-r--r--lib/libfido2/src/reset.c40
-rw-r--r--lib/libfido2/src/rs256.c204
-rw-r--r--lib/libfido2/src/types.h171
-rw-r--r--lib/libfido2/src/u2f.c751
70 files changed, 14154 insertions, 0 deletions
diff --git a/lib/libfido2/LICENSE b/lib/libfido2/LICENSE
new file mode 100644
index 00000000000..340cc352a0f
--- /dev/null
+++ b/lib/libfido2/LICENSE
@@ -0,0 +1,24 @@
+Copyright (c) 2018 Yubico AB. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/lib/libfido2/Makefile b/lib/libfido2/Makefile
new file mode 100644
index 00000000000..84254aa54cb
--- /dev/null
+++ b/lib/libfido2/Makefile
@@ -0,0 +1,55 @@
+# $OpenBSD: Makefile,v 1.1 2019/11/14 21:14:10 djm Exp $
+
+.PATH: ${.CURDIR}/man ${.CURDIR}/src
+
+CFLAGS+= -I${.CURDIR}/src -D_FIDO_INTERNAL -DHAVE_UNISTD_H
+
+WARNINGS= yes
+CDIAGFLAGS+= -Wall -Wextra
+CDIAGFLAGS+= -Werror
+
+SYMBOL_LIST= Symbols.list
+VERSION_SCRIPT= Symbols.map
+CLEANFILES= ${SYMBOL_LIST} ${VERSION_SCRIPT}
+
+LIB= fido2
+SRCS= aes256.c assert.c authkey.c bio.c blob.c buf.c cbor.c cred.c credman.c
+SRCS+= dev.c ecdh.c eddsa.c err.c es256.c hid.c hid_openbsd.c info.c io.c
+SRCS+= iso7816.c log.c pin.c reset.c rs256.c u2f.c
+
+HDRS= fido.h fido/bio.h fido/credman.h fido/eddsa.h fido/err.h
+HDRS+= fido/es256.h fido/param.h fido/rs256.h
+
+NOMAN= # XXX pending cleanup
+#MAN= eddsa_pk.3 es256_pk.3 fido.3 fido2-assert.1 fido2-cred.1 fido2-token.1
+#MAN+= fido_assert.3 fido_assert_allow_cred.3 fido_assert_set.3
+#MAN+= fido_assert_verify.3 fido_bio_dev.3 fido_bio_enroll.3
+#MAN+= fido_bio_info.3 fido_bio_template.3 fido_cbor_info.3 fido_cred.3
+#MAN+= fido_cred_exclude.3 fido_cred_set.3 fido_cred_verify.3 fido_credman.3
+#MAN+= fido_dev_get_assert.3 fido_dev_info_manifest.3 fido_dev_make_cred.3
+#MAN+= fido_dev_open.3 fido_dev_set_io_functions.3 fido_dev_set_pin.3
+#MAN+= fido_strerr.3 rs256_pk.3
+
+includes:
+ @for i in $(HDRS); do \
+ j="test -d ${DESTDIR}/usr/include/`dirname $$i` || \
+ ${INSTALL} -d -o ${BINOWN} -g ${BINGRP} -m 755 \
+ ${DESTDIR}/usr/include/`dirname $$i`"; \
+ echo $$j; \
+ eval "$$j"; \
+ j="cmp -s src/$$i ${DESTDIR}/usr/include/$$i || \
+ ${INSTALL} ${INSTALL_COPY} -o ${BINOWN} -g ${BINGRP} \
+ -m 444 ${.CURDIR}/src/$$i ${DESTDIR}/usr/include/$$i"; \
+ echo $$j; \
+ eval "$$j"; \
+ done
+
+${SYMBOL_LIST}: ${.CURDIR}/src/export.llvm
+ sort ${.CURDIR}/src/export.llvm | sed 's/^_//' > $@.tmp && mv $@.tmp $@
+
+${VERSION_SCRIPT}: ${SYMBOL_LIST}
+ { printf '{\n\tglobal:\n'; \
+ sed '/^[._a-zA-Z]/s/$$/;/; s/^/ /' ${SYMBOL_LIST}; \
+ printf '\n\tlocal:\n\t\t*;\n};\n'; } >$@.tmp && mv $@.tmp $@
+
+.include <bsd.lib.mk>
diff --git a/lib/libfido2/README.openbsd b/lib/libfido2/README.openbsd
new file mode 100644
index 00000000000..993cc478a25
--- /dev/null
+++ b/lib/libfido2/README.openbsd
@@ -0,0 +1,3 @@
+This is an import of https://github.com/Yubico/libfido2 rev 4f6739e7dc
+
+Only a subset of the upstream files have been imported: src/, man/ and LICENSE.
diff --git a/lib/libfido2/man/eddsa_pk.3 b/lib/libfido2/man/eddsa_pk.3
new file mode 100644
index 00000000000..4923df23e2c
--- /dev/null
+++ b/lib/libfido2/man/eddsa_pk.3
@@ -0,0 +1,122 @@
+.\" Copyright (c) 2019 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt EDDSA_PK 3
+.Os
+.Sh NAME
+.Nm eddsa_pk_new ,
+.Nm eddsa_pk_free ,
+.Nm eddsa_pk_from_EVP_PKEY ,
+.Nm eddsa_pk_from_ptr ,
+.Nm eddsa_pk_to_EVP_PKEY
+.Nd FIDO 2 COSE EDDSA API
+.Sh SYNOPSIS
+.In openssl/evp.h
+.In fido/eddsa.h
+.Ft eddsa_pk_t *
+.Fn eddsa_pk_new "void"
+.Ft void
+.Fn eddsa_pk_free "eddsa_pk_t **pkp"
+.Ft int
+.Fn eddsa_pk_from_EVP_PKEY "eddsa_pk_t *pk" "const EVP_PKEY *pkey"
+.Ft int
+.Fn eddsa_pk_from_ptr "eddsa_pk_t *pk" "const void *ptr" "size_t len"
+.Ft EVP_PKEY *
+.Fn eddsa_pk_to_EVP_PKEY "const eddsa_pk_t *pk"
+.Sh DESCRIPTION
+EDDSA is the name given in the CBOR Object Signing and Encryption
+(COSE) RFC to EDDSA over Curve25519 with SHA-512.
+The COSE EDDSA API of
+.Em libfido2
+is an auxiliary API with routines to convert between the different
+EDDSA public key types used in
+.Em libfido2
+and
+.Em OpenSSL .
+.Pp
+In
+.Em libfido2 ,
+EDDSA public keys are abstracted by the
+.Vt eddsa_pk_t
+type.
+.Pp
+The
+.Fn eddsa_pk_new
+function returns a pointer to a newly allocated, empty
+.Vt eddsa_pk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn eddsa_pk_free
+function releases the memory backing
+.Fa *pkp ,
+where
+.Fa *pkp
+must have been previously allocated by
+.Fn eddsa_pk_new .
+On return,
+.Fa *pkp
+is set to NULL.
+Either
+.Fa pkp
+or
+.Fa *pkp
+may be NULL, in which case
+.Fn eddsa_pk_free
+is a NOP.
+.Pp
+The
+.Fn eddsa_pk_from_EVP_PKEY
+function fills
+.Fa pk
+with the contents of
+.Fa pkey .
+No references to
+.Fa pkey
+are kept.
+.Pp
+The
+.Fn eddsa_pk_from_ptr
+function fills
+.Fa pk
+with the contents of
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+No references to
+.Fa ptr
+are kept.
+.Pp
+The
+.Fn eddsa_pk_to_EVP_PKEY
+function converts
+.Fa pk
+to a newly allocated
+.Fa EVP_PKEY
+type with a reference count of 1.
+No internal references to the returned pointer are kept.
+If an error occurs,
+.Fn eddsa_pk_to_EVP_PKEY
+returns NULL.
+.Sh RETURN VALUES
+The
+.Fn eddsa_pk_from_EC_KEY
+and
+.Fn eddsa_pk_from_ptr
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr es256_pk 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_cred_pubkey_ptr 3 ,
+.Xr rs256_pk 3
diff --git a/lib/libfido2/man/es256_pk.3 b/lib/libfido2/man/es256_pk.3
new file mode 100644
index 00000000000..15ff12dde69
--- /dev/null
+++ b/lib/libfido2/man/es256_pk.3
@@ -0,0 +1,122 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt ES256_PK 3
+.Os
+.Sh NAME
+.Nm es256_pk_new ,
+.Nm es256_pk_free ,
+.Nm es256_pk_from_EC_KEY ,
+.Nm es256_pk_from_ptr ,
+.Nm es256_pk_to_EVP_PKEY
+.Nd FIDO 2 COSE ES256 API
+.Sh SYNOPSIS
+.In openssl/ec.h
+.In fido/es256.h
+.Ft es256_pk_t *
+.Fn es256_pk_new "void"
+.Ft void
+.Fn es256_pk_free "es256_pk_t **pkp"
+.Ft int
+.Fn es256_pk_from_EC_KEY "es256_pk_t *pk" "const EC_KEY *ec"
+.Ft int
+.Fn es256_pk_from_ptr "es256_pk_t *pk" "const void *ptr" "size_t len"
+.Ft EVP_PKEY *
+.Fn es256_pk_to_EVP_PKEY "const es256_pk_t *pk"
+.Sh DESCRIPTION
+ES256 is the name given in the CBOR Object Signing and Encryption
+(COSE) RFC to ECDSA over P-256 with SHA-256.
+The COSE ES256 API of
+.Em libfido2
+is an auxiliary API with routines to convert between the different
+ECDSA public key types used in
+.Em libfido2
+and
+.Em OpenSSL .
+.Pp
+In
+.Em libfido2 ,
+ES256 public keys are abstracted by the
+.Vt es256_pk_t
+type.
+.Pp
+The
+.Fn es256_pk_new
+function returns a pointer to a newly allocated, empty
+.Vt es256_pk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn es256_pk_free
+function releases the memory backing
+.Fa *pkp ,
+where
+.Fa *pkp
+must have been previously allocated by
+.Fn es256_pk_new .
+On return,
+.Fa *pkp
+is set to NULL.
+Either
+.Fa pkp
+or
+.Fa *pkp
+may be NULL, in which case
+.Fn es256_pk_free
+is a NOP.
+.Pp
+The
+.Fn es256_pk_from_EC_KEY
+function fills
+.Fa pk
+with the contents of
+.Fa ec .
+No references to
+.Fa ec
+are kept.
+.Pp
+The
+.Fn es256_pk_from_ptr
+function fills
+.Fa pk
+with the contents of
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+No references to
+.Fa ptr
+are kept.
+.Pp
+The
+.Fn es256_pk_to_EVP_PKEY
+function converts
+.Fa pk
+to a newly allocated
+.Fa EVP_PKEY
+type with a reference count of 1.
+No internal references to the returned pointer are kept.
+If an error occurs,
+.Fn es256_pk_to_EVP_PKEY
+returns NULL.
+.Sh RETURN VALUES
+The
+.Fn es256_pk_from_EC_KEY
+and
+.Fn es256_pk_from_ptr
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr eddsa_pk 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_cred_pubkey_ptr 3 ,
+.Xr rs256_pk 3
diff --git a/lib/libfido2/man/fido.3 b/lib/libfido2/man/fido.3
new file mode 100644
index 00000000000..73291fbb753
--- /dev/null
+++ b/lib/libfido2/man/fido.3
@@ -0,0 +1,43 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO 3
+.Os
+.Sh NAME
+.Nm fido_init
+.Nd initialise the FIDO 2 library
+.Sh SYNOPSIS
+.In fido.h
+.Ft void
+.Fn fido_init "int flags"
+.Sh DESCRIPTION
+The
+.Fn fido_init
+function initialises the
+.Em libfido2
+library.
+Its invocation must precede that of any other
+.Em libfido2
+function.
+If
+.Dv FIDO_DEBUG
+is set in
+.Fa flags ,
+then
+debug output will be emitted by
+.Em libfido2
+on
+.Em stderr .
+Alternatively, the
+.Ev FIDO_DEBUG
+environment variable may be set.
+Please note that debug output is conditional on
+.Dv _FIDO_DEBUG
+being defined when the library was compiled.
+.Sh SEE ALSO
+.Xr fido_assert 3 ,
+.Xr fido_cred 3 ,
+.Xr fido_dev_info_manifest 3 ,
+.Xr fido_dev_open 3
diff --git a/lib/libfido2/man/fido2-assert.1 b/lib/libfido2/man/fido2-assert.1
new file mode 100644
index 00000000000..2c66c49849e
--- /dev/null
+++ b/lib/libfido2/man/fido2-assert.1
@@ -0,0 +1,220 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO2-ASSERT 1
+.Os
+.Sh NAME
+.Nm fido2-assert
+.Nd get/verify a FIDO 2 assertion
+.Sh SYNOPSIS
+.Nm
+.Fl G
+.Op Fl dhpruv
+.Op Fl i Ar input_file
+.Op Fl o Ar output_file
+.Ar device
+.Nm
+.Fl V
+.Op Fl dhpv
+.Op Fl i Ar input_file
+.Ar key_file
+.Op Ar type
+.Sh DESCRIPTION
+.Nm
+gets or verifies a FIDO 2 assertion.
+.Pp
+The input of
+.Nm
+is defined by the parameters of the assertion to be obtained/verified.
+See the
+.Sx INPUT FORMAT
+section for details.
+.Pp
+The output of
+.Nm
+is defined by the result of the selected operation.
+See the
+.Sx OUTPUT FORMAT
+section for details.
+.Pp
+If an assertion is successfully obtained or verified,
+.Nm
+exits 0.
+Otherwise,
+.Nm
+exits 1.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl G
+Tells
+.Nm
+to obtain a new assertion from
+.Ar device .
+.It Fl V
+Tells
+.Nm
+to verify an assertion using the PEM-encoded public key in
+.Ar key_file
+of type
+.Ar type ,
+where
+.Ar type
+may be
+.Em es256
+(denoting ECDSA over NIST P-256 with SHA-256),
+.Em rs256
+(denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or
+.Em eddsa
+(denoting EDDSA over Curve25519 with SHA-512).
+If
+.Ar type
+is not specified,
+.Em es256
+is assumed.
+.It Fl h
+If obtaining an assertion, enable the FIDO2 hmac-secret
+extension.
+If verifying an assertion, check whether the extension data bit was
+signed by the authenticator.
+.It Fl d
+Causes
+.Nm
+to emit debugging output on
+.Em stderr .
+.It Fl i Ar input_file
+Tells
+.Nm
+to read the parameters of the assertion from
+.Ar input_file
+instead of
+.Em stdin .
+.It Fl o Ar output_file
+Tells
+.Nm
+to write output on
+.Ar output_file
+instead of
+.Em stdout .
+.It Fl p
+If obtaining an assertion, request user presence.
+If verifying an assertion, check whether the user presence bit was
+signed by the authenticator.
+.It Fl r
+Obtain an assertion using a resident credential.
+If
+.Fl r
+is specified,
+.Nm
+will not expect a credential id in its input, and may output
+multiple assertions.
+.It Fl u
+Obtain an assertion using U2F.
+By default,
+.Nm
+will use FIDO2 if supported by the authenticator, and fallback to
+U2F otherwise.
+.It Fl v
+If obtaining an assertion, prompt the user for a PIN and request
+user verification from the authenticator.
+If a
+.Em tty
+is available,
+.Nm
+will use it to obtain the PIN.
+Otherwise,
+.Em stdin
+is used.
+If verifying an assertion, check whether the user verification bit
+was signed by the authenticator.
+.El
+.Sh INPUT FORMAT
+The input of
+.Nm
+consists of base64 blobs and UTF-8 strings separated
+by newline characters ('\\n').
+.Pp
+When obtaining an assertion,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+credential id, if credential not resident (base64 blob);
+.It
+hmac salt, if the FIDO2 hmac-secret extension is enabled
+(base64 blob);
+.El
+.Pp
+When verifying an assertion,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+assertion signature (base64 blob);
+.El
+.Pp
+UTF-8 strings passed to
+.Nm
+must not contain embedded newline or NUL characters.
+.Sh OUTPUT FORMAT
+The output of
+.Nm
+consists of base64 blobs and UTF-8 strings separated
+by newline characters ('\\n').
+.Pp
+For each generated assertion,
+.Nm
+outputs:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+assertion signature (base64 blob);
+.It
+user id, if credential resident (base64 blob);
+.It
+hmac secret, if the FIDO2 hmac-secret extension is enabled
+(base64 blob);
+.El
+.Pp
+When verifying an assertion,
+.Nm
+produces no output.
+.Sh EXAMPLES
+Assuming
+.Pa cred
+contains a
+.Em es256
+credential created according to the steps outlined in
+.Xr fido2-cred 1 ,
+obtain an assertion from an authenticator at
+.Pa /dev/hidraw5
+and verify it:
+.Pp
+.Dl $ echo assertion challenge | openssl sha256 -binary | base64 > assert_param
+.Dl $ echo relying party >> assert_param
+.Dl $ head -1 cred >> assert_param
+.Dl $ tail -n +2 cred > pubkey
+.Dl $ fido2-assert -G -i assert_param /dev/hidraw5 | fido2-assert -V pubkey es256
+.Sh SEE ALSO
+.Xr fido2-cred 1 ,
+.Xr fido2-token 1
diff --git a/lib/libfido2/man/fido2-cred.1 b/lib/libfido2/man/fido2-cred.1
new file mode 100644
index 00000000000..e6f059a9bee
--- /dev/null
+++ b/lib/libfido2/man/fido2-cred.1
@@ -0,0 +1,238 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO2-CRED 1
+.Os
+.Sh NAME
+.Nm fido2-cred
+.Nd make/verify a FIDO 2 credential
+.Sh SYNOPSIS
+.Nm
+.Fl M
+.Op Fl dhqruv
+.Op Fl i Ar input_file
+.Op Fl o Ar output_file
+.Ar device
+.Op Ar type
+.Nm
+.Fl V
+.Op Fl dhv
+.Op Fl i Ar input_file
+.Op Fl o Ar output_file
+.Op Ar type
+.Sh DESCRIPTION
+.Nm
+makes or verifies a FIDO 2 credential.
+.Pp
+A credential
+.Ar type
+may be
+.Em es256
+(denoting ECDSA over NIST P-256 with SHA-256),
+.Em rs256
+(denoting 2048-bit RSA with PKCS#1.5 padding and SHA-256), or
+.Em eddsa
+(denoting EDDSA over Curve25519 with SHA-512).
+If
+.Ar type
+is not specified,
+.Em es256
+is assumed.
+.Pp
+When making a credential, the authenticator may require the user
+to authenticate with a PIN.
+If the
+.Fl q
+option is not specified,
+.Nm
+will prompt the user for the PIN.
+If a
+.Em tty
+is available,
+.Nm
+will use it to obtain the PIN.
+Otherwise,
+.Em stdin
+is used.
+.Pp
+The input of
+.Nm
+is defined by the parameters of the credential to be made/verified.
+See the
+.Sx INPUT FORMAT
+section for details.
+.Pp
+The output of
+.Nm
+is defined by the result of the selected operation.
+See the
+.Sx OUTPUT FORMAT
+section for details.
+.Pp
+If a credential is successfully created or verified,
+.Nm
+exits 0.
+Otherwise,
+.Nm
+exits 1.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl M
+Tells
+.Nm
+to make a new credential on
+.Ar device .
+.It Fl V
+Tells
+.Nm
+to verify a credential.
+.It Fl d
+Causes
+.Nm
+to emit debugging output on
+.Em stderr .
+.It Fl h
+If making a credential, enable the FIDO2 hmac-secret extension.
+If verifying a credential, check whether the extension data bit was
+signed by the authenticator.
+.It Fl i Ar input_file
+Tells
+.Nm
+to read the parameters of the credential from
+.Ar input_file
+instead of
+.Em stdin .
+.It Fl o Ar output_file
+Tells
+.Nm
+to write output on
+.Ar output_file
+instead of
+.Em stdout .
+.It Fl q
+Tells
+.Nm
+to be quiet.
+If a PIN is required and
+.Fl q
+is specified,
+.Nm
+will fail.
+.It Fl r
+Create a resident credential.
+.It Fl u
+Create a U2F credential.
+By default,
+.Nm
+will use FIDO2 if supported by the authenticator, and fallback to
+U2F otherwise.
+.It Fl v
+If making a credential, request user verification.
+If verifying a credential, check whether the user verification bit
+was signed by the authenticator.
+.El
+.Sh INPUT FORMAT
+The input of
+.Nm
+consists of base64 blobs and UTF-8 strings separated
+by newline characters ('\\n').
+.Pp
+When making a credential,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+user name (UTF-8 string);
+.It
+user id (base64 blob).
+.El
+.Pp
+When verifying a credential,
+.Nm
+expects its input to consist of:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+credential format (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+credential id (base64 blob);
+.It
+attestation signature (base64 blob);
+.It
+attestation certificate (optional, base64 blob).
+.El
+.Pp
+UTF-8 strings passed to
+.Nm
+must not contain embedded newline or NUL characters.
+.Sh OUTPUT FORMAT
+The output of
+.Nm
+consists of base64 blobs, UTF-8 strings, and PEM-encoded public
+keys separated by newline characters ('\\n').
+.Pp
+Upon the successful generation of a credential,
+.Nm
+outputs:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+client data hash (base64 blob);
+.It
+relying party id (UTF-8 string);
+.It
+credential format (UTF-8 string);
+.It
+authenticator data (base64 blob);
+.It
+credential id (base64 blob);
+.It
+attestation signature (base64 blob);
+.It
+attestation certificate, if present (base64 blob).
+.El
+.Pp
+Upon the successful verification of a credential,
+.Nm
+outputs:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+credential id (base64 blob);
+.It
+PEM-encoded credential key.
+.El
+.Sh EXAMPLES
+Create a new
+.Em es256
+credential on
+.Pa /dev/hidraw5 ,
+verify it, and save the id and the public key of the credential in
+.Em cred :
+.Pp
+.Dl $ echo credential challenge | openssl sha256 -binary | base64 > cred_param
+.Dl $ echo relying party >> cred_param
+.Dl $ echo user name >> cred_param
+.Dl $ dd if=/dev/urandom bs=1 count=32 | base64 >> cred_param
+.Dl $ fido2-cred -M -i cred_param /dev/hidraw5 | fido2-cred -V -o cred
+.Sh SEE ALSO
+.Xr fido2-assert 1 ,
+.Xr fido2-token 1
+.Sh CAVEATS
+Please note that
+.Nm
+handles Basic Attestation and Self Attestation transparently.
diff --git a/lib/libfido2/man/fido2-token.1 b/lib/libfido2/man/fido2-token.1
new file mode 100644
index 00000000000..9c4674a91ea
--- /dev/null
+++ b/lib/libfido2/man/fido2-token.1
@@ -0,0 +1,158 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO2-TOKEN 1
+.Os
+.Sh NAME
+.Nm fido2-token
+.Nd find and manage a FIDO 2 authenticator
+.Sh SYNOPSIS
+.Nm
+.Op Fl CR
+.Op Fl d
+.Ar device
+.Nm
+.Fl D
+.Op Fl de
+.Fl i
+.Ar id
+.Ar device
+.Nm
+.Fl I
+.Op Fl cd
+.Op Fl k Ar rp_id Fl i Ar cred_id
+.Ar device
+.Nm
+.Fl L
+.Op Fl der
+.Op Fl k Ar rp_id
+.Op device
+.Nm
+.Fl S
+.Op Fl de
+.Op Fl i Ar template_id Fl n Ar template_name
+.Ar device
+.Nm
+.Fl V
+.Sh DESCRIPTION
+.Nm
+manages a FIDO 2 authenticator.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl C Ar device
+Changes the PIN of
+.Ar device .
+The user will be prompted for the current and new PINs.
+.It Fl D Fl i Ar id Ar device
+Deletes the resident credential specified by
+.Ar id
+from
+.Ar device ,
+where
+.Ar id
+is the credential's base64-encoded id.
+The user will be prompted for the PIN.
+.It Fl D Fl e Fl i Ar id Ar device
+Deletes the biometric enrollment specified by
+.Ar id
+from
+.Ar device ,
+where
+.Ar id
+is the enrollment's template base64-encoded id.
+The user will be prompted for the PIN.
+.It Fl I Ar device
+Retrieves information on
+.Ar device .
+.It Fl I Fl c Ar device
+Retrieves resident credential metadata from
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl I Fl k Ar rp_id Fl i Ar cred_id Ar device
+Prints the credential id (base64-encoded) and public key
+(PEM encoded) of the resident credential specified by
+.Ar rp_id
+and
+.Ar cred_id ,
+where
+.Ar rp_id
+is a UTF-8 relying party id, and
+.Ar cred_id
+is a base64-encoded credential id.
+The user will be prompted for the PIN.
+.It Fl L
+Produces a list of authenticators found by the operating system.
+.It Fl L Fl e Ar device
+Produces a list of biometric enrollments on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl L Fl r Ar device
+Produces a list of relying parties with resident credentials on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl L Fl k Ar rp_id Ar device
+Produces a list of resident credentials corresponding to
+relying party
+.Ar rp_id
+on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl R
+Performs a reset on
+.Ar device .
+.Nm
+will NOT prompt for confirmation.
+.It Fl S
+Sets the PIN of
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl S Fl e Ar device
+Performs a new biometric enrollment on
+.Ar device .
+The user will be prompted for the PIN.
+.It Fl S Fl e Fl i Ar template_id Fl n Ar template_name Ar device
+Sets the friendly name of the biometric enrollment specified by
+.Ar template_id
+to
+.Ar template_name
+on
+.Ar device ,
+where
+.Ar template_id
+is base64-encoded and
+.Ar template_name
+is a UTF-8 string.
+The user will be prompted for the PIN.
+.It Fl V
+Prints version information.
+.It Fl d
+Causes
+.Nm
+to emit debugging output on
+.Em stderr .
+.El
+.Pp
+If a
+.Em tty
+is available,
+.Nm
+will use it to prompt for PINs.
+Otherwise,
+.Em stdin
+is used.
+.Pp
+.Nm
+exits 0 on success and 1 on error.
+.Sh SEE ALSO
+.Xr fido2-assert 1 ,
+.Xr fido2-cred 1
+.Sh CAVEATS
+The actual user-flow to perform a reset is outside the scope of the
+FIDO2 specification, and may therefore vary depending on the
+authenticator.
+Yubico authenticators do not allow resets after 5 seconds from
+power-up, and expect a reset to be confirmed by the user through
+touch within 30 seconds.
diff --git a/lib/libfido2/man/fido_assert.3 b/lib/libfido2/man/fido_assert.3
new file mode 100644
index 00000000000..d802520ad0c
--- /dev/null
+++ b/lib/libfido2/man/fido_assert.3
@@ -0,0 +1,190 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_ASSERT 3
+.Os
+.Sh NAME
+.Nm fido_assert_new ,
+.Nm fido_assert_free ,
+.Nm fido_assert_count ,
+.Nm fido_assert_user_display_name ,
+.Nm fido_assert_user_icon ,
+.Nm fido_assert_user_name ,
+.Nm fido_assert_authdata_ptr ,
+.Nm fido_assert_clientdata_hash_ptr ,
+.Nm fido_assert_hmac_secret_ptr ,
+.Nm fido_assert_user_id_ptr ,
+.Nm fido_assert_sig_ptr ,
+.Nm fido_assert_authdata_len ,
+.Nm fido_assert_clientdata_hash_len ,
+.Nm fido_assert_hmac_secret_len ,
+.Nm fido_assert_user_id_len ,
+.Nm fido_assert_sig_len ,
+.Nm fido_assert_sigcount
+.Nd FIDO 2 assertion API
+.Sh SYNOPSIS
+.In fido.h
+.Ft fido_assert_t *
+.Fn fido_assert_new "void"
+.Ft void
+.Fn fido_assert_free "fido_assert_t **assert_p"
+.Ft size_t
+.Fn fido_assert_count "const fido_assert_t *assert"
+.Ft const char *
+.Fn fido_assert_user_display_name "const fido_assert_t *assert" "size_t idx"
+.Ft const char *
+.Fn fido_assert_user_icon "const fido_assert_t *assert" "size_t idx"
+.Ft const char *
+.Fn fido_assert_user_name "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_authdata_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_clientdata_hash_ptr "const fido_assert_t *assert"
+.Ft const unsigned char *
+.Fn fido_assert_hmac_secret_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_user_id_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
+.Fn fido_assert_sig_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_authdata_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_clientdata_hash_len "const fido_assert_t *assert"
+.Ft size_t
+.Fn fido_assert_hmac_secret_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_user_id_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
+.Fn fido_assert_sig_len "const fido_assert_t *assert" "size_t idx"
+.Ft uint32_t
+.Fn fido_assert_sigcount "const fido_assert_t *assert" "size_t idx"
+.Sh DESCRIPTION
+FIDO 2 assertions are abstracted in
+.Em libfido2
+by the
+.Vt fido_assert_t
+type.
+The functions described in this page allow a
+.Vt fido_assert_t
+type to be allocated, deallocated, and inspected.
+For other operations on
+.Vt fido_assert_t ,
+please refer to
+.Xr fido_assert_set 3 ,
+.Xr fido_assert_allow_cred 3 ,
+.Xr fido_assert_verify 3 ,
+and
+.Xr fido_dev_get_assert 3 .
+.Pp
+The
+.Fn fido_assert_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_assert_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_assert_free
+function releases the memory backing
+.Fa *assert_p ,
+where
+.Fa *assert_p
+must have been previously allocated by
+.Fn fido_assert_new .
+On return,
+.Fa *assert_p
+is set to NULL.
+Either
+.Fa assert_p
+or
+.Fa *assert_p
+may be NULL, in which case
+.Fn fido_assert_free
+is a NOP.
+.Pp
+The
+.Fn fido_assert_count
+function returns the number of statements in
+.Fa assert .
+.Pp
+The
+.Fn fido_assert_user_display_name ,
+.Fn fido_assert_user_icon ,
+and
+.Fn fido_assert_user_name ,
+functions return pointers to the user display name, icon, and
+name attributes of statement
+.Fa idx
+in
+.Fa assert .
+If not NULL, the values returned by these functions point to
+NUL-terminated UTF-8 strings.
+.Pp
+The
+.Fn fido_assert_user_id_ptr ,
+.Fn fido_assert_authdata_ptr ,
+.Fn fido_assert_hmac_secret_ptr ,
+and
+.Fn fido_assert_sig_ptr
+functions return pointers to the user ID, authenticator data,
+hmac-secret, and signature attributes of statement
+.Fa idx
+in
+.Fa assert .
+The
+.Fn fido_assert_user_id_len ,
+.Fn fido_assert_authdata_len ,
+.Fn fido_assert_hmac_secret_len ,
+and
+.Fn fido_assert_sig_len
+functions can be used to retrieve the corresponding length of a
+specific attribute.
+.Pp
+The
+.Fn fido_assert_sigcount
+function can be used to obtain the signature counter of statement
+.Fa idx
+in
+.Fa assert .
+.Pp
+Please note that the first statement in
+.Fa assert
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The authenticator data and signature parts of an assertion
+statement are typically passed to a FIDO 2 server for verification.
+.Pp
+The
+.Fn fido_assert_clientdata_hash_ptr
+function returns a pointer to the client data hash of
+.Fa assert .
+The corresponding length can be obtained by
+.Fn fido_assert_clientdata_hash_len .
+.Sh RETURN VALUES
+The
+.Fn fido_assert_user_display_name ,
+.Fn fido_assert_user_icon ,
+.Fn fido_assert_user_name ,
+.Fn fido_assert_authdata_ptr ,
+.Fn fido_assert_clientdata_hash_ptr ,
+.Fn fido_assert_user_id_ptr ,
+and
+.Fn fido_assert_sig_ptr
+functions return NULL if the respective field in
+.Fa assert
+is not set.
+If not NULL, returned pointers are guaranteed to exist until any API
+function that takes
+.Fa assert
+without the
+.Em const
+qualifier is invoked.
+.Sh SEE ALSO
+.Xr fido_assert_allow_cred 3 ,
+.Xr fido_assert_set 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_dev_get_assert 3
diff --git a/lib/libfido2/man/fido_assert_allow_cred.3 b/lib/libfido2/man/fido_assert_allow_cred.3
new file mode 100644
index 00000000000..f08105147f9
--- /dev/null
+++ b/lib/libfido2/man/fido_assert_allow_cred.3
@@ -0,0 +1,47 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_ASSERT_ALLOW_CRED 3
+.Os
+.Sh NAME
+.Nm fido_assert_allow_cred
+.Nd appends a credential ID to the list of credentials allowed in an assertion
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_assert_allow_cred "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Sh DESCRIPTION
+The
+.Fn fido_assert_allow_cred
+function adds
+.Fa ptr
+to the list of credentials allowed in
+.Fa assert ,
+where
+.Fa ptr
+points to a credential ID of
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+If
+.Fn fido_assert_allow_cred
+fails, the existing list of allowed credentials is preserved.
+.Pp
+For the format of a FIDO 2 credential ID, please refer to the
+Web Authentication (webauthn) standard.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_assert_allow_cred
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert 3 ,
+.Xr fido_assert_set 3 ,
+.Xr fido_dev_get_assert 3
diff --git a/lib/libfido2/man/fido_assert_set.3 b/lib/libfido2/man/fido_assert_set.3
new file mode 100644
index 00000000000..7edbee3f536
--- /dev/null
+++ b/lib/libfido2/man/fido_assert_set.3
@@ -0,0 +1,195 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_ASSERT_SET 3
+.Os
+.Sh NAME
+.Nm fido_assert_set ,
+.Nm fido_assert_set_authdata ,
+.Nm fido_assert_set_authdata_raw ,
+.Nm fido_assert_set_clientdata_hash ,
+.Nm fido_assert_set_count ,
+.Nm fido_assert_set_extensions ,
+.Nm fido_assert_set_hmac_salt ,
+.Nm fido_assert_set_up ,
+.Nm fido_assert_set_uv ,
+.Nm fido_assert_set_rp ,
+.Nm fido_assert_set_sig
+.Nd set parameters of a FIDO 2 assertion
+.Sh SYNOPSIS
+.In fido.h
+.Bd -literal
+typedef enum {
+ FIDO_OPT_OMIT = 0, /* use authenticator's default */
+ FIDO_OPT_FALSE, /* explicitly set option to false */
+ FIDO_OPT_TRUE, /* explicitly set option to true */
+} fido_opt_t;
+.Ed
+.Ft int
+.Fn fido_assert_set_authdata "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_authdata_raw "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_clientdata_hash "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_count "fido_assert_t *assert" "size_t n"
+.Ft int
+.Fn fido_assert_set_extensions "fido_assert_t *assert" "int flags"
+.Ft int
+.Fn fido_assert_set_hmac_salt "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_assert_set_up "fido_assert_t *assert" "fido_opt_t up"
+.Ft int
+.Fn fido_assert_set_uv "fido_assert_t *assert" "fido_opt_t uv"
+.Ft int
+.Fn fido_assert_set_rp "fido_assert_t *assert" "const char *id"
+.Ft int
+.Fn fido_assert_set_sig "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len"
+.Sh DESCRIPTION
+The
+.Nm
+set of functions define the various parameters of a FIDO 2
+assertion, allowing a
+.Fa fido_assert_t
+type to be prepared for a subsequent call to
+.Xr fido_dev_get_assert 3
+or
+.Xr fido_assert_verify 3 .
+For the complete specification of a FIDO 2 assertion and the format
+of its constituent parts, please refer to the Web Authentication
+(webauthn) standard.
+.Pp
+The
+.Fn fido_assert_set_count
+function sets the number of assertion statements in
+.Fa assert
+to
+.Fa n .
+.Pp
+The
+.Fn fido_assert_set_authdata
+and
+.Fn fido_assert_set_sig
+functions set the authenticator data and signature parts of the
+statement with index
+.Fa idx
+of
+.Fa assert
+to
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+Please note that the first assertion statement of
+.Fa assert
+has an
+.Fa idx
+of
+.Em 0 .
+The authenticator data passed to
+.Fn fido_assert_set_authdata
+must be a CBOR-encoded byte string, as obtained from
+.Fn fido_assert_authdata_ptr .
+Alternatively, a raw binary blob may be passed to
+.Fn fido_assert_set_authdata_raw .
+.Pp
+The
+.Fn fido_assert_set_clientdata_hash
+and
+.Fn fido_assert_set_hmac_salt
+functions set the client data hash and hmac-salt parts of
+.Fa assert
+to
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+.Pp
+The
+.Fn fido_assert_set_rp
+function sets the relying party
+.Fa id
+of
+.Fa assert ,
+where
+.Fa id
+is a NUL-terminated UTF-8 string.
+The content of
+.Fa id
+is copied, and no references to the passed pointer are kept.
+.Pp
+The
+.Fn fido_assert_set_extensions
+function sets the extensions of
+.Fa assert
+to the bitmask
+.Fa flags .
+At the moment, only the
+.Dv FIDO_EXT_HMAC_SECRET
+extension is supported.
+If
+.Fa flags
+is zero, the extensions of
+.Fa assert
+are cleared.
+.Pp
+The
+.Fn fido_assert_set_up
+and
+.Fn fido_assert_set_uv
+functions set the
+.Fa up
+(user presence) and
+.Fa uv
+(user verification)
+attributes of
+.Fa assert .
+Both are
+.Dv FIDO_OPT_OMIT
+by default, allowing the authenticator to use its default settings.
+.Pp
+Use of the
+.Nm
+set of functions may happen in two distinct situations:
+when asking a FIDO device to produce a series of assertion
+statements, prior to
+.Xr fido_dev_get_assert 3
+(i.e, in the context of a FIDO client), or when verifying assertion
+statements using
+.Xr fido_assert_verify 3
+(i.e, in the context of a FIDO server).
+.Pp
+For a complete description of the generation of a FIDO 2 assertion
+and its verification, please refer to the FIDO 2 specification.
+An example of how to use the
+.Nm
+set of functions can be found in the
+.Pa examples/assert.c
+file shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The
+.Nm
+functions return
+.Dv FIDO_OK
+on success.
+The error codes returned by the
+.Nm
+set of functions are defined in
+.In fido/err.h .
+.Sh SEE ALSO
+.Xr fido_assert_allow_cred 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_dev_get_assert 3
diff --git a/lib/libfido2/man/fido_assert_verify.3 b/lib/libfido2/man/fido_assert_verify.3
new file mode 100644
index 00000000000..5787c45cd34
--- /dev/null
+++ b/lib/libfido2/man/fido_assert_verify.3
@@ -0,0 +1,79 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_ASSERT_VERIFY 3
+.Os
+.Sh NAME
+.Nm fido_assert_verify
+.Nd verifies the signature of a FIDO 2 assertion statement
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_assert_verify "fido_assert_t *assert" "size_t idx" "int cose_alg" "const void *pk"
+.Sh DESCRIPTION
+The
+.Fn fido_assert_verify
+function verifies whether the signature contained in statement index
+.Fa idx
+of
+.Fa assert
+matches the parameters of the assertion.
+Before using
+.Fn fido_assert_verify
+in a sensitive context, the reader is strongly encouraged to make
+herself familiar with the FIDO 2 assertion statement process
+as defined in the Web Authentication (webauthn) standard.
+.Pp
+A brief description follows:
+.Pp
+The
+.Fn fido_assert_verify
+function verifies whether the client data hash, relying party ID,
+user presence and user verification attributes of
+.Fa assert
+have been attested by the holder of the private counterpart of
+the public key
+.Fa pk
+of COSE type
+.Fa cose_alg ,
+where
+.Fa cose_alg
+is
+.Dv COSE_ES256 ,
+.Dv COSE_RS256 ,
+or
+.Dv COSE_EDDSA ,
+and
+.Fa pk
+points to a
+.Vt es256_pk_t ,
+.Vt rs256_pk_t ,
+or
+.Vt eddsa_pk_t
+type accordingly.
+.Pp
+Please note that the first statement in
+.Fa assert
+has an
+.Fa idx
+of 0.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_assert_verify
+are defined in
+.In fido/err.h .
+If
+statement
+.Fa idx
+of
+.Fa assert
+passes verification with
+.Fa pk ,
+then
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert 3 ,
+.Xr fido_assert_set 3
diff --git a/lib/libfido2/man/fido_bio_dev.3 b/lib/libfido2/man/fido_bio_dev.3
new file mode 100644
index 00000000000..0473a325578
--- /dev/null
+++ b/lib/libfido2/man/fido_bio_dev.3
@@ -0,0 +1,120 @@
+.\" Copyright (c) 2019 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_BIO_DEV 3
+.Os
+.Sh NAME
+.Nm fido_bio_dev_get_info ,
+.Nm fido_bio_dev_enroll_begin ,
+.Nm fido_bio_dev_enroll_continue ,
+.Nm fido_bio_dev_enroll_cancel ,
+.Nm fido_bio_dev_enroll_remove ,
+.Nm fido_bio_dev_get_template_array ,
+.Nm fido_bio_dev_set_template_name
+.Nd FIDO 2 biometric authenticator API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Ft int
+.Fn fido_bio_dev_get_info "fido_dev_t *dev" "fido_bio_info_t *info"
+.Ft int
+.Fn fido_bio_dev_enroll_begin "fido_dev_t *dev" "fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms" "const char *pin"
+.Ft int
+.Fn fido_bio_dev_enroll_continue "fido_dev_t *dev" "const fido_bio_template_t *template" "fido_bio_enroll_t *enroll" "uint32_t timeout_ms"
+.Ft int
+.Fn fido_bio_dev_enroll_cancel "fido_dev_t *dev"
+.Ft int
+.Fn fido_bio_dev_enroll_remove "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin"
+.Ft int
+.Fn fido_bio_dev_get_template_array "fido_dev_t *dev" "fido_bio_template_array_t *template_array" "const char *pin"
+.Ft int
+.Fn fido_bio_dev_set_template_name "fido_dev_t *dev" "const fido_bio_template_t *template" "const char *pin"
+.Sh DESCRIPTION
+The functions described in this page allow biometric
+templates on a FIDO2 authenticator to be listed, created,
+removed, and customised.
+For a description of the types involved, please refer to
+.Xr fido_bio_info 3 ,
+.Xr fido_bio_enroll 3 ,
+and
+.Xr fido_bio_template 3 .
+.Pp
+The
+.Fn fido_bio_dev_get_info
+function populates
+.Fa info
+with sensor information from
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_enroll_begin
+function initiates a biometric enrollment on
+.Fa dev ,
+instructing the authenticator to wait
+.Fa timeout_ms
+milliseconds.
+On success,
+.Fa template
+and
+.Fa enroll
+will be populated with the newly created template's
+information and enrollment status, respectively.
+.Pp
+The
+.Fn fido_bio_dev_enroll_continue
+function continues an ongoing enrollment on
+.Fa dev ,
+instructing the authenticator to wait
+.Fa timeout_ms
+milliseconds.
+On success,
+.Fa enroll
+will be updated to reflect the status of the biometric
+enrollment.
+.Pp
+The
+.Fn fido_bio_dev_enroll_cancel
+function cancels an ongoing enrollment on
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_enroll_remove
+function removes
+.Fa template
+from
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_get_template_array
+function populates
+.Fa template_array
+with the templates currently enrolled on
+.Fa dev .
+.Pp
+The
+.Fn fido_bio_dev_set_template_name
+function sets the friendly name of
+.Fa template
+on
+.Fa dev .
+.Pp
+The error codes returned by
+.Fn fido_bio_dev_get_info ,
+.Fn fido_bio_dev_enroll_begin ,
+.Fn fido_bio_dev_enroll_continue ,
+.Fn fido_bio_dev_enroll_cancel ,
+.Fn fido_bio_dev_enroll_remove ,
+.Fn fido_bio_dev_get_template_array ,
+and
+.Fn fido_bio_dev_set_template_name
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_bio_enroll 3 ,
+.Xr fido_bio_info 3 ,
+.Xr fido_bio_template 3
diff --git a/lib/libfido2/man/fido_bio_enroll.3 b/lib/libfido2/man/fido_bio_enroll.3
new file mode 100644
index 00000000000..4d887cfb350
--- /dev/null
+++ b/lib/libfido2/man/fido_bio_enroll.3
@@ -0,0 +1,95 @@
+.\" Copyright (c) 2019 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_BIO_ENROLL 3
+.Os
+.Sh NAME
+.Nm fido_bio_enroll_new ,
+.Nm fido_bio_enroll_free ,
+.Nm fido_bio_enroll_last_status ,
+.Nm fido_bio_enroll_remaining_samples
+.Nd FIDO 2 biometric enrollment API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Bd -literal
+#define FIDO_BIO_ENROLL_FP_GOOD 0x00
+#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
+#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
+#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
+#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
+#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
+#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
+#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
+#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
+#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
+#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
+#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
+#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
+#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
+#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
+.Ed
+.Ft fido_bio_enroll_t *
+.Fn fido_bio_enroll_new "void"
+.Ft void
+.Fn fido_bio_enroll_free "fido_bio_enroll_t **enroll_p"
+.Ft uint8_t
+.Fn fido_bio_enroll_last_status "const fido_bio_enroll_t *enroll"
+.Ft uint8_t
+.Fn fido_bio_enroll_remaining_samples "const fido_bio_enroll_t *enroll"
+.Sh DESCRIPTION
+Ongoing FIDO 2 biometric enrollments are abstracted in
+.Em libfido2
+by the
+.Vt fido_bio_enroll_t
+type.
+.Pp
+The functions described in this page allow a
+.Vt fido_bio_enroll_t
+type to be allocated, deallocated, and inspected.
+For device operations on
+.Vt fido_bio_enroll_t ,
+please refer to
+.Xr fido_bio_dev 3 .
+.Pp
+The
+.Fn fido_bio_enroll_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_enroll_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_enroll_free
+function releases the memory backing
+.Fa *enroll_p ,
+where
+.Fa *enroll_p
+must have been previously allocated by
+.Fn fido_bio_enroll_new .
+On return,
+.Fa *enroll_p
+is set to NULL.
+Either
+.Fa enroll_p
+or
+.Fa *enroll_p
+may be NULL, in which case
+.Fn fido_bio_enroll_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_enroll_last_status
+function returns the enrollment status of
+.Fa enroll .
+.Pp
+The
+.Fn fido_bio_enroll_remaining_samples
+function returns the number of samples left for
+.Fa enroll
+to complete.
+.Sh SEE ALSO
+.Xr fido_bio_dev 3 ,
+.Xr fido_bio_template 3
diff --git a/lib/libfido2/man/fido_bio_info.3 b/lib/libfido2/man/fido_bio_info.3
new file mode 100644
index 00000000000..0c208ae400a
--- /dev/null
+++ b/lib/libfido2/man/fido_bio_info.3
@@ -0,0 +1,81 @@
+.\" Copyright (c) 2019 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_BIO_INFO 3
+.Os
+.Sh NAME
+.Nm fido_bio_info_new ,
+.Nm fido_bio_info_free ,
+.Nm fido_bio_info_type ,
+.Nm fido_bio_info_max_samples
+.Nd FIDO 2 biometric sensor information API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Ft fido_bio_info_t *
+.Fn fido_bio_info_new "void"
+.Ft void
+.Fn fido_bio_info_free "fido_bio_info_t **info_p"
+.Ft uint8_t
+.Fn fido_bio_info_type "const fido_bio_info_t *info"
+.Ft uint8_t
+.Fn fido_bio_info_max_samples "const fido_bio_info_t *info"
+.Sh DESCRIPTION
+Biometric sensor metadata is abstracted in
+.Em libfido2
+by the
+.Vt fido_bio_info_t
+type.
+.Pp
+The functions described in this page allow a
+.Vt fido_bio_info_t
+type to be allocated, deallocated, and inspected.
+For device operations on
+.Vt fido_bio_info_t ,
+please refer to
+.Xr fido_bio_dev 3 .
+.Pp
+The
+.Fn fido_bio_info_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_info_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_info_free
+function releases the memory backing
+.Fa *info_p ,
+where
+.Fa *info_p
+must have been previously allocated by
+.Fn fido_bio_info_new .
+On return,
+.Fa *info_p
+is set to NULL.
+Either
+.Fa info_p
+or
+.Fa *info_p
+may be NULL, in which case
+.Fn fido_bio_info_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_info_type
+function returns the fingerprint sensor type, which is
+.Dv 1
+for touch sensors, and
+.Dv 2
+for swipe sensors.
+.Pp
+The
+.Fn fido_bio_info_max_samples
+function returns the maximum number of successful samples
+required for enrollment.
+.Sh SEE ALSO
+.Xr fido_bio_dev 3 ,
+.Xr fido_bio_enroll 3 ,
+.Xr fido_bio_template 3
diff --git a/lib/libfido2/man/fido_bio_template.3 b/lib/libfido2/man/fido_bio_template.3
new file mode 100644
index 00000000000..6947e88c024
--- /dev/null
+++ b/lib/libfido2/man/fido_bio_template.3
@@ -0,0 +1,169 @@
+.\" Copyright (c) 2019 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_BIO_TEMPLATE 3
+.Os
+.Sh NAME
+.Nm fido_bio_template ,
+.Nm fido_bio_template_array_count ,
+.Nm fido_bio_template_array_free ,
+.Nm fido_bio_template_array_new ,
+.Nm fido_bio_template_free ,
+.Nm fido_bio_template_id_len ,
+.Nm fido_bio_template_id_ptr ,
+.Nm fido_bio_template_name ,
+.Nm fido_bio_template_new ,
+.Nm fido_bio_template_set_id ,
+.Nm fido_bio_template_set_name
+.Nd FIDO 2 biometric template API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/bio.h
+.Ft fido_bio_template_t *
+.Fn fido_bio_template_new "void"
+.Ft void
+.Fn fido_bio_template_free "fido_bio_template_t **template_p"
+.Ft const char *
+.Fn fido_bio_template_name "const fido_bio_template_t *template"
+.Ft const unsigned char *
+.Fn fido_bio_template_id_ptr "const fido_bio_template_t *template"
+.Ft size_t
+.Fn fido_bio_template_id_len "const fido_bio_template_t *template"
+.Ft int
+.Fn fido_bio_template_set_id "fido_bio_template_t *template" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_bio_template_set_name "fido_bio_template_t *template" "const char *name"
+.Ft fido_bio_template_array_t *
+.Fn fido_bio_template_array_new "void"
+.Ft void
+.Fn fido_bio_template_array_free "fido_bio_template_array_t **template_array_p"
+.Ft size_t
+.Fn fido_bio_template_array_count "const fido_bio_template_array_t *template_array"
+.Ft const fido_bio_template_t *
+.Fn fido_bio_template "const fido_bio_template_array_t *template_array" "size_t idx"
+.Sh DESCRIPTION
+Existing FIDO 2 biometric enrollments are abstracted in
+.Em libfido2
+by the
+.Vt fido_bio_template_t
+and
+.Vt fido_bio_template_array_t
+types.
+.Pp
+The functions described in this page allow a
+.Vt fido_bio_template_t
+type to be allocated, deallocated, changed, and inspected,
+and a
+.Vt fido_bio_template_array_t
+type to be allocated, deallocated, and inspected.
+For device operations on
+.Vt fido_bio_template_t
+and
+.Vt fido_bio_template_array_t ,
+please refer to
+.Xr fido_bio_dev 3 .
+.Pp
+The
+.Fn fido_bio_template_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_template_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_template_free
+function releases the memory backing
+.Fa *template_p ,
+where
+.Fa *template_p
+must have been previously allocated by
+.Fn fido_bio_template_new .
+On return,
+.Fa *template_p
+is set to NULL.
+Either
+.Fa template_p
+or
+.Fa *template_p
+may be NULL, in which case
+.Fn fido_bio_template_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_template_name
+function returns a pointer to a NUL-terminated string containing
+the friendly name of
+.Fa template ,
+or NULL if
+.Fa template
+does not have a friendly name set.
+.Pp
+The
+.Fn fido_bio_template_id_ptr
+function returns a pointer to the template id of
+.Fa template ,
+or NULL if
+.Fa template
+does not have an id.
+The corresponding length can be obtained by
+.Fn fido_bio_template_id_len .
+.Pp
+The
+.Fn fido_bio_template_set_name
+function sets the friendly name of
+.Fa template
+to
+.Fa name .
+If
+.Fa name
+is NULL, the friendly name of
+.Fa template
+is unset.
+.Pp
+The
+.Fn fido_bio_template_array_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_bio_template_array_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_bio_template_array_free
+function releases the memory backing
+.Fa *template_array_p ,
+where
+.Fa *template_array_p
+must have been previously allocated by
+.Fn fido_bio_template_array_new .
+On return,
+.Fa *template_array_p
+is set to NULL.
+Either
+.Fa template_array_p
+or
+.Fa *template_array_p
+may be NULL, in which case
+.Fn fido_bio_template_array_free
+is a NOP.
+.Pp
+The
+.Fn fido_bio_template_array_count
+function returns the number of templates in
+.Fa template_array .
+.Pp
+The
+.Fn fido_bio_template
+function returns a pointer to the template at index
+.Fa idx
+in
+.Fa template_array .
+Please note that the first template in
+.Fa template_array
+has an
+.Fa idx
+(index) value of 0.
+.Sh SEE ALSO
+.Xr fido_bio_dev 3 ,
+.Xr fido_bio_enroll 3
diff --git a/lib/libfido2/man/fido_cbor_info.3 b/lib/libfido2/man/fido_cbor_info.3
new file mode 100644
index 00000000000..de7a0d9f1b4
--- /dev/null
+++ b/lib/libfido2/man/fido_cbor_info.3
@@ -0,0 +1,153 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_CBOR_INFO 3
+.Os
+.Sh NAME
+.Nm fido_cbor_info_new ,
+.Nm fido_cbor_info_free ,
+.Nm fido_dev_get_cbor_info ,
+.Nm fido_cbor_info_aaguid_ptr ,
+.Nm fido_cbor_info_extensions_ptr ,
+.Nm fido_cbor_info_protocols_ptr ,
+.Nm fido_cbor_info_versions_ptr ,
+.Nm fido_cbor_info_options_name_ptr ,
+.Nm fido_cbor_info_options_value_ptr ,
+.Nm fido_cbor_info_aaguid_len ,
+.Nm fido_cbor_info_extensions_len ,
+.Nm fido_cbor_info_protocols_len ,
+.Nm fido_cbor_info_versions_len ,
+.Nm fido_cbor_info_options_len ,
+.Nm fido_cbor_info_maxmsgsiz
+.Nd FIDO 2 CBOR Info API
+.Sh SYNOPSIS
+.In fido.h
+.Ft fido_cbor_info_t *
+.Fn fido_cbor_info_new "void"
+.Ft void
+.Fn fido_cbor_info_free "fido_cbor_info_t **ci_p"
+.Ft int
+.Fn fido_dev_get_cbor_info "fido_dev_t *dev" "fido_cbor_info_t *ci"
+.Ft const unsigned char *
+.Fn fido_cbor_info_aaguid_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_extensions_ptr "const fido_cbor_info_t *ci"
+.Ft const uint8_t *
+.Fn fido_cbor_info_protocols_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_versions_ptr "const fido_cbor_info_t *ci"
+.Ft char **
+.Fn fido_cbor_info_options_name_ptr "const fido_cbor_info_t *ci"
+.Ft const bool *
+.Fn fido_cbor_info_options_value_ptr "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_aaguid_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_extensions_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_protocols_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_versions_len "const fido_cbor_info_t *ci"
+.Ft size_t
+.Fn fido_cbor_info_options_len "const fido_cbor_info_t *ci"
+.Ft uint64_t
+.Fn fido_cbor_info_maxmsgsiz "const fido_cbor_info_t *ci"
+.Sh DESCRIPTION
+The
+.Fn fido_cbor_info_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_cbor_info_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_cbor_info_free
+function releases the memory backing
+.Fa *ci_p ,
+where
+.Fa *ci_p
+must have been previously allocated by
+.Fn fido_cbor_info_new .
+On return,
+.Fa *ci_p
+is set to NULL.
+Either
+.Fa ci_p
+or
+.Fa *ci_p
+may be NULL, in which case
+.Fn fido_cbor_info_free
+is a NOP.
+.Pp
+The
+.Fn fido_dev_get_cbor_info
+function transmits a
+.Dv CTAP_CBOR_GETINFO
+command to
+.Fa dev
+and fills
+.Fa ci
+with attributes retrieved from the command's response.
+The
+.Fn fido_dev_get_cbor_info
+function may block.
+.Pp
+The
+.Fn fido_cbor_info_aaguid_ptr ,
+.Fn fido_cbor_info_extensions_ptr ,
+.Fn fido_cbor_info_protocols_ptr ,
+and
+.Fn fido_cbor_info_versions_ptr
+functions return pointers to the AAGUID, supported extensions,
+PIN protocol and CTAP version strings of
+.Fa ci .
+The corresponding length of a given attribute can be
+obtained by
+.Fn fido_cbor_info_aaguid_len ,
+.Fn fido_cbor_info_extensions_len ,
+.Fn fido_cbor_info_protocols_len ,
+or
+.Fn fido_cbor_info_versions_len .
+.Pp
+The
+.Fn fido_cbor_info_options_name_ptr
+and
+.Fn fido_cbor_info_options_value_ptr
+functions return pointers to the array of option names and their
+respective values
+in
+.Fa ci .
+The length of the options array is returned by
+.Fn fido_cbor_info_options_len .
+.Pp
+The
+.Fn fido_cbor_info_maxmsgsiz
+function returns the maximum message size of
+.Fa ci .
+.Pp
+A complete example of how to use these functions can be found in the
+.Pa example/info.c
+file shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The
+.Fn fido_cbor_info_aaguid_ptr ,
+.Fn fido_cbor_info_extensions_ptr ,
+.Fn fido_cbor_info_protocols_ptr ,
+.Fn fido_cbor_info_versions_ptr ,
+.Fn fido_cbor_info_options_name_ptr ,
+and
+.Fn fido_cbor_info_options_value_ptr
+functions return NULL if the respective field in
+.Fa ci
+is absent.
+If not NULL, returned pointers are guaranteed to exist until any
+API function that takes
+.Fa ci
+without the
+.Em const
+qualifier is invoked.
+.Sh SEE ALSO
+.Xr fido_dev_open 3
diff --git a/lib/libfido2/man/fido_cred.3 b/lib/libfido2/man/fido_cred.3
new file mode 100644
index 00000000000..420ae878dcd
--- /dev/null
+++ b/lib/libfido2/man/fido_cred.3
@@ -0,0 +1,157 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_CRED 3
+.Os
+.Sh NAME
+.Nm fido_cred_new ,
+.Nm fido_cred_free ,
+.Nm fido_cred_fmt ,
+.Nm fido_cred_authdata_ptr ,
+.Nm fido_cred_clientdata_hash_ptr ,
+.Nm fido_cred_id_ptr ,
+.Nm fido_cred_pubkey_ptr ,
+.Nm fido_cred_sig_ptr ,
+.Nm fido_cred_x5c_ptr ,
+.Nm fido_cred_authdata_len ,
+.Nm fido_cred_clientdata_hash_len ,
+.Nm fido_cred_id_len ,
+.Nm fido_cred_pubkey_len ,
+.Nm fido_cred_sig_len ,
+.Nm fido_cred_x5c_len
+.Nd FIDO 2 credential API
+.Sh SYNOPSIS
+.In fido.h
+.Ft fido_cred_t *
+.Fn fido_cred_new "void"
+.Ft void
+.Fn fido_cred_free "fido_cred_t **cred_p"
+.Ft const char *
+.Fn fido_cred_fmt "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_authdata_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_clientdata_hash_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_id_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_pubkey_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_sig_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_x5c_ptr "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_authdata_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_clientdata_hash_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_id_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_pubkey_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_sig_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_x5c_len "const fido_cred_t *cred"
+.Sh DESCRIPTION
+FIDO 2 credentials are abstracted in
+.Em libfido2
+by the
+.Vt fido_cred_t
+type.
+The functions described in this page allow a
+.Vt fido_cred_t
+type to be allocated, deallocated, and inspected.
+For other operations on
+.Vt fido_cred_t ,
+please refer to
+.Xr fido_cred_set 3 ,
+.Xr fido_cred_exclude 3 ,
+.Xr fido_cred_verify 3 ,
+and
+.Xr fido_dev_make_cred 3 .
+.Pp
+The
+.Fn fido_cred_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_cred_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_cred_free
+function releases the memory backing
+.Fa *cred_p ,
+where
+.Fa *cred_p
+must have been previously allocated by
+.Fn fido_cred_new .
+On return,
+.Fa *cred_p
+is set to NULL.
+Either
+.Fa cred_p
+or
+.Fa *cred_p
+may be NULL, in which case
+.Fn fido_cred_free
+is a NOP.
+.Pp
+The
+.Fn fido_cred_fmt
+function returns a pointer to a NUL-terminated string containing
+the format of
+.Fa cred ,
+or NULL if
+.Fa cred
+does not have a format set.
+.Pp
+The
+.Fn fido_cred_authdata_ptr ,
+.Fn fido_cred_clientdata_hash_ptr ,
+.Fn fido_cred_id_ptr ,
+.Fn fido_cred_pubkey_ptr ,
+.Fn fido_cred_sig_ptr ,
+and
+.Fn fido_cred_x5c_ptr
+functions return pointers to the authenticator data, client data
+hash, ID, public key, signature and x509 certificate parts of
+.Fa cred ,
+or NULL if the respective entry is not set.
+.Pp
+The corresponding length can be obtained by
+.Fn fido_cred_authdata_len ,
+.Fn fido_cred_clientdata_hash_len ,
+.Fn fido_cred_id_len ,
+.Fn fido_cred_pubkey_len ,
+and
+.Fn fido_cred_sig_len .
+.Pp
+The authenticator data, x509 certificate, and signature parts of a
+credential are typically passed to a FIDO 2 server for verification.
+.Sh RETURN VALUES
+The authenticator data returned by
+.Fn fido_cred_authdata_ptr
+is a CBOR-encoded byte string, as obtained from the authenticator.
+.Pp
+If not NULL, pointers returned by
+.Fn fido_cred_fmt ,
+.Fn fido_cred_authdata_ptr ,
+.Fn fido_cred_clientdata_hash_ptr ,
+.Fn fido_cred_id_ptr ,
+.Fn fido_cred_pubkey_ptr ,
+.Fn fido_cred_sig_ptr ,
+and
+.Fn fido_cred_x5c_ptr
+are guaranteed to exist until any API function that takes
+.Fa cred
+without the
+.Em const
+qualifier is invoked.
+.Sh SEE ALSO
+.Xr fido_cred_exclude 3 ,
+.Xr fido_cred_set 3 ,
+.Xr fido_cred_verify 3 ,
+.Xr fido_credman 3 ,
+.Xr fido_dev_make_cred 3
diff --git a/lib/libfido2/man/fido_cred_exclude.3 b/lib/libfido2/man/fido_cred_exclude.3
new file mode 100644
index 00000000000..ef466177e62
--- /dev/null
+++ b/lib/libfido2/man/fido_cred_exclude.3
@@ -0,0 +1,60 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_CRED_EXCLUDE 3
+.Os
+.Sh NAME
+.Nm fido_cred_exclude
+.Nd appends a credential ID to a credential's list of excluded credentials
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_cred_exclude "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Sh DESCRIPTION
+The
+.Fn fido_cred_exclude
+function adds
+.Fa ptr
+to the list of credentials excluded by
+.Fa cred ,
+where
+.Fa ptr
+points to a credential ID of
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+If
+.Fn fido_cred_exclude
+fails, the existing list of excluded credentials is preserved.
+.Pp
+If
+.Nm
+returns success and
+.Fa cred
+is later passed to
+.Xr fido_dev_make_cred 3
+on a device that contains the credential
+denoted by
+.Fa ptr ,
+then
+.Xr fido_dev_make_cred 3
+will fail.
+.Pp
+For the format of a FIDO 2 credential ID, please refer to the
+Web Authentication (webauthn) standard.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_cred_exclude
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred 3 ,
+.Xr fido_cred_set 3 ,
+.Xr fido_dev_make_cred 3
diff --git a/lib/libfido2/man/fido_cred_set.3 b/lib/libfido2/man/fido_cred_set.3
new file mode 100644
index 00000000000..63c37503123
--- /dev/null
+++ b/lib/libfido2/man/fido_cred_set.3
@@ -0,0 +1,241 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_CRED_SET 3
+.Os
+.Sh NAME
+.Nm fido_cred_set ,
+.Nm fido_cred_set_authdata ,
+.Nm fido_cred_set_authdata_raw ,
+.Nm fido_cred_set_x509 ,
+.Nm fido_cred_set_sig ,
+.Nm fido_cred_set_clientdata_hash ,
+.Nm fido_cred_set_rp ,
+.Nm fido_cred_set_user ,
+.Nm fido_cred_set_extensions ,
+.Nm fido_cred_set_rk ,
+.Nm fido_cred_set uv ,
+.Nm fido_cred_set_fmt ,
+.Nm fido_cred_set_type
+.Nd set parameters of a FIDO 2 credential
+.Sh SYNOPSIS
+.In fido.h
+.Bd -literal
+typedef enum {
+ FIDO_OPT_OMIT = 0, /* use authenticator's default */
+ FIDO_OPT_FALSE, /* explicitly set option to false */
+ FIDO_OPT_TRUE, /* explicitly set option to true */
+} fido_opt_t;
+.Ed
+.Ft int
+.Fn fido_cred_set_authdata "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_authdata_raw "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_x509 "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_sig "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_clientdata_hash "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_rp "fido_cred_t *cred" "const char *id" "const char *name"
+.Ft int
+.Fn fido_cred_set_user "fido_cred_t *cred" "const unsigned char *user_id" "size_t user_id_len" "const char *name" "const char *display_name" "const char *icon"
+.Ft int
+.Fn fido_cred_set_extensions "fido_cred_t *cred" "int flags"
+.Ft int
+.Fn fido_cred_set_rk "fido_cred_t *cred" "fido_opt_t rk"
+.Ft int
+.Fn fido_cred_set_uv "fido_cred_t *cred" "fido_opt_t uv"
+.Ft int
+.Fn fido_cred_set_fmt "fido_cred_t *cred" "const char *ptr"
+.Ft int
+.Fn fido_cred_set_type "fido_cred_t *cred" "int cose_alg"
+.Sh DESCRIPTION
+The
+.Nm
+set of functions define the various parameters of a FIDO 2
+credential, allowing a
+.Fa fido_cred_t
+type to be prepared for a subsequent call to
+.Xr fido_dev_make_cred 3
+or
+.Xr fido_cred_verify 3 .
+For the complete specification of a FIDO 2 credential and the format
+of its constituent parts, please refer to the Web Authentication
+(webauthn) standard.
+.Pp
+The
+.Fn fido_cred_set_authdata ,
+.Fn fido_cred_set_x509 ,
+.Fn fido_cred_set_sig ,
+and
+.Fn fido_cred_set_clientdata_hash
+functions set the authenticator data, attestation certificate,
+signature and client data hash parts of
+.Fa cred
+to
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+A copy of
+.Fa ptr
+is made, and no references to the passed pointer are kept.
+The authenticator data passed to
+.Fn fido_cred_set_authdata
+must be a CBOR-encoded byte string, as obtained from
+.Fn fido_cred_authdata_ptr .
+Alternatively, a raw binary blob may be passed to
+.Fn fido_cred_set_authdata_raw .
+.Pp
+The
+.Fn fido_cred_set_rp
+function sets the relying party
+.Fa id
+and
+.Fa name
+parameters of
+.Fa cred ,
+where
+.Fa id
+and
+.Fa name
+are NUL-terminated UTF-8 strings.
+The contents of
+.Fa id
+and
+.Fa name
+are copied, and no references to the passed pointers are kept.
+.Pp
+The
+.Fn fido_cred_set_user
+function sets the user attributes of
+.Fa cred ,
+where
+.Fa user_id
+points to
+.Fa user_id_len
+bytes and
+.Fa name ,
+.Fa display_name ,
+and
+.Fa icon
+are NUL-terminated UTF-8 strings.
+The contents of
+.Fa user_id ,
+.Fa name ,
+.Fa display_name ,
+and
+.Fa icon
+are copied, and no references to the passed pointers are kept.
+Previously set user attributes are flushed.
+The
+.Fa user_id ,
+.Fa name ,
+.Fa display_name ,
+and
+.Fa icon
+parameters may be NULL.
+.Pp
+The
+.Fn fido_cred_set_extensions
+function sets the extensions of
+.Fa cred
+to the bitmask
+.Fa flags .
+At the moment, only the
+.Dv FIDO_EXT_HMAC_SECRET
+extension is supported.
+If
+.Fa flags
+is zero, the extensions of
+.Fa cred
+are cleared.
+.Pp
+The
+.Fn fido_cred_set_rk
+and
+.Fn fido_cred_set_uv
+functions set the
+.Em rk
+(resident key)
+and
+.Em uv
+(user verification)
+attributes of
+.Fa cred .
+Both are
+.Dv FIDO_OPT_OMIT
+by default, allowing the authenticator to use its default settings.
+.Pp
+The
+.Fn fido_cred_set_fmt
+function sets the format of
+.Fa cred
+to
+.Fa fmt ,
+where
+.Fa fmt
+must be either
+.Vt "packed"
+(the format used in FIDO 2)
+or
+.Vt "fido-u2f"
+(the format used by U2F).
+A copy of
+.Fa fmt
+is made, and no references to the passed pointer are kept.
+Note that not all authenticators support FIDO2 and therefore may not
+be able to generate
+.Vt "packed" .
+.Pp
+The
+.Fn fido_cred_set_type
+function sets the type of
+.Fa cred to
+.Fa cose_alg ,
+where
+.Fa cose_alg
+is
+.Dv COSE_ES256 ,
+.Dv COSE_RS256 ,
+or
+.Dv COSE_EDDSA .
+The type of a credential may only be set once.
+Note that not all authenticators support COSE_RS256 or COSE_EDDSA.
+.Pp
+Use of the
+.Nm
+set of functions may happen in two distinct situations:
+when generating a new credential on a FIDO device, prior to
+.Xr fido_dev_make_cred 3
+(i.e, in the context of a FIDO client), or when validating
+a generated credential using
+.Xr fido_cred_verify 3
+(i.e, in the context of a FIDO server).
+.Pp
+For a complete description of the generation of a FIDO 2 credential
+and its verification, please refer to the FIDO 2 specification.
+A concrete utilisation example of the
+.Nm
+set of functions can be found in the
+.Pa cred.c
+example shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The error codes returned by the
+.Nm
+set of functions are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred_exclude 3 ,
+.Xr fido_cred_verify 3 ,
+.Xr fido_dev_make_cred 3
diff --git a/lib/libfido2/man/fido_cred_verify.3 b/lib/libfido2/man/fido_cred_verify.3
new file mode 100644
index 00000000000..e0bc6454126
--- /dev/null
+++ b/lib/libfido2/man/fido_cred_verify.3
@@ -0,0 +1,64 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_CRED_VERIFY 3
+.Os
+.Sh NAME
+.Nm fido_cred_verify
+.Nd verifies the signature of a FIDO 2 credential
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_cred_verify "const fido_cred_t *cred"
+.Sh DESCRIPTION
+The
+.Fn fido_cred_verify
+function verifies whether the signature contained in
+.Fa cred
+matches the attributes of the credential.
+Before using
+.Fn fido_cred_verify
+in a sensitive context, the reader is strongly encouraged to make
+herself familiar with the FIDO 2 credential attestation process
+as defined in the Web Authentication (webauthn) standard.
+.Pp
+A brief description follows:
+.Pp
+The
+.Fn fido_cred_verify
+function verifies whether the client data hash, relying party ID,
+credential ID, type, and resident key and user verification
+attributes of
+.Fa cred
+have been attested by the holder of the private counterpart of
+the public key contained in the credential's x509 certificate.
+.Pp
+Please note that the x509 certificate itself is not verified.
+.Pp
+The attestation statement formats supported by
+.Fn fido_cred_verify
+are
+.Em packed
+and
+.Em fido-u2f .
+The attestation type implemented by
+.Fn fido_cred_verify
+is
+.Em Basic Attestation .
+The attestation key pair is assumed to be of the type ES256.
+Other attestation formats and types are not supported.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_cred_verify
+are defined in
+.In fido/err.h .
+If
+.Fa cred
+passes verification, then
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred 3 ,
+.Xr fido_cred_set 3
diff --git a/lib/libfido2/man/fido_credman.3 b/lib/libfido2/man/fido_credman.3
new file mode 100644
index 00000000000..728cdeb6102
--- /dev/null
+++ b/lib/libfido2/man/fido_credman.3
@@ -0,0 +1,299 @@
+.\" Copyright (c) 2019 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_CREDMAN 3
+.Os
+.Sh NAME
+.Nm fido_credman_metadata_new ,
+.Nm fido_credman_rk_new ,
+.Nm fido_credman_rp_new ,
+.Nm fido_credman_metadata_free ,
+.Nm fido_credman_rk_free ,
+.Nm fido_credman_rp_free ,
+.Nm fido_credman_rk_existing ,
+.Nm fido_credman_rk_remaining ,
+.Nm fido_credman_rk ,
+.Nm fido_credman_rk_count ,
+.Nm fido_credman_rp_id ,
+.Nm fido_credman_rp_name ,
+.Nm fido_credman_rp_count ,
+.Nm fido_credman_rp_id_hash_ptr ,
+.Nm fido_credman_rp_id_hash_len ,
+.Nm fido_credman_get_dev_metadata ,
+.Nm fido_credman_get_dev_rk ,
+.Nm fido_credman_del_dev_rk ,
+.Nm fido_credman_get_dev_rp
+.Nd FIDO 2 credential management API
+.Sh SYNOPSIS
+.In fido.h
+.In fido/credman.h
+.Ft fido_credman_metadata_t *
+.Fn fido_credman_metadata_new "void"
+.Ft fido_credman_rk_t *
+.Fn fido_credman_rk_new "void"
+.Ft fido_credman_rp_t *
+.Fn fido_credman_rp_new "void"
+.Ft void
+.Fn fido_credman_metadata_free "fido_credman_metadata_t **metadata_p"
+.Ft void
+.Fn fido_credman_rk_free "fido_credman_rk_t **rk_p"
+.Ft void
+.Fn fido_credman_rp_free "fido_credman_rp_t **rp_p"
+.Ft uint64_t
+.Fn fido_credman_rk_existing "const fido_credman_metadata_t *metadata"
+.Ft uint64_t
+.Fn fido_credman_rk_remaining "const fido_credman_metadata_t *metadata"
+.Ft const fido_cred_t *
+.Fn fido_credman_rk "const fido_credman_rk_t *rk" "size_t idx"
+.Ft size_t
+.Fn fido_credman_rk_count "const fido_credman_rk_t *rk"
+.Ft const char *
+.Fn fido_credman_rp_id "const fido_credman_rp_t *rp" "size_t idx"
+.Ft const char *
+.Fn fido_credman_rp_name "const fido_credman_rp_t *rp" "size_t idx"
+.Ft size_t
+.Fn fido_credman_rp_count "const fido_credman_rp_t *rp"
+.Ft const unsigned char *
+.Fn fido_credman_rp_id_hash_ptr "const fido_credman_rp_t *rp" "size_t idx"
+.Ft size_t
+.Fn fido_credman_rp_id_hash_len "const fido_credman_rp_t *" "size_t idx"
+.Ft int
+.Fn fido_credman_get_dev_metadata "fido_dev_t *dev" "fido_credman_metadata_t *metadata" "const char *pin"
+.Ft int
+.Fn fido_credman_get_dev_rk "fido_dev_t *dev" "const char *rp_id" "fido_credman_rk_t *rk" "const char *pin"
+.Ft int
+.Fn fido_credman_del_dev_rk "fido_dev_t *dev" const unsigned char *cred_id" "size_t cred_id_len" "const char *pin"
+.Ft int
+.Fn fido_credman_get_dev_rp "fido_dev_t *dev" "fido_credman_rp_t *rp" "const char *pin"
+.Sh DESCRIPTION
+The credential management API of
+.Em libfido2
+allows resident credentials on a FIDO2 authenticator to be listed,
+inspected, and removed.
+Please note that not all authenticators support credential management.
+To obtain information on what an authenticator supports, please
+refer to
+.Xr fido_cbor_info 3 .
+.Pp
+The
+.Vt fido_credman_metadata_t
+type abstracts credential management metadata.
+.Pp
+The
+.Fn fido_credman_metadata_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_credman_metadata_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_credman_metadata_free
+function releases the memory backing
+.Fa *metadata_p ,
+where
+.Fa *metadata_p
+must have been previously allocated by
+.Fn fido_credman_metadata_new .
+On return,
+.Fa *metadata_p
+is set to NULL.
+Either
+.Fa metadata_p
+or
+.Fa *metadata_p
+may be NULL, in which case
+.Fn fido_credman_metadata_free
+is a NOP.
+.Pp
+The
+.Fn fido_credman_get_dev_metadata
+function populates
+.Fa metadata
+with information retrieved from
+.Fa dev .
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Fn fido_credman_rk_existing
+function inspects
+.Fa metadata
+and returns the number of resident credentials on the
+authenticator.
+The
+.Fn fido_credman_rk_remaining
+function inspects
+.Fa metadata
+and returns the estimated number of resident credentials that can
+be created on the authenticator.
+.Pp
+The
+.Vt fido_credman_rk_t
+type abstracts the set of resident credentials belonging to a
+given relying party.
+.Pp
+The
+.Fn fido_credman_rk_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_credman_rk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_credman_rk_free
+function releases the memory backing
+.Fa *rk_p ,
+where
+.Fa *rk_p
+must have been previously allocated by
+.Fn fido_credman_rk_new .
+On return,
+.Fa *rk_p
+is set to NULL.
+Either
+.Fa rk_p
+or
+.Fa *rk_p
+may be NULL, in which case
+.Fn fido_credman_rk_free
+is a NOP.
+.Pp
+The
+.Fn fido_credman_get_dev_rk
+function populates
+.Fa rk
+with the set of resident credentials belonging to
+.Fa rp_id
+in
+.Fa dev .
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Fn fido_credman_rk_count
+function returns the number of resident credentials in
+.Fa rk .
+The
+.Fn fido_credman_rk
+function returns a pointer to the credential at index
+.Fa idx
+in
+.Fa rk .
+Please note that the first credential in
+.Fa rk
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The
+.Fn fido_credman_del_dev_rk
+function deletes the resident credential identified by
+.Fa cred_id
+from
+.Fa dev ,
+where
+.Fa cred_id
+points to
+.Fa cred_id_len
+bytes.
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Vt fido_credman_rp_t
+type abstracts information about a relying party.
+.Pp
+The
+.Fn fido_credman_rp_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_credman_rp_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_credman_rp_free
+function releases the memory backing
+.Fa *rp_p ,
+where
+.Fa *rp_p
+must have been previously allocated by
+.Fn fido_credman_rp_new .
+On return,
+.Fa *rp_p
+is set to NULL.
+Either
+.Fa rp_p
+or
+.Fa *rp_p
+may be NULL, in which case
+.Fn fido_credman_rp_free
+is a NOP.
+.Pp
+The
+.Fn fido_credman_get_dev_rp
+function populates
+.Fa rp
+with information about relying parties with resident credentials
+in
+.Fa dev .
+A valid
+.Fa pin
+must be provided.
+.Pp
+The
+.Fn fido_credman_rp_count
+function returns the number of relying parties in
+.Fa rp .
+.Pp
+The
+.Fn fido_credman_rp_id
+and
+.Fn fido_credman_rp_name
+functions return pointers to the id and name of relying party
+.Fa idx
+in
+.Fa rp .
+If not NULL, the values returned by these functions point to
+NUL-terminated UTF-8 strings.
+Please note that the first relying party in
+.Fa rp
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The
+.Fn fido_credman_rp_id_hash_ptr
+function returns a pointer to the hashed id of relying party
+.Fa idx
+in
+.Fa rp .
+The corresponding length can be obtained by
+.Fn fido_credman_rp_id_hash_len .
+Please note that the first relying party in
+.Fa rp
+has an
+.Fa idx
+(index) value of 0.
+.Sh RETURN VALUES
+The
+.Fn fido_credman_get_dev_metadata ,
+.Fn fido_credman_get_dev_rk ,
+.Fn fido_credman_del_dev_rk ,
+and
+.Fn fido_credman_get_dev_rp
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+Functions returning pointers are not guaranteed to succeed, and
+should have their return values checked for NULL.
+.Sh SEE ALSO
+.Xr fido_cbor_info 3 ,
+.Xr fido_cred 3
diff --git a/lib/libfido2/man/fido_dev_get_assert.3 b/lib/libfido2/man/fido_dev_get_assert.3
new file mode 100644
index 00000000000..8d4311fb4d0
--- /dev/null
+++ b/lib/libfido2/man/fido_dev_get_assert.3
@@ -0,0 +1,76 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_DEV_GET_ASSERT 3
+.Os
+.Sh NAME
+.Nm fido_dev_get_assert
+.Nd obtains an assertion from a FIDO device
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_get_assert "fido_dev_t *dev" " fido_assert_t *assert" "const char *pin"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_get_assert
+function asks the FIDO device represented by
+.Fa dev
+for an assertion according to the following parameters defined in
+.Fa assert :
+.Pp
+.Bl -dash -compact
+.It
+.Nm relying party ID ;
+.It
+.Nm client data hash ;
+.It
+.Nm list of allowed credential IDs ;
+.It
+.Nm user presence and user verification attributes .
+.El
+.Pp
+See
+.Xr fido_assert_set 3
+for information on how these values are set.
+.Pp
+If a PIN is not needed to authenticate the request against
+.Fa dev ,
+then
+.Fa pin
+may be NULL.
+Otherwise
+.Fa pin
+must point to a NUL-terminated UTF-8 string.
+.Pp
+After a successful call to
+.Fn fido_dev_get_assert ,
+the
+.Xr fido_assert_count 3 ,
+.Xr fido_assert_user_display_name 3 ,
+.Xr fido_assert_user_icon 3 ,
+.Xr fido_assert_user_name 3 ,
+.Xr fido_assert_authdata_ptr 3 ,
+.Xr fido_assert_user_id_ptr 3 ,
+.Xr fido_assert_sig_ptr 3 ,
+and
+.Xr fido_assert_sigcount 3
+functions may be invoked on
+.Fa assert
+to retrieve the various attributes of the generated assertion.
+.Pp
+Please note that
+.Fn fido_dev_get_assert
+is synchronous and will block if necessary.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_get_assert
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert 3 ,
+.Xr fido_assert_set 3
diff --git a/lib/libfido2/man/fido_dev_info_manifest.3 b/lib/libfido2/man/fido_dev_info_manifest.3
new file mode 100644
index 00000000000..e7ca8124bce
--- /dev/null
+++ b/lib/libfido2/man/fido_dev_info_manifest.3
@@ -0,0 +1,143 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_DEV_INFO_MANIFEST 3
+.Os
+.Sh NAME
+.Nm fido_dev_info_manifest ,
+.Nm fido_dev_info_new ,
+.Nm fido_dev_info_free ,
+.Nm fido_dev_info_ptr ,
+.Nm fido_dev_info_path ,
+.Nm fido_dev_info_product ,
+.Nm fido_dev_info_vendor ,
+.Nm fido_dev_info_manufacturer_string ,
+.Nm fido_dev_info_product_string
+.Nd FIDO 2 device discovery functions
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_info_manifest "fido_dev_info_t *devlist" "size_t ilen" "size_t *olen"
+.Ft fido_dev_info_t *
+.Fn fido_dev_info_new "size_t n"
+.Ft void
+.Fn fido_dev_info_free "fido_dev_info_t **devlist_p" "size_t n"
+.Ft const fido_dev_info_t *
+.Fn fido_dev_info_ptr "const fido_dev_info_t *devlist" "size_t i"
+.Ft const char *
+.Fn fido_dev_info_path "const fido_dev_info_t *di"
+.Ft int16_t
+.Fn fido_dev_info_product "const fido_dev_info_t *di"
+.Ft int16_t
+.Fn fido_dev_info_vendor "const fido_dev_info_t *di"
+.Ft const char *
+.Fn fido_dev_info_manufacturer_string "const fido_dev_info_t *di"
+.Ft const char *
+.Fn fido_dev_info_product_string "const fido_dev_info_t *di"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_info_manifest
+function fills
+.Fa devlist
+with up to
+.Fa ilen
+FIDO devices found by the underlying operating system.
+Currently only USB HID devices are supported.
+The number of discovered devices is returned in
+.Fa olen ,
+where
+.Fa olen
+is an addressable pointer.
+.Pp
+The
+.Fn fido_dev_info_new
+function returns a pointer to a newly allocated, empty device list
+with
+.Fa n
+available slots.
+If memory is not available, NULL is returned.
+.Pp
+The
+.Fn fido_dev_info_free
+function releases the memory backing
+.Fa *devlist_p ,
+where
+.Fa *devlist_p
+must have been previously allocated by
+.Fn fido_dev_info_new .
+The number
+.Fa n
+of allocated slots must also be provided.
+On return,
+.Fa *devlist_p
+is set to NULL.
+Either
+.Fa devlist_p
+or
+.Fa *devlist_p
+may be NULL, in which case
+.Fn fido_dev_info_free
+is a NOP.
+.Pp
+The
+.Fn fido_dev_info_ptr
+function returns a pointer to slot number
+.Fa i
+of
+.Fa devlist .
+It is the caller's responsibility to ensure that
+.Fa i
+is bounded.
+Please note that the first slot has index 0.
+.Pp
+The
+.Fn fido_dev_info_path
+returns the filesystem path or subsystem-specific identification
+string of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_product
+function returns the product ID of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_vendor
+function returns the vendor ID of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_manufacturer_string
+function returns the manufacturer string of
+.Fa di .
+.Pp
+The
+.Fn fido_dev_info_product_string
+function returns the product string of
+.Fa di .
+.Pp
+An example of how to use the functions described in this document
+can be found in the
+.Pa examples/manifest.c
+file shipped with
+.Em libfido2 .
+.Sh RETURN VALUES
+The
+.Fn fido_dev_info_manifest
+function always returns
+.Dv FIDO_OK .
+If a discovery error occurs, the
+.Fa olen
+pointer is set to 0.
+.Pp
+The pointers returned by
+.Fn fido_dev_info_ptr ,
+.Fn fido_dev_info_path ,
+.Fn fido_dev_info_manufacturer_string ,
+and
+.Fn fido_dev_info_product_string
+are guaranteed to exist until
+.Fn fido_dev_info_free
+is called on the corresponding device list.
diff --git a/lib/libfido2/man/fido_dev_make_cred.3 b/lib/libfido2/man/fido_dev_make_cred.3
new file mode 100644
index 00000000000..031f30c1dd2
--- /dev/null
+++ b/lib/libfido2/man/fido_dev_make_cred.3
@@ -0,0 +1,77 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_DEV_MAKE_CRED 3
+.Os
+.Sh NAME
+.Nm fido_dev_make_cred
+.Nd generates a new credential on a FIDO device
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_make_cred "fido_dev_t *dev" " fido_cred_t *cred" "const char *pin"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_make_cred
+function asks the FIDO device represented by
+.Fa dev
+to generate a new credential according to the following parameters
+defined in
+.Fa cred :
+.Pp
+.Bl -dash -compact
+.It
+.Nm type ;
+.It
+.Nm client data hash ;
+.It
+.Nm relying party ;
+.It
+.Nm user attributes ;
+.It
+.Nm list of excluded credential IDs ;
+.It
+.Nm resident key and user verification attributes .
+.El
+.Pp
+See
+.Xr fido_cred_set 3
+for information on how these values are set.
+.Pp
+If a PIN is not needed to authenticate the request against
+.Fa dev ,
+then
+.Fa pin
+may be NULL.
+Otherwise
+.Fa pin
+must point to a NUL-terminated UTF-8 string.
+.Pp
+After a successful call to
+.Fn fido_dev_make_cred ,
+the
+.Xr fido_cred_authdata_ptr 3 ,
+.Xr fido_cred_pubkey_ptr 3 ,
+.Xr fido_cred_x5c_ptr 3 ,
+and
+.Xr fido_cred_sig_ptr 3
+functions may be invoked on
+.Fa cred
+to retrieve the various parts of the generated credential.
+.Pp
+Please note that
+.Fn fido_dev_make_cred
+is synchronous and will block if necessary.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_make_cred
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_cred 3 ,
+.Xr fido_cred_set 3
diff --git a/lib/libfido2/man/fido_dev_open.3 b/lib/libfido2/man/fido_dev_open.3
new file mode 100644
index 00000000000..0beec68ef49
--- /dev/null
+++ b/lib/libfido2/man/fido_dev_open.3
@@ -0,0 +1,159 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_DEV_OPEN 3
+.Os
+.Sh NAME
+.Nm fido_dev_open ,
+.Nm fido_dev_close ,
+.Nm fido_dev_cancel ,
+.Nm fido_dev_new ,
+.Nm fido_dev_free ,
+.Nm fido_dev_force_fido2 ,
+.Nm fido_dev_force_u2f ,
+.Nm fido_dev_is_fido2 ,
+.Nm fido_dev_protocol ,
+.Nm fido_dev_build ,
+.Nm fido_dev_flags ,
+.Nm fido_dev_major ,
+.Nm fido_dev_minor
+.Nd FIDO 2 device open/close and related functions
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_open "fido_dev_t *dev" "const char *path"
+.Ft int
+.Fn fido_dev_close "fido_dev_t *dev"
+.Ft int
+.Fn fido_dev_cancel "fido_dev_t *dev"
+.Ft fido_dev_t *
+.Fn fido_dev_new "void"
+.Ft void
+.Fn fido_dev_free "fido_dev_t **dev_p"
+.Ft void
+.Fn fido_dev_force_fido2 "fido_dev_t *dev"
+.Ft void
+.Fn fido_dev_force_u2f "fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_is_fido2 "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_protocol "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_build "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_flags "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_major "const fido_dev_t *dev"
+.Ft uint8_t
+.Fn fido_dev_minor "const fido_dev_t *dev"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_open
+function opens the device pointed to by
+.Fa path ,
+where
+.Fa dev
+is a freshly allocated or otherwise closed
+.Vt fido_dev_t .
+.Pp
+The
+.Fn fido_dev_close
+function closes the device represented by
+.Fa dev .
+If
+.Fa dev
+is already closed,
+.Fn fido_dev_close
+is a NOP.
+.Pp
+The
+.Fn fido_dev_cancel
+function cancels any pending requests on
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_new
+function returns a pointer to a newly allocated, empty
+.Vt fido_dev_t .
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn fido_dev_free
+function releases the memory backing
+.Fa *dev_p ,
+where
+.Fa *dev_p
+must have been previously allocated by
+.Fn fido_dev_new .
+On return,
+.Fa *dev_p
+is set to NULL.
+Either
+.Fa dev_p
+or
+.Fa *dev_p
+may be NULL, in which case
+.Fn fido_dev_free
+is a NOP.
+.Pp
+The
+.Fn fido_dev_force_fido2
+function can be used to force CTAP2 communication with
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_force_u2f
+function can be used to force CTAP1 (U2F) communication with
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_is_fido2
+function returns
+.Dv true
+if
+.Fa dev
+is a FIDO 2 device.
+.Pp
+The
+.Fn fido_dev_protocol
+function returns the CTAPHID protocol version identifier of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_build
+function returns the CTAPHID build version number of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_flags
+function returns the CTAPHID capabilities flags of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_major
+function returns the CTAPHID major version number of
+.Fa dev .
+.Pp
+The
+.Fn fido_dev_minor
+function returns the CTAPHID minor version number of
+.Fa dev .
+.Pp
+For the format and meaning of the CTAPHID parameters returned by
+functions above, please refer to the FIDO Client to Authenticator
+Protocol (CTAP) specification.
+.Sh RETURN VALUES
+On success,
+.Fn fido_dev_open
+and
+.Fn fido_dev_close
+return
+.Dv FIDO_OK .
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr fido_dev_info_manifest 3 ,
+.Xr fido_dev_set_io_functions 3
diff --git a/lib/libfido2/man/fido_dev_set_io_functions.3 b/lib/libfido2/man/fido_dev_set_io_functions.3
new file mode 100644
index 00000000000..3ec263e68e8
--- /dev/null
+++ b/lib/libfido2/man/fido_dev_set_io_functions.3
@@ -0,0 +1,95 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_DEV_SET_IO_FUNCTIONS 3
+.Os
+.Sh NAME
+.Nm fido_dev_set_io_functions
+.Nd FIDO 2 device I/O interface
+.Sh SYNOPSIS
+.In fido.h
+.Bd -literal
+typedef void *fido_dev_io_open_t(const char *);
+typedef void fido_dev_io_close_t(void *);
+typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
+typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
+
+typedef struct fido_dev_io {
+ fido_dev_io_open_t *open;
+ fido_dev_io_close_t *close;
+ fido_dev_io_read_t *read;
+ fido_dev_io_write_t *write;
+} fido_dev_io_t;
+.Ed
+.Ft int
+.Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io"
+.Sh DESCRIPTION
+The
+.Nm
+interface defines the I/O handlers used to talk to
+.Fa dev .
+Its usage is optional.
+By default,
+.Em libfido2
+will use the operating system's native HID interface to talk to
+a FIDO device.
+.Pp
+A
+.Vt fido_dev_io_open_t
+function is expected to return a non-NULL opaque pointer on success,
+and NULL on error.
+The returned opaque pointer is never dereferenced by
+.Em libfido2 .
+.Pp
+A
+.Vt fido_dev_io_close_t
+function receives the opaque handle obtained from
+.Vt fido_dev_io_open_t .
+It is not expected to be idempotent.
+.Pp
+A
+.Vt fido_dev_io_read_t
+function reads from
+.Fa dev .
+The first parameter taken is the opaque handle obtained from
+.Vt fido_dev_io_open_t .
+The read buffer is pointed to by the second parameter, and the
+third parameter holds its size.
+Finally, the last argument passed to
+.Vt fido_dev_io_read_t
+is the number of milliseconds the caller is willing to sleep,
+should the call need to block.
+If this value holds -1,
+.Vt fido_dev_io_read_t
+may block indefinitely.
+The number of bytes read is returned.
+On error, -1 is returned.
+.Pp
+Conversely, a
+.Vt fido_dev_io_write_t
+function writes to
+.Fa dev .
+The first parameter taken is the opaque handle returned by
+.Vt fido_dev_io_open_t .
+The write buffer is pointed to by the second parameter, and the
+third parameter holds its size.
+A
+.Vt fido_dev_io_write_t
+function may block.
+The number of bytes written is returned.
+On error, -1 is returned.
+.Pp
+No references to
+.Fa io
+are held by
+.Fn fido_dev_set_io_functions .
+.Sh RETURN VALUES
+On success,
+.Fn fido_dev_set_io_functions
+returns
+.Dv FIDO_OK .
+On error, a different error code defined in
+.In fido/err.h
+is returned.
diff --git a/lib/libfido2/man/fido_dev_set_pin.3 b/lib/libfido2/man/fido_dev_set_pin.3
new file mode 100644
index 00000000000..fc4fda519c5
--- /dev/null
+++ b/lib/libfido2/man/fido_dev_set_pin.3
@@ -0,0 +1,88 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_DEV_SET_PIN 3
+.Os
+.Sh NAME
+.Nm fido_dev_set_pin ,
+.Nm fido_dev_get_retry_count ,
+.Nm fido_dev_reset
+.Nd FIDO 2 device management functions
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_set_pin "fido_dev_t *dev" "const char *pin" "const char *oldpin"
+.Ft int
+.Fn fido_dev_get_retry_count "fido_dev_t *dev" "int *retries"
+.Ft int
+.Fn fido_dev_reset "fido_dev_t *dev"
+.Sh DESCRIPTION
+The
+.Fn fido_dev_set_pin
+function sets the PIN of device
+.Fa dev
+to
+.Fa pin ,
+where
+.Fa pin
+is a NUL-terminated UTF-8 string.
+If
+.Fa oldpin
+is not NULL, the device's PIN is changed from
+.Fa oldpin
+to
+.Fa pin ,
+where
+.Fa pin
+and
+.Fa oldpin
+are NUL-terminated UTF-8 strings.
+.Pp
+The
+.Fn fido_dev_get_retry_count
+function fills
+.Fa retries
+with the number of PIN retries left in
+.Fa dev
+before lock-out, where
+.Fa retries
+is an addressable pointer.
+.Pp
+The
+.Fn fido_dev_reset
+function performs a reset on
+.Fa dev ,
+resetting the device's PIN and erasing credentials stored on the
+device.
+.Pp
+Please note that
+.Fn fido_dev_set_pin ,
+.Fn fido_dev_get_retry_count ,
+and
+.Fn fido_dev_reset
+are synchronous and will block if necessary.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_set_pin ,
+.Fn fido_dev_get_retry_count ,
+and
+.Fn fido_dev_reset
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh CAVEATS
+Regarding
+.Fn fido_dev_reset ,
+the actual user-flow to perform a reset is outside the scope of the
+FIDO2 specification, and may therefore vary depending on the
+authenticator.
+Yubico authenticators will return
+.Dv FIDO_ERR_NOT_ALLOWED
+if a reset is issued later than 5 seconds after power-up, and
+.Dv FIDO_ERR_ACTION_TIMEOUT
+if the user fails to confirm the reset by touching the key
+within 30 seconds.
diff --git a/lib/libfido2/man/fido_strerr.3 b/lib/libfido2/man/fido_strerr.3
new file mode 100644
index 00000000000..b492eb800c6
--- /dev/null
+++ b/lib/libfido2/man/fido_strerr.3
@@ -0,0 +1,27 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt FIDO_STRERR 3
+.Os
+.Sh NAME
+.Nm fido_strerr
+.Nd FIDO 2 error codes
+.Sh SYNOPSIS
+.In fido.h
+.Ft const char *
+.Fn fido_strerr "int n"
+.Sh DESCRIPTION
+The
+.Fn fido_strerr
+function translates the error code
+.Fa n
+into a readable string,
+where
+.Fa n
+is an error code defined in
+.In fido/err.h .
+.Fn fido_strerr
+never returns NULL.
+Returned pointers point to static strings.
diff --git a/lib/libfido2/man/rs256_pk.3 b/lib/libfido2/man/rs256_pk.3
new file mode 100644
index 00000000000..c55ada763ad
--- /dev/null
+++ b/lib/libfido2/man/rs256_pk.3
@@ -0,0 +1,122 @@
+.\" Copyright (c) 2018 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.
+.\"
+.Dd $Mdocdate: November 14 2019 $
+.Dt RS256_PK 3
+.Os
+.Sh NAME
+.Nm rs256_pk_new ,
+.Nm rs256_pk_free ,
+.Nm rs256_pk_from_RSA ,
+.Nm rs256_pk_from_ptr ,
+.Nm rs256_pk_to_EVP_PKEY
+.Nd FIDO 2 COSE RS256 API
+.Sh SYNOPSIS
+.In openssl/rsa.h
+.In fido/rs256.h
+.Ft rs256_pk_t *
+.Fn rs256_pk_new "void"
+.Ft void
+.Fn rs256_pk_free "rs256_pk_t **pkp"
+.Ft int
+.Fn rs256_pk_from_RSA "rs256_pk_t *pk" "const RSA *rsa"
+.Ft int
+.Fn rs256_pk_from_ptr "rs256_pk_t *pk" "const void *ptr" "size_t len"
+.Ft EVP_PKEY *
+.Fn rs256_pk_to_EVP_PKEY "const rs256_pk_t *pk"
+.Sh DESCRIPTION
+RS256 is the name given in the CBOR Object Signing and Encryption
+(COSE) RFC to PKCS#1.5 2048-bit RSA with SHA-256.
+The COSE RS256 API of
+.Em libfido2
+is an auxiliary API with routines to convert between the different
+RSA public key types used in
+.Em libfido2
+and
+.Em OpenSSL .
+.Pp
+In
+.Em libfido2 ,
+RS256 public keys are abstracted by the
+.Vt rs256_pk_t
+type.
+.Pp
+The
+.Fn rs256_pk_new
+function returns a pointer to a newly allocated, empty
+.Vt rs256_pk_t
+type.
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
+.Fn rs256_pk_free
+function releases the memory backing
+.Fa *pkp ,
+where
+.Fa *pkp
+must have been previously allocated by
+.Fn rs256_pk_new .
+On return,
+.Fa *pkp
+is set to NULL.
+Either
+.Fa pkp
+or
+.Fa *pkp
+may be NULL, in which case
+.Fn rs256_pk_free
+is a NOP.
+.Pp
+The
+.Fn rs256_pk_from_RSA
+function fills
+.Fa pk
+with the contents of
+.Fa rsa .
+No references to
+.Fa rsa
+are kept.
+.Pp
+The
+.Fn rs256_pk_from_ptr
+function fills
+.Fa pk
+with the contents of
+.Fa ptr ,
+where
+.Fa ptr
+points to
+.Fa len
+bytes.
+No references to
+.Fa ptr
+are kept.
+.Pp
+The
+.Fn rs256_pk_to_EVP_PKEY
+function converts
+.Fa pk
+to a newly allocated
+.Fa EVP_PKEY
+type with a reference count of 1.
+No internal references to the returned pointer are kept.
+If an error occurs,
+.Fn rs256_pk_to_EVP_PKEY
+returns NULL.
+.Sh RETURN VALUES
+The
+.Fn rs256_pk_from_RSA
+and
+.Fn rs256_pk_from_ptr
+functions return
+.Dv FIDO_OK
+on success.
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr eddsa_pk 3 ,
+.Xr es256_pk 3 ,
+.Xr fido_assert_verify 3 ,
+.Xr fido_cred_pubkey_ptr 3
diff --git a/lib/libfido2/shlib_version b/lib/libfido2/shlib_version
new file mode 100644
index 00000000000..0aaa36f6ba1
--- /dev/null
+++ b/lib/libfido2/shlib_version
@@ -0,0 +1,3 @@
+major=1
+minor=2
+
diff --git a/lib/libfido2/src/aes256.c b/lib/libfido2/src/aes256.c
new file mode 100644
index 00000000000..478fad90eeb
--- /dev/null
+++ b/lib/libfido2/src/aes256.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018 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 <openssl/evp.h>
+#include <string.h>
+
+#include "fido.h"
+
+int
+aes256_cbc_enc(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
+{
+ EVP_CIPHER_CTX *ctx = NULL;
+ unsigned char iv[32];
+ int len;
+ int ok = -1;
+
+ memset(iv, 0, sizeof(iv));
+ out->ptr = NULL;
+ out->len = 0;
+
+ /* sanity check */
+ if (in->len > INT_MAX || (in->len % 16) != 0 ||
+ (out->ptr = calloc(1, in->len)) == NULL) {
+ log_debug("%s: in->len=%zu", __func__, in->len);
+ goto fail;
+ }
+
+ if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
+ !EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
+ !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
+ !EVP_EncryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
+ len < 0 || (size_t)len != in->len) {
+ log_debug("%s: EVP_Encrypt", __func__);
+ goto fail;
+ }
+
+ out->len = (size_t)len;
+
+ ok = 0;
+fail:
+ if (ctx != NULL)
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (ok < 0) {
+ free(out->ptr);
+ out->ptr = NULL;
+ out->len = 0;
+ }
+
+ return (ok);
+}
+
+int
+aes256_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
+{
+ EVP_CIPHER_CTX *ctx = NULL;
+ unsigned char iv[32];
+ int len;
+ int ok = -1;
+
+ memset(iv, 0, sizeof(iv));
+ out->ptr = NULL;
+ out->len = 0;
+
+ /* sanity check */
+ if (in->len > INT_MAX || (in->len % 16) != 0 ||
+ (out->ptr = calloc(1, in->len)) == NULL) {
+ log_debug("%s: in->len=%zu", __func__, in->len);
+ goto fail;
+ }
+
+ if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
+ !EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
+ !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
+ !EVP_DecryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
+ len < 0 || (size_t)len > in->len + 32) {
+ log_debug("%s: EVP_Decrypt", __func__);
+ goto fail;
+ }
+
+ out->len = (size_t)len;
+
+ ok = 0;
+fail:
+ if (ctx != NULL)
+ EVP_CIPHER_CTX_free(ctx);
+
+ if (ok < 0) {
+ free(out->ptr);
+ out->ptr = NULL;
+ out->len = 0;
+ }
+
+ return (ok);
+}
diff --git a/lib/libfido2/src/assert.c b/lib/libfido2/src/assert.c
new file mode 100644
index 00000000000..76a9dfe4ff2
--- /dev/null
+++ b/lib/libfido2/src/assert.c
@@ -0,0 +1,1085 @@
+/*
+ * Copyright (c) 2018 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 <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <string.h>
+#include "fido.h"
+#include "fido/es256.h"
+#include "fido/rs256.h"
+#include "fido/eddsa.h"
+
+static int
+adjust_assert_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_assert_t *assert = arg;
+ uint64_t n;
+
+ /* numberOfCredentials; see section 6.2 */
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 5) {
+ log_debug("%s: cbor_type", __func__);
+ return (0); /* ignore */
+ }
+
+ if (decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+
+ if (assert->stmt_len != 0 || assert->stmt_cnt != 1 ||
+ (size_t)n < assert->stmt_cnt) {
+ log_debug("%s: stmt_len=%zu, stmt_cnt=%zu, n=%zu", __func__,
+ assert->stmt_len, assert->stmt_cnt, (size_t)n);
+ return (-1);
+ }
+
+ if (fido_assert_set_count(assert, (size_t)n) != FIDO_OK) {
+ log_debug("%s: fido_assert_set_count", __func__);
+ return (-1);
+ }
+
+ assert->stmt_len = 0; /* XXX */
+
+ return (0);
+}
+
+static int
+parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_assert_stmt *stmt = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* credential id */
+ return (decode_cred_id(val, &stmt->id));
+ case 2: /* authdata */
+ return (decode_assert_authdata(val, &stmt->authdata_cbor,
+ &stmt->authdata, &stmt->authdata_ext,
+ &stmt->hmac_secret_enc));
+ case 3: /* signature */
+ return (fido_blob_decode(val, &stmt->sig));
+ case 4: /* user attributes */
+ return (decode_user(val, &stmt->user));
+ default: /* ignore */
+ log_debug("%s: cbor type", __func__);
+ return (0);
+ }
+}
+
+static int
+fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
+ const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
+{
+ fido_blob_t f;
+ cbor_item_t *argv[7];
+ int r;
+
+ memset(argv, 0, sizeof(argv));
+ memset(&f, 0, sizeof(f));
+
+ /* do we have everything we need? */
+ if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
+ log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
+ (void *)assert->rp_id, (void *)assert->cdh.ptr);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if ((argv[0] = cbor_build_string(assert->rp_id)) == NULL ||
+ (argv[1] = fido_blob_encode(&assert->cdh)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* allowed credentials */
+ if (assert->allow_list.len) {
+ const fido_blob_array_t *cl = &assert->allow_list;
+ if ((argv[2] = encode_pubkey_list(cl)) == NULL) {
+ log_debug("%s: encode_pubkey_list", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ }
+
+ /* hmac-secret extension */
+ if (assert->ext & FIDO_EXT_HMAC_SECRET)
+ if ((argv[3] = encode_hmac_secret_param(ecdh, pk,
+ &assert->hmac_salt)) == NULL) {
+ log_debug("%s: encode_hmac_secret_param", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* options */
+ if (assert->up != FIDO_OPT_OMIT || assert->uv != FIDO_OPT_OMIT)
+ if ((argv[4] = encode_assert_options(assert->up,
+ assert->uv)) == NULL) {
+ log_debug("%s: encode_assert_options", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* pin authentication */
+ if (pin) {
+ if (pk == NULL || ecdh == NULL) {
+ log_debug("%s: pin=%p, pk=%p, ecdh=%p", __func__,
+ (const void *)pin, (const void *)pk,
+ (const void *)ecdh);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ if ((r = add_cbor_pin_params(dev, &assert->cdh, pk, ecdh, pin,
+ &argv[5], &argv[6])) != FIDO_OK) {
+ log_debug("%s: add_cbor_pin_params", __func__);
+ goto fail;
+ }
+ }
+
+ /* frame and transmit */
+ if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, 7, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ free(f.ptr);
+
+ return (r);
+}
+
+static int
+fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ fido_assert_reset_rx(assert);
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ /* start with room for a single assertion */
+ if ((assert->stmt = calloc(1, sizeof(fido_assert_stmt))) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ assert->stmt_len = 0;
+ assert->stmt_cnt = 1;
+
+ /* adjust as needed */
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, assert,
+ adjust_assert_count)) != FIDO_OK) {
+ log_debug("%s: adjust_assert_count", __func__);
+ return (r);
+ }
+
+ /* parse the first assertion */
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len,
+ &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
+ log_debug("%s: parse_assert_reply", __func__);
+ return (r);
+ }
+
+ assert->stmt_len++;
+
+ return (FIDO_OK);
+}
+
+static int
+fido_get_next_assert_tx(fido_dev_t *dev)
+{
+ const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+
+ if (tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
+ log_debug("%s: tx", __func__);
+ return (FIDO_ERR_TX);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ /* sanity check */
+ if (assert->stmt_len >= assert->stmt_cnt) {
+ log_debug("%s: stmt_len=%zu, stmt_cnt=%zu", __func__,
+ assert->stmt_len, assert->stmt_cnt);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len,
+ &assert->stmt[assert->stmt_len], parse_assert_reply)) != FIDO_OK) {
+ log_debug("%s: parse_assert_reply", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
+ const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin)) != FIDO_OK ||
+ (r = fido_dev_get_assert_rx(dev, assert, ms)) != FIDO_OK)
+ return (r);
+
+ while (assert->stmt_len < assert->stmt_cnt) {
+ if ((r = fido_get_next_assert_tx(dev)) != FIDO_OK ||
+ (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
+ return (r);
+ assert->stmt_len++;
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+decrypt_hmac_secrets(fido_assert_t *assert, const fido_blob_t *key)
+{
+ for (size_t i = 0; i < assert->stmt_cnt; i++) {
+ fido_assert_stmt *stmt = &assert->stmt[i];
+ if (stmt->hmac_secret_enc.ptr != NULL) {
+ if (aes256_cbc_dec(key, &stmt->hmac_secret_enc,
+ &stmt->hmac_secret) < 0) {
+ log_debug("%s: aes256_cbc_dec %zu", __func__, i);
+ return (-1);
+ }
+ }
+ }
+
+ return (0);
+}
+
+int
+fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
+{
+ fido_blob_t *ecdh = NULL;
+ es256_pk_t *pk = NULL;
+ int r;
+
+ if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
+ log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
+ (void *)assert->rp_id, (void *)assert->cdh.ptr);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ if (fido_dev_is_fido2(dev) == false) {
+ if (pin != NULL || assert->ext != 0)
+ return (FIDO_ERR_UNSUPPORTED_OPTION);
+ return (u2f_authenticate(dev, assert, -1));
+ }
+
+ if (pin != NULL || assert->ext != 0) {
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+ }
+
+ r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
+ if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET)
+ if (decrypt_hmac_secrets(assert, ecdh) < 0) {
+ log_debug("%s: decrypt_hmac_secrets", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+fail:
+ es256_pk_free(&pk);
+ fido_blob_free(&ecdh);
+
+ return (r);
+}
+
+int
+check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
+{
+ log_debug("%s: flags=%02x", __func__, flags);
+ log_debug("%s: up=%d, uv=%d", __func__, up, uv);
+
+ if (up == FIDO_OPT_TRUE &&
+ (flags & CTAP_AUTHDATA_USER_PRESENT) == 0) {
+ log_debug("%s: CTAP_AUTHDATA_USER_PRESENT", __func__);
+ return (-1); /* user not present */
+ }
+
+ if (uv == FIDO_OPT_TRUE &&
+ (flags & CTAP_AUTHDATA_USER_VERIFIED) == 0) {
+ log_debug("%s: CTAP_AUTHDATA_USER_VERIFIED", __func__);
+ return (-1); /* user not verified */
+ }
+
+ return (0);
+}
+
+static int
+check_extensions(int authdata_ext, int ext)
+{
+ if (authdata_ext != ext) {
+ log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
+ authdata_ext, ext);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata,
+ const fido_blob_t *authdata_cbor)
+{
+ cbor_item_t *item = NULL;
+ unsigned char *authdata_ptr = NULL;
+ size_t authdata_len;
+ struct cbor_load_result cbor;
+ SHA256_CTX ctx;
+ int ok = -1;
+
+ if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
+ &cbor)) == NULL || cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false) {
+ log_debug("%s: authdata", __func__);
+ goto fail;
+ }
+
+ authdata_ptr = cbor_bytestring_handle(item);
+ authdata_len = cbor_bytestring_length(item);
+
+ if (cose_alg != COSE_EDDSA) {
+ if (dgst->len < SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
+ SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
+ SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
+ SHA256_Final(dgst->ptr, &ctx) == 0) {
+ log_debug("%s: sha256", __func__);
+ goto fail;
+ }
+ dgst->len = SHA256_DIGEST_LENGTH;
+ } else {
+ if (SIZE_MAX - authdata_len < clientdata->len ||
+ dgst->len < authdata_len + clientdata->len) {
+ log_debug("%s: memcpy", __func__);
+ goto fail;
+ }
+ memcpy(dgst->ptr, authdata_ptr, authdata_len);
+ memcpy(dgst->ptr + authdata_len, clientdata->ptr,
+ clientdata->len);
+ dgst->len = authdata_len + clientdata->len;
+ }
+
+ ok = 0;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ return (ok);
+}
+
+int
+verify_sig_es256(const fido_blob_t *dgst, const es256_pk_t *pk,
+ const fido_blob_t *sig)
+{
+ EVP_PKEY *pkey = NULL;
+ EC_KEY *ec = NULL;
+ int ok = -1;
+
+ /* ECDSA_verify needs ints */
+ if (dgst->len > INT_MAX || sig->len > INT_MAX) {
+ log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
+ dgst->len, sig->len);
+ return (-1);
+ }
+
+ if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
+ (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
+ log_debug("%s: pk -> ec", __func__);
+ goto fail;
+ }
+
+ if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
+ (int)sig->len, ec) != 1) {
+ log_debug("%s: ECDSA_verify", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+
+ return (ok);
+}
+
+int
+verify_sig_rs256(const fido_blob_t *dgst, const rs256_pk_t *pk,
+ const fido_blob_t *sig)
+{
+ EVP_PKEY *pkey = NULL;
+ RSA *rsa = NULL;
+ int ok = -1;
+
+ /* RSA_verify needs unsigned ints */
+ if (dgst->len > UINT_MAX || sig->len > UINT_MAX) {
+ log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
+ dgst->len, sig->len);
+ return (-1);
+ }
+
+ if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
+ (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) {
+ log_debug("%s: pk -> ec", __func__);
+ goto fail;
+ }
+
+ if (RSA_verify(NID_sha256, dgst->ptr, (unsigned int)dgst->len, sig->ptr,
+ (unsigned int)sig->len, rsa) != 1) {
+ log_debug("%s: RSA_verify", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+
+ return (ok);
+}
+
+int
+verify_sig_eddsa(const fido_blob_t *dgst, const eddsa_pk_t *pk,
+ const fido_blob_t *sig)
+{
+ EVP_PKEY *pkey = NULL;
+ EVP_MD_CTX *mdctx = NULL;
+ int ok = -1;
+
+ /* EVP_DigestVerify needs ints */
+ if (dgst->len > INT_MAX || sig->len > INT_MAX) {
+ log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
+ dgst->len, sig->len);
+ return (-1);
+ }
+
+ if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
+ log_debug("%s: pk -> pkey", __func__);
+ goto fail;
+ }
+
+ if ((mdctx = EVP_MD_CTX_new()) == NULL) {
+ log_debug("%s: EVP_MD_CTX_new", __func__);
+ goto fail;
+ }
+
+ if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
+ log_debug("%s: EVP_DigestVerifyInit", __func__);
+ goto fail;
+ }
+
+ if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
+ dgst->len) != 1) {
+ log_debug("%s: EVP_DigestVerify", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (mdctx != NULL)
+ EVP_MD_CTX_free(mdctx);
+
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+
+ return (ok);
+}
+
+int
+fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
+ const void *pk)
+{
+ unsigned char buf[1024];
+ fido_blob_t dgst;
+ const fido_assert_stmt *stmt = NULL;
+ int ok = -1;
+ int r;
+
+ dgst.ptr = buf;
+ dgst.len = sizeof(buf);
+
+ if (idx >= assert->stmt_len || pk == NULL) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ stmt = &assert->stmt[idx];
+
+ /* do we have everything we need? */
+ if (assert->cdh.ptr == NULL || assert->rp_id == NULL ||
+ stmt->authdata_cbor.ptr == NULL || stmt->sig.ptr == NULL) {
+ log_debug("%s: cdh=%p, rp_id=%s, authdata=%p, sig=%p", __func__,
+ (void *)assert->cdh.ptr, assert->rp_id,
+ (void *)stmt->authdata_cbor.ptr, (void *)stmt->sig.ptr);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ if (check_flags(stmt->authdata.flags, assert->up, assert->uv) < 0) {
+ log_debug("%s: check_flags", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (check_extensions(stmt->authdata_ext, assert->ext) < 0) {
+ log_debug("%s: check_extensions", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (check_rp_id(assert->rp_id, stmt->authdata.rp_id_hash) != 0) {
+ log_debug("%s: check_rp_id", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (get_signed_hash(cose_alg, &dgst, &assert->cdh,
+ &stmt->authdata_cbor) < 0) {
+ log_debug("%s: get_signed_hash", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto out;
+ }
+
+ switch (cose_alg) {
+ case COSE_ES256:
+ ok = verify_sig_es256(&dgst, pk, &stmt->sig);
+ break;
+ case COSE_RS256:
+ ok = verify_sig_rs256(&dgst, pk, &stmt->sig);
+ break;
+ case COSE_EDDSA:
+ ok = verify_sig_eddsa(&dgst, pk, &stmt->sig);
+ break;
+ default:
+ log_debug("%s: unsupported cose_alg %d", __func__, cose_alg);
+ r = FIDO_ERR_UNSUPPORTED_OPTION;
+ goto out;
+ }
+
+ if (ok < 0)
+ r = FIDO_ERR_INVALID_SIG;
+ else
+ r = FIDO_OK;
+out:
+ explicit_bzero(buf, sizeof(buf));
+
+ return (r);
+}
+
+int
+fido_assert_set_clientdata_hash(fido_assert_t *assert,
+ const unsigned char *hash, size_t hash_len)
+{
+ if (fido_blob_set(&assert->cdh, hash, hash_len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
+ size_t salt_len)
+{
+ if ((salt_len != 32 && salt_len != 64) ||
+ fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_set_rp(fido_assert_t *assert, const char *id)
+{
+ if (assert->rp_id != NULL) {
+ free(assert->rp_id);
+ assert->rp_id = NULL;
+ }
+
+ if (id == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ if ((assert->rp_id = strdup(id)) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
+ size_t len)
+{
+ fido_blob_t id;
+ fido_blob_t *list_ptr;
+ int r;
+
+ memset(&id, 0, sizeof(id));
+
+ if (assert->allow_list.len == SIZE_MAX) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if (fido_blob_set(&id, ptr, len) < 0 || (list_ptr =
+ recallocarray(assert->allow_list.ptr, assert->allow_list.len,
+ assert->allow_list.len + 1, sizeof(fido_blob_t))) == NULL) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ list_ptr[assert->allow_list.len++] = id;
+ assert->allow_list.ptr = list_ptr;
+
+ return (FIDO_OK);
+fail:
+ free(id.ptr);
+
+ return (r);
+
+}
+
+int
+fido_assert_set_extensions(fido_assert_t *assert, int ext)
+{
+ if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ assert->ext = ext;
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_set_options(fido_assert_t *assert, bool up, bool uv)
+{
+ assert->up = up ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
+ assert->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_set_up(fido_assert_t *assert, fido_opt_t up)
+{
+ assert->up = up;
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_set_uv(fido_assert_t *assert, fido_opt_t uv)
+{
+ assert->uv = uv;
+
+ return (FIDO_OK);
+}
+
+const unsigned char *
+fido_assert_clientdata_hash_ptr(const fido_assert_t *assert)
+{
+ return (assert->cdh.ptr);
+}
+
+size_t
+fido_assert_clientdata_hash_len(const fido_assert_t *assert)
+{
+ return (assert->cdh.len);
+}
+
+fido_assert_t *
+fido_assert_new(void)
+{
+ return (calloc(1, sizeof(fido_assert_t)));
+}
+
+void
+fido_assert_reset_tx(fido_assert_t *assert)
+{
+ free(assert->rp_id);
+ free(assert->cdh.ptr);
+ free(assert->hmac_salt.ptr);
+ free_blob_array(&assert->allow_list);
+
+ memset(&assert->cdh, 0, sizeof(assert->cdh));
+ memset(&assert->hmac_salt, 0, sizeof(assert->hmac_salt));
+ memset(&assert->allow_list, 0, sizeof(assert->allow_list));
+
+ assert->rp_id = NULL;
+ assert->up = FIDO_OPT_OMIT;
+ assert->uv = FIDO_OPT_OMIT;
+ assert->ext = 0;
+}
+
+void
+fido_assert_reset_rx(fido_assert_t *assert)
+{
+ for (size_t i = 0; i < assert->stmt_cnt; i++) {
+ free(assert->stmt[i].user.id.ptr);
+ free(assert->stmt[i].user.icon);
+ free(assert->stmt[i].user.name);
+ free(assert->stmt[i].user.display_name);
+ free(assert->stmt[i].id.ptr);
+ if (assert->stmt[i].hmac_secret.ptr != NULL) {
+ explicit_bzero(assert->stmt[i].hmac_secret.ptr,
+ assert->stmt[i].hmac_secret.len);
+ }
+ free(assert->stmt[i].hmac_secret.ptr);
+ free(assert->stmt[i].hmac_secret_enc.ptr);
+ free(assert->stmt[i].authdata_cbor.ptr);
+ free(assert->stmt[i].sig.ptr);
+ memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
+ }
+
+ free(assert->stmt);
+
+ assert->stmt = NULL;
+ assert->stmt_len = 0;
+ assert->stmt_cnt = 0;
+}
+
+void
+fido_assert_free(fido_assert_t **assert_p)
+{
+ fido_assert_t *assert;
+
+ if (assert_p == NULL || (assert = *assert_p) == NULL)
+ return;
+
+ fido_assert_reset_tx(assert);
+ fido_assert_reset_rx(assert);
+
+ free(assert);
+
+ *assert_p = NULL;
+}
+
+size_t
+fido_assert_count(const fido_assert_t *assert)
+{
+ return (assert->stmt_len);
+}
+
+const char *
+fido_assert_rp_id(const fido_assert_t *assert)
+{
+ return (assert->rp_id);
+}
+
+uint8_t
+fido_assert_flags(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].authdata.flags);
+}
+
+uint32_t
+fido_assert_sigcount(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].authdata.sigcount);
+}
+
+const unsigned char *
+fido_assert_authdata_ptr(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].authdata_cbor.ptr);
+}
+
+size_t
+fido_assert_authdata_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].authdata_cbor.len);
+}
+
+const unsigned char *
+fido_assert_sig_ptr(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].sig.ptr);
+}
+
+size_t
+fido_assert_sig_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].sig.len);
+}
+
+const unsigned char *
+fido_assert_id_ptr(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].id.ptr);
+}
+
+size_t
+fido_assert_id_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].id.len);
+}
+
+const unsigned char *
+fido_assert_user_id_ptr(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].user.id.ptr);
+}
+
+size_t
+fido_assert_user_id_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].user.id.len);
+}
+
+const char *
+fido_assert_user_icon(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].user.icon);
+}
+
+const char *
+fido_assert_user_name(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].user.name);
+}
+
+const char *
+fido_assert_user_display_name(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].user.display_name);
+}
+
+const unsigned char *
+fido_assert_hmac_secret_ptr(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].hmac_secret.ptr);
+}
+
+size_t
+fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].hmac_secret.len);
+}
+
+static void
+fido_assert_clean_authdata(fido_assert_stmt *as)
+{
+ free(as->authdata_cbor.ptr);
+ free(as->hmac_secret_enc.ptr);
+
+ memset(&as->authdata_ext, 0, sizeof(as->authdata_ext));
+ memset(&as->authdata_cbor, 0, sizeof(as->authdata_cbor));
+ memset(&as->authdata, 0, sizeof(as->authdata));
+ memset(&as->hmac_secret_enc, 0, sizeof(as->hmac_secret_enc));
+}
+
+int
+fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
+ const unsigned char *ptr, size_t len)
+{
+ cbor_item_t *item = NULL;
+ fido_assert_stmt *stmt = NULL;
+ struct cbor_load_result cbor;
+ int r;
+
+ if (idx >= assert->stmt_len || ptr == NULL || len == 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ stmt = &assert->stmt[idx];
+ fido_assert_clean_authdata(stmt);
+
+ if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if (decode_assert_authdata(item, &stmt->authdata_cbor, &stmt->authdata,
+ &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
+ log_debug("%s: decode_assert_authdata", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ if (r != FIDO_OK)
+ fido_assert_clean_authdata(stmt);
+
+ return (r);
+}
+
+int
+fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
+ const unsigned char *ptr, size_t len)
+{
+ cbor_item_t *item = NULL;
+ fido_assert_stmt *stmt = NULL;
+ int r;
+
+ if (idx >= assert->stmt_len || ptr == NULL || len == 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ stmt = &assert->stmt[idx];
+ fido_assert_clean_authdata(stmt);
+
+ if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
+ log_debug("%s: cbor_build_bytestring", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (decode_assert_authdata(item, &stmt->authdata_cbor, &stmt->authdata,
+ &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
+ log_debug("%s: decode_assert_authdata", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ if (r != FIDO_OK)
+ fido_assert_clean_authdata(stmt);
+
+ return (r);
+}
+
+static void
+fido_assert_clean_sig(fido_assert_stmt *as)
+{
+ free(as->sig.ptr);
+ as->sig.ptr = NULL;
+ as->sig.len = 0;
+}
+
+int
+fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
+ size_t len)
+{
+ unsigned char *sig;
+
+ if (idx >= a->stmt_len || ptr == NULL || len == 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ fido_assert_clean_sig(&a->stmt[idx]);
+
+ if ((sig = malloc(len)) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ memcpy(sig, ptr, len);
+ a->stmt[idx].sig.ptr = sig;
+ a->stmt[idx].sig.len = len;
+
+ return (FIDO_OK);
+}
+
+/* XXX shrinking leaks memory; fortunately that shouldn't happen */
+int
+fido_assert_set_count(fido_assert_t *assert, size_t n)
+{
+ void *new_stmt;
+
+#ifdef FIDO_FUZZ
+ if (n > UINT8_MAX) {
+ log_debug("%s: n > UINT8_MAX", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+#endif
+
+ new_stmt = recallocarray(assert->stmt, assert->stmt_cnt, n,
+ sizeof(fido_assert_stmt));
+ if (new_stmt == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ assert->stmt = new_stmt;
+ assert->stmt_cnt = n;
+ assert->stmt_len = n;
+
+ return (FIDO_OK);
+}
diff --git a/lib/libfido2/src/authkey.c b/lib/libfido2/src/authkey.c
new file mode 100644
index 00000000000..eec797d28b3
--- /dev/null
+++ b/lib/libfido2/src/authkey.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+
+static int
+parse_authkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ es256_pk_t *authkey = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 1) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ return (es256_pk_decode(val, authkey));
+}
+
+static int
+fido_dev_authkey_tx(fido_dev_t *dev)
+{
+ fido_blob_t f;
+ cbor_item_t *argv[2];
+ int r;
+
+ log_debug("%s: dev=%p", __func__, (void *)dev);
+
+ memset(&f, 0, sizeof(f));
+ memset(argv, 0, sizeof(argv));
+
+ /* add command parameters */
+ if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ (argv[1] = cbor_build_uint8(2)) == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* frame and transmit */
+ if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ free(f.ptr);
+
+ return (r);
+}
+
+static int
+fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+
+ log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev,
+ (void *)authkey, ms);
+
+ memset(authkey, 0, sizeof(*authkey));
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ return (parse_cbor_reply(reply, (size_t)reply_len, authkey,
+ parse_authkey));
+}
+
+static int
+fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_authkey_tx(dev)) != FIDO_OK ||
+ (r = fido_dev_authkey_rx(dev, authkey, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey)
+{
+ return (fido_dev_authkey_wait(dev, authkey, -1));
+}
diff --git a/lib/libfido2/src/bio.c b/lib/libfido2/src/bio.c
new file mode 100644
index 00000000000..a5cfade1f23
--- /dev/null
+++ b/lib/libfido2/src/bio.c
@@ -0,0 +1,844 @@
+/*
+ * Copyright (c) 2019 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 <string.h>
+
+#include "fido.h"
+#include "fido/bio.h"
+#include "fido/es256.h"
+
+#define CMD_ENROLL_BEGIN 0x01
+#define CMD_ENROLL_NEXT 0x02
+#define CMD_ENROLL_CANCEL 0x03
+#define CMD_ENUM 0x04
+#define CMD_SET_NAME 0x05
+#define CMD_ENROLL_REMOVE 0x06
+#define CMD_GET_INFO 0x07
+
+static int
+bio_prepare_hmac(uint8_t cmd, cbor_item_t **argv, size_t argc,
+ cbor_item_t **param, fido_blob_t *hmac_data)
+{
+ const uint8_t prefix[2] = { 0x01 /* modality */, cmd };
+ int ok = -1;
+ size_t cbor_alloc_len;
+ size_t cbor_len;
+ unsigned char *cbor = NULL;
+
+ if (argv == NULL || param == NULL)
+ return (fido_blob_set(hmac_data, prefix, sizeof(prefix)));
+
+ if ((*param = cbor_flatten_vector(argv, argc)) == NULL) {
+ log_debug("%s: cbor_flatten_vector", __func__);
+ goto fail;
+ }
+
+ if ((cbor_len = cbor_serialize_alloc(*param, &cbor,
+ &cbor_alloc_len)) == 0 || cbor_len > SIZE_MAX - sizeof(prefix)) {
+ log_debug("%s: cbor_serialize_alloc", __func__);
+ goto fail;
+ }
+
+ if ((hmac_data->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
+ log_debug("%s: malloc", __func__);
+ goto fail;
+ }
+
+ memcpy(hmac_data->ptr, prefix, sizeof(prefix));
+ memcpy(hmac_data->ptr + sizeof(prefix), cbor, cbor_len);
+ hmac_data->len = cbor_len + sizeof(prefix);
+
+ ok = 0;
+fail:
+ free(cbor);
+
+ return (ok);
+}
+
+static int
+bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
+ const char *pin, const fido_blob_t *token)
+{
+ cbor_item_t *argv[5];
+ es256_pk_t *pk = NULL;
+ fido_blob_t *ecdh = NULL;
+ fido_blob_t f;
+ fido_blob_t hmac;
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&f, 0, sizeof(f));
+ memset(&hmac, 0, sizeof(hmac));
+ memset(&argv, 0, sizeof(argv));
+
+ /* modality, subCommand */
+ if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ (argv[1] = cbor_build_uint8(cmd)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ /* subParams */
+ if (pin || token) {
+ if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2],
+ &hmac) < 0) {
+ log_debug("%s: bio_prepare_hmac", __func__);
+ goto fail;
+ }
+ }
+
+ /* pinProtocol, pinAuth */
+ if (pin) {
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+ if ((r = add_cbor_pin_params(dev, &hmac, pk, ecdh, pin,
+ &argv[4], &argv[3])) != FIDO_OK) {
+ log_debug("%s: add_cbor_pin_params", __func__);
+ goto fail;
+ }
+ } else if (token) {
+ if ((argv[3] = encode_pin_opt()) == NULL ||
+ (argv[4] = encode_pin_auth(token, &hmac)) == NULL) {
+ log_debug("%s: encode pin", __func__);
+ goto fail;
+ }
+ }
+
+ /* framing and transmission */
+ if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, 5, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ es256_pk_free(&pk);
+ fido_blob_free(&ecdh);
+ free(f.ptr);
+ free(hmac.ptr);
+
+ return (r);
+}
+
+static void
+bio_reset_template(fido_bio_template_t *t)
+{
+ free(t->name);
+ free(t->id.ptr);
+ t->name = NULL;
+ memset(&t->id, 0, sizeof(t->id));
+}
+
+static void
+bio_reset_template_array(fido_bio_template_array_t *ta)
+{
+ for (size_t i = 0; i < ta->n_alloc; i++)
+ bio_reset_template(&ta->ptr[i]);
+
+ free(ta->ptr);
+ ta->ptr = NULL;
+ memset(ta, 0, sizeof(*ta));
+}
+
+static int
+decode_template(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_bio_template_t *t = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* id */
+ return (fido_blob_decode(val, &t->id));
+ case 2: /* name */
+ return (cbor_string_copy(val, &t->name));
+ }
+
+ return (0); /* ignore */
+}
+
+static int
+decode_template_array(const cbor_item_t *item, void *arg)
+{
+ fido_bio_template_array_t *ta = arg;
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ if (ta->n_rx >= ta->n_alloc) {
+ log_debug("%s: n_rx >= n_alloc", __func__);
+ return (-1);
+ }
+
+ if (cbor_map_iter(item, &ta->ptr[ta->n_rx], decode_template) < 0) {
+ log_debug("%s: decode_template", __func__);
+ return (-1);
+ }
+
+ ta->n_rx++;
+
+ return (0);
+}
+
+static int
+bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_bio_template_array_t *ta = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 7) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ if (cbor_isa_array(val) == false ||
+ cbor_array_is_definite(val) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ if (ta->ptr != NULL || ta->n_alloc != 0 || ta->n_rx != 0) {
+ log_debug("%s: ptr != NULL || n_alloc != 0 || n_rx != 0",
+ __func__);
+ return (-1);
+ }
+
+ if ((ta->ptr = calloc(cbor_array_size(val), sizeof(*ta->ptr))) == NULL)
+ return (-1);
+
+ ta->n_alloc = cbor_array_size(val);
+
+ if (cbor_array_iter(val, ta, decode_template_array) < 0) {
+ log_debug("%s: decode_template_array", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ bio_reset_template_array(ta);
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, ta,
+ bio_parse_template_array)) != FIDO_OK) {
+ log_debug("%s: bio_parse_template_array" , __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
+ const char *pin, int ms)
+{
+ int r;
+
+ if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK ||
+ (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
+ const char *pin)
+{
+ if (pin == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (bio_get_template_array_wait(dev, ta, pin, -1));
+}
+
+static int
+bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
+ const char *pin, int ms)
+{
+ cbor_item_t *argv[2];
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&argv, 0, sizeof(argv));
+
+ if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
+ (argv[1] = cbor_build_string(t->name)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK ||
+ (r = rx_cbor_status(dev, ms)) != FIDO_OK) {
+ log_debug("%s: tx/rx", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+
+ return (r);
+}
+
+int
+fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
+ const char *pin)
+{
+ if (pin == NULL || t->name == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (bio_set_template_name_wait(dev, t, pin, -1));
+}
+
+static void
+bio_reset_enroll(fido_bio_enroll_t *e)
+{
+ e->remaining_samples = 0;
+ e->last_status = 0;
+
+ if (e->token)
+ fido_blob_free(&e->token);
+}
+
+static int
+bio_parse_enroll_status(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_bio_enroll_t *e = arg;
+ uint64_t x;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 5:
+ if (decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+ e->last_status = (uint8_t)x;
+ break;
+ case 6:
+ if (decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+ e->remaining_samples = (uint8_t)x;
+ break;
+ default:
+ return (0); /* ignore */
+ }
+
+ return (0);
+}
+
+static int
+bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_blob_t *id = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 4) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ return (fido_blob_decode(val, id));
+}
+
+static int
+bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
+ fido_bio_enroll_t *e, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ bio_reset_template(t);
+
+ e->remaining_samples = 0;
+ e->last_status = 0;
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, e,
+ bio_parse_enroll_status)) != FIDO_OK) {
+ log_debug("%s: bio_parse_enroll_status", __func__);
+ return (r);
+ }
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, &t->id,
+ bio_parse_template_id)) != FIDO_OK) {
+ log_debug("%s: bio_parse_template_id", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
+ fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
+{
+ cbor_item_t *argv[3];
+ const uint8_t cmd = CMD_ENROLL_BEGIN;
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&argv, 0, sizeof(argv));
+
+ if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
+ (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
+ log_debug("%s: tx/rx", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+
+ return (r);
+}
+
+int
+fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
+ fido_bio_enroll_t *e, uint32_t timo_ms, const char *pin)
+{
+ es256_pk_t *pk = NULL;
+ fido_blob_t *ecdh = NULL;
+ fido_blob_t *token = NULL;
+ int r;
+
+ if (pin == NULL || e->token != NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ if ((token = fido_blob_new()) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+
+ if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
+ log_debug("%s: fido_dev_get_pin_token", __func__);
+ goto fail;
+ }
+
+ e->token = token;
+ token = NULL;
+fail:
+ es256_pk_free(&pk);
+ fido_blob_free(&ecdh);
+ fido_blob_free(&token);
+
+ if (r != FIDO_OK)
+ return (r);
+
+ return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1));
+}
+
+static int
+bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ e->remaining_samples = 0;
+ e->last_status = 0;
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, e,
+ bio_parse_enroll_status)) != FIDO_OK) {
+ log_debug("%s: bio_parse_enroll_status", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
+ fido_bio_enroll_t *e, uint32_t timo_ms, int ms)
+{
+ cbor_item_t *argv[3];
+ const uint8_t cmd = CMD_ENROLL_NEXT;
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&argv, 0, sizeof(argv));
+
+ if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
+ (argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
+ (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
+ log_debug("%s: tx/rx", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+
+ return (r);
+}
+
+int
+fido_bio_dev_enroll_continue(fido_dev_t *dev, const fido_bio_template_t *t,
+ fido_bio_enroll_t *e, uint32_t timo_ms)
+{
+ if (e->token == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1));
+}
+
+static int
+bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
+{
+ const uint8_t cmd = CMD_ENROLL_CANCEL;
+ int r;
+
+ if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL)) != FIDO_OK ||
+ (r = rx_cbor_status(dev, ms)) != FIDO_OK) {
+ log_debug("%s: tx/rx", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+int
+fido_bio_dev_enroll_cancel(fido_dev_t *dev)
+{
+ return (bio_enroll_cancel_wait(dev, -1));
+}
+
+static int
+bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
+ const char *pin, int ms)
+{
+ cbor_item_t *argv[1];
+ const uint8_t cmd = CMD_ENROLL_REMOVE;
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&argv, 0, sizeof(argv));
+
+ if ((argv[0] = fido_blob_encode(&t->id)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK ||
+ (r = rx_cbor_status(dev, ms)) != FIDO_OK) {
+ log_debug("%s: tx/rx", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+
+ return (r);
+}
+
+int
+fido_bio_dev_enroll_remove(fido_dev_t *dev, const fido_bio_template_t *t,
+ const char *pin)
+{
+ return (bio_enroll_remove_wait(dev, t, pin, -1));
+}
+
+static void
+bio_reset_info(fido_bio_info_t *i)
+{
+ i->type = 0;
+ i->max_samples = 0;
+}
+
+static int
+bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_bio_info_t *i = arg;
+ uint64_t x;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 2:
+ if (decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+ i->type = (uint8_t)x;
+ break;
+ case 3:
+ if (decode_uint64(val, &x) < 0 || x > UINT8_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+ i->max_samples = (uint8_t)x;
+ break;
+ default:
+ return (0); /* ignore */
+ }
+
+ return (0);
+}
+
+static int
+bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ bio_reset_info(i);
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, i,
+ bio_parse_info)) != FIDO_OK) {
+ log_debug("%s: bio_parse_info" , __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
+{
+ int r;
+
+ if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL)) != FIDO_OK ||
+ (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
+ log_debug("%s: tx/rx", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+int
+fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
+{
+ return (bio_get_info_wait(dev, i, -1));
+}
+
+const char *
+fido_bio_template_name(const fido_bio_template_t *t)
+{
+ return (t->name);
+}
+
+const unsigned char *
+fido_bio_template_id_ptr(const fido_bio_template_t *t)
+{
+ return (t->id.ptr);
+}
+
+size_t
+fido_bio_template_id_len(const fido_bio_template_t *t)
+{
+ return (t->id.len);
+}
+
+size_t
+fido_bio_template_array_count(const fido_bio_template_array_t *ta)
+{
+ return (ta->n_rx);
+}
+
+fido_bio_template_array_t *
+fido_bio_template_array_new(void)
+{
+ return (calloc(1, sizeof(fido_bio_template_array_t)));
+}
+
+fido_bio_template_t *
+fido_bio_template_new(void)
+{
+ return (calloc(1, sizeof(fido_bio_template_t)));
+}
+
+void
+fido_bio_template_array_free(fido_bio_template_array_t **tap)
+{
+ fido_bio_template_array_t *ta;
+
+ if (tap == NULL || (ta = *tap) == NULL)
+ return;
+
+ bio_reset_template_array(ta);
+ free(ta);
+ *tap = NULL;
+}
+
+void
+fido_bio_template_free(fido_bio_template_t **tp)
+{
+ fido_bio_template_t *t;
+
+ if (tp == NULL || (t = *tp) == NULL)
+ return;
+
+ bio_reset_template(t);
+ free(t);
+ *tp = NULL;
+}
+
+int
+fido_bio_template_set_name(fido_bio_template_t *t, const char *name)
+{
+ free(t->name);
+ t->name = NULL;
+
+ if (name && (t->name = strdup(name)) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ return (FIDO_OK);
+}
+
+int
+fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
+ size_t len)
+{
+ free(t->id.ptr);
+ t->id.ptr = NULL;
+ t->id.len = 0;
+
+ if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
+ return (FIDO_ERR_INTERNAL);
+
+ return (FIDO_OK);
+}
+
+const fido_bio_template_t *
+fido_bio_template(const fido_bio_template_array_t *ta, size_t idx)
+{
+ if (idx >= ta->n_alloc)
+ return (NULL);
+
+ return (&ta->ptr[idx]);
+}
+
+fido_bio_enroll_t *
+fido_bio_enroll_new(void)
+{
+ return (calloc(1, sizeof(fido_bio_enroll_t)));
+}
+
+fido_bio_info_t *
+fido_bio_info_new(void)
+{
+ return (calloc(1, sizeof(fido_bio_info_t)));
+}
+
+uint8_t
+fido_bio_info_type(const fido_bio_info_t *i)
+{
+ return (i->type);
+}
+
+uint8_t
+fido_bio_info_max_samples(const fido_bio_info_t *i)
+{
+ return (i->max_samples);
+}
+
+void
+fido_bio_enroll_free(fido_bio_enroll_t **ep)
+{
+ fido_bio_enroll_t *e;
+
+ if (ep == NULL || (e = *ep) == NULL)
+ return;
+
+ bio_reset_enroll(e);
+
+ free(e);
+ *ep = NULL;
+}
+
+void
+fido_bio_info_free(fido_bio_info_t **ip)
+{
+ fido_bio_info_t *i;
+
+ if (ip == NULL || (i = *ip) == NULL)
+ return;
+
+ free(i);
+ *ip = NULL;
+}
+
+uint8_t
+fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *e)
+{
+ return (e->remaining_samples);
+}
+
+uint8_t
+fido_bio_enroll_last_status(const fido_bio_enroll_t *e)
+{
+ return (e->last_status);
+}
diff --git a/lib/libfido2/src/blob.c b/lib/libfido2/src/blob.c
new file mode 100644
index 00000000000..5415d87c74d
--- /dev/null
+++ b/lib/libfido2/src/blob.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+
+fido_blob_t *
+fido_blob_new(void)
+{
+ return (calloc(1, sizeof(fido_blob_t)));
+}
+
+int
+fido_blob_set(fido_blob_t *b, const unsigned char *ptr, size_t len)
+{
+ if (b->ptr != NULL) {
+ explicit_bzero(b->ptr, b->len);
+ free(b->ptr);
+ b->ptr = NULL;
+ }
+
+ b->len = 0;
+
+ if (ptr == NULL || len == 0) {
+ log_debug("%s: ptr=%p, len=%zu", __func__, (const void *)ptr,
+ len);
+ return (-1);
+ }
+
+ if ((b->ptr = malloc(len)) == NULL) {
+ log_debug("%s: malloc", __func__);
+ return (-1);
+ }
+
+ memcpy(b->ptr, ptr, len);
+ b->len = len;
+
+ return (0);
+}
+
+void
+fido_blob_free(fido_blob_t **bp)
+{
+ fido_blob_t *b;
+
+ if (bp == NULL || (b = *bp) == NULL)
+ return;
+
+ if (b->ptr) {
+ explicit_bzero(b->ptr, b->len);
+ free(b->ptr);
+ }
+
+ explicit_bzero(b, sizeof(*b));
+ free(b);
+
+ *bp = NULL;
+}
+
+void
+free_blob_array(fido_blob_array_t *array)
+{
+ if (array->ptr == NULL)
+ return;
+
+ for (size_t i = 0; i < array->len; i++) {
+ fido_blob_t *b = &array->ptr[i];
+ if (b->ptr != NULL) {
+ explicit_bzero(b->ptr, b->len);
+ free(b->ptr);
+ b->ptr = NULL;
+ }
+ }
+
+ free(array->ptr);
+ array->ptr = NULL;
+ array->len = 0;
+}
+
+cbor_item_t *
+fido_blob_encode(const fido_blob_t *b)
+{
+ if (b == NULL || b->ptr == NULL)
+ return (NULL);
+
+ return (cbor_build_bytestring(b->ptr, b->len));
+}
+
+int
+fido_blob_decode(const cbor_item_t *item, fido_blob_t *b)
+{
+ return (cbor_bytestring_copy(item, &b->ptr, &b->len));
+}
+
+int
+fido_blob_is_empty(const fido_blob_t *b)
+{
+ return (b->ptr == NULL || b->len == 0);
+}
diff --git a/lib/libfido2/src/blob.h b/lib/libfido2/src/blob.h
new file mode 100644
index 00000000000..6358c0594ae
--- /dev/null
+++ b/lib/libfido2/src/blob.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2018 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 _BLOB_H
+#define _BLOB_H
+
+typedef struct fido_blob {
+ unsigned char *ptr;
+ size_t len;
+} fido_blob_t;
+
+typedef struct fido_blob_array {
+ fido_blob_t *ptr;
+ size_t len;
+} fido_blob_array_t;
+
+cbor_item_t *fido_blob_encode(const fido_blob_t *);
+fido_blob_t *fido_blob_new(void);
+int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
+int fido_blob_is_empty(const fido_blob_t *);
+int fido_blob_set(fido_blob_t *, const unsigned char *, size_t);
+void fido_blob_free(fido_blob_t **);
+void free_blob_array(fido_blob_array_t *);
+
+#endif /* !_BLOB_H */
diff --git a/lib/libfido2/src/buf.c b/lib/libfido2/src/buf.c
new file mode 100644
index 00000000000..67ff95a2d36
--- /dev/null
+++ b/lib/libfido2/src/buf.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+
+int
+buf_read(const unsigned char **buf, size_t *len, void *dst, size_t count)
+{
+ if (count > *len)
+ return (-1);
+
+ memcpy(dst, *buf, count);
+ *buf += count;
+ *len -= count;
+
+ return (0);
+}
+
+int
+buf_write(unsigned char **buf, size_t *len, const void *src, size_t count)
+{
+ if (count > *len)
+ return (-1);
+
+ memcpy(*buf, src, count);
+ *buf += count;
+ *len -= count;
+
+ return (0);
+}
diff --git a/lib/libfido2/src/cbor.c b/lib/libfido2/src/cbor.c
new file mode 100644
index 00000000000..40e76223ead
--- /dev/null
+++ b/lib/libfido2/src/cbor.c
@@ -0,0 +1,1514 @@
+/*
+ * Copyright (c) 2018 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 <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include <string.h>
+#include "fido.h"
+
+static int
+check_key_type(cbor_item_t *item)
+{
+ if (item->type == CBOR_TYPE_UINT || item->type == CBOR_TYPE_NEGINT ||
+ item->type == CBOR_TYPE_STRING)
+ return (0);
+
+ log_debug("%s: invalid type: %d", __func__, item->type);
+
+ return (-1);
+}
+
+/*
+ * Validate CTAP2 canonical CBOR encoding rules for maps.
+ */
+static int
+ctap_check_cbor(cbor_item_t *prev, cbor_item_t *curr)
+{
+ size_t curr_len;
+ size_t prev_len;
+
+ if (check_key_type(prev) < 0 || check_key_type(curr) < 0)
+ return (-1);
+
+ if (prev->type != curr->type) {
+ if (prev->type < curr->type)
+ return (0);
+ log_debug("%s: unsorted types", __func__);
+ return (-1);
+ }
+
+ if (curr->type == CBOR_TYPE_UINT || curr->type == CBOR_TYPE_NEGINT) {
+ if (cbor_int_get_width(curr) >= cbor_int_get_width(prev) &&
+ cbor_get_int(curr) > cbor_get_int(prev))
+ return (0);
+ } else {
+ curr_len = cbor_string_length(curr);
+ prev_len = cbor_string_length(prev);
+
+ if (curr_len > prev_len || (curr_len == prev_len &&
+ memcmp(cbor_string_handle(prev), cbor_string_handle(curr),
+ curr_len) < 0))
+ return (0);
+ }
+
+ log_debug("%s: invalid cbor", __func__);
+
+ return (-1);
+}
+
+int
+cbor_map_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
+ const cbor_item_t *, void *))
+{
+ struct cbor_pair *v;
+ size_t n;
+
+ if ((v = cbor_map_handle(item)) == NULL) {
+ log_debug("%s: cbor_map_handle", __func__);
+ return (-1);
+ }
+
+ n = cbor_map_size(item);
+
+ for (size_t i = 0; i < n; i++) {
+ if (v[i].key == NULL || v[i].value == NULL) {
+ log_debug("%s: key=%p, value=%p for i=%zu", __func__,
+ (void *)v[i].key, (void *)v[i].value, i);
+ return (-1);
+ }
+ if (i && ctap_check_cbor(v[i - 1].key, v[i].key) < 0) {
+ log_debug("%s: ctap_check_cbor", __func__);
+ return (-1);
+ }
+ if (f(v[i].key, v[i].value, arg) < 0) {
+ log_debug("%s: iterator < 0 on i=%zu", __func__, i);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int
+cbor_array_iter(const cbor_item_t *item, void *arg, int(*f)(const cbor_item_t *,
+ void *))
+{
+ cbor_item_t **v;
+ size_t n;
+
+ if ((v = cbor_array_handle(item)) == NULL) {
+ log_debug("%s: cbor_array_handle", __func__);
+ return (-1);
+ }
+
+ n = cbor_array_size(item);
+
+ for (size_t i = 0; i < n; i++)
+ if (v[i] == NULL || f(v[i], arg) < 0) {
+ log_debug("%s: iterator < 0 on i=%zu,%p", __func__, i,
+ (void *)v[i]);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+parse_cbor_reply(const unsigned char *blob, size_t blob_len, void *arg,
+ int(*parser)(const cbor_item_t *, const cbor_item_t *, void *))
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor;
+ int r;
+
+ if (blob_len < 1) {
+ log_debug("%s: blob_len=%zu", __func__, blob_len);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+
+ if (blob[0] != FIDO_OK) {
+ log_debug("%s: blob[0]=0x%02x", __func__, blob[0]);
+ r = blob[0];
+ goto fail;
+ }
+
+ if ((item = cbor_load(blob + 1, blob_len - 1, &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ r = FIDO_ERR_RX_NOT_CBOR;
+ goto fail;
+ }
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ r = FIDO_ERR_RX_INVALID_CBOR;
+ goto fail;
+ }
+
+ if (cbor_map_iter(item, arg, parser) < 0) {
+ log_debug("%s: cbor_map_iter", __func__);
+ r = FIDO_ERR_RX_INVALID_CBOR;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ return (r);
+}
+
+void
+cbor_vector_free(cbor_item_t **item, size_t len)
+{
+ for (size_t i = 0; i < len; i++)
+ if (item[i] != NULL)
+ cbor_decref(&item[i]);
+}
+
+int
+cbor_bytestring_copy(const cbor_item_t *item, unsigned char **buf, size_t *len)
+{
+ if (*buf != NULL || *len != 0) {
+ log_debug("%s: dup", __func__);
+ return (-1);
+ }
+
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ *len = cbor_bytestring_length(item);
+ if ((*buf = malloc(*len)) == NULL) {
+ *len = 0;
+ return (-1);
+ }
+
+ memcpy(*buf, cbor_bytestring_handle(item), *len);
+
+ return (0);
+}
+
+int
+cbor_string_copy(const cbor_item_t *item, char **str)
+{
+ size_t len;
+
+ if (*str != NULL) {
+ log_debug("%s: dup", __func__);
+ return (-1);
+ }
+
+ if (cbor_isa_string(item) == false ||
+ cbor_string_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ if ((len = cbor_string_length(item)) == SIZE_MAX ||
+ (*str = malloc(len + 1)) == NULL)
+ return (-1);
+
+ memcpy(*str, cbor_string_handle(item), len);
+ (*str)[len] = '\0';
+
+ return (0);
+}
+
+int
+cbor_add_bytestring(cbor_item_t *item, const char *key,
+ const unsigned char *value, size_t value_len)
+{
+ struct cbor_pair pair;
+ int ok = -1;
+
+ memset(&pair, 0, sizeof(pair));
+
+ if ((pair.key = cbor_build_string(key)) == NULL ||
+ (pair.value = cbor_build_bytestring(value, value_len)) == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ goto fail;
+ }
+
+ if (!cbor_map_add(item, pair)) {
+ log_debug("%s: cbor_map_add", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pair.key)
+ cbor_decref(&pair.key);
+ if (pair.value)
+ cbor_decref(&pair.value);
+
+ return (ok);
+}
+
+int
+cbor_add_string(cbor_item_t *item, const char *key, const char *value)
+{
+ struct cbor_pair pair;
+ int ok = -1;
+
+ memset(&pair, 0, sizeof(pair));
+
+ if ((pair.key = cbor_build_string(key)) == NULL ||
+ (pair.value = cbor_build_string(value)) == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ goto fail;
+ }
+
+ if (!cbor_map_add(item, pair)) {
+ log_debug("%s: cbor_map_add", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pair.key)
+ cbor_decref(&pair.key);
+ if (pair.value)
+ cbor_decref(&pair.value);
+
+ return (ok);
+}
+
+int
+cbor_add_bool(cbor_item_t *item, const char *key, fido_opt_t value)
+{
+ struct cbor_pair pair;
+ int ok = -1;
+
+ memset(&pair, 0, sizeof(pair));
+
+ if ((pair.key = cbor_build_string(key)) == NULL ||
+ (pair.value = cbor_build_bool(value == FIDO_OPT_TRUE)) == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ goto fail;
+ }
+
+ if (!cbor_map_add(item, pair)) {
+ log_debug("%s: cbor_map_add", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pair.key)
+ cbor_decref(&pair.key);
+ if (pair.value)
+ cbor_decref(&pair.value);
+
+ return (ok);
+}
+
+static int
+cbor_add_arg(cbor_item_t *item, uint8_t n, cbor_item_t *arg)
+{
+ struct cbor_pair pair;
+ int ok = -1;
+
+ memset(&pair, 0, sizeof(pair));
+
+ if (arg == NULL)
+ return (0); /* empty argument */
+
+ if ((pair.key = cbor_build_uint8(n)) == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ goto fail;
+ }
+
+ pair.value = arg;
+
+ if (!cbor_map_add(item, pair)) {
+ log_debug("%s: cbor_map_add", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pair.key)
+ cbor_decref(&pair.key);
+
+ return (ok);
+}
+
+cbor_item_t *
+cbor_flatten_vector(cbor_item_t *argv[], size_t argc)
+{
+ cbor_item_t *map;
+ uint8_t i;
+
+ if (argc > UINT8_MAX - 1)
+ return (NULL);
+
+ if ((map = cbor_new_definite_map(argc)) == NULL)
+ return (NULL);
+
+ for (i = 0; i < argc; i++)
+ if (cbor_add_arg(map, i + 1, argv[i]) < 0)
+ break;
+
+ if (i != argc) {
+ cbor_decref(&map);
+ map = NULL;
+ }
+
+ return (map);
+}
+
+int
+cbor_build_frame(uint8_t cmd, cbor_item_t *argv[], size_t argc, fido_blob_t *f)
+{
+ cbor_item_t *flat = NULL;
+ unsigned char *cbor = NULL;
+ size_t cbor_len;
+ size_t cbor_alloc_len;
+ int ok = -1;
+
+ if ((flat = cbor_flatten_vector(argv, argc)) == NULL)
+ goto fail;
+
+ cbor_len = cbor_serialize_alloc(flat, &cbor, &cbor_alloc_len);
+ if (cbor_len == 0 || cbor_len == SIZE_MAX) {
+ log_debug("%s: cbor_len=%zu", __func__, cbor_len);
+ goto fail;
+ }
+
+ if ((f->ptr = malloc(cbor_len + 1)) == NULL)
+ goto fail;
+
+ f->len = cbor_len + 1;
+ f->ptr[0] = cmd;
+ memcpy(f->ptr + 1, cbor, f->len - 1);
+
+ ok = 0;
+fail:
+ if (flat != NULL)
+ cbor_decref(&flat);
+
+ free(cbor);
+
+ return (ok);
+}
+
+cbor_item_t *
+encode_rp_entity(const fido_rp_t *rp)
+{
+ cbor_item_t *item = NULL;
+
+ if ((item = cbor_new_definite_map(2)) == NULL)
+ return (NULL);
+
+ if ((rp->id && cbor_add_string(item, "id", rp->id) < 0) ||
+ (rp->name && cbor_add_string(item, "name", rp->name) < 0)) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+
+ return (item);
+}
+
+cbor_item_t *
+encode_user_entity(const fido_user_t *user)
+{
+ cbor_item_t *item = NULL;
+ const fido_blob_t *id = &user->id;
+ const char *display = user->display_name;
+
+ if ((item = cbor_new_definite_map(4)) == NULL)
+ return (NULL);
+
+ if ((id->ptr && cbor_add_bytestring(item, "id", id->ptr, id->len) < 0) ||
+ (user->icon && cbor_add_string(item, "icon", user->icon) < 0) ||
+ (user->name && cbor_add_string(item, "name", user->name) < 0) ||
+ (display && cbor_add_string(item, "displayName", display) < 0)) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+
+ return (item);
+}
+
+cbor_item_t *
+encode_pubkey_param(int cose_alg)
+{
+ cbor_item_t *item = NULL;
+ cbor_item_t *body = NULL;
+ struct cbor_pair alg;
+ int ok = -1;
+
+ memset(&alg, 0, sizeof(alg));
+
+ if ((item = cbor_new_definite_array(1)) == NULL ||
+ (body = cbor_new_definite_map(2)) == NULL ||
+ cose_alg > -1 || cose_alg < INT16_MIN)
+ goto fail;
+
+ alg.key = cbor_build_string("alg");
+
+ if (-cose_alg - 1 > UINT8_MAX)
+ alg.value = cbor_build_negint16((uint16_t)(-cose_alg - 1));
+ else
+ alg.value = cbor_build_negint8((uint8_t)(-cose_alg - 1));
+
+ if (alg.key == NULL || alg.value == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ goto fail;
+ }
+
+ if (cbor_map_add(body, alg) == false ||
+ cbor_add_string(body, "type", "public-key") < 0 ||
+ cbor_array_push(item, body) == false)
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ if (item != NULL) {
+ cbor_decref(&item);
+ item = NULL;
+ }
+ }
+
+ if (body != NULL)
+ cbor_decref(&body);
+ if (alg.key != NULL)
+ cbor_decref(&alg.key);
+ if (alg.value != NULL)
+ cbor_decref(&alg.value);
+
+ return (item);
+}
+
+cbor_item_t *
+encode_pubkey(const fido_blob_t *pubkey)
+{
+ cbor_item_t *cbor_key = NULL;
+
+ if ((cbor_key = cbor_new_definite_map(2)) == NULL ||
+ cbor_add_bytestring(cbor_key, "id", pubkey->ptr, pubkey->len) < 0 ||
+ cbor_add_string(cbor_key, "type", "public-key") < 0) {
+ if (cbor_key)
+ cbor_decref(&cbor_key);
+ return (NULL);
+ }
+
+ return (cbor_key);
+}
+
+cbor_item_t *
+encode_pubkey_list(const fido_blob_array_t *list)
+{
+ cbor_item_t *array = NULL;
+ cbor_item_t *key = NULL;
+
+ if ((array = cbor_new_definite_array(list->len)) == NULL)
+ goto fail;
+
+ for (size_t i = 0; i < list->len; i++) {
+ if ((key = encode_pubkey(&list->ptr[i])) == NULL ||
+ cbor_array_push(array, key) == false)
+ goto fail;
+ cbor_decref(&key);
+ }
+
+ return (array);
+fail:
+ if (key != NULL)
+ cbor_decref(&key);
+ if (array != NULL)
+ cbor_decref(&array);
+
+ return (NULL);
+}
+
+cbor_item_t *
+encode_extensions(int ext)
+{
+ cbor_item_t *item = NULL;
+
+ if (ext == 0 || ext != FIDO_EXT_HMAC_SECRET)
+ return (NULL);
+
+ if ((item = cbor_new_definite_map(1)) == NULL)
+ return (NULL);
+
+ if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+
+ return (item);
+}
+
+cbor_item_t *
+encode_options(fido_opt_t rk, fido_opt_t uv)
+{
+ cbor_item_t *item = NULL;
+
+ if ((item = cbor_new_definite_map(2)) == NULL)
+ return (NULL);
+
+ if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
+ (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+
+ return (item);
+}
+
+cbor_item_t *
+encode_assert_options(fido_opt_t up, fido_opt_t uv)
+{
+ cbor_item_t *item = NULL;
+
+ if ((item = cbor_new_definite_map(2)) == NULL)
+ return (NULL);
+
+ if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
+ (uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+
+ return (item);
+}
+
+cbor_item_t *
+encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data)
+{
+ const EVP_MD *md = NULL;
+ unsigned char dgst[SHA256_DIGEST_LENGTH];
+ unsigned int dgst_len;
+
+ if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr,
+ (int)hmac_key->len, data->ptr, (int)data->len, dgst,
+ &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
+ return (NULL);
+
+ return (cbor_build_bytestring(dgst, 16));
+}
+
+cbor_item_t *
+encode_pin_opt(void)
+{
+ return (cbor_build_uint8(1));
+}
+
+cbor_item_t *
+encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin)
+{
+ fido_blob_t pe;
+ cbor_item_t *item = NULL;
+
+ if (aes256_cbc_enc(key, pin, &pe) < 0)
+ return (NULL);
+
+ item = cbor_build_bytestring(pe.ptr, pe.len);
+ free(pe.ptr);
+
+ return (item);
+}
+
+static int
+sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest)
+{
+ if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
+ return (-1);
+
+ digest->len = SHA256_DIGEST_LENGTH;
+
+ if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
+ free(digest->ptr);
+ digest->ptr = NULL;
+ digest->len = 0;
+ return (-1);
+ }
+
+ return (0);
+}
+
+cbor_item_t *
+encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
+ const fido_blob_t *pin)
+{
+ unsigned char dgst[SHA256_DIGEST_LENGTH];
+ unsigned int dgst_len;
+ cbor_item_t *item = NULL;
+ const EVP_MD *md = NULL;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ HMAC_CTX ctx;
+#else
+ HMAC_CTX *ctx = NULL;
+#endif
+ fido_blob_t *npe = NULL; /* new pin, encrypted */
+ fido_blob_t *ph = NULL; /* pin hash */
+ fido_blob_t *phe = NULL; /* pin hash, encrypted */
+ int ok = -1;
+
+ if ((npe = fido_blob_new()) == NULL ||
+ (ph = fido_blob_new()) == NULL ||
+ (phe = fido_blob_new()) == NULL)
+ goto fail;
+
+ if (aes256_cbc_enc(key, new_pin, npe) < 0) {
+ log_debug("%s: aes256_cbc_enc 1", __func__);
+ goto fail;
+ }
+
+ if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
+ log_debug("%s: sha256", __func__);
+ goto fail;
+ }
+
+ ph->len = 16; /* first 16 bytes */
+
+ if (aes256_cbc_enc(key, ph, phe) < 0) {
+ log_debug("%s: aes256_cbc_enc 2", __func__);
+ goto fail;
+ }
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ HMAC_CTX_init(&ctx);
+
+ if ((md = EVP_sha256()) == NULL ||
+ HMAC_Init_ex(&ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
+ HMAC_Update(&ctx, npe->ptr, (int)npe->len) == 0 ||
+ HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 ||
+ HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
+ log_debug("%s: HMAC", __func__);
+ goto fail;
+ }
+#else
+ if ((ctx = HMAC_CTX_new()) == NULL ||
+ (md = EVP_sha256()) == NULL ||
+ HMAC_Init_ex(ctx, key->ptr, (int)key->len, md, NULL) == 0 ||
+ HMAC_Update(ctx, npe->ptr, (int)npe->len) == 0 ||
+ HMAC_Update(ctx, phe->ptr, (int)phe->len) == 0 ||
+ HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
+ log_debug("%s: HMAC", __func__);
+ goto fail;
+ }
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+ if ((item = cbor_build_bytestring(dgst, 16)) == NULL) {
+ log_debug("%s: cbor_build_bytestring", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ fido_blob_free(&npe);
+ fido_blob_free(&ph);
+ fido_blob_free(&phe);
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (ctx != NULL)
+ HMAC_CTX_free(ctx);
+#endif
+
+ if (ok < 0) {
+ if (item != NULL) {
+ cbor_decref(&item);
+ item = NULL;
+ }
+ }
+
+ return (item);
+}
+
+cbor_item_t *
+encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin)
+{
+ const EVP_MD *md = NULL;
+ unsigned char dgst[SHA256_DIGEST_LENGTH];
+ unsigned int dgst_len;
+ cbor_item_t *item = NULL;
+ fido_blob_t *pe = NULL;
+
+ if ((pe = fido_blob_new()) == NULL)
+ goto fail;
+
+ if (aes256_cbc_enc(key, pin, pe) < 0) {
+ log_debug("%s: aes256_cbc_enc", __func__);
+ goto fail;
+ }
+
+ if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr,
+ (int)key->len, pe->ptr, (int)pe->len, dgst, &dgst_len) == NULL ||
+ dgst_len != SHA256_DIGEST_LENGTH) {
+ log_debug("%s: HMAC", __func__);
+ goto fail;
+ }
+
+ item = cbor_build_bytestring(dgst, 16);
+fail:
+ fido_blob_free(&pe);
+
+ return (item);
+}
+
+cbor_item_t *
+encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin)
+{
+ cbor_item_t *item = NULL;
+ fido_blob_t *ph = NULL;
+ fido_blob_t *phe = NULL;
+
+ if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL)
+ goto fail;
+
+ if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
+ log_debug("%s: SHA256", __func__);
+ goto fail;
+ }
+
+ ph->len = 16; /* first 16 bytes */
+
+ if (aes256_cbc_enc(shared, ph, phe) < 0) {
+ log_debug("%s: aes256_cbc_enc", __func__);
+ goto fail;
+ }
+
+ item = cbor_build_bytestring(phe->ptr, phe->len);
+fail:
+ fido_blob_free(&ph);
+ fido_blob_free(&phe);
+
+ return (item);
+}
+
+cbor_item_t *
+encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
+ const fido_blob_t *hmac_salt)
+{
+ cbor_item_t *item = NULL;
+ cbor_item_t *param = NULL;
+ cbor_item_t *argv[3];
+ struct cbor_pair pair;
+
+ memset(argv, 0, sizeof(argv));
+ memset(&pair, 0, sizeof(pair));
+
+ if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) {
+ log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p", __func__,
+ (const void *)ecdh, (const void *)pk,
+ (const void *)hmac_salt->ptr);
+ goto fail;
+ }
+
+ if (hmac_salt->len != 32 && hmac_salt->len != 64) {
+ log_debug("%s: hmac_salt->len=%zu", __func__, hmac_salt->len);
+ goto fail;
+ }
+
+ /* XXX not pin, but salt */
+ if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
+ (argv[1] = encode_pin_enc(ecdh, hmac_salt)) == NULL ||
+ (argv[2] = encode_set_pin_auth(ecdh, hmac_salt)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ if ((param = cbor_flatten_vector(argv, 3)) == NULL) {
+ log_debug("%s: cbor_flatten_vector", __func__);
+ goto fail;
+ }
+
+ if ((item = cbor_new_definite_map(1)) == NULL) {
+ log_debug("%s: cbor_new_definite_map", __func__);
+ goto fail;
+ }
+
+ if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
+ log_debug("%s: cbor_build", __func__);
+ goto fail;
+ }
+
+ pair.value = param;
+
+ if (!cbor_map_add(item, pair)) {
+ log_debug("%s: cbor_map_add", __func__);
+ cbor_decref(&item);
+ item = NULL;
+ goto fail;
+ }
+
+fail:
+ for (size_t i = 0; i < 3; i++)
+ if (argv[i] != NULL)
+ cbor_decref(&argv[i]);
+
+ if (param != NULL)
+ cbor_decref(&param);
+ if (pair.key != NULL)
+ cbor_decref(&pair.key);
+
+ return (item);
+}
+
+int
+decode_fmt(const cbor_item_t *item, char **fmt)
+{
+ char *type = NULL;
+
+ if (cbor_string_copy(item, &type) < 0) {
+ log_debug("%s: cbor_string_copy", __func__);
+ return (-1);
+ }
+
+ if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) {
+ log_debug("%s: type=%s", __func__, type);
+ free(type);
+ return (-1);
+ }
+
+ *fmt = type;
+
+ return (0);
+}
+
+struct cose_key {
+ int kty;
+ int alg;
+ int crv;
+};
+
+static int
+find_cose_alg(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ struct cose_key *cose_key = arg;
+
+ if (cbor_isa_uint(key) == true &&
+ cbor_int_get_width(key) == CBOR_INT_8) {
+ switch (cbor_get_uint8(key)) {
+ case 1:
+ if (cbor_isa_uint(val) == false ||
+ cbor_get_int(val) > INT_MAX || cose_key->kty != 0) {
+ log_debug("%s: kty", __func__);
+ return (-1);
+ }
+
+ cose_key->kty = (int)cbor_get_int(val);
+
+ break;
+ case 3:
+ if (cbor_isa_negint(val) == false ||
+ cbor_get_int(val) > INT_MAX || cose_key->alg != 0) {
+ log_debug("%s: alg", __func__);
+ return (-1);
+ }
+
+ cose_key->alg = -(int)cbor_get_int(val) - 1;
+
+ break;
+ }
+ } else if (cbor_isa_negint(key) == true &&
+ cbor_int_get_width(key) == CBOR_INT_8) {
+ if (cbor_get_uint8(key) == 0) {
+ /* get crv if not rsa, otherwise ignore */
+ if (cbor_isa_uint(val) == true &&
+ cbor_get_int(val) <= INT_MAX &&
+ cose_key->crv == 0)
+ cose_key->crv = (int)cbor_get_int(val);
+ }
+ }
+
+ return (0);
+}
+
+static int
+get_cose_alg(const cbor_item_t *item, int *cose_alg)
+{
+ struct cose_key cose_key;
+
+ memset(&cose_key, 0, sizeof(cose_key));
+
+ *cose_alg = 0;
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, &cose_key, find_cose_alg) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ switch (cose_key.alg) {
+ case COSE_ES256:
+ if (cose_key.kty != COSE_KTY_EC2 ||
+ cose_key.crv != COSE_P256) {
+ log_debug("%s: invalid kty/crv", __func__);
+ return (-1);
+ }
+
+ break;
+ case COSE_EDDSA:
+ if (cose_key.kty != COSE_KTY_OKP ||
+ cose_key.crv != COSE_ED25519) {
+ log_debug("%s: invalid kty/crv", __func__);
+ return (-1);
+ }
+
+ break;
+ case COSE_RS256:
+ if (cose_key.kty != COSE_KTY_RSA) {
+ log_debug("%s: invalid kty/crv", __func__);
+ return (-1);
+ }
+
+ break;
+ default:
+ log_debug("%s: unknown alg %d", __func__, cose_key.alg);
+
+ return (-1);
+ }
+
+ *cose_alg = cose_key.alg;
+
+ return (0);
+}
+
+int
+decode_pubkey(const cbor_item_t *item, int *type, void *key)
+{
+ if (get_cose_alg(item, type) < 0) {
+ log_debug("%s: get_cose_alg", __func__);
+ return (-1);
+ }
+
+ switch (*type) {
+ case COSE_ES256:
+ if (es256_pk_decode(item, key) < 0) {
+ log_debug("%s: es256_pk_decode", __func__);
+ return (-1);
+ }
+ break;
+ case COSE_RS256:
+ if (rs256_pk_decode(item, key) < 0) {
+ log_debug("%s: rs256_pk_decode", __func__);
+ return (-1);
+ }
+ break;
+ case COSE_EDDSA:
+ if (eddsa_pk_decode(item, key) < 0) {
+ log_debug("%s: eddsa_pk_decode", __func__);
+ return (-1);
+ }
+ break;
+ default:
+ log_debug("%s: invalid cose_alg %d", __func__, *type);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
+ fido_attcred_t *attcred)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor;
+ uint16_t id_len;
+ int ok = -1;
+
+ log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, *len);
+
+ if (buf_read(buf, len, &attcred->aaguid, sizeof(attcred->aaguid)) < 0) {
+ log_debug("%s: buf_read aaguid", __func__);
+ return (-1);
+ }
+
+ if (buf_read(buf, len, &id_len, sizeof(id_len)) < 0) {
+ log_debug("%s: buf_read id_len", __func__);
+ return (-1);
+ }
+
+ attcred->id.len = (size_t)be16toh(id_len);
+ if ((attcred->id.ptr = malloc(attcred->id.len)) == NULL)
+ return (-1);
+
+ log_debug("%s: attcred->id.len=%zu", __func__, attcred->id.len);
+
+ if (buf_read(buf, len, attcred->id.ptr, attcred->id.len) < 0) {
+ log_debug("%s: buf_read id", __func__);
+ return (-1);
+ }
+
+ if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ log_xxd(*buf, *len);
+ goto fail;
+ }
+
+ if (decode_pubkey(item, &attcred->type, &attcred->pubkey) < 0) {
+ log_debug("%s: decode_pubkey", __func__);
+ goto fail;
+ }
+
+ if (attcred->type != cose_alg) {
+ log_debug("%s: cose_alg mismatch (%d != %d)", __func__,
+ attcred->type, cose_alg);
+ goto fail;
+ }
+
+ *buf += cbor.read;
+ *len -= cbor.read;
+
+ ok = 0;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ return (ok);
+}
+
+static int
+decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ int *authdata_ext = arg;
+ char *type = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
+ log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ if (cbor_isa_float_ctrl(val) == false ||
+ cbor_float_get_width(val) != CBOR_FLOAT_0 ||
+ cbor_is_bool(val) == false || *authdata_ext != 0) {
+ log_debug("%s: cbor type", __func__);
+ goto out;
+ }
+
+ if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
+ *authdata_ext |= FIDO_EXT_HMAC_SECRET;
+
+ ok = 0;
+out:
+ free(type);
+
+ return (ok);
+}
+
+static int
+decode_extensions(const unsigned char **buf, size_t *len, int *authdata_ext)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor;
+ int ok = -1;
+
+ log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, *len);
+
+ *authdata_ext = 0;
+
+ if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ log_xxd(*buf, *len);
+ goto fail;
+ }
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_size(item) != 1 ||
+ cbor_map_iter(item, authdata_ext, decode_extension) < 0) {
+ log_debug("%s: cbor type", __func__);
+ goto fail;
+ }
+
+ *buf += cbor.read;
+ *len -= cbor.read;
+
+ ok = 0;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ return (ok);
+}
+
+static int
+decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_blob_t *out = arg;
+ char *type = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
+ log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ ok = cbor_bytestring_copy(val, &out->ptr, &out->len);
+out:
+ free(type);
+
+ return (ok);
+}
+
+static int
+decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor;
+ int ok = -1;
+
+ log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf, *len);
+
+ if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ log_xxd(*buf, *len);
+ goto fail;
+ }
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_size(item) != 1 ||
+ cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) {
+ log_debug("%s: cbor type", __func__);
+ goto fail;
+ }
+
+ *buf += cbor.read;
+ *len -= cbor.read;
+
+ ok = 0;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ return (ok);
+}
+
+int
+decode_cred_authdata(const cbor_item_t *item, int cose_alg,
+ fido_blob_t *authdata_cbor, fido_authdata_t *authdata,
+ fido_attcred_t *attcred, int *authdata_ext)
+{
+ const unsigned char *buf = NULL;
+ size_t len;
+ size_t alloc_len;
+
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ if (authdata_cbor->ptr != NULL ||
+ (authdata_cbor->len = cbor_serialize_alloc(item,
+ &authdata_cbor->ptr, &alloc_len)) == 0) {
+ log_debug("%s: cbor_serialize_alloc", __func__);
+ return (-1);
+ }
+
+ buf = cbor_bytestring_handle(item);
+ len = cbor_bytestring_length(item);
+
+ log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
+
+ if (buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
+ log_debug("%s: buf_read", __func__);
+ return (-1);
+ }
+
+ authdata->sigcount = be32toh(authdata->sigcount);
+
+ if (attcred != NULL) {
+ if ((authdata->flags & CTAP_AUTHDATA_ATT_CRED) == 0 ||
+ decode_attcred(&buf, &len, cose_alg, attcred) < 0)
+ return (-1);
+ }
+
+ if (authdata_ext != NULL) {
+ if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
+ decode_extensions(&buf, &len, authdata_ext) < 0)
+ return (-1);
+ }
+
+ /* XXX we should probably ensure that len == 0 at this point */
+
+ return (FIDO_OK);
+}
+
+int
+decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
+ fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc)
+{
+ const unsigned char *buf = NULL;
+ size_t len;
+ size_t alloc_len;
+
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ if (authdata_cbor->ptr != NULL ||
+ (authdata_cbor->len = cbor_serialize_alloc(item,
+ &authdata_cbor->ptr, &alloc_len)) == 0) {
+ log_debug("%s: cbor_serialize_alloc", __func__);
+ return (-1);
+ }
+
+ buf = cbor_bytestring_handle(item);
+ len = cbor_bytestring_length(item);
+
+ log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
+
+ if (buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
+ log_debug("%s: buf_read", __func__);
+ return (-1);
+ }
+
+ authdata->sigcount = be32toh(authdata->sigcount);
+
+ *authdata_ext = 0;
+ if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
+ /* XXX semantic leap: extensions -> hmac_secret */
+ if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) {
+ log_debug("%s: decode_hmac_secret", __func__);
+ return (-1);
+ }
+ *authdata_ext = FIDO_EXT_HMAC_SECRET;
+ }
+
+ /* XXX we should probably ensure that len == 0 at this point */
+
+ return (FIDO_OK);
+}
+
+static int
+decode_x5c(const cbor_item_t *item, void *arg)
+{
+ fido_blob_t *x5c = arg;
+
+ if (x5c->len)
+ return (0); /* ignore */
+
+ return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len));
+}
+
+static int
+decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_attstmt_t *attstmt = arg;
+ char *name = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &name) < 0) {
+ log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ if (!strcmp(name, "alg")) {
+ if (cbor_isa_negint(val) == false ||
+ cbor_int_get_width(val) != CBOR_INT_8 ||
+ cbor_get_uint8(val) != -COSE_ES256 - 1) {
+ log_debug("%s: alg", __func__);
+ goto out;
+ }
+ } else if (!strcmp(name, "sig")) {
+ if (cbor_bytestring_copy(val, &attstmt->sig.ptr,
+ &attstmt->sig.len) < 0) {
+ log_debug("%s: sig", __func__);
+ goto out;
+ }
+ } else if (!strcmp(name, "x5c")) {
+ if (cbor_isa_array(val) == false ||
+ cbor_array_is_definite(val) == false ||
+ cbor_array_iter(val, &attstmt->x5c, decode_x5c) < 0) {
+ log_debug("%s: x5c", __func__);
+ goto out;
+ }
+ }
+
+ ok = 0;
+out:
+ free(name);
+
+ return (ok);
+}
+
+int
+decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+decode_uint64(const cbor_item_t *item, uint64_t *n)
+{
+ if (cbor_isa_uint(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ *n = cbor_get_int(item);
+
+ return (0);
+}
+
+static int
+decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_blob_t *id = arg;
+ char *name = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &name) < 0) {
+ log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ if (!strcmp(name, "id"))
+ if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) {
+ log_debug("%s: cbor_bytestring_copy", __func__);
+ goto out;
+ }
+
+ ok = 0;
+out:
+ free(name);
+
+ return (ok);
+}
+
+int
+decode_cred_id(const cbor_item_t *item, fido_blob_t *id)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, id, decode_cred_id_entry) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_user_t *user = arg;
+ char *name = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &name) < 0) {
+ log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ if (!strcmp(name, "icon")) {
+ if (cbor_string_copy(val, &user->icon) < 0) {
+ log_debug("%s: icon", __func__);
+ goto out;
+ }
+ } else if (!strcmp(name, "name")) {
+ if (cbor_string_copy(val, &user->name) < 0) {
+ log_debug("%s: name", __func__);
+ goto out;
+ }
+ } else if (!strcmp(name, "displayName")) {
+ if (cbor_string_copy(val, &user->display_name) < 0) {
+ log_debug("%s: display_name", __func__);
+ goto out;
+ }
+ } else if (!strcmp(name, "id")) {
+ if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) {
+ log_debug("%s: id", __func__);
+ goto out;
+ }
+ }
+
+ ok = 0;
+out:
+ free(name);
+
+ return (ok);
+}
+
+int
+decode_user(const cbor_item_t *item, fido_user_t *user)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, user, decode_user_entry) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+decode_rp_entity_entry(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_rp_t *rp = arg;
+ char *name = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &name) < 0) {
+ log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ if (!strcmp(name, "id")) {
+ if (cbor_string_copy(val, &rp->id) < 0) {
+ log_debug("%s: id", __func__);
+ goto out;
+ }
+ } else if (!strcmp(name, "name")) {
+ if (cbor_string_copy(val, &rp->name) < 0) {
+ log_debug("%s: name", __func__);
+ goto out;
+ }
+ }
+
+ ok = 0;
+out:
+ free(name);
+
+ return (ok);
+}
+
+int
+decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, rp, decode_rp_entity_entry) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/lib/libfido2/src/cred.c b/lib/libfido2/src/cred.c
new file mode 100644
index 00000000000..45423677f01
--- /dev/null
+++ b/lib/libfido2/src/cred.c
@@ -0,0 +1,1031 @@
+/*
+ * Copyright (c) 2018 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 <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <openssl/x509.h>
+
+#include <string.h>
+#include "fido.h"
+#include "fido/es256.h"
+
+static int
+parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_cred_t *cred = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* fmt */
+ return (decode_fmt(val, &cred->fmt));
+ case 2: /* authdata */
+ return (decode_cred_authdata(val, cred->type,
+ &cred->authdata_cbor, &cred->authdata, &cred->attcred,
+ &cred->authdata_ext));
+ case 3: /* attestation statement */
+ return (decode_attstmt(val, &cred->attstmt));
+ default: /* ignore */
+ log_debug("%s: cbor type", __func__);
+ return (0);
+ }
+}
+
+static int
+fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
+{
+ fido_blob_t f;
+ fido_blob_t *ecdh = NULL;
+ es256_pk_t *pk = NULL;
+ cbor_item_t *argv[9];
+ int r;
+
+ memset(&f, 0, sizeof(f));
+ memset(argv, 0, sizeof(argv));
+
+ if (cred->cdh.ptr == NULL || cred->type == 0) {
+ log_debug("%s: cdh=%p, type=%d", __func__,
+ (void *)cred->cdh.ptr, cred->type);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if ((argv[0] = fido_blob_encode(&cred->cdh)) == NULL ||
+ (argv[1] = encode_rp_entity(&cred->rp)) == NULL ||
+ (argv[2] = encode_user_entity(&cred->user)) == NULL ||
+ (argv[3] = encode_pubkey_param(cred->type)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* excluded credentials */
+ if (cred->excl.len)
+ if ((argv[4] = encode_pubkey_list(&cred->excl)) == NULL) {
+ log_debug("%s: encode_pubkey_list", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* extensions */
+ if (cred->ext)
+ if ((argv[5] = encode_extensions(cred->ext)) == NULL) {
+ log_debug("%s: encode_extensions", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* options */
+ if (cred->rk != FIDO_OPT_OMIT || cred->uv != FIDO_OPT_OMIT)
+ if ((argv[6] = encode_options(cred->rk, cred->uv)) == NULL) {
+ log_debug("%s: encode_options", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ /* pin authentication */
+ if (pin) {
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+ if ((r = add_cbor_pin_params(dev, &cred->cdh, pk, ecdh, pin,
+ &argv[7], &argv[8])) != FIDO_OK) {
+ log_debug("%s: add_cbor_pin_params", __func__);
+ goto fail;
+ }
+ }
+
+ /* framing and transmission */
+ if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, 9, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ es256_pk_free(&pk);
+ fido_blob_free(&ecdh);
+ cbor_vector_free(argv, nitems(argv));
+ free(f.ptr);
+
+ return (r);
+}
+
+static int
+fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ fido_cred_reset_rx(cred);
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, cred,
+ parse_makecred_reply)) != FIDO_OK) {
+ log_debug("%s: parse_makecred_reply", __func__);
+ return (r);
+ }
+
+ if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
+ fido_blob_is_empty(&cred->attcred.id) ||
+ fido_blob_is_empty(&cred->attstmt.sig)) {
+ fido_cred_reset_rx(cred);
+ return (FIDO_ERR_INVALID_CBOR);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK ||
+ (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
+{
+ if (fido_dev_is_fido2(dev) == false) {
+ if (pin != NULL || cred->rk == FIDO_OPT_TRUE || cred->ext != 0)
+ return (FIDO_ERR_UNSUPPORTED_OPTION);
+ return (u2f_register(dev, cred, -1));
+ }
+
+ return (fido_dev_make_cred_wait(dev, cred, pin, -1));
+}
+
+static int
+check_extensions(int authdata_ext, int ext)
+{
+ if (authdata_ext != ext) {
+ log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
+ authdata_ext, ext);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+check_rp_id(const char *id, const unsigned char *obtained_hash)
+{
+ unsigned char expected_hash[SHA256_DIGEST_LENGTH];
+
+ explicit_bzero(expected_hash, sizeof(expected_hash));
+
+ if (SHA256((const unsigned char *)id, strlen(id),
+ expected_hash) != expected_hash) {
+ log_debug("%s: sha256", __func__);
+ return (-1);
+ }
+
+ return (timingsafe_bcmp(expected_hash, obtained_hash,
+ SHA256_DIGEST_LENGTH));
+}
+
+static int
+get_signed_hash_packed(fido_blob_t *dgst, const fido_blob_t *clientdata,
+ const fido_blob_t *authdata_cbor)
+{
+ cbor_item_t *item = NULL;
+ unsigned char *authdata_ptr = NULL;
+ size_t authdata_len;
+ struct cbor_load_result cbor;
+ SHA256_CTX ctx;
+ int ok = -1;
+
+ if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
+ &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ goto fail;
+ }
+
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ goto fail;
+ }
+
+ authdata_ptr = cbor_bytestring_handle(item);
+ authdata_len = cbor_bytestring_length(item);
+
+ if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
+ SHA256_Update(&ctx, authdata_ptr, authdata_len) == 0 ||
+ SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
+ SHA256_Final(dgst->ptr, &ctx) == 0) {
+ log_debug("%s: sha256", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ return (ok);
+}
+
+static int
+get_signed_hash_u2f(fido_blob_t *dgst, const unsigned char *rp_id,
+ size_t rp_id_len, const fido_blob_t *clientdata, const fido_blob_t *id,
+ const es256_pk_t *pk)
+{
+ const uint8_t zero = 0;
+ const uint8_t four = 4; /* uncompressed point */
+ SHA256_CTX ctx;
+
+ if (dgst->len != SHA256_DIGEST_LENGTH || SHA256_Init(&ctx) == 0 ||
+ SHA256_Update(&ctx, &zero, sizeof(zero)) == 0 ||
+ SHA256_Update(&ctx, rp_id, rp_id_len) == 0 ||
+ SHA256_Update(&ctx, clientdata->ptr, clientdata->len) == 0 ||
+ SHA256_Update(&ctx, id->ptr, id->len) == 0 ||
+ SHA256_Update(&ctx, &four, sizeof(four)) == 0 ||
+ SHA256_Update(&ctx, pk->x, sizeof(pk->x)) == 0 ||
+ SHA256_Update(&ctx, pk->y, sizeof(pk->y)) == 0 ||
+ SHA256_Final(dgst->ptr, &ctx) == 0) {
+ log_debug("%s: sha256", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
+ const fido_blob_t *sig)
+{
+ BIO *rawcert = NULL;
+ X509 *cert = NULL;
+ EVP_PKEY *pkey = NULL;
+ EC_KEY *ec;
+ int ok = -1;
+
+ /* openssl needs ints */
+ if (dgst->len > INT_MAX || x5c->len > INT_MAX || sig->len > INT_MAX) {
+ log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
+ __func__, dgst->len, x5c->len, sig->len);
+ return (-1);
+ }
+
+ /* fetch key from x509 */
+ if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
+ (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
+ (pkey = X509_get_pubkey(cert)) == NULL ||
+ (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
+ log_debug("%s: x509 key", __func__);
+ goto fail;
+ }
+
+ if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
+ (int)sig->len, ec) != 1) {
+ log_debug("%s: ECDSA_verify", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (rawcert != NULL)
+ BIO_free(rawcert);
+ if (cert != NULL)
+ X509_free(cert);
+ if (pkey != NULL)
+ EVP_PKEY_free(pkey);
+
+ return (ok);
+}
+
+int
+fido_cred_verify(const fido_cred_t *cred)
+{
+ unsigned char buf[SHA256_DIGEST_LENGTH];
+ fido_blob_t dgst;
+ int r;
+
+ dgst.ptr = buf;
+ dgst.len = sizeof(buf);
+
+ /* do we have everything we need? */
+ if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
+ cred->attstmt.x5c.ptr == NULL || cred->attstmt.sig.ptr == NULL ||
+ cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
+ cred->rp.id == NULL) {
+ log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, fmt=%p "
+ "id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
+ (void *)cred->authdata_cbor.ptr,
+ (void *)cred->attstmt.x5c.ptr,
+ (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
+ (void *)cred->attcred.id.ptr, cred->rp.id);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ if (check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
+ log_debug("%s: check_rp_id", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (check_flags(cred->authdata.flags, FIDO_OPT_TRUE, cred->uv) < 0) {
+ log_debug("%s: check_flags", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (check_extensions(cred->authdata_ext, cred->ext) < 0) {
+ log_debug("%s: check_extensions", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (!strcmp(cred->fmt, "packed")) {
+ if (get_signed_hash_packed(&dgst, &cred->cdh,
+ &cred->authdata_cbor) < 0) {
+ log_debug("%s: get_signed_hash_packed", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto out;
+ }
+ } else {
+ if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
+ sizeof(cred->authdata.rp_id_hash), &cred->cdh,
+ &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
+ log_debug("%s: get_signed_hash_u2f", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto out;
+ }
+ }
+
+ if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
+ log_debug("%s: verify_sig", __func__);
+ r = FIDO_ERR_INVALID_SIG;
+ goto out;
+ }
+
+ r = FIDO_OK;
+out:
+ explicit_bzero(buf, sizeof(buf));
+
+ return (r);
+}
+
+int
+fido_cred_verify_self(const fido_cred_t *cred)
+{
+ unsigned char buf[SHA256_DIGEST_LENGTH];
+ fido_blob_t dgst;
+ int ok = -1;
+ int r;
+
+ dgst.ptr = buf;
+ dgst.len = sizeof(buf);
+
+ /* do we have everything we need? */
+ if (cred->cdh.ptr == NULL || cred->authdata_cbor.ptr == NULL ||
+ cred->attstmt.x5c.ptr != NULL || cred->attstmt.sig.ptr == NULL ||
+ cred->fmt == NULL || cred->attcred.id.ptr == NULL ||
+ cred->rp.id == NULL) {
+ log_debug("%s: cdh=%p, authdata=%p, x5c=%p, sig=%p, fmt=%p "
+ "id=%p, rp.id=%s", __func__, (void *)cred->cdh.ptr,
+ (void *)cred->authdata_cbor.ptr,
+ (void *)cred->attstmt.x5c.ptr,
+ (void *)cred->attstmt.sig.ptr, (void *)cred->fmt,
+ (void *)cred->attcred.id.ptr, cred->rp.id);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ if (check_rp_id(cred->rp.id, cred->authdata.rp_id_hash) != 0) {
+ log_debug("%s: check_rp_id", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (check_flags(cred->authdata.flags, FIDO_OPT_TRUE, cred->uv) < 0) {
+ log_debug("%s: check_flags", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (check_extensions(cred->authdata_ext, cred->ext) < 0) {
+ log_debug("%s: check_extensions", __func__);
+ r = FIDO_ERR_INVALID_PARAM;
+ goto out;
+ }
+
+ if (!strcmp(cred->fmt, "packed")) {
+ if (get_signed_hash_packed(&dgst, &cred->cdh,
+ &cred->authdata_cbor) < 0) {
+ log_debug("%s: get_signed_hash_packed", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto out;
+ }
+ } else {
+ if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
+ sizeof(cred->authdata.rp_id_hash), &cred->cdh,
+ &cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
+ log_debug("%s: get_signed_hash_u2f", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto out;
+ }
+ }
+
+ switch (cred->attcred.type) {
+ case COSE_ES256:
+ ok = verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
+ &cred->attstmt.sig);
+ break;
+ case COSE_RS256:
+ ok = verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
+ &cred->attstmt.sig);
+ break;
+ case COSE_EDDSA:
+ ok = verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
+ &cred->attstmt.sig);
+ break;
+ default:
+ log_debug("%s: unsupported cose_alg %d", __func__,
+ cred->attcred.type);
+ r = FIDO_ERR_UNSUPPORTED_OPTION;
+ goto out;
+ }
+
+ if (ok < 0)
+ r = FIDO_ERR_INVALID_SIG;
+ else
+ r = FIDO_OK;
+
+out:
+ explicit_bzero(buf, sizeof(buf));
+
+ return (r);
+}
+
+fido_cred_t *
+fido_cred_new(void)
+{
+ return (calloc(1, sizeof(fido_cred_t)));
+}
+
+static void
+fido_cred_clean_authdata(fido_cred_t *cred)
+{
+ free(cred->authdata_cbor.ptr);
+ free(cred->attcred.id.ptr);
+
+ memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
+ memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor));
+ memset(&cred->authdata, 0, sizeof(cred->authdata));
+ memset(&cred->attcred, 0, sizeof(cred->attcred));
+}
+
+void
+fido_cred_reset_tx(fido_cred_t *cred)
+{
+ free(cred->cdh.ptr);
+ free(cred->rp.id);
+ free(cred->rp.name);
+ free(cred->user.id.ptr);
+ free(cred->user.icon);
+ free(cred->user.name);
+ free(cred->user.display_name);
+ free_blob_array(&cred->excl);
+
+ memset(&cred->cdh, 0, sizeof(cred->cdh));
+ memset(&cred->rp, 0, sizeof(cred->rp));
+ memset(&cred->user, 0, sizeof(cred->user));
+ memset(&cred->excl, 0, sizeof(cred->excl));
+
+ cred->type = 0;
+ cred->ext = 0;
+ cred->rk = FIDO_OPT_OMIT;
+ cred->uv = FIDO_OPT_OMIT;
+}
+
+static void
+fido_cred_clean_x509(fido_cred_t *cred)
+{
+ free(cred->attstmt.x5c.ptr);
+ cred->attstmt.x5c.ptr = NULL;
+ cred->attstmt.x5c.len = 0;
+}
+
+static void
+fido_cred_clean_sig(fido_cred_t *cred)
+{
+ free(cred->attstmt.sig.ptr);
+ cred->attstmt.sig.ptr = NULL;
+ cred->attstmt.sig.len = 0;
+}
+
+void
+fido_cred_reset_rx(fido_cred_t *cred)
+{
+ free(cred->fmt);
+ cred->fmt = NULL;
+
+ fido_cred_clean_authdata(cred);
+ fido_cred_clean_x509(cred);
+ fido_cred_clean_sig(cred);
+}
+
+void
+fido_cred_free(fido_cred_t **cred_p)
+{
+ fido_cred_t *cred;
+
+ if (cred_p == NULL || (cred = *cred_p) == NULL)
+ return;
+
+ fido_cred_reset_tx(cred);
+ fido_cred_reset_rx(cred);
+
+ free(cred);
+
+ *cred_p = NULL;
+}
+
+int
+fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor;
+ int r;
+
+ fido_cred_clean_authdata(cred);
+
+ if (ptr == NULL || len == 0) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
+ log_debug("%s: cbor_load", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if (decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
+ &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
+ log_debug("%s: decode_cred_authdata", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ if (r != FIDO_OK)
+ fido_cred_clean_authdata(cred);
+
+ return (r);
+
+}
+
+int
+fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
+ size_t len)
+{
+ cbor_item_t *item = NULL;
+ int r;
+
+ fido_cred_clean_authdata(cred);
+
+ if (ptr == NULL || len == 0) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if ((item = cbor_build_bytestring(ptr, len)) == NULL) {
+ log_debug("%s: cbor_build_bytestring", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
+ &cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
+ log_debug("%s: decode_cred_authdata", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ if (r != FIDO_OK)
+ fido_cred_clean_authdata(cred);
+
+ return (r);
+
+}
+
+int
+fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+ unsigned char *x509;
+
+ fido_cred_clean_x509(cred);
+
+ if (ptr == NULL || len == 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ if ((x509 = malloc(len)) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ memcpy(x509, ptr, len);
+ cred->attstmt.x5c.ptr = x509;
+ cred->attstmt.x5c.len = len;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+ unsigned char *sig;
+
+ fido_cred_clean_sig(cred);
+
+ if (ptr == NULL || len == 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ if ((sig = malloc(len)) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ memcpy(sig, ptr, len);
+ cred->attstmt.sig.ptr = sig;
+ cred->attstmt.sig.len = len;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
+{
+ fido_blob_t id_blob;
+ fido_blob_t *list_ptr;
+
+ memset(&id_blob, 0, sizeof(id_blob));
+
+ if (fido_blob_set(&id_blob, id_ptr, id_len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ if (cred->excl.len == SIZE_MAX) {
+ free(id_blob.ptr);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ if ((list_ptr = recallocarray(cred->excl.ptr, cred->excl.len,
+ cred->excl.len + 1, sizeof(fido_blob_t))) == NULL) {
+ free(id_blob.ptr);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ list_ptr[cred->excl.len++] = id_blob;
+ cred->excl.ptr = list_ptr;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
+ size_t hash_len)
+{
+ if (fido_blob_set(&cred->cdh, hash, hash_len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_rp(fido_cred_t *cred, const char *id, const char *name)
+{
+ fido_rp_t *rp = &cred->rp;
+
+ if (rp->id != NULL) {
+ free(rp->id);
+ rp->id = NULL;
+ }
+ if (rp->name != NULL) {
+ free(rp->name);
+ rp->name = NULL;
+ }
+
+ if (id != NULL && (rp->id = strdup(id)) == NULL)
+ goto fail;
+ if (name != NULL && (rp->name = strdup(name)) == NULL)
+ goto fail;
+
+ return (FIDO_OK);
+fail:
+ free(rp->id);
+ free(rp->name);
+ rp->id = NULL;
+ rp->name = NULL;
+
+ return (FIDO_ERR_INTERNAL);
+}
+
+int
+fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
+ size_t user_id_len, const char *name, const char *display_name,
+ const char *icon)
+{
+ fido_user_t *up = &cred->user;
+
+ if (up->id.ptr != NULL) {
+ free(up->id.ptr);
+ up->id.ptr = NULL;
+ up->id.len = 0;
+ }
+ if (up->name != NULL) {
+ free(up->name);
+ up->name = NULL;
+ }
+ if (up->display_name != NULL) {
+ free(up->display_name);
+ up->display_name = NULL;
+ }
+ if (up->icon != NULL) {
+ free(up->icon);
+ up->icon = NULL;
+ }
+
+ if (user_id != NULL) {
+ if ((up->id.ptr = malloc(user_id_len)) == NULL)
+ goto fail;
+ memcpy(up->id.ptr, user_id, user_id_len);
+ up->id.len = user_id_len;
+ }
+ if (name != NULL && (up->name = strdup(name)) == NULL)
+ goto fail;
+ if (display_name != NULL &&
+ (up->display_name = strdup(display_name)) == NULL)
+ goto fail;
+ if (icon != NULL && (up->icon = strdup(icon)) == NULL)
+ goto fail;
+
+ return (FIDO_OK);
+fail:
+ free(up->id.ptr);
+ free(up->name);
+ free(up->display_name);
+ free(up->icon);
+
+ up->id.ptr = NULL;
+ up->id.len = 0;
+ up->name = NULL;
+ up->display_name = NULL;
+ up->icon = NULL;
+
+ return (FIDO_ERR_INTERNAL);
+}
+
+int
+fido_cred_set_extensions(fido_cred_t *cred, int ext)
+{
+ if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ cred->ext = ext;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_options(fido_cred_t *cred, bool rk, bool uv)
+{
+ cred->rk = rk ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
+ cred->uv = uv ? FIDO_OPT_TRUE : FIDO_OPT_FALSE;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_rk(fido_cred_t *cred, fido_opt_t rk)
+{
+ cred->rk = rk;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_uv(fido_cred_t *cred, fido_opt_t uv)
+{
+ cred->uv = uv;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
+{
+ free(cred->fmt);
+ cred->fmt = NULL;
+
+ if (fmt == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f"))
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ if ((cred->fmt = strdup(fmt)) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_set_type(fido_cred_t *cred, int cose_alg)
+{
+ if ((cose_alg != COSE_ES256 && cose_alg != COSE_RS256 &&
+ cose_alg != COSE_EDDSA) || cred->type != 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ cred->type = cose_alg;
+
+ return (FIDO_OK);
+}
+
+int
+fido_cred_type(const fido_cred_t *cred)
+{
+ return (cred->type);
+}
+
+uint8_t
+fido_cred_flags(const fido_cred_t *cred)
+{
+ return (cred->authdata.flags);
+}
+
+const unsigned char *
+fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
+{
+ return (cred->cdh.ptr);
+}
+
+size_t
+fido_cred_clientdata_hash_len(const fido_cred_t *cred)
+{
+ return (cred->cdh.len);
+}
+
+const unsigned char *
+fido_cred_x5c_ptr(const fido_cred_t *cred)
+{
+ return (cred->attstmt.x5c.ptr);
+}
+
+size_t
+fido_cred_x5c_len(const fido_cred_t *cred)
+{
+ return (cred->attstmt.x5c.len);
+}
+
+const unsigned char *
+fido_cred_sig_ptr(const fido_cred_t *cred)
+{
+ return (cred->attstmt.sig.ptr);
+}
+
+size_t
+fido_cred_sig_len(const fido_cred_t *cred)
+{
+ return (cred->attstmt.sig.len);
+}
+
+const unsigned char *
+fido_cred_authdata_ptr(const fido_cred_t *cred)
+{
+ return (cred->authdata_cbor.ptr);
+}
+
+size_t
+fido_cred_authdata_len(const fido_cred_t *cred)
+{
+ return (cred->authdata_cbor.len);
+}
+
+const unsigned char *
+fido_cred_pubkey_ptr(const fido_cred_t *cred)
+{
+ const void *ptr;
+
+ switch (cred->attcred.type) {
+ case COSE_ES256:
+ ptr = &cred->attcred.pubkey.es256;
+ break;
+ case COSE_RS256:
+ ptr = &cred->attcred.pubkey.rs256;
+ break;
+ case COSE_EDDSA:
+ ptr = &cred->attcred.pubkey.eddsa;
+ break;
+ default:
+ ptr = NULL;
+ break;
+ }
+
+ return (ptr);
+}
+
+size_t
+fido_cred_pubkey_len(const fido_cred_t *cred)
+{
+ size_t len;
+
+ switch (cred->attcred.type) {
+ case COSE_ES256:
+ len = sizeof(cred->attcred.pubkey.es256);
+ break;
+ case COSE_RS256:
+ len = sizeof(cred->attcred.pubkey.rs256);
+ break;
+ case COSE_EDDSA:
+ len = sizeof(cred->attcred.pubkey.eddsa);
+ break;
+ default:
+ len = 0;
+ break;
+ }
+
+ return (len);
+}
+
+const unsigned char *
+fido_cred_id_ptr(const fido_cred_t *cred)
+{
+ return (cred->attcred.id.ptr);
+}
+
+size_t
+fido_cred_id_len(const fido_cred_t *cred)
+{
+ return (cred->attcred.id.len);
+}
+
+const char *
+fido_cred_fmt(const fido_cred_t *cred)
+{
+ return (cred->fmt);
+}
+
+const char *
+fido_cred_rp_id(const fido_cred_t *cred)
+{
+ return (cred->rp.id);
+}
+
+const char *
+fido_cred_rp_name(const fido_cred_t *cred)
+{
+ return (cred->rp.name);
+}
+
+const char *
+fido_cred_user_name(const fido_cred_t *cred)
+{
+ return (cred->user.name);
+}
+
+const char *
+fido_cred_display_name(const fido_cred_t *cred)
+{
+ return (cred->user.display_name);
+}
+
+const unsigned char *
+fido_cred_user_id_ptr(const fido_cred_t *cred)
+{
+ return (cred->user.id.ptr);
+}
+
+size_t
+fido_cred_user_id_len(const fido_cred_t *cred)
+{
+ return (cred->user.id.len);
+}
diff --git a/lib/libfido2/src/credman.c b/lib/libfido2/src/credman.c
new file mode 100644
index 00000000000..5a8b6edc406
--- /dev/null
+++ b/lib/libfido2/src/credman.c
@@ -0,0 +1,736 @@
+/*
+ * Copyright (c) 2019 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 <openssl/sha.h>
+
+#include <string.h>
+
+#include "fido.h"
+#include "fido/credman.h"
+#include "fido/es256.h"
+
+#define CMD_CRED_METADATA 0x01
+#define CMD_RP_BEGIN 0x02
+#define CMD_RP_NEXT 0x03
+#define CMD_RK_BEGIN 0x04
+#define CMD_RK_NEXT 0x05
+#define CMD_DELETE_CRED 0x06
+
+static int
+credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n,
+ size_t size)
+{
+ void *new_ptr;
+
+#ifdef FIDO_FUZZ
+ if (n > UINT8_MAX) {
+ log_debug("%s: n > UINT8_MAX", __func__);
+ return (-1);
+ }
+#endif
+
+ if (n < *n_alloc)
+ return (0);
+
+ /* sanity check */
+ if (*n_rx > 0 || *n_rx > *n_alloc || n < *n_alloc) {
+ log_debug("%s: n=%zu, n_rx=%zu, n_alloc=%zu", __func__, n,
+ *n_rx, *n_alloc);
+ return (-1);
+ }
+
+ if ((new_ptr = recallocarray(*ptr, *n_alloc, n, size)) == NULL)
+ return (-1);
+
+ *ptr = new_ptr;
+ *n_alloc = n;
+
+ return (0);
+}
+
+static int
+credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param,
+ fido_blob_t *hmac_data)
+{
+ cbor_item_t *param_cbor[2];
+ size_t n;
+ int ok = -1;
+
+ memset(&param_cbor, 0, sizeof(param_cbor));
+
+ if (body == NULL)
+ return (fido_blob_set(hmac_data, &cmd, sizeof(cmd)));
+
+ switch (cmd) {
+ case CMD_RK_BEGIN:
+ n = 1;
+ param_cbor[n - 1] = fido_blob_encode(body);
+ break;
+ case CMD_DELETE_CRED:
+ n = 2;
+ param_cbor[n - 1] = encode_pubkey(body);
+ break;
+ default:
+ log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
+ return (-1);
+ }
+
+ if (param_cbor[n - 1] == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ return (-1);
+ }
+ if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
+ log_debug("%s: cbor_flatten_vector", __func__);
+ goto fail;
+ }
+ if (cbor_build_frame(cmd, param_cbor, n, hmac_data) < 0) {
+ log_debug("%s: cbor_build_frame", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ cbor_vector_free(param_cbor, nitems(param_cbor));
+
+ return (ok);
+}
+
+static int
+credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
+ const char *pin)
+{
+ fido_blob_t f;
+ fido_blob_t *ecdh = NULL;
+ fido_blob_t hmac;
+ es256_pk_t *pk = NULL;
+ cbor_item_t *argv[4];
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&f, 0, sizeof(f));
+ memset(&hmac, 0, sizeof(hmac));
+ memset(&argv, 0, sizeof(argv));
+
+ /* subCommand */
+ if ((argv[0] = cbor_build_uint8(cmd)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ /* pinProtocol, pinAuth */
+ if (pin != NULL) {
+ if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) {
+ log_debug("%s: credman_prepare_hmac", __func__);
+ goto fail;
+ }
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+ if ((r = add_cbor_pin_params(dev, &hmac, pk, ecdh, pin,
+ &argv[3], &argv[2])) != FIDO_OK) {
+ log_debug("%s: add_cbor_pin_params", __func__);
+ goto fail;
+ }
+ }
+
+ /* framing and transmission */
+ if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, 4, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ es256_pk_free(&pk);
+ fido_blob_free(&ecdh);
+ cbor_vector_free(argv, nitems(argv));
+ free(f.ptr);
+ free(hmac.ptr);
+
+ return (r);
+}
+
+static int
+credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_credman_metadata_t *metadata = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 1:
+ return (decode_uint64(val, &metadata->rk_existing));
+ case 2:
+ return (decode_uint64(val, &metadata->rk_remaining));
+ default:
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+}
+
+static int
+credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[512];
+ int reply_len;
+ int r;
+
+ memset(metadata, 0, sizeof(*metadata));
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, metadata,
+ credman_parse_metadata)) != FIDO_OK) {
+ log_debug("%s: credman_parse_metadata", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
+ const char *pin, int ms)
+{
+ int r;
+
+ if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK ||
+ (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
+ const char *pin)
+{
+ if (fido_dev_is_fido2(dev) == false)
+ return (FIDO_ERR_INVALID_COMMAND);
+ if (pin == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (credman_get_metadata_wait(dev, metadata, pin, -1));
+}
+
+static int
+credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_cred_t *cred = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 6: /* user entity */
+ return (decode_user(val, &cred->user));
+ case 7:
+ return (decode_cred_id(val, &cred->attcred.id));
+ case 8:
+ if (decode_pubkey(val, &cred->attcred.type,
+ &cred->attcred.pubkey) < 0)
+ return (-1);
+ cred->type = cred->attcred.type; /* XXX */
+ return (0);
+ default:
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+}
+
+static void
+credman_reset_rk(fido_credman_rk_t *rk)
+{
+ for (size_t i = 0; i < rk->n_alloc; i++) {
+ fido_cred_reset_tx(&rk->ptr[i]);
+ fido_cred_reset_rx(&rk->ptr[i]);
+ }
+
+ free(rk->ptr);
+ rk->ptr = NULL;
+ memset(rk, 0, sizeof(*rk));
+}
+
+static int
+credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_credman_rk_t *rk = arg;
+ uint64_t n;
+
+ /* totalCredentials */
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 9) {
+ log_debug("%s: cbor_type", __func__);
+ return (0); /* ignore */
+ }
+
+ if (decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+
+ if (credman_grow_array((void **)&rk->ptr, &rk->n_alloc, &rk->n_rx,
+ (size_t)n, sizeof(*rk->ptr)) < 0) {
+ log_debug("%s: credman_grow_array", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ credman_reset_rk(rk);
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ /* adjust as needed */
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, rk,
+ credman_parse_rk_count)) != FIDO_OK) {
+ log_debug("%s: credman_parse_rk_count", __func__);
+ return (r);
+ }
+
+ if (rk->n_alloc == 0) {
+ log_debug("%s: n_alloc=0", __func__);
+ return (FIDO_OK);
+ }
+
+ /* parse the first rk */
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, &rk->ptr[0],
+ credman_parse_rk)) != FIDO_OK) {
+ log_debug("%s: credman_parse_rk", __func__);
+ return (r);
+ }
+
+ rk->n_rx++;
+
+ return (FIDO_OK);
+}
+
+static int
+credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ /* sanity check */
+ if (rk->n_rx >= rk->n_alloc) {
+ log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rk->n_rx,
+ rk->n_alloc);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, &rk->ptr[rk->n_rx],
+ credman_parse_rk)) != FIDO_OK) {
+ log_debug("%s: credman_parse_rk", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
+ const char *pin, int ms)
+{
+ fido_blob_t rp_dgst;
+ uint8_t dgst[SHA256_DIGEST_LENGTH];
+ int r;
+
+ if (SHA256((const unsigned char *)rp_id, strlen(rp_id), dgst) != dgst) {
+ log_debug("%s: sha256", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ rp_dgst.ptr = dgst;
+ rp_dgst.len = sizeof(dgst);
+
+ if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK ||
+ (r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
+ return (r);
+
+ while (rk->n_rx < rk->n_alloc) {
+ if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK ||
+ (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
+ return (r);
+ rk->n_rx++;
+ }
+
+ return (FIDO_OK);
+}
+
+int
+fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
+ fido_credman_rk_t *rk, const char *pin)
+{
+ if (fido_dev_is_fido2(dev) == false)
+ return (FIDO_ERR_INVALID_COMMAND);
+ if (pin == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
+}
+
+static int
+credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
+ size_t cred_id_len, const char *pin, int ms)
+{
+ fido_blob_t cred;
+ int r;
+
+ memset(&cred, 0, sizeof(cred));
+
+ if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK ||
+ (r = rx_cbor_status(dev, ms)) != FIDO_OK)
+ goto fail;
+
+ r = FIDO_OK;
+fail:
+ free(cred.ptr);
+
+ return (r);
+}
+
+int
+fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
+ size_t cred_id_len, const char *pin)
+{
+ if (fido_dev_is_fido2(dev) == false)
+ return (FIDO_ERR_INVALID_COMMAND);
+ if (pin == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
+}
+
+static int
+credman_parse_rp(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ struct fido_credman_single_rp *rp = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 3:
+ return (decode_rp_entity(val, &rp->rp_entity));
+ case 4:
+ return (fido_blob_decode(val, &rp->rp_id_hash));
+ default:
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+}
+
+static void
+credman_reset_rp(fido_credman_rp_t *rp)
+{
+ for (size_t i = 0; i < rp->n_alloc; i++) {
+ free(rp->ptr[i].rp_entity.id);
+ free(rp->ptr[i].rp_entity.name);
+ rp->ptr[i].rp_entity.id = NULL;
+ rp->ptr[i].rp_entity.name = NULL;
+ free(rp->ptr[i].rp_id_hash.ptr);
+ memset(&rp->ptr[i].rp_id_hash, 0,
+ sizeof(rp->ptr[i].rp_id_hash));
+ }
+
+ free(rp->ptr);
+ rp->ptr = NULL;
+ memset(rp, 0, sizeof(*rp));
+}
+
+static int
+credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_credman_rp_t *rp = arg;
+ uint64_t n;
+
+ /* totalRPs */
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 5) {
+ log_debug("%s: cbor_type", __func__);
+ return (0); /* ignore */
+ }
+
+ if (decode_uint64(val, &n) < 0 || n > SIZE_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+
+ if (credman_grow_array((void **)&rp->ptr, &rp->n_alloc, &rp->n_rx,
+ (size_t)n, sizeof(*rp->ptr)) < 0) {
+ log_debug("%s: credman_grow_array", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ credman_reset_rp(rp);
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ /* adjust as needed */
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, rp,
+ credman_parse_rp_count)) != FIDO_OK) {
+ log_debug("%s: credman_parse_rp_count", __func__);
+ return (r);
+ }
+
+ if (rp->n_alloc == 0) {
+ log_debug("%s: n_alloc=0", __func__);
+ return (FIDO_OK);
+ }
+
+ /* parse the first rp */
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, &rp->ptr[0],
+ credman_parse_rp)) != FIDO_OK) {
+ log_debug("%s: credman_parse_rp", __func__);
+ return (r);
+ }
+
+ rp->n_rx++;
+
+ return (FIDO_OK);
+}
+
+static int
+credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ /* sanity check */
+ if (rp->n_rx >= rp->n_alloc) {
+ log_debug("%s: n_rx=%zu, n_alloc=%zu", __func__, rp->n_rx,
+ rp->n_alloc);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, &rp->ptr[rp->n_rx],
+ credman_parse_rp)) != FIDO_OK) {
+ log_debug("%s: credman_parse_rp", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
+ int ms)
+{
+ int r;
+
+ if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK ||
+ (r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
+ return (r);
+
+ while (rp->n_rx < rp->n_alloc) {
+ if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK ||
+ (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
+ return (r);
+ rp->n_rx++;
+ }
+
+ return (FIDO_OK);
+}
+
+int
+fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
+{
+ if (fido_dev_is_fido2(dev) == false)
+ return (FIDO_ERR_INVALID_COMMAND);
+ if (pin == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (credman_get_rp_wait(dev, rp, pin, -1));
+}
+
+fido_credman_rk_t *
+fido_credman_rk_new(void)
+{
+ return (calloc(1, sizeof(fido_credman_rk_t)));
+}
+
+void
+fido_credman_rk_free(fido_credman_rk_t **rk_p)
+{
+ fido_credman_rk_t *rk;
+
+ if (rk_p == NULL || (rk = *rk_p) == NULL)
+ return;
+
+ credman_reset_rk(rk);
+ free(rk);
+ *rk_p = NULL;
+}
+
+size_t
+fido_credman_rk_count(const fido_credman_rk_t *rk)
+{
+ return (rk->n_rx);
+}
+
+const fido_cred_t *
+fido_credman_rk(const fido_credman_rk_t *rk, size_t idx)
+{
+ if (idx >= rk->n_alloc)
+ return (NULL);
+
+ return (&rk->ptr[idx]);
+}
+
+fido_credman_metadata_t *
+fido_credman_metadata_new(void)
+{
+ return (calloc(1, sizeof(fido_credman_metadata_t)));
+}
+
+void
+fido_credman_metadata_free(fido_credman_metadata_t **metadata_p)
+{
+ fido_credman_metadata_t *metadata;
+
+ if (metadata_p == NULL || (metadata = *metadata_p) == NULL)
+ return;
+
+ free(metadata);
+ *metadata_p = NULL;
+}
+
+uint64_t
+fido_credman_rk_existing(const fido_credman_metadata_t *metadata)
+{
+ return (metadata->rk_existing);
+}
+
+uint64_t
+fido_credman_rk_remaining(const fido_credman_metadata_t *metadata)
+{
+ return (metadata->rk_remaining);
+}
+
+fido_credman_rp_t *
+fido_credman_rp_new(void)
+{
+ return (calloc(1, sizeof(fido_credman_rp_t)));
+}
+
+void
+fido_credman_rp_free(fido_credman_rp_t **rp_p)
+{
+ fido_credman_rp_t *rp;
+
+ if (rp_p == NULL || (rp = *rp_p) == NULL)
+ return;
+
+ credman_reset_rp(rp);
+ free(rp);
+ *rp_p = NULL;
+}
+
+size_t
+fido_credman_rp_count(const fido_credman_rp_t *rp)
+{
+ return (rp->n_rx);
+}
+
+const char *
+fido_credman_rp_id(const fido_credman_rp_t *rp, size_t idx)
+{
+ if (idx >= rp->n_alloc)
+ return (NULL);
+
+ return (rp->ptr[idx].rp_entity.id);
+}
+
+const char *
+fido_credman_rp_name(const fido_credman_rp_t *rp, size_t idx)
+{
+ if (idx >= rp->n_alloc)
+ return (NULL);
+
+ return (rp->ptr[idx].rp_entity.name);
+}
+
+size_t
+fido_credman_rp_id_hash_len(const fido_credman_rp_t *rp, size_t idx)
+{
+ if (idx >= rp->n_alloc)
+ return (0);
+
+ return (rp->ptr[idx].rp_id_hash.len);
+}
+
+const unsigned char *
+fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *rp, size_t idx)
+{
+ if (idx >= rp->n_alloc)
+ return (NULL);
+
+ return (rp->ptr[idx].rp_id_hash.ptr);
+}
diff --git a/lib/libfido2/src/dev.c b/lib/libfido2/src/dev.c
new file mode 100644
index 00000000000..8d4b6e71569
--- /dev/null
+++ b/lib/libfido2/src/dev.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2018 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 <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "fido.h"
+
+#if defined(_WIN32)
+#include <windows.h>
+
+#include <winternl.h>
+#include <winerror.h>
+#include <stdio.h>
+#include <bcrypt.h>
+#include <sal.h>
+
+static int
+obtain_nonce(uint64_t *nonce)
+{
+ NTSTATUS status;
+
+ status = BCryptGenRandom(NULL, (unsigned char *)nonce, sizeof(*nonce),
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+
+ if (!NT_SUCCESS(status))
+ return (-1);
+
+ return (0);
+}
+#elif defined(__OpenBSD__)
+static int
+obtain_nonce(uint64_t *nonce)
+{
+ arc4random_buf(nonce, sizeof(*nonce));
+ return 0;
+}
+#elif defined(HAS_DEV_URANDOM)
+static int
+obtain_nonce(uint64_t *nonce)
+{
+ int fd = -1;
+ int ok = -1;
+ ssize_t r;
+
+ if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0)
+ goto fail;
+ if ((r = read(fd, nonce, sizeof(*nonce))) < 0 ||
+ (size_t)r != sizeof(*nonce))
+ goto fail;
+
+ ok = 0;
+fail:
+ if (fd != -1)
+ close(fd);
+
+ return (ok);
+}
+#else
+#error "please provide an implementation of obtain_nonce() for your platform"
+#endif /* _WIN32 */
+
+static int
+fido_dev_open_tx(fido_dev_t *dev, const char *path)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT;
+
+ if (dev->io_handle != NULL) {
+ log_debug("%s: handle=%p", __func__, dev->io_handle);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ if (dev->io.open == NULL || dev->io.close == NULL) {
+ log_debug("%s: NULL open/close", __func__);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ if (obtain_nonce(&dev->nonce) < 0) {
+ log_debug("%s: obtain_nonce", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((dev->io_handle = dev->io.open(path)) == NULL) {
+ log_debug("%s: dev->io.open", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if (tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) {
+ log_debug("%s: tx", __func__);
+ dev->io.close(dev->io_handle);
+ dev->io_handle = NULL;
+ return (FIDO_ERR_TX);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_open_rx(fido_dev_t *dev, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_INIT;
+ int n;
+
+ if ((n = rx(dev, cmd, &dev->attr, sizeof(dev->attr), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ goto fail;
+ }
+
+#ifdef FIDO_FUZZ
+ dev->attr.nonce = dev->nonce;
+#endif
+
+ if ((size_t)n != sizeof(dev->attr) || dev->attr.nonce != dev->nonce) {
+ log_debug("%s: invalid nonce", __func__);
+ goto fail;
+ }
+
+ dev->cid = dev->attr.cid;
+
+ return (FIDO_OK);
+fail:
+ dev->io.close(dev->io_handle);
+ dev->io_handle = NULL;
+
+ return (FIDO_ERR_RX);
+}
+
+static int
+fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK ||
+ (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_open(fido_dev_t *dev, const char *path)
+{
+ return (fido_dev_open_wait(dev, path, -1));
+}
+
+int
+fido_dev_close(fido_dev_t *dev)
+{
+ if (dev->io_handle == NULL || dev->io.close == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ dev->io.close(dev->io_handle);
+ dev->io_handle = NULL;
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_cancel(fido_dev_t *dev)
+{
+ if (tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CANCEL, NULL, 0) < 0)
+ return (FIDO_ERR_TX);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
+{
+ if (dev->io_handle != NULL) {
+ log_debug("%s: NULL handle", __func__);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ if (io == NULL || io->open == NULL || io->close == NULL ||
+ io->read == NULL || io->write == NULL) {
+ log_debug("%s: NULL function", __func__);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ dev->io.open = io->open;
+ dev->io.close = io->close;
+ dev->io.read = io->read;
+ dev->io.write = io->write;
+
+ return (FIDO_OK);
+}
+
+void
+fido_init(int flags)
+{
+ if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
+ log_init();
+}
+
+fido_dev_t *
+fido_dev_new(void)
+{
+ fido_dev_t *dev;
+ fido_dev_io_t io;
+
+ if ((dev = calloc(1, sizeof(*dev))) == NULL)
+ return (NULL);
+
+ dev->cid = CTAP_CID_BROADCAST;
+
+ io.open = hid_open;
+ io.close = hid_close;
+ io.read = hid_read;
+ io.write = hid_write;
+
+ if (fido_dev_set_io_functions(dev, &io) != FIDO_OK) {
+ log_debug("%s: fido_dev_set_io_functions", __func__);
+ fido_dev_free(&dev);
+ return (NULL);
+ }
+
+ return (dev);
+}
+
+void
+fido_dev_free(fido_dev_t **dev_p)
+{
+ fido_dev_t *dev;
+
+ if (dev_p == NULL || (dev = *dev_p) == NULL)
+ return;
+
+ free(dev);
+
+ *dev_p = NULL;
+}
+
+uint8_t
+fido_dev_protocol(const fido_dev_t *dev)
+{
+ return (dev->attr.protocol);
+}
+
+uint8_t
+fido_dev_major(const fido_dev_t *dev)
+{
+ return (dev->attr.major);
+}
+
+uint8_t
+fido_dev_minor(const fido_dev_t *dev)
+{
+ return (dev->attr.minor);
+}
+
+uint8_t
+fido_dev_build(const fido_dev_t *dev)
+{
+ return (dev->attr.build);
+}
+
+uint8_t
+fido_dev_flags(const fido_dev_t *dev)
+{
+ return (dev->attr.flags);
+}
+
+bool
+fido_dev_is_fido2(const fido_dev_t *dev)
+{
+ return (dev->attr.flags & FIDO_CAP_CBOR);
+}
+
+void
+fido_dev_force_u2f(fido_dev_t *dev)
+{
+ dev->attr.flags &= ~FIDO_CAP_CBOR;
+}
+
+void
+fido_dev_force_fido2(fido_dev_t *dev)
+{
+ dev->attr.flags |= FIDO_CAP_CBOR;
+}
diff --git a/lib/libfido2/src/ecdh.c b/lib/libfido2/src/ecdh.c
new file mode 100644
index 00000000000..aa8c5c3be36
--- /dev/null
+++ b/lib/libfido2/src/ecdh.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2018 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 <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include "fido.h"
+#include "fido/es256.h"
+
+static int
+do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh)
+{
+ EVP_PKEY *pk_evp = NULL;
+ EVP_PKEY *sk_evp = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ fido_blob_t *secret = NULL;
+ int ok = -1;
+
+ *ecdh = NULL;
+
+ /* allocate blobs for secret & ecdh */
+ if ((secret = fido_blob_new()) == NULL ||
+ (*ecdh = fido_blob_new()) == NULL)
+ goto fail;
+
+ /* wrap the keys as openssl objects */
+ if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
+ (sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
+ log_debug("%s: es256_to_EVP_PKEY", __func__);
+ goto fail;
+ }
+
+ /* set ecdh parameters */
+ if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
+ EVP_PKEY_derive_init(ctx) <= 0 ||
+ EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
+ log_debug("%s: EVP_PKEY_derive_init", __func__);
+ goto fail;
+ }
+
+ /* perform ecdh */
+ if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
+ (secret->ptr = calloc(1, secret->len)) == NULL ||
+ EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
+ log_debug("%s: EVP_PKEY_derive", __func__);
+ goto fail;
+ }
+
+ /* use sha256 as a kdf on the resulting secret */
+ (*ecdh)->len = SHA256_DIGEST_LENGTH;
+ if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL ||
+ SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) {
+ log_debug("%s: sha256", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (pk_evp != NULL)
+ EVP_PKEY_free(pk_evp);
+ if (sk_evp != NULL)
+ EVP_PKEY_free(sk_evp);
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+ if (ok < 0)
+ fido_blob_free(ecdh);
+
+ fido_blob_free(&secret);
+
+ return (ok);
+}
+
+int
+fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
+{
+ es256_sk_t *sk = NULL; /* our private key */
+ es256_pk_t *ak = NULL; /* authenticator's public key */
+ int r;
+
+ *pk = NULL; /* our public key; returned */
+ *ecdh = NULL; /* shared ecdh secret; returned */
+
+ if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
+ log_debug("%s: es256_derive_pk", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if ((ak = es256_pk_new()) == NULL ||
+ fido_dev_authkey(dev, ak) != FIDO_OK) {
+ log_debug("%s: fido_dev_authkey", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (do_ecdh(sk, ak, ecdh) < 0) {
+ log_debug("%s: do_ecdh", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ es256_sk_free(&sk);
+ es256_pk_free(&ak);
+
+ if (r != FIDO_OK) {
+ es256_pk_free(pk);
+ fido_blob_free(ecdh);
+ }
+
+ return (r);
+}
diff --git a/lib/libfido2/src/eddsa.c b/lib/libfido2/src/eddsa.c
new file mode 100644
index 00000000000..c346979414b
--- /dev/null
+++ b/lib/libfido2/src/eddsa.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019 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 <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+
+#include <string.h>
+#include "fido.h"
+#include "fido/eddsa.h"
+
+#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
+EVP_PKEY *
+EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key,
+ size_t keylen)
+{
+ (void)type;
+ (void)e;
+ (void)key;
+ (void)keylen;
+
+ return (NULL);
+}
+
+int
+EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
+ size_t *len)
+{
+ (void)pkey;
+ (void)pub;
+ (void)len;
+
+ return (0);
+}
+
+int
+EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ (void)ctx;
+ (void)sigret;
+ (void)siglen;
+ (void)tbs;
+ (void)tbslen;
+
+ return (0);
+}
+#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+EVP_MD_CTX *
+EVP_MD_CTX_new(void)
+{
+ return (NULL);
+}
+
+void
+EVP_MD_CTX_free(EVP_MD_CTX *ctx)
+{
+ (void)ctx;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+static int
+decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
+{
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false ||
+ cbor_bytestring_length(item) != xy_len) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ memcpy(xy, cbor_bytestring_handle(item), xy_len);
+
+ return (0);
+}
+
+static int
+decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ eddsa_pk_t *k = arg;
+
+ if (cbor_isa_negint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8)
+ return (0); /* ignore */
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* x coordinate */
+ return (decode_coord(val, &k->x, sizeof(k->x)));
+ }
+
+ return (0); /* ignore */
+}
+
+int
+eddsa_pk_decode(const cbor_item_t *item, eddsa_pk_t *k)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, k, decode_pubkey_point) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+eddsa_pk_t *
+eddsa_pk_new(void)
+{
+ return (calloc(1, sizeof(eddsa_pk_t)));
+}
+
+void
+eddsa_pk_free(eddsa_pk_t **pkp)
+{
+ eddsa_pk_t *pk;
+
+ if (pkp == NULL || (pk = *pkp) == NULL)
+ return;
+
+ explicit_bzero(pk, sizeof(*pk));
+ free(pk);
+
+ *pkp = NULL;
+}
+
+int
+eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len)
+{
+ if (len < sizeof(*pk))
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ memcpy(pk, ptr, sizeof(*pk));
+
+ return (FIDO_OK);
+}
+
+EVP_PKEY *
+eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *k)
+{
+ EVP_PKEY *pkey = NULL;
+
+ if ((pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, k->x,
+ sizeof(k->x))) == NULL)
+ log_debug("%s: EVP_PKEY_new_raw_public_key", __func__);
+
+ return (pkey);
+}
+
+int
+eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
+{
+ size_t len = 0;
+
+ if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 ||
+ len != sizeof(pk->x))
+ return (FIDO_ERR_INTERNAL);
+ if (EVP_PKEY_get_raw_public_key(pkey, pk->x, &len) != 1 ||
+ len != sizeof(pk->x))
+ return (FIDO_ERR_INTERNAL);
+
+ return (FIDO_OK);
+}
diff --git a/lib/libfido2/src/err.c b/lib/libfido2/src/err.c
new file mode 100644
index 00000000000..5d3efd4aba7
--- /dev/null
+++ b/lib/libfido2/src/err.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2018 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 "fido/err.h"
+
+const char *
+fido_strerr(int n)
+{
+ switch (n) {
+ case FIDO_ERR_SUCCESS:
+ return "FIDO_ERR_SUCCESS";
+ case FIDO_ERR_INVALID_COMMAND:
+ return "FIDO_ERR_INVALID_COMMAND";
+ case FIDO_ERR_INVALID_PARAMETER:
+ return "FIDO_ERR_INVALID_PARAMETER";
+ case FIDO_ERR_INVALID_LENGTH:
+ return "FIDO_ERR_INVALID_LENGTH";
+ case FIDO_ERR_INVALID_SEQ:
+ return "FIDO_ERR_INVALID_SEQ";
+ case FIDO_ERR_TIMEOUT:
+ return "FIDO_ERR_TIMEOUT";
+ case FIDO_ERR_CHANNEL_BUSY:
+ return "FIDO_ERR_CHANNEL_BUSY";
+ case FIDO_ERR_LOCK_REQUIRED:
+ return "FIDO_ERR_LOCK_REQUIRED";
+ case FIDO_ERR_INVALID_CHANNEL:
+ return "FIDO_ERR_INVALID_CHANNEL";
+ case FIDO_ERR_CBOR_UNEXPECTED_TYPE:
+ return "FIDO_ERR_UNEXPECTED_TYPE";
+ case FIDO_ERR_INVALID_CBOR:
+ return "FIDO_ERR_INVALID_CBOR";
+ case FIDO_ERR_MISSING_PARAMETER:
+ return "FIDO_ERR_MISSING_PARAMETER";
+ case FIDO_ERR_LIMIT_EXCEEDED:
+ return "FIDO_ERR_LIMIT_EXCEEDED";
+ case FIDO_ERR_UNSUPPORTED_EXTENSION:
+ return "FIDO_ERR_UNSUPPORTED_EXTENSION";
+ case FIDO_ERR_CREDENTIAL_EXCLUDED:
+ return "FIDO_ERR_CREDENTIAL_EXCLUDED";
+ case FIDO_ERR_PROCESSING:
+ return "FIDO_ERR_PROCESSING";
+ case FIDO_ERR_INVALID_CREDENTIAL:
+ return "FIDO_ERR_INVALID_CREDENTIAL";
+ case FIDO_ERR_USER_ACTION_PENDING:
+ return "FIDO_ERR_ACTION_PENDING";
+ case FIDO_ERR_OPERATION_PENDING:
+ return "FIDO_ERR_OPERATION_PENDING";
+ case FIDO_ERR_NO_OPERATIONS:
+ return "FIDO_ERR_NO_OPERATIONS";
+ case FIDO_ERR_UNSUPPORTED_ALGORITHM:
+ return "FIDO_ERR_UNSUPPORTED_ALGORITHM";
+ case FIDO_ERR_OPERATION_DENIED:
+ return "FIDO_ERR_OPERATION_DENIED";
+ case FIDO_ERR_KEY_STORE_FULL:
+ return "FIDO_ERR_STORE_FULL";
+ case FIDO_ERR_NOT_BUSY:
+ return "FIDO_ERR_NOT_BUSY";
+ case FIDO_ERR_NO_OPERATION_PENDING:
+ return "FIDO_ERR_OPERATION_PENDING";
+ case FIDO_ERR_UNSUPPORTED_OPTION:
+ return "FIDO_ERR_UNSUPPORTED_OPTION";
+ case FIDO_ERR_INVALID_OPTION:
+ return "FIDO_ERR_INVALID_OPTION";
+ case FIDO_ERR_KEEPALIVE_CANCEL:
+ return "FIDO_ERR_KEEPALIVE_CANCEL";
+ case FIDO_ERR_NO_CREDENTIALS:
+ return "FIDO_ERR_NO_CREDENTIALS";
+ case FIDO_ERR_USER_ACTION_TIMEOUT:
+ return "FIDO_ERR_ACTION_TIMEOUT";
+ case FIDO_ERR_NOT_ALLOWED:
+ return "FIDO_ERR_NOT_ALLOWED";
+ case FIDO_ERR_PIN_INVALID:
+ return "FIDO_ERR_PIN_INVALID";
+ case FIDO_ERR_PIN_BLOCKED:
+ return "FIDO_ERR_PIN_BLOCKED";
+ case FIDO_ERR_PIN_AUTH_INVALID:
+ return "FIDO_ERR_AUTH_INVALID";
+ case FIDO_ERR_PIN_AUTH_BLOCKED:
+ return "FIDO_ERR_AUTH_BLOCKED";
+ case FIDO_ERR_PIN_NOT_SET:
+ return "FIDO_ERR_NOT_SET";
+ case FIDO_ERR_PIN_REQUIRED:
+ return "FIDO_ERR_PIN_REQUIRED";
+ case FIDO_ERR_PIN_POLICY_VIOLATION:
+ return "FIDO_ERR_POLICY_VIOLATION";
+ case FIDO_ERR_PIN_TOKEN_EXPIRED:
+ return "FIDO_ERR_TOKEN_EXPIRED";
+ case FIDO_ERR_REQUEST_TOO_LARGE:
+ return "FIDO_ERR_TOO_LARGE";
+ case FIDO_ERR_ACTION_TIMEOUT:
+ return "FIDO_ERR_ACTION_TIMEOUT";
+ case FIDO_ERR_UP_REQUIRED:
+ return "FIDO_ERR_UP_REQUIRED";
+ case FIDO_ERR_ERR_OTHER:
+ return "FIDO_ERR_OTHER";
+ case FIDO_ERR_SPEC_LAST:
+ return "FIDO_ERR_SPEC_LAST";
+ case FIDO_ERR_TX:
+ return "FIDO_ERR_TX";
+ case FIDO_ERR_RX:
+ return "FIDO_ERR_RX";
+ case FIDO_ERR_RX_NOT_CBOR:
+ return "FIDO_ERR_RX_NOT_CBOR";
+ case FIDO_ERR_RX_INVALID_CBOR:
+ return "FIDO_ERR_RX_INVALID_CBOR";
+ case FIDO_ERR_INVALID_PARAM:
+ return "FIDO_ERR_INVALID_PARAM";
+ case FIDO_ERR_INVALID_SIG:
+ return "FIDO_ERR_INVALID_SIG";
+ case FIDO_ERR_INVALID_ARGUMENT:
+ return "FIDO_ERR_INVALID_ARGUMENT";
+ case FIDO_ERR_USER_PRESENCE_REQUIRED:
+ return "FIDO_ERR_USER_PRESENCE_REQUIRED";
+ case FIDO_ERR_INTERNAL:
+ return "FIDO_ERR_INTERNAL";
+ default:
+ return "FIDO_ERR_UNKNOWN";
+ }
+}
diff --git a/lib/libfido2/src/es256.c b/lib/libfido2/src/es256.c
new file mode 100644
index 00000000000..e8f59344fc0
--- /dev/null
+++ b/lib/libfido2/src/es256.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2018 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 <openssl/bn.h>
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+
+#include <string.h>
+#include "fido.h"
+#include "fido/es256.h"
+
+static int
+decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
+{
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false ||
+ cbor_bytestring_length(item) != xy_len) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ memcpy(xy, cbor_bytestring_handle(item), xy_len);
+
+ return (0);
+}
+
+static int
+decode_pubkey_point(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ es256_pk_t *k = arg;
+
+ if (cbor_isa_negint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8)
+ return (0); /* ignore */
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* x coordinate */
+ return (decode_coord(val, &k->x, sizeof(k->x)));
+ case 2: /* y coordinate */
+ return (decode_coord(val, &k->y, sizeof(k->y)));
+ }
+
+ return (0); /* ignore */
+}
+
+int
+es256_pk_decode(const cbor_item_t *item, es256_pk_t *k)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, k, decode_pubkey_point) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+cbor_item_t *
+es256_pk_encode(const es256_pk_t *pk, int ecdh)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_pair argv[5];
+ int alg;
+ int ok = -1;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((item = cbor_new_definite_map(5)) == NULL)
+ goto fail;
+
+ /* kty */
+ if ((argv[0].key = cbor_build_uint8(1)) == NULL ||
+ (argv[0].value = cbor_build_uint8(2)) == NULL ||
+ !cbor_map_add(item, argv[0]))
+ goto fail;
+
+ /*
+ * "The COSEAlgorithmIdentifier used is -25 (ECDH-ES +
+ * HKDF-256) although this is NOT the algorithm actually
+ * used. Setting this to a different value may result in
+ * compatibility issues."
+ */
+ if (ecdh)
+ alg = COSE_ECDH_ES256;
+ else
+ alg = COSE_ES256;
+
+ /* alg */
+ if ((argv[1].key = cbor_build_uint8(3)) == NULL ||
+ (argv[1].value = cbor_build_negint8(-alg - 1)) == NULL ||
+ !cbor_map_add(item, argv[1]))
+ goto fail;
+
+ /* crv */
+ if ((argv[2].key = cbor_build_negint8(0)) == NULL ||
+ (argv[2].value = cbor_build_uint8(1)) == NULL ||
+ !cbor_map_add(item, argv[2]))
+ goto fail;
+
+ /* x */
+ if ((argv[3].key = cbor_build_negint8(1)) == NULL ||
+ (argv[3].value = cbor_build_bytestring(pk->x,
+ sizeof(pk->x))) == NULL || !cbor_map_add(item, argv[3]))
+ goto fail;
+
+ /* y */
+ if ((argv[4].key = cbor_build_negint8(2)) == NULL ||
+ (argv[4].value = cbor_build_bytestring(pk->y,
+ sizeof(pk->y))) == NULL || !cbor_map_add(item, argv[4]))
+ goto fail;
+
+ ok = 0;
+fail:
+ if (ok < 0) {
+ if (item != NULL) {
+ cbor_decref(&item);
+ item = NULL;
+ }
+ }
+
+ for (size_t i = 0; i < 5; i++) {
+ if (argv[i].key)
+ cbor_decref(&argv[i].key);
+ if (argv[i].value)
+ cbor_decref(&argv[i].value);
+ }
+
+ return (item);
+}
+
+es256_sk_t *
+es256_sk_new(void)
+{
+ return (calloc(1, sizeof(es256_sk_t)));
+}
+
+void
+es256_sk_free(es256_sk_t **skp)
+{
+ es256_sk_t *sk;
+
+ if (skp == NULL || (sk = *skp) == NULL)
+ return;
+
+ explicit_bzero(sk, sizeof(*sk));
+ free(sk);
+
+ *skp = NULL;
+}
+
+es256_pk_t *
+es256_pk_new(void)
+{
+ return (calloc(1, sizeof(es256_pk_t)));
+}
+
+void
+es256_pk_free(es256_pk_t **pkp)
+{
+ es256_pk_t *pk;
+
+ if (pkp == NULL || (pk = *pkp) == NULL)
+ return;
+
+ explicit_bzero(pk, sizeof(*pk));
+ free(pk);
+
+ *pkp = NULL;
+}
+
+int
+es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len)
+{
+ if (len < sizeof(*pk))
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ memcpy(pk, ptr, sizeof(*pk));
+
+ return (FIDO_OK);
+}
+
+int
+es256_pk_set_x(es256_pk_t *pk, const unsigned char *x)
+{
+ memcpy(pk->x, x, sizeof(pk->x));
+
+ return (0);
+}
+
+int
+es256_pk_set_y(es256_pk_t *pk, const unsigned char *y)
+{
+ memcpy(pk->y, y, sizeof(pk->y));
+
+ return (0);
+}
+
+int
+es256_sk_create(es256_sk_t *key)
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY_CTX *kctx = NULL;
+ EVP_PKEY *p = NULL;
+ EVP_PKEY *k = NULL;
+ const EC_KEY *ec;
+ const BIGNUM *d;
+ const int nid = NID_X9_62_prime256v1;
+ int n;
+ int ok = -1;
+
+ if ((pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL)) == NULL ||
+ EVP_PKEY_paramgen_init(pctx) <= 0 ||
+ EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) <= 0 ||
+ EVP_PKEY_paramgen(pctx, &p) <= 0) {
+ log_debug("%s: EVP_PKEY_paramgen", __func__);
+ goto fail;
+ }
+
+ if ((kctx = EVP_PKEY_CTX_new(p, NULL)) == NULL ||
+ EVP_PKEY_keygen_init(kctx) <= 0 || EVP_PKEY_keygen(kctx, &k) <= 0) {
+ log_debug("%s: EVP_PKEY_keygen", __func__);
+ goto fail;
+ }
+
+ if ((ec = EVP_PKEY_get0_EC_KEY(k)) == NULL ||
+ (d = EC_KEY_get0_private_key(ec)) == NULL ||
+ (n = BN_num_bytes(d)) < 0 || (size_t)n > sizeof(key->d) ||
+ (n = BN_bn2bin(d, key->d)) < 0 || (size_t)n > sizeof(key->d)) {
+ log_debug("%s: EC_KEY_get0_private_key", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (p != NULL)
+ EVP_PKEY_free(p);
+ if (k != NULL)
+ EVP_PKEY_free(k);
+ if (pctx != NULL)
+ EVP_PKEY_CTX_free(pctx);
+ if (kctx != NULL)
+ EVP_PKEY_CTX_free(kctx);
+
+ return (ok);
+}
+
+EVP_PKEY *
+es256_pk_to_EVP_PKEY(const es256_pk_t *k)
+{
+ BN_CTX *bnctx = NULL;
+ EC_KEY *ec = NULL;
+ EC_POINT *q = NULL;
+ EVP_PKEY *pkey = NULL;
+ BIGNUM *x = NULL;
+ BIGNUM *y = NULL;
+ const EC_GROUP *g = NULL;
+ const int nid = NID_X9_62_prime256v1;
+ int ok = -1;
+
+ if ((bnctx = BN_CTX_new()) == NULL ||
+ (x = BN_CTX_get(bnctx)) == NULL ||
+ (y = BN_CTX_get(bnctx)) == NULL)
+ goto fail;
+
+ if (BN_bin2bn(k->x, sizeof(k->x), x) == NULL ||
+ BN_bin2bn(k->y, sizeof(k->y), y) == NULL) {
+ log_debug("%s: BN_bin2bn", __func__);
+ goto fail;
+ }
+
+ if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
+ (g = EC_KEY_get0_group(ec)) == NULL) {
+ log_debug("%s: EC_KEY init", __func__);
+ goto fail;
+ }
+
+ if ((q = EC_POINT_new(g)) == NULL ||
+ EC_POINT_set_affine_coordinates_GFp(g, q, x, y, bnctx) == 0 ||
+ EC_KEY_set_public_key(ec, q) == 0) {
+ log_debug("%s: EC_KEY_set_public_key", __func__);
+ goto fail;
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL ||
+ EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
+ log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
+ goto fail;
+ }
+
+ ec = NULL; /* at this point, ec belongs to evp */
+
+ ok = 0;
+fail:
+ if (bnctx != NULL)
+ BN_CTX_free(bnctx);
+ if (ec != NULL)
+ EC_KEY_free(ec);
+ if (q != NULL)
+ EC_POINT_free(q);
+ if (ok < 0 && pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+
+ return (pkey);
+}
+
+int
+es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec)
+{
+ BN_CTX *ctx = NULL;
+ BIGNUM *x = NULL;
+ BIGNUM *y = NULL;
+ const EC_POINT *q = NULL;
+ const EC_GROUP *g = NULL;
+ int ok = FIDO_ERR_INTERNAL;
+ int n;
+
+ if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
+ (g = EC_KEY_get0_group(ec)) == NULL)
+ goto fail;
+
+ if ((ctx = BN_CTX_new()) == NULL ||
+ (x = BN_CTX_get(ctx)) == NULL ||
+ (y = BN_CTX_get(ctx)) == NULL)
+ goto fail;
+
+ if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, ctx) == 0 ||
+ (n = BN_num_bytes(x)) < 0 || (size_t)n > sizeof(pk->x) ||
+ (n = BN_num_bytes(y)) < 0 || (size_t)n > sizeof(pk->y)) {
+ log_debug("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
+ goto fail;
+ }
+
+ if ((n = BN_bn2bin(x, pk->x)) < 0 || (size_t)n > sizeof(pk->x) ||
+ (n = BN_bn2bin(y, pk->y)) < 0 || (size_t)n > sizeof(pk->y)) {
+ log_debug("%s: BN_bn2bin", __func__);
+ goto fail;
+ }
+
+ ok = FIDO_OK;
+fail:
+ if (ctx != NULL)
+ BN_CTX_free(ctx);
+
+ return (ok);
+}
+
+EVP_PKEY *
+es256_sk_to_EVP_PKEY(const es256_sk_t *k)
+{
+ BN_CTX *bnctx = NULL;
+ EC_KEY *ec = NULL;
+ EVP_PKEY *pkey = NULL;
+ BIGNUM *d = NULL;
+ const int nid = NID_X9_62_prime256v1;
+ int ok = -1;
+
+ if ((bnctx = BN_CTX_new()) == NULL || (d = BN_CTX_get(bnctx)) == NULL ||
+ BN_bin2bn(k->d, sizeof(k->d), d) == NULL) {
+ log_debug("%s: BN_bin2bn", __func__);
+ goto fail;
+ }
+
+ if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
+ EC_KEY_set_private_key(ec, d) == 0) {
+ log_debug("%s: EC_KEY_set_private_key", __func__);
+ goto fail;
+ }
+
+ if ((pkey = EVP_PKEY_new()) == NULL ||
+ EVP_PKEY_assign_EC_KEY(pkey, ec) == 0) {
+ log_debug("%s: EVP_PKEY_assign_EC_KEY", __func__);
+ goto fail;
+ }
+
+ ec = NULL; /* at this point, ec belongs to evp */
+
+ ok = 0;
+fail:
+ if (bnctx != NULL)
+ BN_CTX_free(bnctx);
+ if (ec != NULL)
+ EC_KEY_free(ec);
+ if (ok < 0 && pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+
+ return (pkey);
+}
+
+int
+es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk)
+{
+ BIGNUM *d = NULL;
+ EC_KEY *ec = NULL;
+ EC_POINT *q = NULL;
+ const EC_GROUP *g = NULL;
+ const int nid = NID_X9_62_prime256v1;
+ int ok = -1;
+
+ if ((d = BN_bin2bn(sk->d, (int)sizeof(sk->d), NULL)) == NULL ||
+ (ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
+ (g = EC_KEY_get0_group(ec)) == NULL ||
+ (q = EC_POINT_new(g)) == NULL) {
+ log_debug("%s: get", __func__);
+ goto fail;
+ }
+
+ if (EC_POINT_mul(g, q, d, NULL, NULL, NULL) == 0 ||
+ EC_KEY_set_public_key(ec, q) == 0 ||
+ es256_pk_from_EC_KEY(pk, ec) != FIDO_OK) {
+ log_debug("%s: set", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (d != NULL)
+ BN_clear_free(d);
+ if (q != NULL)
+ EC_POINT_free(q);
+ if (ec != NULL)
+ EC_KEY_free(ec);
+
+ return (ok);
+}
diff --git a/lib/libfido2/src/export.llvm b/lib/libfido2/src/export.llvm
new file mode 100644
index 00000000000..ef99a2617df
--- /dev/null
+++ b/lib/libfido2/src/export.llvm
@@ -0,0 +1,178 @@
+_eddsa_pk_free
+_eddsa_pk_from_EVP_PKEY
+_eddsa_pk_from_ptr
+_eddsa_pk_new
+_eddsa_pk_to_EVP_PKEY
+_es256_pk_free
+_es256_pk_from_EC_KEY
+_es256_pk_from_ptr
+_es256_pk_new
+_es256_pk_to_EVP_PKEY
+_fido_assert_allow_cred
+_fido_assert_authdata_len
+_fido_assert_authdata_ptr
+_fido_assert_clientdata_hash_len
+_fido_assert_clientdata_hash_ptr
+_fido_assert_count
+_fido_assert_flags
+_fido_assert_free
+_fido_assert_hmac_secret_len
+_fido_assert_hmac_secret_ptr
+_fido_assert_id_len
+_fido_assert_id_ptr
+_fido_assert_new
+_fido_assert_rp_id
+_fido_assert_set_authdata
+_fido_assert_set_authdata_raw
+_fido_assert_set_clientdata_hash
+_fido_assert_set_count
+_fido_assert_set_extensions
+_fido_assert_set_hmac_salt
+_fido_assert_set_options
+_fido_assert_set_rp
+_fido_assert_set_sig
+_fido_assert_set_up
+_fido_assert_set_uv
+_fido_assert_sigcount
+_fido_assert_sig_len
+_fido_assert_sig_ptr
+_fido_assert_user_display_name
+_fido_assert_user_icon
+_fido_assert_user_id_len
+_fido_assert_user_id_ptr
+_fido_assert_user_name
+_fido_assert_verify
+_fido_bio_dev_enroll_begin
+_fido_bio_dev_enroll_cancel
+_fido_bio_dev_enroll_continue
+_fido_bio_dev_enroll_remove
+_fido_bio_dev_get_info
+_fido_bio_dev_get_template_array
+_fido_bio_dev_set_template_name
+_fido_bio_enroll_free
+_fido_bio_enroll_last_status
+_fido_bio_enroll_new
+_fido_bio_enroll_remaining_samples
+_fido_bio_info_free
+_fido_bio_info_max_samples
+_fido_bio_info_new
+_fido_bio_info_type
+_fido_bio_template
+_fido_bio_template_array_count
+_fido_bio_template_array_free
+_fido_bio_template_array_new
+_fido_bio_template_free
+_fido_bio_template_id_len
+_fido_bio_template_id_ptr
+_fido_bio_template_name
+_fido_bio_template_new
+_fido_bio_template_set_id
+_fido_bio_template_set_name
+_fido_cbor_info_aaguid_len
+_fido_cbor_info_aaguid_ptr
+_fido_cbor_info_extensions_len
+_fido_cbor_info_extensions_ptr
+_fido_cbor_info_free
+_fido_cbor_info_maxmsgsiz
+_fido_cbor_info_new
+_fido_cbor_info_options_len
+_fido_cbor_info_options_name_ptr
+_fido_cbor_info_options_value_ptr
+_fido_cbor_info_protocols_len
+_fido_cbor_info_protocols_ptr
+_fido_cbor_info_versions_len
+_fido_cbor_info_versions_ptr
+_fido_cred_authdata_len
+_fido_cred_authdata_ptr
+_fido_cred_clientdata_hash_len
+_fido_cred_clientdata_hash_ptr
+_fido_cred_display_name
+_fido_cred_exclude
+_fido_cred_flags
+_fido_cred_fmt
+_fido_cred_free
+_fido_cred_id_len
+_fido_cred_id_ptr
+_fido_credman_del_dev_rk
+_fido_credman_get_dev_metadata
+_fido_credman_get_dev_rk
+_fido_credman_get_dev_rp
+_fido_credman_metadata_free
+_fido_credman_metadata_new
+_fido_credman_rk
+_fido_credman_rk_count
+_fido_credman_rk_existing
+_fido_credman_rk_free
+_fido_credman_rk_new
+_fido_credman_rk_remaining
+_fido_credman_rp_count
+_fido_credman_rp_free
+_fido_credman_rp_id
+_fido_credman_rp_id_hash_len
+_fido_credman_rp_id_hash_ptr
+_fido_credman_rp_name
+_fido_credman_rp_new
+_fido_cred_new
+_fido_cred_pubkey_len
+_fido_cred_pubkey_ptr
+_fido_cred_rp_id
+_fido_cred_rp_name
+_fido_cred_set_authdata
+_fido_cred_set_authdata_raw
+_fido_cred_set_clientdata_hash
+_fido_cred_set_extensions
+_fido_cred_set_fmt
+_fido_cred_set_options
+_fido_cred_set_rk
+_fido_cred_set_rp
+_fido_cred_set_sig
+_fido_cred_set_type
+_fido_cred_set_user
+_fido_cred_set_uv
+_fido_cred_set_x509
+_fido_cred_sig_len
+_fido_cred_sig_ptr
+_fido_cred_type
+_fido_cred_user_id_len
+_fido_cred_user_id_ptr
+_fido_cred_user_name
+_fido_cred_verify
+_fido_cred_verify_self
+_fido_cred_x5c_len
+_fido_cred_x5c_ptr
+_fido_dev_build
+_fido_dev_cancel
+_fido_dev_close
+_fido_dev_flags
+_fido_dev_force_fido2
+_fido_dev_force_u2f
+_fido_dev_free
+_fido_dev_get_assert
+_fido_dev_get_cbor_info
+_fido_dev_get_retry_count
+_fido_dev_info_free
+_fido_dev_info_manifest
+_fido_dev_info_manufacturer_string
+_fido_dev_info_new
+_fido_dev_info_path
+_fido_dev_info_product
+_fido_dev_info_product_string
+_fido_dev_info_ptr
+_fido_dev_info_vendor
+_fido_dev_is_fido2
+_fido_dev_major
+_fido_dev_make_cred
+_fido_dev_minor
+_fido_dev_new
+_fido_dev_open
+_fido_dev_protocol
+_fido_dev_reset
+_fido_dev_set_io_functions
+_fido_dev_set_pin
+_fido_init
+_fido_strerr
+_rs256_pk_free
+_rs256_pk_from_ptr
+_rs256_pk_from_RSA
+_rs256_pk_new
+_rs256_pk_to_EVP_PKEY
diff --git a/lib/libfido2/src/extern.h b/lib/libfido2/src/extern.h
new file mode 100644
index 00000000000..2f69094f33c
--- /dev/null
+++ b/lib/libfido2/src/extern.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2018 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 _EXTERN_H
+#define _EXTERN_H
+
+/* aes256 */
+int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
+int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
+
+/* cbor encoding functions */
+cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t);
+cbor_item_t *encode_assert_options(fido_opt_t, fido_opt_t);
+cbor_item_t *encode_change_pin_auth(const fido_blob_t *, const fido_blob_t *,
+ const fido_blob_t *);
+cbor_item_t *encode_extensions(int);
+cbor_item_t *encode_hmac_secret_param(const fido_blob_t *, const es256_pk_t *,
+ const fido_blob_t *);
+cbor_item_t *encode_options(fido_opt_t, fido_opt_t);
+cbor_item_t *encode_pin_auth(const fido_blob_t *, const fido_blob_t *);
+cbor_item_t *encode_pin_enc(const fido_blob_t *, const fido_blob_t *);
+cbor_item_t *encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *);
+cbor_item_t *encode_pin_opt(void);
+cbor_item_t *encode_pubkey(const fido_blob_t *);
+cbor_item_t *encode_pubkey_list(const fido_blob_array_t *);
+cbor_item_t *encode_pubkey_param(int);
+cbor_item_t *encode_rp_entity(const fido_rp_t *);
+cbor_item_t *encode_set_pin_auth(const fido_blob_t *, const fido_blob_t *);
+cbor_item_t *encode_user_entity(const fido_user_t *);
+cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
+
+/* cbor decoding functions */
+int decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
+int decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
+ fido_authdata_t *, fido_attcred_t *, int *);
+int decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
+ fido_authdata_t *, int *, fido_blob_t *);
+int decode_cred_id(const cbor_item_t *, fido_blob_t *);
+int decode_fmt(const cbor_item_t *, char **);
+int decode_pubkey(const cbor_item_t *, int *, void *);
+int decode_rp_entity(const cbor_item_t *, fido_rp_t *);
+int decode_uint64(const cbor_item_t *, uint64_t *);
+int decode_user(const cbor_item_t *, fido_user_t *);
+int es256_pk_decode(const cbor_item_t *, es256_pk_t *);
+int rs256_pk_decode(const cbor_item_t *, rs256_pk_t *);
+int eddsa_pk_decode(const cbor_item_t *, eddsa_pk_t *);
+
+/* auxiliary cbor routines */
+int cbor_add_bool(cbor_item_t *, const char *, fido_opt_t);
+int cbor_add_bytestring(cbor_item_t *, const char *, const unsigned char *,
+ size_t);
+int cbor_add_string(cbor_item_t *, const char *, const char *);
+int cbor_array_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
+ void *));
+int cbor_build_frame(uint8_t, cbor_item_t *[], size_t, fido_blob_t *);
+int cbor_bytestring_copy(const cbor_item_t *, unsigned char **, size_t *);
+int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
+ const cbor_item_t *, void *));
+int cbor_string_copy(const cbor_item_t *, char **);
+int parse_cbor_reply(const unsigned char *, size_t, void *,
+ int(*)(const cbor_item_t *, const cbor_item_t *, void *));
+int add_cbor_pin_params(fido_dev_t *, const fido_blob_t *, const es256_pk_t *,
+ const fido_blob_t *,const char *, cbor_item_t **, cbor_item_t **);
+void cbor_vector_free(cbor_item_t **, size_t);
+
+#ifndef nitems
+#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+/* buf */
+int buf_read(const unsigned char **, size_t *, void *, size_t);
+int buf_write(unsigned char **, size_t *, const void *, size_t);
+
+/* hid i/o */
+void *hid_open(const char *);
+void hid_close(void *);
+int hid_read(void *, unsigned char *, size_t, int);
+int hid_write(void *, const unsigned char *, size_t);
+
+/* generic i/o */
+int rx(fido_dev_t *, uint8_t, void *, size_t, int);
+int tx(fido_dev_t *, uint8_t, const void *, size_t);
+int rx_cbor_status(fido_dev_t *, int);
+
+/* log */
+#ifdef FIDO_NO_DIAGNOSTIC
+#define log_init(...) do { /* nothing */ } while (0)
+#define log_debug(...) do { /* nothing */ } while (0)
+#define log_xxd(...) do { /* nothing */ } while (0)
+#else
+#ifdef __GNUC__
+void log_init(void);
+void log_debug(const char *, ...) __attribute__((__format__ (printf, 1, 2)));
+void log_xxd(const void *, size_t);
+#else
+void log_init(void);
+void log_debug(const char *, ...);
+void log_xxd(const void *, size_t);
+#endif /* __GNUC__ */
+#endif /* FIDO_NO_DIAGNOSTIC */
+
+/* u2f */
+int u2f_register(fido_dev_t *, fido_cred_t *, int);
+int u2f_authenticate(fido_dev_t *, fido_assert_t *, int);
+
+/* unexposed fido ops */
+int fido_dev_authkey(fido_dev_t *, es256_pk_t *);
+int fido_dev_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *,
+ const es256_pk_t *, fido_blob_t *);
+int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **);
+
+/* misc */
+void fido_assert_reset_rx(fido_assert_t *);
+void fido_assert_reset_tx(fido_assert_t *);
+void fido_cred_reset_rx(fido_cred_t *);
+void fido_cred_reset_tx(fido_cred_t *);
+int check_rp_id(const char *, const unsigned char *);
+int check_flags(uint8_t, fido_opt_t, fido_opt_t);
+
+/* crypto */
+int verify_sig_es256(const fido_blob_t *, const es256_pk_t *,
+ const fido_blob_t *);
+int verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *,
+ const fido_blob_t *);
+int verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *,
+ const fido_blob_t *);
+
+#endif /* !_EXTERN_H */
diff --git a/lib/libfido2/src/fido.h b/lib/libfido2/src/fido.h
new file mode 100644
index 00000000000..c1f7d831080
--- /dev/null
+++ b/lib/libfido2/src/fido.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2018 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_H
+#define _FIDO_H
+
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef void *fido_dev_io_open_t(const char *);
+typedef void fido_dev_io_close_t(void *);
+typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
+typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
+
+typedef struct fido_dev_io {
+ fido_dev_io_open_t *open;
+ fido_dev_io_close_t *close;
+ fido_dev_io_read_t *read;
+ fido_dev_io_write_t *write;
+} fido_dev_io_t;
+
+typedef enum {
+ FIDO_OPT_OMIT = 0, /* use authenticator's default */
+ FIDO_OPT_FALSE, /* explicitly set option to false */
+ FIDO_OPT_TRUE, /* explicitly set option to true */
+} fido_opt_t;
+
+#ifdef _FIDO_INTERNAL
+#include <cbor.h>
+#include <limits.h>
+
+#include "blob.h"
+#if !defined(__OpenBSD__)
+#include "../openbsd-compat/openbsd-compat.h"
+#endif
+#include "iso7816.h"
+#include "types.h"
+#include "extern.h"
+#endif
+
+#include "fido/err.h"
+#include "fido/param.h"
+
+#ifndef _FIDO_INTERNAL
+typedef struct fido_assert fido_assert_t;
+typedef struct fido_cbor_info fido_cbor_info_t;
+typedef struct fido_cred fido_cred_t;
+typedef struct fido_dev fido_dev_t;
+typedef struct fido_dev_info fido_dev_info_t;
+typedef struct es256_pk es256_pk_t;
+typedef struct es256_sk es256_sk_t;
+typedef struct rs256_pk rs256_pk_t;
+typedef struct eddsa_pk eddsa_pk_t;
+#endif
+
+fido_assert_t *fido_assert_new(void);
+fido_cred_t *fido_cred_new(void);
+fido_dev_t *fido_dev_new(void);
+fido_dev_info_t *fido_dev_info_new(size_t);
+fido_cbor_info_t *fido_cbor_info_new(void);
+
+void fido_assert_free(fido_assert_t **);
+void fido_cbor_info_free(fido_cbor_info_t **);
+void fido_cred_free(fido_cred_t **);
+void fido_dev_force_fido2(fido_dev_t *);
+void fido_dev_force_u2f(fido_dev_t *);
+void fido_dev_free(fido_dev_t **);
+void fido_dev_info_free(fido_dev_info_t **, size_t);
+
+/* fido_init() flags. */
+#define FIDO_DEBUG 0x01
+
+void fido_init(int);
+
+const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
+const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t);
+
+char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *);
+char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *);
+char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *);
+const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *);
+const char *fido_assert_rp_id(const fido_assert_t *);
+const char *fido_assert_user_display_name(const fido_assert_t *, size_t);
+const char *fido_assert_user_icon(const fido_assert_t *, size_t);
+const char *fido_assert_user_name(const fido_assert_t *, size_t);
+const char *fido_cred_display_name(const fido_cred_t *);
+const char *fido_cred_fmt(const fido_cred_t *);
+const char *fido_cred_rp_id(const fido_cred_t *);
+const char *fido_cred_rp_name(const fido_cred_t *);
+const char *fido_cred_user_name(const fido_cred_t *);
+const char *fido_dev_info_manufacturer_string(const fido_dev_info_t *);
+const char *fido_dev_info_path(const fido_dev_info_t *);
+const char *fido_dev_info_product_string(const fido_dev_info_t *);
+const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t);
+const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
+const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
+const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_clientdata_hash_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_id_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
+
+int fido_assert_allow_cred(fido_assert_t *, const unsigned char *, size_t);
+int fido_assert_set_authdata(fido_assert_t *, size_t, const unsigned char *,
+ size_t);
+int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *,
+ size_t);
+int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
+ size_t);
+int fido_assert_set_count(fido_assert_t *, size_t);
+int fido_assert_set_extensions(fido_assert_t *, int);
+int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t);
+int fido_assert_set_options(fido_assert_t *, bool, bool) __attribute__((__deprecated__));
+int fido_assert_set_rp(fido_assert_t *, const char *);
+int fido_assert_set_up(fido_assert_t *, fido_opt_t);
+int fido_assert_set_uv(fido_assert_t *, fido_opt_t);
+int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t);
+int fido_assert_verify(const fido_assert_t *, size_t, int, const void *);
+int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t);
+int fido_cred_set_authdata(fido_cred_t *, const unsigned char *, size_t);
+int fido_cred_set_authdata_raw(fido_cred_t *, const unsigned char *, size_t);
+int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t);
+int fido_cred_set_extensions(fido_cred_t *, int);
+int fido_cred_set_fmt(fido_cred_t *, const char *);
+int fido_cred_set_options(fido_cred_t *, bool, bool) __attribute__((__deprecated__));
+int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
+int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
+int fido_cred_set_sig(fido_cred_t *, const unsigned char *, size_t);
+int fido_cred_set_type(fido_cred_t *, int);
+int fido_cred_set_uv(fido_cred_t *, fido_opt_t);
+int fido_cred_type(const fido_cred_t *);
+int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
+ const char *, const char *, const char *);
+int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
+int fido_cred_verify(const fido_cred_t *);
+int fido_cred_verify_self(const fido_cred_t *);
+int fido_dev_cancel(fido_dev_t *);
+int fido_dev_close(fido_dev_t *);
+int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
+int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
+int fido_dev_get_retry_count(fido_dev_t *, int *);
+int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
+int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *);
+int fido_dev_open(fido_dev_t *, const char *);
+int fido_dev_reset(fido_dev_t *);
+int fido_dev_set_io_functions(fido_dev_t *, const fido_dev_io_t *);
+int fido_dev_set_pin(fido_dev_t *, const char *, const char *);
+
+size_t fido_assert_authdata_len(const fido_assert_t *, size_t);
+size_t fido_assert_clientdata_hash_len(const fido_assert_t *);
+size_t fido_assert_count(const fido_assert_t *);
+size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t);
+size_t fido_assert_id_len(const fido_assert_t *, size_t);
+size_t fido_assert_sig_len(const fido_assert_t *, size_t);
+size_t fido_assert_user_id_len(const fido_assert_t *, size_t);
+size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *);
+size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *);
+size_t fido_cbor_info_options_len(const fido_cbor_info_t *);
+size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *);
+size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
+size_t fido_cred_authdata_len(const fido_cred_t *);
+size_t fido_cred_clientdata_hash_len(const fido_cred_t *);
+size_t fido_cred_id_len(const fido_cred_t *);
+size_t fido_cred_user_id_len(const fido_cred_t *);
+size_t fido_cred_pubkey_len(const fido_cred_t *);
+size_t fido_cred_sig_len(const fido_cred_t *);
+size_t fido_cred_x5c_len(const fido_cred_t *);
+
+uint8_t fido_assert_flags(const fido_assert_t *, size_t);
+uint32_t fido_assert_sigcount(const fido_assert_t *, size_t);
+uint8_t fido_cred_flags(const fido_cred_t *);
+uint8_t fido_dev_protocol(const fido_dev_t *);
+uint8_t fido_dev_major(const fido_dev_t *);
+uint8_t fido_dev_minor(const fido_dev_t *);
+uint8_t fido_dev_build(const fido_dev_t *);
+uint8_t fido_dev_flags(const fido_dev_t *);
+int16_t fido_dev_info_vendor(const fido_dev_info_t *);
+int16_t fido_dev_info_product(const fido_dev_info_t *);
+uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
+
+bool fido_dev_is_fido2(const fido_dev_t *);
+
+#endif /* !_FIDO_H */
diff --git a/lib/libfido2/src/fido/bio.h b/lib/libfido2/src/fido/bio.h
new file mode 100644
index 00000000000..31dffe49df1
--- /dev/null
+++ b/lib/libfido2/src/fido/bio.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 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_BIO_H
+#define _FIDO_BIO_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "fido/err.h"
+#include "fido/param.h"
+
+#ifdef _FIDO_INTERNAL
+struct fido_bio_template {
+ fido_blob_t id;
+ char *name;
+};
+
+struct fido_bio_template_array {
+ struct fido_bio_template *ptr;
+ size_t n_alloc; /* number of allocated entries */
+ size_t n_rx; /* number of populated entries */
+};
+
+struct fido_bio_enroll {
+ uint8_t remaining_samples;
+ uint8_t last_status;
+ fido_blob_t *token;
+};
+
+struct fido_bio_info {
+ uint8_t type;
+ uint8_t max_samples;
+};
+#endif
+
+typedef struct fido_bio_template fido_bio_template_t;
+typedef struct fido_bio_template_array fido_bio_template_array_t;
+typedef struct fido_bio_enroll fido_bio_enroll_t;
+typedef struct fido_bio_info fido_bio_info_t;
+
+#define FIDO_BIO_ENROLL_FP_GOOD 0x00
+#define FIDO_BIO_ENROLL_FP_TOO_HIGH 0x01
+#define FIDO_BIO_ENROLL_FP_TOO_LOW 0x02
+#define FIDO_BIO_ENROLL_FP_TOO_LEFT 0x03
+#define FIDO_BIO_ENROLL_FP_TOO_RIGHT 0x04
+#define FIDO_BIO_ENROLL_FP_TOO_FAST 0x05
+#define FIDO_BIO_ENROLL_FP_TOO_SLOW 0x06
+#define FIDO_BIO_ENROLL_FP_POOR_QUALITY 0x07
+#define FIDO_BIO_ENROLL_FP_TOO_SKEWED 0x08
+#define FIDO_BIO_ENROLL_FP_TOO_SHORT 0x09
+#define FIDO_BIO_ENROLL_FP_MERGE_FAILURE 0x0a
+#define FIDO_BIO_ENROLL_FP_EXISTS 0x0b
+#define FIDO_BIO_ENROLL_FP_DATABASE_FULL 0x0c
+#define FIDO_BIO_ENROLL_NO_USER_ACTIVITY 0x0d
+#define FIDO_BIO_ENROLL_NO_USER_PRESENCE_TRANSITION 0x0e
+
+const char *fido_bio_template_name(const fido_bio_template_t *);
+const fido_bio_template_t *fido_bio_template(const fido_bio_template_array_t *,
+ size_t);
+const unsigned char *fido_bio_template_id_ptr(const fido_bio_template_t *);
+fido_bio_enroll_t *fido_bio_enroll_new(void);
+fido_bio_info_t *fido_bio_info_new(void);
+fido_bio_template_array_t *fido_bio_template_array_new(void);
+fido_bio_template_t *fido_bio_template_new(void);
+int fido_bio_dev_enroll_begin(fido_dev_t *, fido_bio_template_t *,
+ fido_bio_enroll_t *, uint32_t, const char *);
+int fido_bio_dev_enroll_cancel(fido_dev_t *);
+int fido_bio_dev_enroll_continue(fido_dev_t *, const fido_bio_template_t *,
+ fido_bio_enroll_t *, uint32_t);
+int fido_bio_dev_enroll_remove(fido_dev_t *, const fido_bio_template_t *,
+ const char *);
+int fido_bio_dev_get_info(fido_dev_t *, fido_bio_info_t *);
+int fido_bio_dev_get_template_array(fido_dev_t *, fido_bio_template_array_t *,
+ const char *);
+int fido_bio_dev_set_template_name(fido_dev_t *, const fido_bio_template_t *,
+ const char *);
+int fido_bio_template_set_id(fido_bio_template_t *, const unsigned char *,
+ size_t);
+int fido_bio_template_set_name(fido_bio_template_t *, const char *);
+size_t fido_bio_template_array_count(const fido_bio_template_array_t *);
+size_t fido_bio_template_id_len(const fido_bio_template_t *);
+uint8_t fido_bio_enroll_last_status(const fido_bio_enroll_t *);
+uint8_t fido_bio_enroll_remaining_samples(const fido_bio_enroll_t *);
+uint8_t fido_bio_info_max_samples(const fido_bio_info_t *);
+uint8_t fido_bio_info_type(const fido_bio_info_t *);
+void fido_bio_enroll_free(fido_bio_enroll_t **);
+void fido_bio_info_free(fido_bio_info_t **);
+void fido_bio_template_array_free(fido_bio_template_array_t **);
+void fido_bio_template_free(fido_bio_template_t **);
+
+#endif /* !_FIDO_BIO_H */
diff --git a/lib/libfido2/src/fido/credman.h b/lib/libfido2/src/fido/credman.h
new file mode 100644
index 00000000000..1c7cafef723
--- /dev/null
+++ b/lib/libfido2/src/fido/credman.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 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_CREDMAN_H
+#define _FIDO_CREDMAN_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "fido/err.h"
+#include "fido/param.h"
+
+#ifdef _FIDO_INTERNAL
+struct fido_credman_metadata {
+ uint64_t rk_existing;
+ uint64_t rk_remaining;
+};
+
+struct fido_credman_single_rp {
+ fido_rp_t rp_entity;
+ fido_blob_t rp_id_hash;
+};
+
+struct fido_credman_rp {
+ struct fido_credman_single_rp *ptr;
+ size_t n_alloc; /* number of allocated entries */
+ size_t n_rx; /* number of populated entries */
+};
+
+struct fido_credman_rk {
+ fido_cred_t *ptr;
+ size_t n_alloc; /* number of allocated entries */
+ size_t n_rx; /* number of populated entries */
+};
+#endif
+
+typedef struct fido_credman_metadata fido_credman_metadata_t;
+typedef struct fido_credman_rk fido_credman_rk_t;
+typedef struct fido_credman_rp fido_credman_rp_t;
+
+const char *fido_credman_rp_id(const fido_credman_rp_t *, size_t);
+const char *fido_credman_rp_name(const fido_credman_rp_t *, size_t);
+
+const fido_cred_t *fido_credman_rk(const fido_credman_rk_t *, size_t);
+const unsigned char *fido_credman_rp_id_hash_ptr(const fido_credman_rp_t *,
+ size_t);
+
+fido_credman_metadata_t *fido_credman_metadata_new(void);
+fido_credman_rk_t *fido_credman_rk_new(void);
+fido_credman_rp_t *fido_credman_rp_new(void);
+
+int fido_credman_del_dev_rk(fido_dev_t *, const unsigned char *, size_t,
+ const char *);
+int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *,
+ const char *);
+int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *,
+ const char *);
+int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *);
+
+size_t fido_credman_rk_count(const fido_credman_rk_t *);
+size_t fido_credman_rp_count(const fido_credman_rp_t *);
+size_t fido_credman_rp_id_hash_len(const fido_credman_rp_t *, size_t);
+
+uint64_t fido_credman_rk_existing(const fido_credman_metadata_t *);
+uint64_t fido_credman_rk_remaining(const fido_credman_metadata_t *);
+
+void fido_credman_metadata_free(fido_credman_metadata_t **);
+void fido_credman_rk_free(fido_credman_rk_t **);
+void fido_credman_rp_free(fido_credman_rp_t **);
+
+#endif /* !_FIDO_CREDMAN_H */
diff --git a/lib/libfido2/src/fido/eddsa.h b/lib/libfido2/src/fido/eddsa.h
new file mode 100644
index 00000000000..9de272d584b
--- /dev/null
+++ b/lib/libfido2/src/fido/eddsa.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2019 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_EDDSA_H
+#define _FIDO_EDDSA_H
+
+#include <openssl/ec.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+eddsa_pk_t *eddsa_pk_new(void);
+void eddsa_pk_free(eddsa_pk_t **);
+EVP_PKEY *eddsa_pk_to_EVP_PKEY(const eddsa_pk_t *);
+
+int eddsa_pk_from_EVP_PKEY(eddsa_pk_t *, const EVP_PKEY *);
+int eddsa_pk_from_ptr(eddsa_pk_t *, const void *, size_t);
+
+#ifdef _FIDO_INTERNAL
+
+#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
+#define EVP_PKEY_ED25519 EVP_PKEY_NONE
+int EVP_PKEY_get_raw_public_key(const EVP_PKEY *, unsigned char *, size_t *);
+EVP_PKEY *EVP_PKEY_new_raw_public_key(int, ENGINE *, const unsigned char *,
+ size_t);
+int EVP_DigestVerify(EVP_MD_CTX *, const unsigned char *, size_t,
+ const unsigned char *, size_t);
+#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+EVP_MD_CTX *EVP_MD_CTX_new(void);
+void EVP_MD_CTX_free(EVP_MD_CTX *);
+#endif
+
+#endif /* _FIDO_INTERNAL */
+
+#endif /* !_FIDO_EDDSA_H */
diff --git a/lib/libfido2/src/fido/err.h b/lib/libfido2/src/fido/err.h
new file mode 100644
index 00000000000..11f52bcff92
--- /dev/null
+++ b/lib/libfido2/src/fido/err.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018 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_ERR_H
+#define _FIDO_ERR_H
+
+#define FIDO_ERR_SUCCESS 0x00
+#define FIDO_ERR_INVALID_COMMAND 0x01
+#define FIDO_ERR_INVALID_PARAMETER 0x02
+#define FIDO_ERR_INVALID_LENGTH 0x03
+#define FIDO_ERR_INVALID_SEQ 0x04
+#define FIDO_ERR_TIMEOUT 0x05
+#define FIDO_ERR_CHANNEL_BUSY 0x06
+#define FIDO_ERR_LOCK_REQUIRED 0x0a
+#define FIDO_ERR_INVALID_CHANNEL 0x0b
+#define FIDO_ERR_CBOR_UNEXPECTED_TYPE 0x11
+#define FIDO_ERR_INVALID_CBOR 0x12
+#define FIDO_ERR_MISSING_PARAMETER 0x14
+#define FIDO_ERR_LIMIT_EXCEEDED 0x15
+#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16
+#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19
+#define FIDO_ERR_PROCESSING 0x21
+#define FIDO_ERR_INVALID_CREDENTIAL 0x22
+#define FIDO_ERR_USER_ACTION_PENDING 0x23
+#define FIDO_ERR_OPERATION_PENDING 0x24
+#define FIDO_ERR_NO_OPERATIONS 0x25
+#define FIDO_ERR_UNSUPPORTED_ALGORITHM 0x26
+#define FIDO_ERR_OPERATION_DENIED 0x27
+#define FIDO_ERR_KEY_STORE_FULL 0x28
+#define FIDO_ERR_NOT_BUSY 0x29
+#define FIDO_ERR_NO_OPERATION_PENDING 0x2a
+#define FIDO_ERR_UNSUPPORTED_OPTION 0x2b
+#define FIDO_ERR_INVALID_OPTION 0x2c
+#define FIDO_ERR_KEEPALIVE_CANCEL 0x2d
+#define FIDO_ERR_NO_CREDENTIALS 0x2e
+#define FIDO_ERR_USER_ACTION_TIMEOUT 0x2f
+#define FIDO_ERR_NOT_ALLOWED 0x30
+#define FIDO_ERR_PIN_INVALID 0x31
+#define FIDO_ERR_PIN_BLOCKED 0x32
+#define FIDO_ERR_PIN_AUTH_INVALID 0x33
+#define FIDO_ERR_PIN_AUTH_BLOCKED 0x34
+#define FIDO_ERR_PIN_NOT_SET 0x35
+#define FIDO_ERR_PIN_REQUIRED 0x36
+#define FIDO_ERR_PIN_POLICY_VIOLATION 0x37
+#define FIDO_ERR_PIN_TOKEN_EXPIRED 0x38
+#define FIDO_ERR_REQUEST_TOO_LARGE 0x39
+#define FIDO_ERR_ACTION_TIMEOUT 0x3a
+#define FIDO_ERR_UP_REQUIRED 0x3b
+#define FIDO_ERR_ERR_OTHER 0x7f
+#define FIDO_ERR_SPEC_LAST 0xdf
+
+/* defined internally */
+#define FIDO_OK FIDO_ERR_SUCCESS
+#define FIDO_ERR_TX -1
+#define FIDO_ERR_RX -2
+#define FIDO_ERR_RX_NOT_CBOR -3
+#define FIDO_ERR_RX_INVALID_CBOR -4
+#define FIDO_ERR_INVALID_PARAM -5
+#define FIDO_ERR_INVALID_SIG -6
+#define FIDO_ERR_INVALID_ARGUMENT -7
+#define FIDO_ERR_USER_PRESENCE_REQUIRED -8
+#define FIDO_ERR_INTERNAL -9
+
+const char *fido_strerr(int);
+
+#endif /* _FIDO_ERR_H */
diff --git a/lib/libfido2/src/fido/es256.h b/lib/libfido2/src/fido/es256.h
new file mode 100644
index 00000000000..d3d13dde3fe
--- /dev/null
+++ b/lib/libfido2/src/fido/es256.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 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_ES256_H
+#define _FIDO_ES256_H
+
+#include <openssl/ec.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+es256_pk_t *es256_pk_new(void);
+void es256_pk_free(es256_pk_t **);
+EVP_PKEY *es256_pk_to_EVP_PKEY(const es256_pk_t *);
+
+int es256_pk_from_EC_KEY(es256_pk_t *, const EC_KEY *);
+int es256_pk_from_ptr(es256_pk_t *, const void *, size_t);
+
+#ifdef _FIDO_INTERNAL
+es256_sk_t *es256_sk_new(void);
+void es256_sk_free(es256_sk_t **);
+EVP_PKEY *es256_sk_to_EVP_PKEY(const es256_sk_t *);
+
+int es256_derive_pk(const es256_sk_t *, es256_pk_t *);
+int es256_sk_create(es256_sk_t *);
+
+int es256_pk_set_x(es256_pk_t *, const unsigned char *);
+int es256_pk_set_y(es256_pk_t *, const unsigned char *);
+#endif
+
+#endif /* !_FIDO_ES256_H */
diff --git a/lib/libfido2/src/fido/param.h b/lib/libfido2/src/fido/param.h
new file mode 100644
index 00000000000..9e12ac62eff
--- /dev/null
+++ b/lib/libfido2/src/fido/param.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2018 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_PARAM_H
+#define _FIDO_PARAM_H
+
+/* Authentication data flags. */
+#define CTAP_AUTHDATA_USER_PRESENT 0x01
+#define CTAP_AUTHDATA_USER_VERIFIED 0x04
+#define CTAP_AUTHDATA_ATT_CRED 0x40
+#define CTAP_AUTHDATA_EXT_DATA 0x80
+
+/* CTAPHID command opcodes. */
+#define CTAP_CMD_PING 0x01
+#define CTAP_CMD_MSG 0x03
+#define CTAP_CMD_LOCK 0x04
+#define CTAP_CMD_INIT 0x06
+#define CTAP_CMD_WINK 0x08
+#define CTAP_CMD_CBOR 0x10
+#define CTAP_CMD_CANCEL 0x11
+#define CTAP_KEEPALIVE 0x3b
+#define CTAP_FRAME_INIT 0x80
+
+/* CTAPHID CBOR command opcodes. */
+#define CTAP_CBOR_MAKECRED 0x01
+#define CTAP_CBOR_ASSERT 0x02
+#define CTAP_CBOR_GETINFO 0x04
+#define CTAP_CBOR_CLIENT_PIN 0x06
+#define CTAP_CBOR_RESET 0x07
+#define CTAP_CBOR_NEXT_ASSERT 0x08
+#define CTAP_CBOR_BIO_ENROLL_PRE 0x40
+#define CTAP_CBOR_CRED_MGMT_PRE 0x41
+
+/* U2F command opcodes. */
+#define U2F_CMD_REGISTER 0x01
+#define U2F_CMD_AUTH 0x02
+
+/* U2F command flags. */
+#define U2F_AUTH_SIGN 0x03
+#define U2F_AUTH_CHECK 0x07
+
+/* ISO7816-4 status words. */
+#define SW_CONDITIONS_NOT_SATISFIED 0x6985
+#define SW_WRONG_DATA 0x6a80
+#define SW_NO_ERROR 0x9000
+
+/* HID Broadcast channel ID. */
+#define CTAP_CID_BROADCAST 0xffffffff
+
+/* Expected size of a HID report in bytes. */
+#define CTAP_RPT_SIZE 64
+
+/* Randomness device on UNIX-like platforms. */
+#ifndef FIDO_RANDOM_DEV
+#define FIDO_RANDOM_DEV "/dev/urandom"
+#endif
+
+/* CTAP capability bits. */
+#define FIDO_CAP_WINK 0x01 /* if set, device supports CTAP_CMD_WINK */
+#define FIDO_CAP_CBOR 0x04 /* if set, device supports CTAP_CMD_CBOR */
+#define FIDO_CAP_NMSG 0x08 /* if set, device doesn't support CTAP_CMD_MSG */
+
+/* Supported COSE algorithms. */
+#define COSE_ES256 -7
+#define COSE_EDDSA -8
+#define COSE_ECDH_ES256 -25
+#define COSE_RS256 -257
+
+/* Supported COSE types. */
+#define COSE_KTY_OKP 1
+#define COSE_KTY_EC2 2
+#define COSE_KTY_RSA 3
+
+/* Supported curves. */
+#define COSE_P256 1
+#define COSE_ED25519 6
+
+/* Supported extensions. */
+#define FIDO_EXT_HMAC_SECRET 0x01
+
+#endif /* !_FIDO_PARAM_H */
diff --git a/lib/libfido2/src/fido/rs256.h b/lib/libfido2/src/fido/rs256.h
new file mode 100644
index 00000000000..d2fa162eb1d
--- /dev/null
+++ b/lib/libfido2/src/fido/rs256.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 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_RS256_H
+#define _FIDO_RS256_H
+
+#include <openssl/rsa.h>
+
+#include <stdint.h>
+#include <stdlib.h>
+
+rs256_pk_t *rs256_pk_new(void);
+void rs256_pk_free(rs256_pk_t **);
+EVP_PKEY *rs256_pk_to_EVP_PKEY(const rs256_pk_t *);
+
+int rs256_pk_from_RSA(rs256_pk_t *, const RSA *);
+int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t);
+
+#endif /* !_FIDO_RS256_H */
diff --git a/lib/libfido2/src/hid.c b/lib/libfido2/src/hid.c
new file mode 100644
index 00000000000..783d22c9a37
--- /dev/null
+++ b/lib/libfido2/src/hid.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+
+fido_dev_info_t *
+fido_dev_info_new(size_t n)
+{
+ return (calloc(n, sizeof(fido_dev_info_t)));
+}
+
+void
+fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
+{
+ fido_dev_info_t *devlist;
+
+ if (devlist_p == NULL || (devlist = *devlist_p) == NULL)
+ return;
+
+ for (size_t i = 0; i < n; i++) {
+ const fido_dev_info_t *di = &devlist[i];
+ free(di->path);
+ free(di->manufacturer);
+ free(di->product);
+ }
+
+ free(devlist);
+
+ *devlist_p = NULL;
+}
+
+const fido_dev_info_t *
+fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i)
+{
+ return (&devlist[i]);
+}
+
+const char *
+fido_dev_info_path(const fido_dev_info_t *di)
+{
+ return (di->path);
+}
+
+int16_t
+fido_dev_info_vendor(const fido_dev_info_t *di)
+{
+ return (di->vendor_id);
+}
+
+int16_t
+fido_dev_info_product(const fido_dev_info_t *di)
+{
+ return (di->product_id);
+}
+
+const char *
+fido_dev_info_manufacturer_string(const fido_dev_info_t *di)
+{
+ return (di->manufacturer);
+}
+
+const char *
+fido_dev_info_product_string(const fido_dev_info_t *di)
+{
+ return (di->product);
+}
diff --git a/lib/libfido2/src/hid_openbsd.c b/lib/libfido2/src/hid_openbsd.c
new file mode 100644
index 00000000000..92b7c05b6f7
--- /dev/null
+++ b/lib/libfido2/src/hid_openbsd.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2019 Google LLC. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <sys/types.h>
+
+#include <sys/ioctl.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include <poll.h>
+
+#include "fido.h"
+
+#define MAX_UHID 64
+#define MAX_REPORT_LEN (sizeof(((struct usb_ctl_report *)(NULL))->ucr_data))
+
+struct hid_openbsd {
+ int fd;
+ size_t report_in_len;
+ size_t report_out_len;
+};
+
+int
+fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
+{
+ size_t i;
+ char path[64];
+ int is_fido, fd;
+ struct usb_device_info udi;
+ report_desc_t rdesc = NULL;
+ hid_data_t hdata = NULL;
+ hid_item_t hitem;
+ fido_dev_info_t *di;
+
+ if (ilen == 0)
+ return (FIDO_OK); /* nothing to do */
+
+ if (devlist == NULL || olen == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
+ snprintf(path, sizeof(path), "/dev/uhid%zu", i);
+ if ((fd = open(path, O_RDWR)) == -1) {
+ if (errno != ENOENT && errno != ENXIO) {
+ log_debug("%s: open %s: %s", __func__, path,
+ strerror(errno));
+ }
+ continue;
+ }
+ memset(&udi, 0, sizeof(udi));
+ if (ioctl(fd, USB_GET_DEVICEINFO, &udi) != 0) {
+ log_debug("%s: get device info %s: %s", __func__,
+ path, strerror(errno));
+ close(fd);
+ continue;
+ }
+ if ((rdesc = hid_get_report_desc(fd)) == NULL) {
+ log_debug("%s: failed to get report descriptor: %s",
+ __func__, path);
+ close(fd);
+ continue;
+ }
+ if ((hdata = hid_start_parse(rdesc,
+ 1<<hid_collection, -1)) == NULL) {
+ log_debug("%s: failed to parse report descriptor: %s",
+ __func__, path);
+ hid_dispose_report_desc(rdesc);
+ close(fd);
+ continue;
+ }
+ is_fido = 0;
+ for (is_fido = 0; !is_fido;) {
+ memset(&hitem, 0, sizeof(hitem));
+ if (hid_get_item(hdata, &hitem) <= 0)
+ break;
+ if ((hitem._usage_page & 0xFFFF0000) == 0xf1d00000)
+ is_fido = 1;
+ }
+ hid_end_parse(hdata);
+ hid_dispose_report_desc(rdesc);
+ close(fd);
+
+ if (!is_fido)
+ continue;
+
+ log_debug("%s: %s: bus = 0x%02x, addr = 0x%02x",
+ __func__, path, udi.udi_bus, udi.udi_addr);
+ log_debug("%s: %s: vendor = \"%s\", product = \"%s\"",
+ __func__, path, udi.udi_vendor, udi.udi_product);
+ log_debug("%s: %s: productNo = 0x%04x, vendorNo = 0x%04x, "
+ "releaseNo = 0x%04x", __func__, path, udi.udi_productNo,
+ udi.udi_vendorNo, udi.udi_releaseNo);
+
+ di = &devlist[*olen];
+ memset(di, 0, sizeof(*di));
+ if ((di->path = strdup(path)) == NULL ||
+ (di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
+ (di->product = strdup(udi.udi_product)) == NULL) {
+ free(di->path);
+ free(di->manufacturer);
+ free(di->product);
+ explicit_bzero(di, sizeof(*di));
+ return FIDO_ERR_INTERNAL;
+ }
+ di->vendor_id = udi.udi_vendorNo;
+ di->product_id = udi.udi_productNo;
+ (*olen)++;
+ }
+
+ return FIDO_OK;
+}
+
+/*
+ * Workaround for OpenBSD <=6.6-current (as of 201910) bug that loses
+ * sync of DATA0/DATA1 sequence bit across uhid open/close.
+ * Send pings until we get a response - early pings with incorrect
+ * sequence bits will be ignored as duplicate packets by the device.
+ */
+static int
+terrible_ping_kludge(struct hid_openbsd *ctx)
+{
+ u_char data[256];
+ int i, n;
+ struct pollfd pfd;
+
+ if (sizeof(data) < ctx->report_out_len + 1)
+ return -1;
+ for (i = 0; i < 4; i++) {
+ memset(data, 0, sizeof(data));
+ /* broadcast channel ID */
+ data[1] = 0xff;
+ data[2] = 0xff;
+ data[3] = 0xff;
+ data[4] = 0xff;
+ /* Ping command */
+ data[5] = 0x81;
+ /* One byte ping only, Vasili */
+ data[6] = 0;
+ data[7] = 1;
+ log_debug("%s: send ping %d", __func__, i);
+ if (hid_write(ctx, data, ctx->report_out_len + 1) == -1)
+ return -1;
+ log_debug("%s: wait reply", __func__);
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = ctx->fd;
+ pfd.events = POLLIN;
+ if ((n = poll(&pfd, 1, 100)) == -1) {
+ log_debug("%s: poll: %s", __func__, strerror(errno));
+ return -1;
+ } else if (n == 0) {
+ log_debug("%s: timed out", __func__);
+ continue;
+ }
+ if (hid_read(ctx, data, ctx->report_out_len, 250) == -1)
+ return -1;
+ /*
+ * Ping isn't always supported on the broadcast channel,
+ * so we might get an error, but we don't care - we're
+ * synched now.
+ */
+ log_debug("%s: got reply", __func__);
+ log_xxd(data, ctx->report_out_len);
+ return 0;
+ }
+ log_debug("%s: no response", __func__);
+ return -1;
+}
+
+void *
+hid_open(const char *path)
+{
+ struct hid_openbsd *ret = NULL;
+ report_desc_t rdesc = NULL;
+ int len, usb_report_id = 0;
+
+ if ((ret = calloc(1, sizeof(*ret))) == NULL ||
+ (ret->fd = open(path, O_RDWR)) < 0) {
+ free(ret);
+ return (NULL);
+ }
+ if (ioctl(ret->fd, USB_GET_REPORT_ID, &usb_report_id) != 0) {
+ log_debug("%s: failed to get report ID: %s", __func__,
+ strerror(errno));
+ goto fail;
+ }
+ if ((rdesc = hid_get_report_desc(ret->fd)) == NULL) {
+ log_debug("%s: failed to get report descriptor", __func__);
+ goto fail;
+ }
+ if ((len = hid_report_size(rdesc, hid_input, usb_report_id)) <= 0 ||
+ (size_t)len > MAX_REPORT_LEN) {
+ log_debug("%s: bad input report size %d", __func__, len);
+ goto fail;
+ }
+ ret->report_in_len = (size_t)len;
+ if ((len = hid_report_size(rdesc, hid_output, usb_report_id)) <= 0 ||
+ (size_t)len > MAX_REPORT_LEN) {
+ log_debug("%s: bad output report size %d", __func__, len);
+ fail:
+ hid_dispose_report_desc(rdesc);
+ close(ret->fd);
+ free(ret);
+ return NULL;
+ }
+ ret->report_out_len = (size_t)len;
+ hid_dispose_report_desc(rdesc);
+ log_debug("%s: USB report ID %d, inlen = %zu outlen = %zu", __func__,
+ usb_report_id, ret->report_in_len, ret->report_out_len);
+
+ /*
+ * OpenBSD (as of 201910) has a bug that causes it to lose
+ * track of the DATA0/DATA1 sequence toggle across uhid device
+ * open and close. This is a terrible hack to work around it.
+ */
+ if (terrible_ping_kludge(ret) != 0) {
+ hid_close(ret);
+ return NULL;
+ }
+
+ return (ret);
+}
+
+void
+hid_close(void *handle)
+{
+ struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
+
+ close(ctx->fd);
+ free(ctx);
+}
+
+int
+hid_read(void *handle, unsigned char *buf, size_t len, int ms)
+{
+ struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
+ ssize_t r;
+
+ (void)ms; /* XXX */
+
+ if (len != ctx->report_in_len) {
+ log_debug("%s: invalid len: got %zu, want %zu", __func__,
+ len, ctx->report_in_len);
+ return (-1);
+ }
+ if ((r = read(ctx->fd, buf, len)) == -1 || (size_t)r != len) {
+ log_debug("%s: read: %s", __func__, strerror(errno));
+ return (-1);
+ }
+ return ((int)len);
+}
+
+int
+hid_write(void *handle, const unsigned char *buf, size_t len)
+{
+ struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
+ ssize_t r;
+
+ if (len != ctx->report_out_len + 1) {
+ log_debug("%s: invalid len: got %zu, want %zu", __func__,
+ len, ctx->report_out_len);
+ return (-1);
+ }
+ if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 ||
+ (size_t)r != len - 1) {
+ log_debug("%s: write: %s", __func__, strerror(errno));
+ return (-1);
+ }
+ return ((int)len);
+}
diff --git a/lib/libfido2/src/info.c b/lib/libfido2/src/info.c
new file mode 100644
index 00000000000..35f8cdba682
--- /dev/null
+++ b/lib/libfido2/src/info.c
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+
+static int
+decode_version(const cbor_item_t *item, void *arg)
+{
+ fido_str_array_t *v = arg;
+ const size_t i = v->len;
+
+ /* keep ptr[x] and len consistent */
+ if (cbor_string_copy(item, &v->ptr[i]) < 0) {
+ log_debug("%s: cbor_string_copy", __func__);
+ return (-1);
+ }
+
+ v->len++;
+
+ return (0);
+}
+
+static int
+decode_versions(const cbor_item_t *item, fido_str_array_t *v)
+{
+ v->ptr = NULL;
+ v->len = 0;
+
+ if (cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ v->ptr = calloc(cbor_array_size(item), sizeof(char *));
+ if (v->ptr == NULL)
+ return (-1);
+
+ if (cbor_array_iter(item, v, decode_version) < 0) {
+ log_debug("%s: decode_version", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+decode_extension(const cbor_item_t *item, void *arg)
+{
+ fido_str_array_t *e = arg;
+ const size_t i = e->len;
+
+ /* keep ptr[x] and len consistent */
+ if (cbor_string_copy(item, &e->ptr[i]) < 0) {
+ log_debug("%s: cbor_string_copy", __func__);
+ return (-1);
+ }
+
+ e->len++;
+
+ return (0);
+}
+
+static int
+decode_extensions(const cbor_item_t *item, fido_str_array_t *e)
+{
+ e->ptr = NULL;
+ e->len = 0;
+
+ if (cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ e->ptr = calloc(cbor_array_size(item), sizeof(char *));
+ if (e->ptr == NULL)
+ return (-1);
+
+ if (cbor_array_iter(item, e, decode_extension) < 0) {
+ log_debug("%s: decode_extension", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+decode_aaguid(const cbor_item_t *item, unsigned char *aaguid, size_t aaguid_len)
+{
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false ||
+ cbor_bytestring_length(item) != aaguid_len) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ memcpy(aaguid, cbor_bytestring_handle(item), aaguid_len);
+
+ return (0);
+}
+
+static int
+decode_option(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_opt_array_t *o = arg;
+ const size_t i = o->len;
+
+ if (cbor_isa_float_ctrl(val) == false ||
+ cbor_float_get_width(val) != CBOR_FLOAT_0 ||
+ cbor_is_bool(val) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ if (cbor_string_copy(key, &o->name[i]) < 0) {
+ log_debug("%s: cbor_string_copy", __func__);
+ return (0); /* ignore */
+ }
+
+ /* keep name/value and len consistent */
+ o->value[i] = cbor_ctrl_value(val) == CBOR_CTRL_TRUE;
+ o->len++;
+
+ return (0);
+}
+
+static int
+decode_options(const cbor_item_t *item, fido_opt_array_t *o)
+{
+ o->name = NULL;
+ o->value = NULL;
+ o->len = 0;
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ o->name = calloc(cbor_map_size(item), sizeof(char *));
+ o->value = calloc(cbor_map_size(item), sizeof(bool));
+ if (o->name == NULL || o->value == NULL)
+ return (-1);
+
+ return (cbor_map_iter(item, o, decode_option));
+}
+
+static int
+decode_protocol(const cbor_item_t *item, void *arg)
+{
+ fido_byte_array_t *p = arg;
+ const size_t i = p->len;
+
+ if (cbor_isa_uint(item) == false ||
+ cbor_int_get_width(item) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ /* keep ptr[x] and len consistent */
+ p->ptr[i] = cbor_get_uint8(item);
+ p->len++;
+
+ return (0);
+}
+
+static int
+decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
+{
+ p->ptr = NULL;
+ p->len = 0;
+
+ if (cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ p->ptr = calloc(cbor_array_size(item), sizeof(uint8_t));
+ if (p->ptr == NULL)
+ return (-1);
+
+ if (cbor_array_iter(item, p, decode_protocol) < 0) {
+ log_debug("%s: decode_protocol", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_cbor_info_t *ci = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* versions */
+ return (decode_versions(val, &ci->versions));
+ case 2: /* extensions */
+ return (decode_extensions(val, &ci->extensions));
+ case 3: /* aaguid */
+ return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
+ case 4: /* options */
+ return (decode_options(val, &ci->options));
+ case 5: /* maxMsgSize */
+ return (decode_uint64(val, &ci->maxmsgsiz));
+ case 6: /* pinProtocols */
+ return (decode_protocols(val, &ci->protocols));
+ default: /* ignore */
+ log_debug("%s: cbor type", __func__);
+ return (0);
+ }
+}
+
+static int
+fido_dev_get_cbor_info_tx(fido_dev_t *dev)
+{
+ const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+
+ log_debug("%s: dev=%p", __func__, (void *)dev);
+
+ if (tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
+ log_debug("%s: tx", __func__);
+ return (FIDO_ERR_TX);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[512];
+ int reply_len;
+
+ log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
+ (void *)ci, ms);
+
+ memset(ci, 0, sizeof(*ci));
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ return (parse_cbor_reply(reply, (size_t)reply_len, ci,
+ parse_reply_element));
+}
+
+static int
+fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK ||
+ (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
+{
+ return (fido_dev_get_cbor_info_wait(dev, ci, -1));
+}
+
+/*
+ * get/set functions for fido_cbor_info_t; always at the end of the file
+ */
+
+fido_cbor_info_t *
+fido_cbor_info_new(void)
+{
+ return (calloc(1, sizeof(fido_cbor_info_t)));
+}
+
+static void
+free_str_array(fido_str_array_t *sa)
+{
+ for (size_t i = 0; i < sa->len; i++)
+ free(sa->ptr[i]);
+
+ free(sa->ptr);
+ sa->ptr = NULL;
+ sa->len = 0;
+}
+
+static void
+free_opt_array(fido_opt_array_t *oa)
+{
+ for (size_t i = 0; i < oa->len; i++)
+ free(oa->name[i]);
+
+ free(oa->name);
+ free(oa->value);
+ oa->name = NULL;
+ oa->value = NULL;
+}
+
+static void
+free_byte_array(fido_byte_array_t *ba)
+{
+ free(ba->ptr);
+
+ ba->ptr = NULL;
+ ba->len = 0;
+}
+
+void
+fido_cbor_info_free(fido_cbor_info_t **ci_p)
+{
+ fido_cbor_info_t *ci;
+
+ if (ci_p == NULL || (ci = *ci_p) == NULL)
+ return;
+
+ free_str_array(&ci->versions);
+ free_str_array(&ci->extensions);
+ free_opt_array(&ci->options);
+ free_byte_array(&ci->protocols);
+ free(ci);
+
+ *ci_p = NULL;
+}
+
+char **
+fido_cbor_info_versions_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->versions.ptr);
+}
+
+size_t
+fido_cbor_info_versions_len(const fido_cbor_info_t *ci)
+{
+ return (ci->versions.len);
+}
+
+char **
+fido_cbor_info_extensions_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->extensions.ptr);
+}
+
+size_t
+fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
+{
+ return (ci->extensions.len);
+}
+
+const unsigned char *
+fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->aaguid);
+}
+
+size_t
+fido_cbor_info_aaguid_len(const fido_cbor_info_t *ci)
+{
+ return (sizeof(ci->aaguid));
+}
+
+char **
+fido_cbor_info_options_name_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->options.name);
+}
+
+const bool *
+fido_cbor_info_options_value_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->options.value);
+}
+
+size_t
+fido_cbor_info_options_len(const fido_cbor_info_t *ci)
+{
+ return (ci->options.len);
+}
+
+uint64_t
+fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
+{
+ return (ci->maxmsgsiz);
+}
+
+const uint8_t *
+fido_cbor_info_protocols_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->protocols.ptr);
+}
+
+size_t
+fido_cbor_info_protocols_len(const fido_cbor_info_t *ci)
+{
+ return (ci->protocols.len);
+}
diff --git a/lib/libfido2/src/io.c b/lib/libfido2/src/io.c
new file mode 100644
index 00000000000..ce4f497f8b8
--- /dev/null
+++ b/lib/libfido2/src/io.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2018 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "fido.h"
+#include "packed.h"
+
+PACKED_TYPE(frame_t,
+struct frame {
+ uint32_t cid; /* channel id */
+ union {
+ uint8_t type;
+ struct {
+ uint8_t cmd;
+ uint8_t bcnth;
+ uint8_t bcntl;
+ uint8_t data[CTAP_RPT_SIZE - 7];
+ } init;
+ struct {
+ uint8_t seq;
+ uint8_t data[CTAP_RPT_SIZE - 5];
+ } cont;
+ } body;
+})
+
+#ifndef MIN
+#define MIN(x, y) ((x) > (y) ? (y) : (x))
+#endif
+
+static size_t
+tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
+{
+ struct frame *fp;
+ unsigned char pkt[sizeof(*fp) + 1];
+ int n;
+
+ if (d->io.write == NULL || (cmd & 0x80) == 0)
+ return (0);
+
+ memset(&pkt, 0, sizeof(pkt));
+ fp = (struct frame *)(pkt + 1);
+ fp->cid = d->cid;
+ fp->body.init.cmd = 0x80 | cmd;
+ fp->body.init.bcnth = (count >> 8) & 0xff;
+ fp->body.init.bcntl = count & 0xff;
+ count = MIN(count, sizeof(fp->body.init.data));
+ if (count)
+ memcpy(&fp->body.init.data, buf, count);
+
+ n = d->io.write(d->io_handle, pkt, sizeof(pkt));
+ if (n < 0 || (size_t)n != sizeof(pkt))
+ return (0);
+
+ return (count);
+}
+
+static size_t
+tx_frame(fido_dev_t *d, int seq, const void *buf, size_t count)
+{
+ struct frame *fp;
+ unsigned char pkt[sizeof(*fp) + 1];
+ int n;
+
+ if (d->io.write == NULL || seq < 0 || seq > UINT8_MAX)
+ return (0);
+
+ memset(&pkt, 0, sizeof(pkt));
+ fp = (struct frame *)(pkt + 1);
+ fp->cid = d->cid;
+ fp->body.cont.seq = (uint8_t)seq;
+ count = MIN(count, sizeof(fp->body.cont.data));
+ memcpy(&fp->body.cont.data, buf, count);
+
+ n = d->io.write(d->io_handle, pkt, sizeof(pkt));
+ if (n < 0 || (size_t)n != sizeof(pkt))
+ return (0);
+
+ return (count);
+}
+
+int
+tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
+{
+ int seq = 0;
+ size_t sent;
+
+ log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
+ (void *)d, cmd, buf, count);
+ log_xxd(buf, count);
+
+ if (d->io_handle == NULL || count > UINT16_MAX) {
+ log_debug("%s: invalid argument (%p, %zu)", __func__,
+ d->io_handle, count);
+ return (-1);
+ }
+
+ if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
+ log_debug("%s: tx_preamble", __func__);
+ return (-1);
+ }
+
+ while (sent < count) {
+ if (seq & 0x80) {
+ log_debug("%s: seq & 0x80", __func__);
+ return (-1);
+ }
+ const uint8_t *p = (const uint8_t *)buf + sent;
+ size_t n = tx_frame(d, seq++, p, count - sent);
+ if (n == 0) {
+ log_debug("%s: tx_frame", __func__);
+ return (-1);
+ }
+ sent += n;
+ }
+
+ return (0);
+}
+
+static int
+rx_frame(fido_dev_t *d, struct frame *fp, int ms)
+{
+ int n;
+
+ if (d->io.read == NULL)
+ return (-1);
+
+ n = d->io.read(d->io_handle, (unsigned char *)fp, sizeof(*fp), ms);
+ if (n < 0 || (size_t)n != sizeof(*fp))
+ return (-1);
+
+ return (0);
+}
+
+static int
+rx_preamble(fido_dev_t *d, struct frame *fp, int ms)
+{
+ do {
+ if (rx_frame(d, fp, ms) < 0)
+ return (-1);
+#ifdef FIDO_FUZZ
+ fp->cid = d->cid;
+#endif
+ } while (fp->cid == d->cid &&
+ fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
+
+ return (0);
+}
+
+int
+rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
+{
+ struct frame f;
+ uint16_t r;
+ uint16_t flen;
+ int seq;
+
+ if (d->io_handle == NULL || (cmd & 0x80) == 0) {
+ log_debug("%s: invalid argument (%p, 0x%02x)", __func__,
+ d->io_handle, cmd);
+ return (-1);
+ }
+
+ if (rx_preamble(d, &f, ms) < 0) {
+ log_debug("%s: rx_preamble", __func__);
+ return (-1);
+ }
+
+ log_debug("%s: initiation frame at %p, len %zu", __func__, (void *)&f,
+ sizeof(f));
+ log_xxd(&f, sizeof(f));
+
+#ifdef FIDO_FUZZ
+ f.cid = d->cid;
+ f.body.init.cmd = cmd;
+#endif
+
+ if (f.cid != d->cid || f.body.init.cmd != cmd) {
+ log_debug("%s: cid (0x%x, 0x%x), cmd (0x%02x, 0x%02x)",
+ __func__, f.cid, d->cid, f.body.init.cmd, cmd);
+ return (-1);
+ }
+
+ flen = (f.body.init.bcnth << 8) | f.body.init.bcntl;
+ if (count < (size_t)flen) {
+ log_debug("%s: count < flen (%zu, %zu)", __func__, count,
+ (size_t)flen);
+ return (-1);
+ }
+ if (flen < sizeof(f.body.init.data)) {
+ memcpy(buf, f.body.init.data, flen);
+ return (flen);
+ }
+
+ memcpy(buf, f.body.init.data, sizeof(f.body.init.data));
+ r = sizeof(f.body.init.data);
+ seq = 0;
+
+ while ((size_t)r < flen) {
+ if (rx_frame(d, &f, ms) < 0) {
+ log_debug("%s: rx_frame", __func__);
+ return (-1);
+ }
+
+ log_debug("%s: continuation frame at %p, len %zu", __func__,
+ (void *)&f, sizeof(f));
+ log_xxd(&f, sizeof(f));
+
+#ifdef FIDO_FUZZ
+ f.cid = d->cid;
+ f.body.cont.seq = seq;
+#endif
+
+ if (f.cid != d->cid || f.body.cont.seq != seq++) {
+ log_debug("%s: cid (0x%x, 0x%x), seq (%d, %d)",
+ __func__, f.cid, d->cid, f.body.cont.seq, seq);
+ return (-1);
+ }
+
+ uint8_t *p = (uint8_t *)buf + r;
+
+ if ((size_t)(flen - r) > sizeof(f.body.cont.data)) {
+ memcpy(p, f.body.cont.data, sizeof(f.body.cont.data));
+ r += sizeof(f.body.cont.data);
+ } else {
+ memcpy(p, f.body.cont.data, flen - r);
+ r += (flen - r); /* break */
+ }
+ }
+
+ log_debug("%s: payload at %p, len %zu", __func__, buf, (size_t)r);
+ log_xxd(buf, r);
+
+ return (r);
+}
+
+int
+rx_cbor_status(fido_dev_t *d, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[2048];
+ int reply_len;
+
+ if ((reply_len = rx(d, cmd, &reply, sizeof(reply), ms)) < 0 ||
+ (size_t)reply_len < 1) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ return (reply[0]);
+}
diff --git a/lib/libfido2/src/iso7816.c b/lib/libfido2/src/iso7816.c
new file mode 100644
index 00000000000..e2ea2813010
--- /dev/null
+++ b/lib/libfido2/src/iso7816.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+
+iso7816_apdu_t *
+iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len)
+{
+ iso7816_apdu_t *apdu;
+ size_t alloc_len;
+
+ alloc_len = sizeof(iso7816_apdu_t) + payload_len;
+
+ if ((apdu = calloc(1, alloc_len)) == NULL)
+ return (NULL);
+
+ apdu->alloc_len = alloc_len;
+ apdu->payload_len = payload_len;
+ apdu->payload_ptr = apdu->payload;
+ apdu->header.ins = ins;
+ apdu->header.p1 = p1;
+ apdu->header.lc2 = (payload_len >> 8) & 0xff;
+ apdu->header.lc3 = payload_len & 0xff;
+
+ return (apdu);
+}
+
+void
+iso7816_free(iso7816_apdu_t **apdu_p)
+{
+ iso7816_apdu_t *apdu;
+
+ if (apdu_p == NULL || (apdu = *apdu_p) == NULL)
+ return;
+
+ explicit_bzero(apdu, apdu->alloc_len);
+ free(apdu);
+
+ *apdu_p = NULL;
+}
+
+int
+iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt)
+{
+ if (cnt > apdu->payload_len || cnt > UINT16_MAX)
+ return (-1);
+
+ memcpy(apdu->payload_ptr, buf, cnt);
+ apdu->payload_ptr += cnt;
+ apdu->payload_len -= (uint16_t)cnt;
+
+ return (0);
+}
+
+const unsigned char *
+iso7816_ptr(const iso7816_apdu_t *apdu)
+{
+ return ((const unsigned char *)&apdu->header);
+}
+
+size_t
+iso7816_len(const iso7816_apdu_t *apdu)
+{
+ return (apdu->alloc_len - sizeof(apdu->alloc_len) -
+ sizeof(apdu->payload_len) - sizeof(apdu->payload_ptr));
+}
diff --git a/lib/libfido2/src/iso7816.h b/lib/libfido2/src/iso7816.h
new file mode 100644
index 00000000000..426cd97207a
--- /dev/null
+++ b/lib/libfido2/src/iso7816.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018 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 _ISO7816_H
+#define _ISO7816_H
+
+#include "packed.h"
+
+PACKED_TYPE(iso7816_header_t,
+struct iso7816_header {
+ uint8_t cla;
+ uint8_t ins;
+ uint8_t p1;
+ uint8_t p2;
+ uint8_t lc1;
+ uint8_t lc2;
+ uint8_t lc3;
+})
+
+PACKED_TYPE(iso7816_apdu_t,
+struct iso7816_apdu {
+ size_t alloc_len;
+ uint16_t payload_len;
+ uint8_t *payload_ptr;
+ iso7816_header_t header;
+ uint8_t payload[];
+})
+
+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);
+size_t iso7816_len(const iso7816_apdu_t *);
+void iso7816_free(iso7816_apdu_t **);
+
+#endif /* !_ISO7816_H */
diff --git a/lib/libfido2/src/log.c b/lib/libfido2/src/log.c
new file mode 100644
index 00000000000..413c16f5606
--- /dev/null
+++ b/lib/libfido2/src/log.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "fido.h"
+
+#ifndef FIDO_NO_DIAGNOSTIC
+
+#ifndef TLS
+#define TLS
+#endif
+
+static TLS int logging;
+
+void
+log_init()
+{
+ logging = 1;
+}
+
+void
+log_xxd(const void *buf, size_t count)
+{
+ const uint8_t *ptr = buf;
+ size_t i;
+
+ if (!logging)
+ return;
+
+ fprintf(stderr, " ");
+
+ for (i = 0; i < count; i++) {
+ fprintf(stderr, "%02x ", *ptr++);
+ if ((i + 1) % 16 == 0 && i + 1 < count)
+ fprintf(stderr, "\n ");
+ }
+
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+void
+log_debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!logging)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+#endif /* !FIDO_NO_DIAGNOSTIC */
diff --git a/lib/libfido2/src/packed.h b/lib/libfido2/src/packed.h
new file mode 100644
index 00000000000..3857c22dd2b
--- /dev/null
+++ b/lib/libfido2/src/packed.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018 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 _PACKED_H
+#define _PACKED_H
+
+#if defined(__GNUC__)
+#define PACKED_TYPE(type, def) \
+ typedef def __attribute__ ((__packed__)) type;
+#elif defined(_MSC_VER)
+#define PACKED_TYPE(type, def) \
+ __pragma(pack(push, 1)) \
+ typedef def type; \
+ __pragma(pack(pop))
+#else
+#error "please provide a way to define packed types on your platform"
+#endif
+
+#endif /* !_PACKED_H */
diff --git a/lib/libfido2/src/pin.c b/lib/libfido2/src/pin.c
new file mode 100644
index 00000000000..5789cd86ea7
--- /dev/null
+++ b/lib/libfido2/src/pin.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2018 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 <string.h>
+#include "fido.h"
+#include "fido/es256.h"
+
+static int
+parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ fido_blob_t *token = arg;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 2) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ return (fido_blob_decode(val, token));
+}
+
+static int
+fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
+ const fido_blob_t *ecdh, const es256_pk_t *pk)
+{
+ fido_blob_t f;
+ fido_blob_t *p = NULL;
+ cbor_item_t *argv[6];
+ int r;
+
+ memset(&f, 0, sizeof(f));
+ memset(argv, 0, sizeof(argv));
+
+ if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
+ (const unsigned char *)pin, strlen(pin)) < 0) {
+ log_debug("%s: fido_blob_set", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ (argv[1] = cbor_build_uint8(5)) == NULL ||
+ (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
+ (argv[5] = encode_pin_hash_enc(ecdh, p)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ fido_blob_free(&p);
+ free(f.ptr);
+
+ return (r);
+}
+
+static int
+fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
+ fido_blob_t *token, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ fido_blob_t *aes_token = NULL;
+ unsigned char reply[2048];
+ int reply_len;
+ int r;
+
+ if ((aes_token = fido_blob_new()) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, aes_token,
+ parse_pintoken)) != FIDO_OK) {
+ log_debug("%s: parse_pintoken", __func__);
+ goto fail;
+ }
+
+ if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
+ log_debug("%s: aes256_cbc_dec", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ fido_blob_free(&aes_token);
+
+ return (r);
+}
+
+static int
+fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin,
+ const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
+ (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_get_pin_token(fido_dev_t *dev, const char *pin,
+ const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token)
+{
+ return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
+}
+
+static int
+pad64(const char *pin, fido_blob_t **ppin)
+{
+ size_t pin_len;
+ size_t ppin_len;
+
+ pin_len = strlen(pin);
+ if (pin_len < 4 || pin_len > 255) {
+ log_debug("%s: invalid pin length", __func__);
+ return (FIDO_ERR_PIN_POLICY_VIOLATION);
+ }
+
+ if ((*ppin = fido_blob_new()) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ ppin_len = (pin_len + 63) & ~63;
+ if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
+ fido_blob_free(ppin);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ memcpy((*ppin)->ptr, pin, pin_len);
+ (*ppin)->len = ppin_len;
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
+{
+ fido_blob_t f;
+ fido_blob_t *ppin = NULL;
+ fido_blob_t *ecdh = NULL;
+ fido_blob_t *opin = NULL;
+ cbor_item_t *argv[6];
+ es256_pk_t *pk = NULL;
+ int r;
+
+ memset(&f, 0, sizeof(f));
+ memset(argv, 0, sizeof(argv));
+
+ if ((opin = fido_blob_new()) == NULL || fido_blob_set(opin,
+ (const unsigned char *)oldpin, strlen(oldpin)) < 0) {
+ log_debug("%s: fido_blob_set", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if ((r = pad64(pin, &ppin)) != FIDO_OK) {
+ log_debug("%s: pad64", __func__);
+ goto fail;
+ }
+
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+
+ if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ (argv[1] = cbor_build_uint8(4)) == NULL ||
+ (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
+ (argv[3] = encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
+ (argv[4] = encode_pin_enc(ecdh, ppin)) == NULL ||
+ (argv[5] = encode_pin_hash_enc(ecdh, opin)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 6, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ es256_pk_free(&pk);
+ fido_blob_free(&ppin);
+ fido_blob_free(&ecdh);
+ fido_blob_free(&opin);
+ free(f.ptr);
+
+ return (r);
+
+}
+
+static int
+fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
+{
+ fido_blob_t f;
+ fido_blob_t *ppin = NULL;
+ fido_blob_t *ecdh = NULL;
+ cbor_item_t *argv[5];
+ es256_pk_t *pk = NULL;
+ int r;
+
+ memset(&f, 0, sizeof(f));
+ memset(argv, 0, sizeof(argv));
+
+ if ((r = pad64(pin, &ppin)) != FIDO_OK) {
+ log_debug("%s: pad64", __func__);
+ goto fail;
+ }
+
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ log_debug("%s: fido_do_ecdh", __func__);
+ goto fail;
+ }
+
+ if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ (argv[1] = cbor_build_uint8(3)) == NULL ||
+ (argv[2] = es256_pk_encode(pk, 0)) == NULL ||
+ (argv[3] = encode_set_pin_auth(ecdh, ppin)) == NULL ||
+ (argv[4] = encode_pin_enc(ecdh, ppin)) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 5, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ es256_pk_free(&pk);
+ fido_blob_free(&ppin);
+ fido_blob_free(&ecdh);
+ free(f.ptr);
+
+ return (r);
+}
+
+static int
+fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
+ int ms)
+{
+ int r;
+
+ if (oldpin != NULL) {
+ if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
+ log_debug("%s: fido_dev_change_pin_tx", __func__);
+ return (r);
+ }
+ } else {
+ if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
+ log_debug("%s: fido_dev_set_pin_tx", __func__);
+ return (r);
+ }
+ }
+
+ if ((r = rx_cbor_status(dev, ms)) != FIDO_OK) {
+ log_debug("%s: rx_cbor_status", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
+{
+ return (fido_dev_set_pin_wait(dev, pin, oldpin, -1));
+}
+
+static int
+parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ int *retries = arg;
+ uint64_t n;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 3) {
+ log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
+ }
+
+ if (decode_uint64(val, &n) < 0 || n > INT_MAX) {
+ log_debug("%s: decode_uint64", __func__);
+ return (-1);
+ }
+
+ *retries = (int)n;
+
+ return (0);
+}
+
+static int
+fido_dev_get_retry_count_tx(fido_dev_t *dev)
+{
+ fido_blob_t f;
+ cbor_item_t *argv[2];
+ int r;
+
+ memset(&f, 0, sizeof(f));
+ memset(argv, 0, sizeof(argv));
+
+ if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ (argv[1] = cbor_build_uint8(1)) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, 2, &f) < 0 ||
+ tx(dev, CTAP_FRAME_INIT | CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ free(f.ptr);
+
+ return (r);
+}
+
+static int
+fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+ unsigned char reply[512];
+ int reply_len;
+ int r;
+
+ *retries = 0;
+
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 0) {
+ log_debug("%s: rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = parse_cbor_reply(reply, (size_t)reply_len, retries,
+ parse_retry_count)) != FIDO_OK) {
+ log_debug("%s: parse_retry_count", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_get_retry_count_tx(dev)) != FIDO_OK ||
+ (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
+{
+ return (fido_dev_get_retry_count_wait(dev, retries, -1));
+}
+
+int
+add_cbor_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
+ const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
+ cbor_item_t **auth, cbor_item_t **opt)
+{
+ fido_blob_t *token = NULL;
+ int r;
+
+ if ((token = fido_blob_new()) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
+ log_debug("%s: fido_dev_get_pin_token", __func__);
+ goto fail;
+ }
+
+ if ((*auth = encode_pin_auth(token, hmac_data)) == NULL ||
+ (*opt = encode_pin_opt()) == NULL) {
+ log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ fido_blob_free(&token);
+
+ return (r);
+}
diff --git a/lib/libfido2/src/reset.c b/lib/libfido2/src/reset.c
new file mode 100644
index 00000000000..fe5ee92972f
--- /dev/null
+++ b/lib/libfido2/src/reset.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 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 <stdlib.h>
+#include "fido.h"
+
+static int
+fido_dev_reset_tx(fido_dev_t *dev)
+{
+ const unsigned char cbor[] = { CTAP_CBOR_RESET };
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_CBOR;
+
+ if (tx(dev, cmd, cbor, sizeof(cbor)) < 0) {
+ log_debug("%s: tx", __func__);
+ return (FIDO_ERR_TX);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+fido_dev_reset_wait(fido_dev_t *dev, int ms)
+{
+ int r;
+
+ if ((r = fido_dev_reset_tx(dev)) != FIDO_OK ||
+ (r = rx_cbor_status(dev, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+int
+fido_dev_reset(fido_dev_t *dev)
+{
+ return (fido_dev_reset_wait(dev, -1));
+}
diff --git a/lib/libfido2/src/rs256.c b/lib/libfido2/src/rs256.c
new file mode 100644
index 00000000000..8cda9c36a27
--- /dev/null
+++ b/lib/libfido2/src/rs256.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2018 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 <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/obj_mac.h>
+
+#include <string.h>
+#include "fido.h"
+#include "fido/rs256.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static int
+RSA_bits(const RSA *r)
+{
+ return (BN_num_bits(r->n));
+}
+
+static int
+RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+{
+ r->n = n;
+ r->e = e;
+ r->d = d;
+
+ return (1);
+}
+
+static void
+RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+{
+ *n = r->n;
+ *e = r->e;
+ *d = r->d;
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+static int
+decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
+{
+ if (cbor_isa_bytestring(item) == false ||
+ cbor_bytestring_is_definite(item) == false ||
+ cbor_bytestring_length(item) != len) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ memcpy(ptr, cbor_bytestring_handle(item), len);
+
+ return (0);
+}
+
+static int
+decode_rsa_pubkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ rs256_pk_t *k = arg;
+
+ if (cbor_isa_negint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8)
+ return (0); /* ignore */
+
+ switch (cbor_get_uint8(key)) {
+ case 0: /* modulus */
+ return (decode_bignum(val, &k->n, sizeof(k->n)));
+ case 1: /* public exponent */
+ return (decode_bignum(val, &k->e, sizeof(k->e)));
+ }
+
+ return (0); /* ignore */
+}
+
+int
+rs256_pk_decode(const cbor_item_t *item, rs256_pk_t *k)
+{
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false ||
+ cbor_map_iter(item, k, decode_rsa_pubkey) < 0) {
+ log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
+rs256_pk_t *
+rs256_pk_new(void)
+{
+ return (calloc(1, sizeof(rs256_pk_t)));
+}
+
+void
+rs256_pk_free(rs256_pk_t **pkp)
+{
+ rs256_pk_t *pk;
+
+ if (pkp == NULL || (pk = *pkp) == NULL)
+ return;
+
+ explicit_bzero(pk, sizeof(*pk));
+ free(pk);
+
+ *pkp = NULL;
+}
+
+int
+rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len)
+{
+ if (len < sizeof(*pk))
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ memcpy(pk, ptr, sizeof(*pk));
+
+ return (FIDO_OK);
+}
+
+EVP_PKEY *
+rs256_pk_to_EVP_PKEY(const rs256_pk_t *k)
+{
+ RSA *rsa = NULL;
+ EVP_PKEY *pkey = NULL;
+ BIGNUM *n = NULL;
+ BIGNUM *e = NULL;
+ int ok = -1;
+
+ if ((n = BN_new()) == NULL || (e = BN_new()) == NULL)
+ goto fail;
+
+ if (BN_bin2bn(k->n, sizeof(k->n), n) == NULL ||
+ BN_bin2bn(k->e, sizeof(k->e), e) == NULL) {
+ log_debug("%s: BN_bin2bn", __func__);
+ goto fail;
+ }
+
+ if ((rsa = RSA_new()) == NULL || RSA_set0_key(rsa, n, e, NULL) == 0) {
+ log_debug("%s: RSA_set0_key", __func__);
+ goto fail;
+ }
+
+ /* at this point, n and e belong to rsa */
+ n = NULL;
+ e = NULL;
+
+ if ((pkey = EVP_PKEY_new()) == NULL ||
+ EVP_PKEY_assign_RSA(pkey, rsa) == 0) {
+ log_debug("%s: EVP_PKEY_assign_RSA", __func__);
+ goto fail;
+ }
+
+ rsa = NULL; /* at this point, rsa belongs to evp */
+
+ ok = 0;
+fail:
+ if (n != NULL)
+ BN_free(n);
+ if (e != NULL)
+ BN_free(e);
+ if (rsa != NULL)
+ RSA_free(rsa);
+ if (ok < 0 && pkey != NULL) {
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ }
+
+ return (pkey);
+}
+
+int
+rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa)
+{
+ const BIGNUM *n = NULL;
+ const BIGNUM *e = NULL;
+ const BIGNUM *d = NULL;
+ int k;
+
+ if (RSA_bits(rsa) != 2048) {
+ log_debug("%s: invalid key length", __func__);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ RSA_get0_key(rsa, &n, &e, &d);
+
+ if (n == NULL || e == NULL) {
+ log_debug("%s: RSA_get0_key", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((k = BN_num_bytes(n)) < 0 || (size_t)k > sizeof(pk->n) ||
+ (k = BN_num_bytes(e)) < 0 || (size_t)k > sizeof(pk->e)) {
+ log_debug("%s: invalid key", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((k = BN_bn2bin(n, pk->n)) < 0 || (size_t)k > sizeof(pk->n) ||
+ (k = BN_bn2bin(e, pk->e)) < 0 || (size_t)k > sizeof(pk->e)) {
+ log_debug("%s: BN_bn2bin", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ return (FIDO_OK);
+}
diff --git a/lib/libfido2/src/types.h b/lib/libfido2/src/types.h
new file mode 100644
index 00000000000..42ed1b7cc89
--- /dev/null
+++ b/lib/libfido2/src/types.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2018 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 _TYPES_H
+#define _TYPES_H
+
+#include "packed.h"
+
+/* COSE ES256 (ECDSA over P-256 with SHA-256) public key */
+typedef struct es256_pk {
+ unsigned char x[32];
+ unsigned char y[32];
+} es256_pk_t;
+
+/* COSE ES256 (ECDSA over P-256 with SHA-256) (secret) key */
+typedef struct es256_sk {
+ unsigned char d[32];
+} es256_sk_t;
+
+/* COSE RS256 (2048-bit RSA with PKCS1 padding and SHA-256) public key */
+typedef struct rs256_pk {
+ unsigned char n[256];
+ unsigned char e[3];
+} rs256_pk_t;
+
+/* COSE EDDSA (ED25519) */
+typedef struct eddsa_pk {
+ unsigned char x[32];
+} eddsa_pk_t;
+
+PACKED_TYPE(fido_authdata_t,
+struct fido_authdata {
+ unsigned char rp_id_hash[32]; /* sha256 of fido_rp.id */
+ uint8_t flags; /* user present/verified */
+ uint32_t sigcount; /* signature counter */
+ /* actually longer */
+})
+
+PACKED_TYPE(fido_attcred_raw_t,
+struct fido_attcred_raw {
+ unsigned char aaguid[16]; /* credential's aaguid */
+ uint16_t id_len; /* credential id length */
+ uint8_t body[]; /* credential id + pubkey */
+})
+
+typedef struct fido_attcred {
+ unsigned char aaguid[16]; /* credential's aaguid */
+ fido_blob_t id; /* credential id */
+ int type; /* credential's cose algorithm */
+ union { /* credential's public key */
+ es256_pk_t es256;
+ rs256_pk_t rs256;
+ eddsa_pk_t eddsa;
+ } pubkey;
+} fido_attcred_t;
+
+typedef struct fido_attstmt {
+ fido_blob_t x5c; /* attestation certificate */
+ fido_blob_t sig; /* attestation signature */
+} fido_attstmt_t;
+
+typedef struct fido_rp {
+ char *id; /* relying party id */
+ char *name; /* relying party name */
+} fido_rp_t;
+
+typedef struct fido_user {
+ fido_blob_t id; /* required */
+ char *icon; /* optional */
+ char *name; /* optional */
+ char *display_name; /* required */
+} fido_user_t;
+
+typedef struct fido_cred {
+ fido_blob_t cdh; /* client data hash */
+ fido_rp_t rp; /* relying party */
+ fido_user_t user; /* user entity */
+ fido_blob_array_t excl; /* list of credential ids to exclude */
+ fido_opt_t rk; /* resident key */
+ fido_opt_t uv; /* user verification */
+ int ext; /* enabled extensions */
+ int type; /* cose algorithm */
+ char *fmt; /* credential format */
+ int authdata_ext; /* decoded extensions */
+ fido_blob_t authdata_cbor; /* raw cbor payload */
+ fido_authdata_t authdata; /* decoded authdata payload */
+ fido_attcred_t attcred; /* returned credential (key + id) */
+ fido_attstmt_t attstmt; /* attestation statement (x509 + sig) */
+} fido_cred_t;
+
+typedef struct _fido_assert_stmt {
+ fido_blob_t id; /* credential id */
+ fido_user_t user; /* user attributes */
+ fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */
+ fido_blob_t hmac_secret; /* hmac secret */
+ int authdata_ext; /* decoded extensions */
+ fido_blob_t authdata_cbor; /* raw cbor payload */
+ fido_authdata_t authdata; /* decoded authdata payload */
+ fido_blob_t sig; /* signature of cdh + authdata */
+} fido_assert_stmt;
+
+typedef struct fido_assert {
+ char *rp_id; /* relying party id */
+ fido_blob_t cdh; /* client data hash */
+ fido_blob_t hmac_salt; /* optional hmac-secret salt */
+ fido_blob_array_t allow_list; /* list of allowed credentials */
+ fido_opt_t up; /* user presence */
+ fido_opt_t uv; /* user verification */
+ int ext; /* enabled extensions */
+ fido_assert_stmt *stmt; /* array of expected assertions */
+ size_t stmt_cnt; /* number of allocated assertions */
+ size_t stmt_len; /* number of received assertions */
+} fido_assert_t;
+
+typedef struct fido_opt_array {
+ char **name;
+ bool *value;
+ size_t len;
+} fido_opt_array_t;
+
+typedef struct fido_str_array {
+ char **ptr;
+ size_t len;
+} fido_str_array_t;
+
+typedef struct fido_byte_array {
+ uint8_t *ptr;
+ size_t len;
+} fido_byte_array_t;
+
+typedef struct fido_cbor_info {
+ fido_str_array_t versions; /* supported versions: fido2|u2f */
+ fido_str_array_t extensions; /* list of supported extensions */
+ unsigned char aaguid[16]; /* aaguid */
+ fido_opt_array_t options; /* list of supported options */
+ uint64_t maxmsgsiz; /* maximum message size */
+ fido_byte_array_t protocols; /* supported pin protocols */
+} fido_cbor_info_t;
+
+typedef struct fido_dev_info {
+ char *path; /* device path */
+ int16_t vendor_id; /* 2-byte vendor id */
+ int16_t product_id; /* 2-byte product id */
+ char *manufacturer; /* manufacturer string */
+ char *product; /* product string */
+} fido_dev_info_t;
+
+PACKED_TYPE(fido_ctap_info_t,
+/* defined in section 8.1.9.1.3 (CTAPHID_INIT) of the fido2 ctap spec */
+struct fido_ctap_info {
+ uint64_t nonce; /* echoed nonce */
+ uint32_t cid; /* channel id */
+ uint8_t protocol; /* ctaphid protocol id */
+ uint8_t major; /* major version number */
+ uint8_t minor; /* minor version number */
+ uint8_t build; /* build version number */
+ uint8_t flags; /* capabilities flags; see FIDO_CAP_* */
+})
+
+typedef struct fido_dev {
+ uint64_t nonce; /* issued nonce */
+ fido_ctap_info_t attr; /* device attributes */
+ uint32_t cid; /* assigned channel id */
+ void *io_handle; /* abstract i/o handle */
+ fido_dev_io_t io; /* i/o functions & data */
+} fido_dev_t;
+
+#endif /* !_TYPES_H */
diff --git a/lib/libfido2/src/u2f.c b/lib/libfido2/src/u2f.c
new file mode 100644
index 00000000000..161e3b580e2
--- /dev/null
+++ b/lib/libfido2/src/u2f.c
@@ -0,0 +1,751 @@
+/*
+ * Copyright (c) 2018 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 <openssl/sha.h>
+#include <openssl/x509.h>
+
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "fido.h"
+#include "fido/es256.h"
+
+#if defined(_MSC_VER)
+static int
+usleep(unsigned int usec)
+{
+ Sleep(usec / 1000);
+
+ return (0);
+}
+#endif
+
+static int
+sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
+{
+ sig->len = *len; /* consume the whole buffer */
+ if ((sig->ptr = calloc(1, sig->len)) == NULL ||
+ buf_read(buf, len, sig->ptr, sig->len) < 0) {
+ log_debug("%s: buf_read", __func__);
+ if (sig->ptr != NULL) {
+ explicit_bzero(sig->ptr, sig->len);
+ free(sig->ptr);
+ sig->ptr = NULL;
+ sig->len = 0;
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+x5c_get(fido_blob_t *x5c, const unsigned char **buf, size_t *len)
+{
+ X509 *cert = NULL;
+ int ok = -1;
+
+ if (*len > LONG_MAX) {
+ log_debug("%s: invalid len %zu", __func__, *len);
+ goto fail;
+ }
+
+ /* find out the certificate's length */
+ const unsigned char *end = *buf;
+ if ((cert = d2i_X509(NULL, &end, (long)*len)) == NULL || end <= *buf ||
+ (x5c->len = (size_t)(end - *buf)) >= *len) {
+ log_debug("%s: d2i_X509", __func__);
+ goto fail;
+ }
+
+ /* read accordingly */
+ if ((x5c->ptr = calloc(1, x5c->len)) == NULL ||
+ buf_read(buf, len, x5c->ptr, x5c->len) < 0) {
+ log_debug("%s: buf_read", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (cert != NULL)
+ X509_free(cert);
+
+ if (ok < 0) {
+ free(x5c->ptr);
+ x5c->ptr = NULL;
+ x5c->len = 0;
+ }
+
+ return (ok);
+}
+
+static int
+authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
+ fido_blob_t *fake_cbor_ad)
+{
+ fido_authdata_t ad;
+ cbor_item_t *item = NULL;
+ size_t alloc_len;
+
+ memset(&ad, 0, sizeof(ad));
+
+ if (SHA256((const void *)rp_id, strlen(rp_id),
+ ad.rp_id_hash) != ad.rp_id_hash) {
+ log_debug("%s: sha256", __func__);
+ return (-1);
+ }
+
+ ad.flags = flags; /* XXX translate? */
+ ad.sigcount = sigcount;
+
+ if ((item = cbor_build_bytestring((const unsigned char *)&ad,
+ sizeof(ad))) == NULL) {
+ log_debug("%s: cbor_build_bytestring", __func__);
+ return (-1);
+ }
+
+ if (fake_cbor_ad->ptr != NULL ||
+ (fake_cbor_ad->len = cbor_serialize_alloc(item, &fake_cbor_ad->ptr,
+ &alloc_len)) == 0) {
+ log_debug("%s: cbor_serialize_alloc", __func__);
+ cbor_decref(&item);
+ return (-1);
+ }
+
+ cbor_decref(&item);
+
+ return (0);
+}
+
+static int
+send_dummy_register(fido_dev_t *dev, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
+ iso7816_apdu_t *apdu = NULL;
+ unsigned char challenge[SHA256_DIGEST_LENGTH];
+ unsigned char application[SHA256_DIGEST_LENGTH];
+ unsigned char reply[2048];
+ int r;
+
+#ifdef FIDO_FUZZ
+ ms = 0; /* XXX */
+#endif
+
+ /* dummy challenge & application */
+ memset(&challenge, 0xff, sizeof(challenge));
+ memset(&application, 0xff, sizeof(application));
+
+ if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
+ SHA256_DIGEST_LENGTH)) == NULL ||
+ iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
+ iso7816_add(apdu, &application, sizeof(application)) < 0) {
+ log_debug("%s: iso7816", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ do {
+ if (tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+ if (rx(dev, cmd, &reply, sizeof(reply), ms) < 2) {
+ log_debug("%s: rx", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+ if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
+ log_debug("%s: usleep", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+ } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
+
+ r = FIDO_OK;
+fail:
+ iso7816_free(&apdu);
+
+ return (r);
+}
+
+static int
+key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
+ int *found, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
+ iso7816_apdu_t *apdu = NULL;
+ unsigned char challenge[SHA256_DIGEST_LENGTH];
+ unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
+ unsigned char reply[8];
+ uint8_t key_id_len;
+ int r;
+
+ if (key_id->len > UINT8_MAX || rp_id == NULL) {
+ log_debug("%s: key_id->len=%zu, rp_id=%p", __func__,
+ key_id->len, (const void *)rp_id);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ memset(&challenge, 0xff, sizeof(challenge));
+ memset(&rp_id_hash, 0, sizeof(rp_id_hash));
+
+ if (SHA256((const void *)rp_id, strlen(rp_id),
+ rp_id_hash) != rp_id_hash) {
+ log_debug("%s: sha256", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ key_id_len = (uint8_t)key_id->len;
+
+ if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, 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 ||
+ iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
+ iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
+ log_debug("%s: iso7816", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+ if (rx(dev, cmd, &reply, sizeof(reply), ms) != 2) {
+ log_debug("%s: rx", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+
+ switch ((reply[0] << 8) | reply[1]) {
+ case SW_CONDITIONS_NOT_SATISFIED:
+ *found = 1; /* key exists */
+ break;
+ case SW_WRONG_DATA:
+ *found = 0; /* key does not exist */
+ break;
+ default:
+ /* unexpected sw */
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ iso7816_free(&apdu);
+
+ return (r);
+}
+
+static int
+parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
+ const unsigned char *reply, size_t len)
+{
+ uint8_t flags;
+ uint32_t sigcount;
+
+ if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
+ log_debug("%s: unexpected sw", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ len -= 2;
+
+ if (buf_read(&reply, &len, &flags, sizeof(flags)) < 0 ||
+ buf_read(&reply, &len, &sigcount, sizeof(sigcount)) < 0) {
+ log_debug("%s: buf_read", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if (sig_get(sig, &reply, &len) < 0) {
+ log_debug("%s: sig_get", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if (authdata_fake(rp_id, flags, sigcount, ad) < 0) {
+ log_debug("%s; authdata_fake", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
+ const fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
+ iso7816_apdu_t *apdu = NULL;
+ unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
+ unsigned char reply[128];
+ int reply_len;
+ uint8_t key_id_len;
+ int r;
+
+#ifdef FIDO_FUZZ
+ ms = 0; /* XXX */
+#endif
+
+ if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
+ rp_id == NULL) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ memset(&rp_id_hash, 0, sizeof(rp_id_hash));
+
+ if (SHA256((const void *)rp_id, strlen(rp_id),
+ rp_id_hash) != rp_id_hash) {
+ log_debug("%s: sha256", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ key_id_len = (uint8_t)key_id->len;
+
+ if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, 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 ||
+ iso7816_add(apdu, &key_id_len, sizeof(key_id_len)) < 0 ||
+ iso7816_add(apdu, key_id->ptr, key_id_len) < 0) {
+ log_debug("%s: iso7816", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ do {
+ if (tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 2) {
+ log_debug("%s: rx", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+ if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
+ log_debug("%s: usleep", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+ } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
+
+ if ((r = parse_auth_reply(sig, ad, rp_id, reply,
+ (size_t)reply_len)) != FIDO_OK) {
+ log_debug("%s: parse_auth_reply", __func__);
+ goto fail;
+ }
+
+fail:
+ iso7816_free(&apdu);
+
+ return (r);
+}
+
+static int
+cbor_blob_from_ec_point(const uint8_t *ec_point, size_t ec_point_len,
+ fido_blob_t *cbor_blob)
+{
+ es256_pk_t *pk = NULL;
+ cbor_item_t *pk_cbor = NULL;
+ size_t alloc_len;
+ int ok = -1;
+
+ /* only handle uncompressed points */
+ if (ec_point_len != 65 || ec_point[0] != 0x04) {
+ log_debug("%s: unexpected format", __func__);
+ goto fail;
+ }
+
+ if ((pk = es256_pk_new()) == NULL ||
+ es256_pk_set_x(pk, &ec_point[1]) < 0 ||
+ es256_pk_set_y(pk, &ec_point[33]) < 0) {
+ log_debug("%s: es256_pk_set", __func__);
+ goto fail;
+ }
+
+ if ((pk_cbor = es256_pk_encode(pk, 0)) == NULL) {
+ log_debug("%s: es256_pk_encode", __func__);
+ goto fail;
+ }
+
+ if ((cbor_blob->len = cbor_serialize_alloc(pk_cbor, &cbor_blob->ptr,
+ &alloc_len)) != 77) {
+ log_debug("%s: cbor_serialize_alloc", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ es256_pk_free(&pk);
+
+ if (pk_cbor)
+ cbor_decref(&pk_cbor);
+
+ return (ok);
+}
+
+static int
+encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
+ const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
+{
+ fido_authdata_t authdata;
+ fido_attcred_raw_t attcred_raw;
+ fido_blob_t pk_blob;
+ fido_blob_t authdata_blob;
+ cbor_item_t *authdata_cbor = NULL;
+ unsigned char *ptr;
+ size_t len;
+ size_t alloc_len;
+ int ok = -1;
+
+ memset(&pk_blob, 0, sizeof(pk_blob));
+ memset(&authdata, 0, sizeof(authdata));
+ memset(&authdata_blob, 0, sizeof(authdata_blob));
+ memset(out, 0, sizeof(*out));
+
+ if (rp_id == NULL) {
+ log_debug("%s: NULL rp_id", __func__);
+ goto fail;
+ }
+
+ if (cbor_blob_from_ec_point(pubkey, pubkey_len, &pk_blob) < 0) {
+ log_debug("%s: cbor_blob_from_ec_point", __func__);
+ goto fail;
+ }
+
+ if (SHA256((const void *)rp_id, strlen(rp_id),
+ authdata.rp_id_hash) != authdata.rp_id_hash) {
+ log_debug("%s: sha256", __func__);
+ goto fail;
+ }
+
+ authdata.flags = (CTAP_AUTHDATA_ATT_CRED | CTAP_AUTHDATA_USER_PRESENT);
+ authdata.sigcount = 0;
+
+ memset(&attcred_raw.aaguid, 0, sizeof(attcred_raw.aaguid));
+ attcred_raw.id_len = (uint16_t)(kh_len << 8); /* XXX */
+
+ len = authdata_blob.len = sizeof(authdata) + sizeof(attcred_raw) +
+ kh_len + pk_blob.len;
+ ptr = authdata_blob.ptr = calloc(1, authdata_blob.len);
+
+ log_debug("%s: ptr=%p, len=%zu", __func__, (void *)ptr, len);
+
+ if (authdata_blob.ptr == NULL)
+ goto fail;
+
+ if (buf_write(&ptr, &len, &authdata, sizeof(authdata)) < 0 ||
+ buf_write(&ptr, &len, &attcred_raw, sizeof(attcred_raw)) < 0 ||
+ buf_write(&ptr, &len, kh, kh_len) < 0 ||
+ buf_write(&ptr, &len, pk_blob.ptr, pk_blob.len) < 0) {
+ log_debug("%s: buf_write", __func__);
+ goto fail;
+ }
+
+ if ((authdata_cbor = fido_blob_encode(&authdata_blob)) == NULL) {
+ log_debug("%s: fido_blob_encode", __func__);
+ goto fail;
+ }
+
+ if ((out->len = cbor_serialize_alloc(authdata_cbor, &out->ptr,
+ &alloc_len)) == 0) {
+ log_debug("%s: cbor_serialize_alloc", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (authdata_cbor)
+ cbor_decref(&authdata_cbor);
+
+ if (pk_blob.ptr) {
+ explicit_bzero(pk_blob.ptr, pk_blob.len);
+ free(pk_blob.ptr);
+ }
+ if (authdata_blob.ptr) {
+ explicit_bzero(authdata_blob.ptr, authdata_blob.len);
+ free(authdata_blob.ptr);
+ }
+
+ return (ok);
+}
+
+static int
+parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
+{
+ fido_blob_t x5c;
+ fido_blob_t sig;
+ fido_blob_t ad;
+ uint8_t dummy;
+ uint8_t pubkey[65];
+ uint8_t kh_len = 0;
+ uint8_t *kh = NULL;
+ int r;
+
+ memset(&x5c, 0, sizeof(x5c));
+ memset(&sig, 0, sizeof(sig));
+ memset(&ad, 0, sizeof(ad));
+ r = FIDO_ERR_RX;
+
+ /* status word */
+ if (len < 2 || ((reply[len - 2] << 8) | reply[len - 1]) != SW_NO_ERROR) {
+ log_debug("%s: unexpected sw", __func__);
+ goto fail;
+ }
+
+ len -= 2;
+
+ /* reserved byte */
+ if (buf_read(&reply, &len, &dummy, sizeof(dummy)) < 0 ||
+ dummy != 0x05) {
+ log_debug("%s: reserved byte", __func__);
+ goto fail;
+ }
+
+ /* pubkey + key handle */
+ if (buf_read(&reply, &len, &pubkey, sizeof(pubkey)) < 0 ||
+ buf_read(&reply, &len, &kh_len, sizeof(kh_len)) < 0 ||
+ (kh = calloc(1, kh_len)) == NULL ||
+ buf_read(&reply, &len, kh, kh_len) < 0) {
+ log_debug("%s: buf_read", __func__);
+ goto fail;
+ }
+
+ /* x5c + sig */
+ if (x5c_get(&x5c, &reply, &len) < 0 ||
+ sig_get(&sig, &reply, &len) < 0) {
+ log_debug("%s: x5c || sig", __func__);
+ goto fail;
+ }
+
+ /* authdata */
+ if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
+ sizeof(pubkey), &ad) < 0) {
+ log_debug("%s: encode_cred_authdata", __func__);
+ goto fail;
+ }
+
+ if (fido_cred_set_fmt(cred, "fido-u2f") != FIDO_OK ||
+ fido_cred_set_authdata(cred, ad.ptr, ad.len) != FIDO_OK ||
+ fido_cred_set_x509(cred, x5c.ptr, x5c.len) != FIDO_OK ||
+ fido_cred_set_sig(cred, sig.ptr, sig.len) != FIDO_OK) {
+ log_debug("%s: fido_cred_set", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (kh) {
+ explicit_bzero(kh, kh_len);
+ free(kh);
+ }
+ if (x5c.ptr) {
+ explicit_bzero(x5c.ptr, x5c.len);
+ free(x5c.ptr);
+ }
+ if (sig.ptr) {
+ explicit_bzero(sig.ptr, sig.len);
+ free(sig.ptr);
+ }
+ if (ad.ptr) {
+ explicit_bzero(ad.ptr, ad.len);
+ free(ad.ptr);
+ }
+
+ return (r);
+}
+
+int
+u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
+{
+ const uint8_t cmd = CTAP_FRAME_INIT | CTAP_CMD_MSG;
+ iso7816_apdu_t *apdu = NULL;
+ unsigned char rp_id_hash[SHA256_DIGEST_LENGTH];
+ unsigned char reply[2048];
+ int reply_len;
+ int found;
+ int r;
+
+#ifdef FIDO_FUZZ
+ ms = 0; /* XXX */
+#endif
+
+ if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
+ log_debug("%s: rk=%d, uv=%d", __func__, cred->rk, cred->uv);
+ return (FIDO_ERR_UNSUPPORTED_OPTION);
+ }
+
+ if (cred->type != COSE_ES256 || cred->cdh.ptr == NULL ||
+ cred->rp.id == NULL || cred->cdh.len != SHA256_DIGEST_LENGTH) {
+ log_debug("%s: type=%d, cdh=(%p,%zu)" , __func__, cred->type,
+ (void *)cred->cdh.ptr, cred->cdh.len);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ for (size_t i = 0; i < cred->excl.len; i++) {
+ if ((r = key_lookup(dev, cred->rp.id, &cred->excl.ptr[i],
+ &found, ms)) != FIDO_OK) {
+ log_debug("%s: key_lookup", __func__);
+ return (r);
+ }
+ if (found) {
+ if ((r = send_dummy_register(dev, ms)) != FIDO_OK) {
+ log_debug("%s: send_dummy_register", __func__);
+ return (r);
+ }
+ return (FIDO_ERR_CREDENTIAL_EXCLUDED);
+ }
+ }
+
+ memset(&rp_id_hash, 0, sizeof(rp_id_hash));
+
+ if (SHA256((const void *)cred->rp.id, strlen(cred->rp.id),
+ rp_id_hash) != rp_id_hash) {
+ log_debug("%s: sha256", __func__);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ if ((apdu = iso7816_new(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) {
+ log_debug("%s: iso7816", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ do {
+ if (tx(dev, cmd, iso7816_ptr(apdu), iso7816_len(apdu)) < 0) {
+ log_debug("%s: tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+ if ((reply_len = rx(dev, cmd, &reply, sizeof(reply), ms)) < 2) {
+ log_debug("%s: rx", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+ if (usleep((ms == -1 ? 100 : ms) * 1000) < 0) {
+ log_debug("%s: usleep", __func__);
+ r = FIDO_ERR_RX;
+ goto fail;
+ }
+ } while (((reply[0] << 8) | reply[1]) == SW_CONDITIONS_NOT_SATISFIED);
+
+ if ((r = parse_register_reply(cred, reply,
+ (size_t)reply_len)) != FIDO_OK) {
+ log_debug("%s: parse_register_reply", __func__);
+ goto fail;
+ }
+fail:
+ iso7816_free(&apdu);
+
+ return (r);
+}
+
+static int
+u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
+ fido_assert_t *fa, size_t idx, int ms)
+{
+ fido_blob_t sig;
+ fido_blob_t ad;
+ int found;
+ int r;
+
+ memset(&sig, 0, sizeof(sig));
+ memset(&ad, 0, sizeof(ad));
+
+ if ((r = key_lookup(dev, fa->rp_id, key_id, &found, ms)) != FIDO_OK) {
+ log_debug("%s: key_lookup", __func__);
+ goto fail;
+ }
+
+ if (!found) {
+ log_debug("%s: not found", __func__);
+ r = FIDO_ERR_CREDENTIAL_EXCLUDED;
+ goto fail;
+ }
+
+ if (fa->up == FIDO_OPT_FALSE) {
+ log_debug("%s: checking for key existence only", __func__);
+ r = FIDO_ERR_USER_PRESENCE_REQUIRED;
+ goto fail;
+ }
+
+ if ((r = do_auth(dev, &fa->cdh, fa->rp_id, key_id, &sig, &ad,
+ ms)) != FIDO_OK) {
+ log_debug("%s: do_auth", __func__);
+ goto fail;
+ }
+
+ if (fido_blob_set(&fa->stmt[idx].id, key_id->ptr, key_id->len) < 0 ||
+ fido_assert_set_authdata(fa, idx, ad.ptr, ad.len) != FIDO_OK ||
+ fido_assert_set_sig(fa, idx, sig.ptr, sig.len) != FIDO_OK) {
+ log_debug("%s: fido_assert_set", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (sig.ptr) {
+ explicit_bzero(sig.ptr, sig.len);
+ free(sig.ptr);
+ }
+ if (ad.ptr) {
+ explicit_bzero(ad.ptr, ad.len);
+ free(ad.ptr);
+ }
+
+ return (r);
+}
+
+int
+u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
+{
+ int nauth_ok = 0;
+ int r;
+
+ if (fa->uv == FIDO_OPT_TRUE || fa->allow_list.ptr == NULL) {
+ log_debug("%s: uv=%d, allow_list=%p", __func__, fa->uv,
+ (void *)fa->allow_list.ptr);
+ return (FIDO_ERR_UNSUPPORTED_OPTION);
+ }
+
+ if ((r = fido_assert_set_count(fa, fa->allow_list.len)) != FIDO_OK) {
+ log_debug("%s: fido_assert_set_count", __func__);
+ return (r);
+ }
+
+ for (size_t i = 0; i < fa->allow_list.len; i++) {
+ if ((r = u2f_authenticate_single(dev, &fa->allow_list.ptr[i],
+ fa, nauth_ok, ms)) == FIDO_OK) {
+ nauth_ok++;
+ } else if (r != FIDO_ERR_CREDENTIAL_EXCLUDED) {
+ log_debug("%s: u2f_authenticate_single", __func__);
+ return (r);
+ }
+ /* ignore credentials that don't exist */
+ }
+
+ fa->stmt_len = nauth_ok;
+
+ if (nauth_ok == 0)
+ return (FIDO_ERR_NO_CREDENTIALS);
+
+ return (FIDO_OK);
+}