update libfido2 to 1.11.0, taking in just over a year of upstream
authordjm <djm@openbsd.org>
Mon, 29 Aug 2022 03:04:29 +0000 (03:04 +0000)
committerdjm <djm@openbsd.org>
Mon, 29 Aug 2022 03:04:29 +0000 (03:04 +0000)
development. "looks ok" miod@ ok sthen@

NB. libfido2 major bump

72 files changed:
lib/libfido2/Makefile
lib/libfido2/NEWS [new file with mode: 0644]
lib/libfido2/README.openbsd
lib/libfido2/man/eddsa_pk_new.3
lib/libfido2/man/es256_pk_new.3
lib/libfido2/man/fido_assert_allow_cred.3
lib/libfido2/man/fido_assert_new.3
lib/libfido2/man/fido_assert_set_authdata.3
lib/libfido2/man/fido_assert_verify.3
lib/libfido2/man/fido_bio_dev_get_info.3
lib/libfido2/man/fido_bio_enroll_new.3
lib/libfido2/man/fido_bio_info_new.3
lib/libfido2/man/fido_bio_template.3
lib/libfido2/man/fido_cbor_info_new.3
lib/libfido2/man/fido_cred_exclude.3
lib/libfido2/man/fido_cred_new.3
lib/libfido2/man/fido_cred_set_authdata.3
lib/libfido2/man/fido_cred_verify.3
lib/libfido2/man/fido_credman_metadata_new.3
lib/libfido2/man/fido_dev_enable_entattest.3
lib/libfido2/man/fido_dev_get_assert.3
lib/libfido2/man/fido_dev_get_touch_begin.3
lib/libfido2/man/fido_dev_info_manifest.3
lib/libfido2/man/fido_dev_largeblob_get.3
lib/libfido2/man/fido_dev_make_cred.3
lib/libfido2/man/fido_dev_open.3
lib/libfido2/man/fido_dev_set_io_functions.3
lib/libfido2/man/fido_dev_set_pin.3
lib/libfido2/man/fido_init.3
lib/libfido2/man/fido_strerr.3
lib/libfido2/man/rs256_pk_new.3
lib/libfido2/shlib_version
lib/libfido2/src/assert.c
lib/libfido2/src/authkey.c
lib/libfido2/src/bio.c
lib/libfido2/src/cbor.c
lib/libfido2/src/compress.c
lib/libfido2/src/config.c
lib/libfido2/src/cred.c
lib/libfido2/src/credman.c
lib/libfido2/src/dev.c
lib/libfido2/src/ecdh.c
lib/libfido2/src/eddsa.c
lib/libfido2/src/es256.c
lib/libfido2/src/export.llvm
lib/libfido2/src/extern.h
lib/libfido2/src/fido.h
lib/libfido2/src/fido/config.h
lib/libfido2/src/fido/eddsa.h
lib/libfido2/src/fido/es256.h
lib/libfido2/src/fido/param.h
lib/libfido2/src/fido/rs256.h
lib/libfido2/src/fido/types.h
lib/libfido2/src/hid.c
lib/libfido2/src/hid_unix.c
lib/libfido2/src/info.c
lib/libfido2/src/io.c
lib/libfido2/src/iso7816.c
lib/libfido2/src/iso7816.h
lib/libfido2/src/largeblob.c
lib/libfido2/src/nfc.c [new file with mode: 0644]
lib/libfido2/src/pin.c
lib/libfido2/src/reset.c
lib/libfido2/src/rs1.c [new file with mode: 0644]
lib/libfido2/src/rs256.c
lib/libfido2/src/time.c [new file with mode: 0644]
lib/libfido2/src/touch.c [new file with mode: 0644]
lib/libfido2/src/tpm.c [new file with mode: 0644]
lib/libfido2/src/types.c [new file with mode: 0644]
lib/libfido2/src/types.h [deleted file]
lib/libfido2/src/u2f.c
lib/libfido2/src/util.c [new file with mode: 0644]

index fc699ff..740e995 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.7 2021/10/26 21:36:23 djm Exp $
+# $OpenBSD: Makefile,v 1.8 2022/08/29 03:04:29 djm Exp $
 
 .PATH: ${.CURDIR}/man ${.CURDIR}/src
 
@@ -20,7 +20,8 @@ LIB=  fido2
 SRCS=  aes256.c assert.c authkey.c bio.c blob.c buf.c cbor.c compress.c
 SRCS+= config.c cred.c credman.c dev.c ecdh.c eddsa.c err.c es256.c
 SRCS+= hid.c hid_openbsd.c hid_unix.c info.c io.c iso7816.c largeblob.c
-SRCS+= log.c pin.c random.c reset.c rs256.c u2f.c
+SRCS+= log.c nfc.c pin.c random.c reset.c rs1.c rs256.c time.c touch.c
+SRCS+= tpm.c types.c u2f.c util.c
 
 HDRS=  fido.h fido/bio.h fido/config.h fido/credman.h fido/eddsa.h fido/err.h
 HDRS+= fido/es256.h fido/param.h fido/rs256.h fido/types.h
diff --git a/lib/libfido2/NEWS b/lib/libfido2/NEWS
new file mode 100644 (file)
index 0000000..f5bd414
--- /dev/null
@@ -0,0 +1,226 @@
+* Version 1.11.0 (2022-05-03)
+ ** Experimental PCSC support; enable with -DUSE_PCSC.
+ ** Improved OpenSSL 3.0 compatibility.
+ ** Use RFC1951 raw deflate to compress CTAP 2.1 largeBlobs.
+ ** winhello: advertise "uv" instead of "clientPin".
+ ** winhello: support hmac-secret in fido_dev_get_assert().
+ ** New API calls:
+  - fido_cbor_info_maxlargeblob.
+ ** Documentation and reliability fixes.
+ ** Separate build and regress targets.
+
+* Version 1.10.0 (2022-01-17)
+ ** hid_osx: handle devices with paths > 511 bytes; gh#462.
+ ** bio: fix CTAP2 canonical CBOR encoding in fido_bio_dev_enroll_*(); gh#480.
+ ** winhello: fallback to GetTopWindow() if GetForegroundWindow() fails.
+ ** winhello: fallback to hid_win.c if webauthn.dll isn't available.
+ ** New API calls:
+  - fido_dev_info_set;
+  - fido_dev_io_handle;
+  - fido_dev_new_with_info;
+  - fido_dev_open_with_info.
+ ** Cygwin and NetBSD build fixes.
+ ** Documentation and reliability fixes.
+ ** Support for TPM 2.0 attestation of COSE_ES256 credentials.
+
+* Version 1.9.0 (2021-10-27)
+ ** Enabled NFC support on Linux.
+ ** Added OpenSSL 3.0 compatibility.
+ ** Removed OpenSSL 1.0 compatibility.
+ ** Support for FIDO 2.1 "minPinLength" extension.
+ ** Support for COSE_EDDSA, COSE_ES256, and COSE_RS1 attestation.
+ ** Support for TPM 2.0 attestation.
+ ** Support for device timeouts; see fido_dev_set_timeout().
+ ** New API calls:
+  - es256_pk_from_EVP_PKEY;
+  - fido_cred_attstmt_len;
+  - fido_cred_attstmt_ptr;
+  - fido_cred_pin_minlen;
+  - fido_cred_set_attstmt;
+  - fido_cred_set_pin_minlen;
+  - fido_dev_set_pin_minlen_rpid;
+  - fido_dev_set_timeout;
+  - rs256_pk_from_EVP_PKEY.
+ ** Reliability and portability fixes.
+ ** Better handling of HID devices without identification strings; gh#381.
+ ** Fixed detection of Windows's native webauthn API; gh#382.
+
+* Version 1.8.0 (2021-07-22)
+ ** Dropped 'Requires.private' entry from pkg-config file.
+ ** Better support for FIDO 2.1 authenticators.
+ ** Support for Windows's native webauthn API.
+ ** Support for attestation format 'none'.
+ ** New API calls:
+  - fido_assert_set_clientdata;
+  - fido_cbor_info_algorithm_cose;
+  - fido_cbor_info_algorithm_count;
+  - fido_cbor_info_algorithm_type;
+  - fido_cbor_info_transports_len;
+  - fido_cbor_info_transports_ptr;
+  - fido_cred_set_clientdata;
+  - fido_cred_set_id;
+  - fido_credman_set_dev_rk;
+  - fido_dev_is_winhello.
+ ** fido2-token: new -Sc option to update a resident credential.
+ ** Documentation and reliability fixes.
+ ** HID access serialisation on Linux.
+
+* Version 1.7.0 (2021-03-29)
+ ** New dependency on zlib.
+ ** Fixed musl build; gh#259.
+ ** hid_win: detect devices with vendor or product IDs > 0x7fff; gh#264.
+ ** Support for FIDO 2.1 authenticator configuration.
+ ** Support for FIDO 2.1 UV token permissions.
+ ** Support for FIDO 2.1 "credBlobs" and "largeBlobs" extensions.
+ ** New API calls:
+  - fido_assert_blob_len;
+  - fido_assert_blob_ptr;
+  - fido_assert_largeblob_key_len;
+  - fido_assert_largeblob_key_ptr;
+  - fido_assert_set_hmac_secret;
+  - fido_cbor_info_maxcredbloblen;
+  - fido_cred_largeblob_key_len;
+  - fido_cred_largeblob_key_ptr;
+  - fido_cred_set_blob;
+  - fido_dev_enable_entattest;
+  - fido_dev_force_pin_change;
+  - fido_dev_has_uv;
+  - fido_dev_largeblob_get;
+  - fido_dev_largeblob_get_array;
+  - fido_dev_largeblob_remove;
+  - fido_dev_largeblob_set;
+  - fido_dev_largeblob_set_array;
+  - fido_dev_set_pin_minlen;
+  - fido_dev_set_sigmask;
+  - fido_dev_supports_credman;
+  - fido_dev_supports_permissions;
+  - fido_dev_supports_uv;
+  - fido_dev_toggle_always_uv.
+ ** New fido_init flag to disable fido_dev_open's U2F fallback; gh#282.
+ ** Experimental NFC support on Linux; enable with -DNFC_LINUX.
+
+* Version 1.6.0 (2020-12-22)
+ ** Fix OpenSSL 1.0 and Cygwin builds.
+ ** hid_linux: fix build on 32-bit systems.
+ ** hid_osx: allow reads from spawned threads.
+ ** Documentation and reliability fixes.
+ ** New API calls:
+  - fido_cred_authdata_raw_len;
+  - fido_cred_authdata_raw_ptr;
+  - fido_cred_sigcount;
+  - fido_dev_get_uv_retry_count;
+  - fido_dev_supports_credman.
+ ** Hardened Windows build.
+ ** Native FreeBSD and NetBSD support.
+ ** Use CTAP2 canonical CBOR when combining hmac-secret and credProtect.
+
+* Version 1.5.0 (2020-09-01)
+ ** hid_linux: return FIDO_OK if no devices are found.
+ ** hid_osx:
+  - repair communication with U2F tokens, gh#166;
+  - reliability fixes.
+ ** fido2-{assert,cred}: new options to explicitly toggle UP, UV.
+ ** Support for configurable report lengths.
+ ** New API calls:
+  - fido_cbor_info_maxcredcntlst;
+  - fido_cbor_info_maxcredidlen;
+  - fido_cred_aaguid_len;
+  - fido_cred_aaguid_ptr;
+  - fido_dev_get_touch_begin;
+  - fido_dev_get_touch_status.
+ ** Use COSE_ECDH_ES256 with CTAP_CBOR_CLIENT_PIN; gh#154.
+ ** Allow CTAP messages up to 2048 bytes; gh#171.
+ ** Ensure we only list USB devices by default.
+
+* Version 1.4.0 (2020-04-15)
+ ** hid_hidapi: hidapi backend; enable with -DUSE_HIDAPI=1.
+ ** Fall back to U2F if the key claims to, but does not support FIDO2.
+ ** FIDO2 credential protection (credprot) support.
+ ** New API calls:
+  - fido_cbor_info_fwversion;
+  - fido_cred_prot;
+  - fido_cred_set_prot;
+  - fido_dev_set_transport_functions;
+  - fido_set_log_handler.
+ ** Support for FreeBSD.
+ ** Support for C++.
+ ** Support for MSYS.
+ ** Fixed EdDSA and RSA self-attestation.
+
+* Version 1.3.1 (2020-02-19)
+ ** fix zero-ing of le1 and le2 when talking to a U2F device.
+ ** dropping sk-libfido2 middleware, please find it in the openssh tree.
+
+* Version 1.3.0 (2019-11-28)
+ ** assert/hmac: encode public key as per spec, gh#60.
+ ** fido2-cred: fix creation of resident keys.
+ ** fido2-{assert,cred}: support for hmac-secret extension.
+ ** hid_osx: detect device removal, gh#56.
+ ** hid_osx: fix device detection in MacOS Catalina.
+ ** New API calls:
+  - fido_assert_set_authdata_raw;
+  - fido_assert_sigcount;
+  - fido_cred_set_authdata_raw;
+  - fido_dev_cancel.
+ ** Middleware library for use by OpenSSH.
+ ** Support for biometric enrollment.
+ ** Support for OpenBSD.
+ ** Support for self-attestation.
+
+* Version 1.2.0 (released 2019-07-26)
+ ** Credential management support.
+ ** New API reflecting FIDO's 3-state booleans (true, false, absent):
+  - fido_assert_set_up;
+  - fido_assert_set_uv;
+  - fido_cred_set_rk;
+  - fido_cred_set_uv.
+ ** Command-line tools for Windows.
+ ** Documentation and reliability fixes.
+ ** fido_{assert,cred}_set_options() are now marked as deprecated.
+
+* Version 1.1.0 (released 2019-05-08)
+ ** MacOS: fix IOKit crash on HID read.
+ ** Windows: fix contents of release file.
+ ** EdDSA (Ed25519) support.
+ ** fido_dev_make_cred: fix order of CBOR map keys.
+ ** fido_dev_get_assert: plug memory leak when operating on U2F devices.
+
+* Version 1.0.0 (released 2019-03-21)
+ ** Native HID support on Linux, MacOS, and Windows.
+ ** fido2-{assert,cred}: new -u option to force U2F on dual authenticators.
+ ** fido2-assert: support for multiple resident keys with the same RP.
+ ** Strict checks for CTAP2 compliance on received CBOR payloads.
+ ** Better fuzzing harnesses.
+ ** Documentation and reliability fixes.
+
+* Version 0.4.0 (released 2019-01-07)
+ ** fido2-assert: print the user id for resident credentials.
+ ** Fix encoding of COSE algorithms when making a credential.
+ ** Rework purpose of fido_cred_set_type; no ABI change.
+ ** Minor documentation and code fixes.
+
+* Version 0.3.0 (released 2018-09-11)
+ ** Various reliability fixes.
+ ** Merged fuzzing instrumentation.
+ ** Added regress tests.
+ ** Added support for FIDO 2's hmac-secret extension.
+ ** New API calls:
+  - fido_assert_hmac_secret_len;
+  - fido_assert_hmac_secret_ptr;
+  - fido_assert_set_extensions;
+  - fido_assert_set_hmac_salt;
+  - fido_cred_set_extensions;
+  - fido_dev_force_fido2.
+ ** Support for native builds with Microsoft Visual Studio 17.
+
+* Version 0.2.0 (released 2018-06-20)
+ ** Added command-line tools.
+ ** Added a couple of missing get functions.
+
+* Version 0.1.1 (released 2018-06-05)
+ ** Added documentation.
+ ** Added OpenSSL 1.0 support.
+ ** Minor fixes.
+
+* Version 0.1.0 (released 2018-05-18)
+ ** First beta release.
index 90f48bc..7b63200 100644 (file)
@@ -1,4 +1,4 @@
-This is an import of https://github.com/Yubico/libfido2 1.8.0 (20210722)
+This is an import of https://github.com/Yubico/libfido2 1.11.0 (2022-05-03)
 
 Local changes:
 
index 09dacc7..8a956b2 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt EDDSA_PK_NEW 3
 .Os
 .Sh NAME
@@ -11,7 +11,7 @@
 .Nm eddsa_pk_from_EVP_PKEY ,
 .Nm eddsa_pk_from_ptr ,
 .Nm eddsa_pk_to_EVP_PKEY
-.Nd FIDO 2 COSE EDDSA API
+.Nd FIDO2 COSE EDDSA API
 .Sh SYNOPSIS
 .In openssl/evp.h
 .In fido/eddsa.h
@@ -106,7 +106,7 @@ If an error occurs,
 returns NULL.
 .Sh RETURN VALUES
 The
-.Fn eddsa_pk_from_EC_KEY
+.Fn eddsa_pk_from_EVP_PKEY
 and
 .Fn eddsa_pk_from_ptr
 functions return
index 6f46e0d..0bfd034 100644 (file)
@@ -1,17 +1,18 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 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: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt ES256_PK_NEW 3
 .Os
 .Sh NAME
 .Nm es256_pk_new ,
 .Nm es256_pk_free ,
 .Nm es256_pk_from_EC_KEY ,
+.Nm es256_pk_from_EVP_PKEY ,
 .Nm es256_pk_from_ptr ,
 .Nm es256_pk_to_EVP_PKEY
-.Nd FIDO 2 COSE ES256 API
+.Nd FIDO2 COSE ES256 API
 .Sh SYNOPSIS
 .In openssl/ec.h
 .In fido/es256.h
@@ -22,6 +23,8 @@
 .Ft int
 .Fn es256_pk_from_EC_KEY "es256_pk_t *pk" "const EC_KEY *ec"
 .Ft int
+.Fn es256_pk_from_EVP_PKEY "es256_pk_t *pk" "const EVP_PKEY *pkey"
+.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"
@@ -79,6 +82,16 @@ No references to
 are kept.
 .Pp
 The
+.Fn es256_pk_from_EVP_PKEY
+function fills
+.Fa pk
+with the contents of
+.Fa pkey .
+No references to
+.Fa pkey
+are kept.
+.Pp
+The
 .Fn es256_pk_from_ptr
 function fills
 .Fa pk
@@ -110,7 +123,8 @@ If an error occurs,
 returns NULL.
 .Sh RETURN VALUES
 The
-.Fn es256_pk_from_EC_KEY
+.Fn es256_pk_from_EC_KEY ,
+.Fn es256_pk_from_EVP_PKEY ,
 and
 .Fn es256_pk_from_ptr
 functions return
index e5b8b18..8beea2f 100644 (file)
@@ -2,12 +2,12 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .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
+.Nd allow a credential in a FIDO2 assertion
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
@@ -31,7 +31,7 @@ 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
+For the format of a FIDO2 credential ID, please refer to the
 Web Authentication (webauthn) standard.
 .Sh RETURN VALUES
 The error codes returned by
index d14fd01..32be4ac 100644 (file)
@@ -1,8 +1,8 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2022 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_ASSERT_NEW 3
 .Os
 .Sh NAME
@@ -31,7 +31,7 @@
 .Nm fido_assert_id_len ,
 .Nm fido_assert_sigcount ,
 .Nm fido_assert_flags
-.Nd FIDO 2 assertion API
+.Nd FIDO2 assertion API
 .Sh SYNOPSIS
 .In fido.h
 .Ft fido_assert_t *
 .Ft uint8_t
 .Fn fido_assert_flags "const fido_assert_t *assert" "size_t idx"
 .Sh DESCRIPTION
-FIDO 2 assertions are abstracted in
-.Em libfido2
-by the
+A FIDO2 assertion is a collection of statements, each statement a
+map between a challenge, a credential, a signature, and ancillary
+attributes.
+In
+.Em libfido2 ,
+a FIDO2 assertion is abstracted by the
 .Vt fido_assert_t
 type.
 The functions described in this page allow a
@@ -153,47 +156,63 @@ 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_blob_ptr ,
-.Fn fido_assert_hmac_secret_ptr ,
-.Fn fido_assert_largeblob_key_ptr ,
+.Fn fido_assert_clientdata_hash_ptr ,
+.Fn fido_assert_id_ptr ,
+.Fn fido_assert_user_id_ptr ,
 .Fn fido_assert_sig_ptr ,
+.Fn fido_assert_sigcount ,
 and
-.Fn fido_assert_id_ptr
-functions return pointers to the user ID, CBOR-encoded
-authenticator data, cred blob, hmac-secret,
-.Dq largeBlobKey ,
-signature, and credential ID attributes of statement
+.Fn fido_assert_flags
+functions return pointers to the CBOR-encoded authenticator data,
+client data hash, credential ID, user ID, signature, signature
+count, and authenticator data flags of statement
 .Fa idx
 in
 .Fa assert .
 .Pp
 The
-.Fn fido_assert_user_id_len ,
-.Fn fido_assert_authdata_len ,
-.Fn fido_assert_blob_len ,
-.Fn fido_assert_hmac_secret_len ,
-.Fn fido_assert_largeblob_key_len ,
-.Fn fido_assert_sig_len ,
-and
-.Fn fido_assert_id_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
+.Fn fido_assert_hmac_secret_ptr
+function returns a pointer to the hmac-secret attribute of statement
 .Fa idx
 in
 .Fa assert .
+The HMAC Secret Extension
+.Pq hmac-secret
+is a CTAP 2.0 extension.
+Note that the resulting hmac-secret varies according to whether
+user verification was performed by the authenticator.
 .Pp
 The
-.Fn fido_assert_flags
-function returns the authenticator data flags of statement
+.Fn fido_assert_blob_ptr
+and
+.Fn fido_assert_largeblob_key_ptr
+functions return pointers to the
+.Dq credBlob
+and
+.Dq largeBlobKey
+attributes of statement
 .Fa idx
 in
 .Fa assert .
+Credential Blob
+.Pq credBlob
+and
+Large Blob Key
+.Pq largeBlobKey
+are CTAP 2.1 extensions.
+.Pp
+The
+.Fn fido_assert_authdata_len ,
+.Fn fido_assert_clientdata_hash_len ,
+.Fn fido_assert_id_len ,
+.Fn fido_assert_user_id_len ,
+.Fn fido_assert_sig_len ,
+.Fn fido_assert_hmac_secret_len ,
+.Fn fido_assert_blob_len ,
+and
+.Fn fido_assert_largeblob_key_len
+functions return the length of a given attribute.
 .Pp
 Please note that the first statement in
 .Fa assert
@@ -202,31 +221,27 @@ has an
 (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 .
+statement are typically passed to a FIDO2 server for verification.
 .Sh RETURN VALUES
 The authenticator data returned by
 .Fn fido_assert_authdata_ptr
 is a CBOR-encoded byte string, as obtained from the authenticator.
 .Pp
 The
+.Fn fido_assert_rp_id ,
 .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_hmac_secret_ptr ,
-.Fn fido_assert_largeblob_key_ptr ,
+.Fn fido_assert_id_ptr ,
 .Fn fido_assert_user_id_ptr ,
+.Fn fido_assert_sig_ptr ,
+.Fn fido_assert_hmac_secret_ptr ,
+.Fn fido_assert_blob_ptr ,
 and
-.Fn fido_assert_sig_ptr
-functions return NULL if the respective field in
+.Fn fido_assert_largeblob_key_ptr
+functions may return NULL if the respective field in
 .Fa assert
 is not set.
 If not NULL, returned pointers are guaranteed to exist until any API
index b5d070f..d4ee263 100644 (file)
@@ -1,8 +1,8 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2022 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_ASSERT_SET_AUTHDATA 3
 .Os
 .Sh NAME
@@ -18,7 +18,7 @@
 .Nm fido_assert_set_uv ,
 .Nm fido_assert_set_rp ,
 .Nm fido_assert_set_sig
-.Nd set parameters of a FIDO 2 assertion
+.Nd set parameters of a FIDO2 assertion
 .Sh SYNOPSIS
 .In fido.h
 .Bd -literal
@@ -29,9 +29,9 @@ typedef enum {
 } 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"
+.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"
+.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 "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
 .Ft int
@@ -43,7 +43,7 @@ typedef enum {
 .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_hmac_secret "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Fn fido_assert_set_hmac_secret "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len"
 .Ft int
 .Fn fido_assert_set_up "fido_assert_t *assert" "fido_opt_t up"
 .Ft int
@@ -55,14 +55,14 @@ typedef enum {
 .Sh DESCRIPTION
 The
 .Nm
-set of functions define the various parameters of a FIDO 2
+set of functions define the various parameters of a FIDO2
 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
+For the complete specification of a FIDO2 assertion and the format
 of its constituent parts, please refer to the Web Authentication
 (webauthn) standard.
 .Pp
@@ -106,11 +106,8 @@ Alternatively, a raw binary blob may be passed to
 .Fn fido_assert_set_authdata_raw .
 .Pp
 The
-.Fn fido_assert_set_clientdata_hash ,
-.Fn fido_assert_set_hmac_salt ,
-and
-.Fn fido_assert_set_hmac_secret
-functions set the client data hash and hmac-salt parts of
+.Fn fido_assert_set_clientdata_hash
+function sets the client data hash of
 .Fa assert
 to
 .Fa ptr ,
@@ -167,6 +164,31 @@ is zero, the extensions of
 are cleared.
 .Pp
 The
+.Fn fido_assert_set_hmac_salt
+and
+.Fn fido_assert_set_hmac_secret
+functions set the hmac-salt and hmac-secret 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.
+The HMAC Secret
+.Pq hmac-secret
+Extension is a CTAP 2.0 extension.
+Note that the resulting hmac-secret varies according to whether
+user verification was performed by the authenticator.
+The
+.Fn fido_assert_set_hmac_secret
+function is normally only useful when writing tests.
+.Pp
+The
 .Fn fido_assert_set_up
 and
 .Fn fido_assert_set_uv
@@ -184,27 +206,22 @@ by default, allowing the authenticator to use its default settings.
 Use of the
 .Nm
 set of functions may happen in two distinct situations:
-when asking a FIDO device to produce a series of assertion
+when asking a FIDO2 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
+(i.e, in the context of a FIDO2 client), or when verifying assertion
 statements using
 .Xr fido_assert_verify 3
-(i.e, in the context of a FIDO server).
+(i.e, in the context of a FIDO2 server).
 .Pp
-For a complete description of the generation of a FIDO 2 assertion
-and its verification, please refer to the FIDO 2 specification.
+For a complete description of the generation of a FIDO2 assertion
+and its verification, please refer to the FIDO2 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 .
-.Pp
-.Fn fido_assert_set_hmac_secret
-is not normally useful in a FIDO client or server \(em it is provided
-to enable testing other functionality that relies on retrieving the
-HMAC secret from an assertion obtained from an authenticator.
 .Sh RETURN VALUES
 The
 .Nm
index 04380b2..1e004b9 100644 (file)
@@ -2,16 +2,16 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_ASSERT_VERIFY 3
 .Os
 .Sh NAME
 .Nm fido_assert_verify
-.Nd verifies the signature of a FIDO 2 assertion statement
+.Nd verifies the signature of a FIDO2 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"
+.Fn fido_assert_verify "const fido_assert_t *assert" "size_t idx" "int cose_alg" "const void *pk"
 .Sh DESCRIPTION
 The
 .Fn fido_assert_verify
@@ -23,7 +23,7 @@ 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
+herself familiar with the FIDO2 assertion statement process
 as defined in the Web Authentication (webauthn) standard.
 .Pp
 A brief description follows:
index df0fb69..1054ff7 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_BIO_DEV_GET_INFO 3
 .Os
 .Sh NAME
@@ -13,7 +13,7 @@
 .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
+.Nd FIDO2 biometric authenticator API
 .Sh SYNOPSIS
 .In fido.h
 .In fido/bio.h
index 603421a..d5f4889 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_BIO_ENROLL_NEW 3
 .Os
 .Sh NAME
@@ -10,7 +10,7 @@
 .Nm fido_bio_enroll_free ,
 .Nm fido_bio_enroll_last_status ,
 .Nm fido_bio_enroll_remaining_samples
-.Nd FIDO 2 biometric enrollment API
+.Nd FIDO2 biometric enrollment API
 .Sh SYNOPSIS
 .In fido.h
 .In fido/bio.h
@@ -40,7 +40,7 @@
 .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
+Ongoing FIDO2 biometric enrollments are abstracted in
 .Em libfido2
 by the
 .Vt fido_bio_enroll_t
index 5712db2..4132036 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_BIO_INFO_NEW 3
 .Os
 .Sh NAME
@@ -10,7 +10,7 @@
 .Nm fido_bio_info_free ,
 .Nm fido_bio_info_type ,
 .Nm fido_bio_info_max_samples
-.Nd FIDO 2 biometric sensor information API
+.Nd FIDO2 biometric sensor information API
 .Sh SYNOPSIS
 .In fido.h
 .In fido/bio.h
index da012b4..6037474 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_BIO_TEMPLATE 3
 .Os
 .Sh NAME
@@ -17,7 +17,7 @@
 .Nm fido_bio_template_new ,
 .Nm fido_bio_template_set_id ,
 .Nm fido_bio_template_set_name
-.Nd FIDO 2 biometric template API
+.Nd FIDO2 biometric template API
 .Sh SYNOPSIS
 .In fido.h
 .In fido/bio.h
@@ -44,7 +44,7 @@
 .Ft const fido_bio_template_t *
 .Fn fido_bio_template "const fido_bio_template_array_t *array" "size_t idx"
 .Sh DESCRIPTION
-Existing FIDO 2 biometric enrollments are abstracted in
+Existing FIDO2 biometric enrollments are abstracted in
 .Em libfido2
 by the
 .Vt fido_bio_template_t
index 93a4aff..b70249a 100644 (file)
@@ -1,8 +1,8 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2022 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_CBOR_INFO_NEW 3
 .Os
 .Sh NAME
 .Nm fido_cbor_info_versions_len ,
 .Nm fido_cbor_info_options_len ,
 .Nm fido_cbor_info_maxmsgsiz ,
+.Nm fido_cbor_info_maxcredbloblen ,
 .Nm fido_cbor_info_maxcredcntlst ,
 .Nm fido_cbor_info_maxcredidlen ,
+.Nm fido_cbor_info_maxlargeblob ,
 .Nm fido_cbor_info_fwversion
-.Nd FIDO 2 CBOR Info API
+.Nd FIDO2 CBOR Info API
 .Sh SYNOPSIS
 .In fido.h
 .Ft fido_cbor_info_t *
@@ -79,6 +81,8 @@
 .Ft uint64_t
 .Fn fido_cbor_info_maxcredidlen "const fido_cbor_info_t *ci"
 .Ft uint64_t
+.Fn fido_cbor_info_maxlargeblob "const fido_cbor_info_t *ci"
+.Ft uint64_t
 .Fn fido_cbor_info_fwversion "const fido_cbor_info_t *ci"
 .Sh DESCRIPTION
 The
@@ -200,6 +204,12 @@ as reported in
 .Fa ci .
 .Pp
 The
+.Fn fido_cbor_info_maxlargeblob
+function returns the maximum length in bytes of an authenticator's
+serialized largeBlob array as reported in
+.Fa ci .
+.Pp
+The
 .Fn fido_cbor_info_fwversion
 function returns the firmware version attribute of
 .Fa ci .
index d27c0aa..f42411c 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_CRED_EXCLUDE 3
 .Os
 .Sh NAME
@@ -44,7 +44,7 @@ then
 .Xr fido_dev_make_cred 3
 will fail.
 .Pp
-For the format of a FIDO 2 credential ID, please refer to the
+For the format of a FIDO2 credential ID, please refer to the
 Web Authentication (webauthn) standard.
 .Sh RETURN VALUES
 The error codes returned by
index f78f6a6..6e25c25 100644 (file)
@@ -1,13 +1,14 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_CRED_NEW 3
 .Os
 .Sh NAME
 .Nm fido_cred_new ,
 .Nm fido_cred_free ,
+.Nm fido_cred_pin_minlen ,
 .Nm fido_cred_prot ,
 .Nm fido_cred_fmt ,
 .Nm fido_cred_rp_id ,
@@ -24,6 +25,7 @@
 .Nm fido_cred_sig_ptr ,
 .Nm fido_cred_user_id_ptr ,
 .Nm fido_cred_x5c_ptr ,
+.Nm fido_cred_attstmt_ptr ,
 .Nm fido_cred_authdata_len ,
 .Nm fido_cred_authdata_raw_len ,
 .Nm fido_cred_clientdata_hash_len ,
 .Nm fido_cred_sig_len ,
 .Nm fido_cred_user_id_len ,
 .Nm fido_cred_x5c_len ,
+.Nm fido_cred_attstmt_len ,
 .Nm fido_cred_type ,
 .Nm fido_cred_flags ,
 .Nm fido_cred_sigcount
-.Nd FIDO 2 credential API
+.Nd FIDO2 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 size_t
+.Fn fido_cred_pin_minlen "const fido_cred_t *cred"
 .Ft int
-.Fn fido_cred_prot "fido_cred_t *cred"
+.Fn fido_cred_prot "const fido_cred_t *cred"
 .Ft const char *
 .Fn fido_cred_fmt "const fido_cred_t *cred"
 .Ft const char *
@@ -76,6 +81,8 @@
 .Fn fido_cred_user_id_ptr "const fido_cred_t *cred"
 .Ft const unsigned char *
 .Fn fido_cred_x5c_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_attstmt_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_user_id_len "const fido_cred_t *cred"
 .Ft size_t
 .Fn fido_cred_x5c_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_attstmt_len "const fido_cred_t *cred"
 .Ft int
 .Fn fido_cred_type "const fido_cred_t *cred"
 .Ft uint8_t
 .Ft uint32_t
 .Fn fido_cred_sigcount "const fido_cred_t *cred"
 .Sh DESCRIPTION
-FIDO 2 credentials are abstracted in
+FIDO2 credentials are abstracted in
 .Em libfido2
 by the
 .Vt fido_cred_t
@@ -146,13 +155,35 @@ may be NULL, in which case
 .Fn fido_cred_free
 is a NOP.
 .Pp
-The
+If the CTAP 2.1
+.Dv FIDO_EXT_MINPINLEN
+extension is enabled on
+.Fa cred ,
+then the
+.Fn fido_cred_pin_minlen
+function returns the minimum PIN length of
+.Fa cred .
+Otherwise,
+.Fn fido_cred_pin_minlen
+returns zero.
+See
+.Xr fido_cred_set_pin_minlen 3
+on how to enable this extension.
+.Pp
+If the CTAP 2.1
+.Dv FIDO_EXT_CRED_PROTECT
+extension is enabled on
+.Fa cred ,
+then the
 .Fn fido_cred_prot
 function returns the protection of
 .Fa cred .
+Otherwise,
+.Fn fido_cred_prot
+returns zero.
 See
 .Xr fido_cred_set_prot 3
-for the values understood by
+for the protection policies understood by
 .Em libfido2 .
 .Pp
 The
@@ -186,12 +217,14 @@ The
 .Fn fido_cred_pubkey_ptr ,
 .Fn fido_cred_sig_ptr ,
 .Fn fido_cred_user_id_ptr ,
+.Fn fido_cred_x5c_ptr ,
 and
-.Fn fido_cred_x5c_ptr
+.Fn fido_cred_attstmt_ptr
 functions return pointers to the CBOR-encoded and raw authenticator
 data, client data hash, ID, authenticator attestation GUID,
 .Dq largeBlobKey ,
-public key, signature, user ID, and x509 certificate parts of
+public key, signature, user ID, x509 certificate, and attestation
+statement parts of
 .Fa cred ,
 or NULL if the respective entry is not set.
 .Pp
@@ -205,11 +238,12 @@ The corresponding length can be obtained by
 .Fn fido_cred_pubkey_len ,
 .Fn fido_cred_sig_len ,
 .Fn fido_cred_user_id_len ,
+.Fn fido_cred_x5c_len ,
 and
-.Fn fido_cred_x5c_len .
+.Fn fido_cred_attstmt_len .
 .Pp
 The authenticator data, x509 certificate, and signature parts of a
-credential are typically passed to a FIDO 2 server for verification.
+credential are typically passed to a FIDO2 server for verification.
 .Pp
 The
 .Fn fido_cred_type
@@ -251,6 +285,8 @@ qualifier is invoked.
 .Sh SEE ALSO
 .Xr fido_cred_exclude 3 ,
 .Xr fido_cred_set_authdata 3 ,
+.Xr fido_cred_set_pin_minlen 3 ,
+.Xr fido_cred_set_prot 3 ,
 .Xr fido_cred_verify 3 ,
 .Xr fido_credman_metadata_new 3 ,
 .Xr fido_dev_largeblob_get 3 ,
index dea4b7d..39359e9 100644 (file)
@@ -1,13 +1,14 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_CRED_SET_AUTHDATA 3
 .Os
 .Sh NAME
 .Nm fido_cred_set_authdata ,
 .Nm fido_cred_set_authdata_raw ,
+.Nm fido_cred_set_attstmt ,
 .Nm fido_cred_set_x509 ,
 .Nm fido_cred_set_sig ,
 .Nm fido_cred_set_id ,
 .Nm fido_cred_set_user ,
 .Nm fido_cred_set_extensions ,
 .Nm fido_cred_set_blob ,
+.Nm fido_cred_set_pin_minlen ,
 .Nm fido_cred_set_prot ,
 .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
+.Nd set parameters of a FIDO2 credential
 .Sh SYNOPSIS
 .In fido.h
 .Bd -literal
@@ -37,6 +39,8 @@ typedef enum {
 .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_attstmt "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"
@@ -55,6 +59,8 @@ typedef enum {
 .Ft int
 .Fn fido_cred_set_blob "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
 .Ft int
+.Fn fido_cred_set_pin_minlen "fido_cred_t *cred" "size_t len"
+.Ft int
 .Fn fido_cred_set_prot "fido_cred_t *cred" "int prot"
 .Ft int
 .Fn fido_cred_set_rk "fido_cred_t *cred" "fido_opt_t rk"
@@ -67,26 +73,28 @@ typedef enum {
 .Sh DESCRIPTION
 The
 .Nm
-set of functions define the various parameters of a FIDO 2
+set of functions define the various parameters of a FIDO2
 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
+For the complete specification of a FIDO2 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_attstmt ,
 .Fn fido_cred_set_x509 ,
 .Fn fido_cred_set_sig ,
 .Fn fido_cred_set_id ,
 and
 .Fn fido_cred_set_clientdata_hash
-functions set the authenticator data, attestation certificate,
-signature, id, and client data hash parts of
+functions set the authenticator data, attestation statement,
+attestation certificate, attestation signature, id, and client
+data hash parts of
 .Fa cred
 to
 .Fa ptr ,
@@ -98,13 +106,13 @@ bytes.
 A copy of
 .Fa ptr
 is made, and no references to the passed pointer are kept.
+.Pp
 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
 An application calling
 .Fn fido_cred_set_authdata
 does not need to call
@@ -112,6 +120,20 @@ does not need to call
 The latter is meant to be used in contexts where the
 credential's authenticator data is not available.
 .Pp
+The attestation statement passed to
+.Fn fido_cred_set_attstmt
+must be a CBOR-encoded map, as obtained from
+.Fn fido_cred_attstmt_ptr .
+An application calling
+.Fn fido_cred_set_attstmt
+does not need to call
+.Fn fido_cred_set_x509
+or
+.Fn fido_cred_set_sig .
+The latter two are meant to be used in contexts where the
+credential's complete attestation statement is not available or
+required.
+.Pp
 The
 .Fn fido_cred_set_clientdata
 function allows an application to set the client data hash of
@@ -183,6 +205,7 @@ At the moment, only the
 .Dv FIDO_EXT_CRED_BLOB ,
 .Dv FIDO_EXT_CRED_PROTECT ,
 .Dv FIDO_EXT_HMAC_SECRET ,
+.Dv FIDO_EXT_MINPINLEN ,
 and
 .Dv FIDO_EXT_LARGEBLOB_KEY
 extensions are supported.
@@ -205,8 +228,32 @@ which must be
 bytes long.
 .Pp
 The
+.Fn fido_cred_set_pin_minlen
+function enables the CTAP 2.1
+.Dv FIDO_EXT_MINPINLEN
+extension on
+.Fa cred
+and sets the expected minimum PIN length of
+.Fa cred
+to
+.Fa len ,
+where
+.Fa len
+is greater than zero.
+If
+.Fa len
+is zero, the
+.Dv FIDO_EXT_MINPINLEN
+extension is disabled on
+.Fa cred .
+.Pp
+The
 .Fn fido_cred_set_prot
-function sets the protection of
+function enables the CTAP 2.1
+.Dv FIDO_EXT_CRED_PROTECT
+extension on
+.Fa cred
+and sets the protection of
 .Fa cred
 to the scalar
 .Fa prot .
@@ -278,15 +325,15 @@ Note that not all authenticators support COSE_RS256 or COSE_EDDSA.
 Use of the
 .Nm
 set of functions may happen in two distinct situations:
-when generating a new credential on a FIDO device, prior to
+when generating a new credential on a FIDO2 device, prior to
 .Xr fido_dev_make_cred 3
-(i.e, in the context of a FIDO client), or when validating
+(i.e, in the context of a FIDO2 client), or when validating
 a generated credential using
 .Xr fido_cred_verify 3
-(i.e, in the context of a FIDO server).
+(i.e, in the context of a FIDO2 server).
 .Pp
-For a complete description of the generation of a FIDO 2 credential
-and its verification, please refer to the FIDO 2 specification.
+For a complete description of the generation of a FIDO2 credential
+and its verification, please refer to the FIDO2 specification.
 A concrete utilisation example of the
 .Nm
 set of functions can be found in the
index 47c1494..9db299e 100644 (file)
@@ -1,36 +1,41 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_CRED_VERIFY 3
 .Os
 .Sh NAME
-.Nm fido_cred_verify
-.Nd verifies the attestation signature of a FIDO 2 credential
+.Nm fido_cred_verify ,
+.Nm fido_cred_verify_self
+.Nd verify the attestation signature of a FIDO2 credential
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
 .Fn fido_cred_verify "const fido_cred_t *cred"
+.Ft int
+.Fn fido_cred_verify_self "const fido_cred_t *cred"
 .Sh DESCRIPTION
 The
 .Fn fido_cred_verify
-function verifies whether the attestation signature contained in
+and
+.Fn fido_cred_verify_self
+functions verify whether the attestation signature contained in
 .Fa cred
 matches the attributes of the credential.
 Before using
 .Fn fido_cred_verify
+or
+.Fn fido_cred_verify_self
 in a sensitive context, the reader is strongly encouraged to make
-herself familiar with the FIDO 2 credential attestation process
+herself familiar with the FIDO2 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/discoverable key and user verification
-attributes of
+credential ID, type, protection policy, minimum PIN length, and
+resident/discoverable 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.
@@ -40,27 +45,44 @@ Please note that the x509 certificate itself is not verified.
 The attestation statement formats supported by
 .Fn fido_cred_verify
 are
-.Em packed
+.Em packed ,
+.Em fido-u2f ,
 and
-.Em fido-u2f .
+.Em tpm .
 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.
+.Pp
+The
+.Fn fido_cred_verify_self
+function verifies whether the client data hash, relying party ID,
+credential ID, type, protection policy, minimum PIN length, and
+resident/discoverable key and user verification attributes of
+.Fa cred
+have been attested by the holder of the credential's private key.
+.Pp
+The attestation statement formats supported by
+.Fn fido_cred_verify_self
+are
+.Em packed
+and
+.Em fido-u2f .
+The attestation type implemented by
+.Fn fido_cred_verify_self
+is
+.Em Self Attestation .
+.Pp
 Other attestation formats and types are not supported.
 .Sh RETURN VALUES
 The error codes returned by
 .Fn fido_cred_verify
+and
+.Fn fido_cred_verify_self
 are defined in
 .In fido/err.h .
 If
 .Fa cred
-does not contain attestation data, then
-.Dv FIDO_ERR_INVALID_ARGUMENT
-is returned.
-If
-.Fa cred
 passes verification, then
 .Dv FIDO_OK
 is returned.
index 7fb3109..a414b44 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_CREDMAN_METADATA_NEW 3
 .Os
 .Sh NAME
@@ -26,7 +26,7 @@
 .Nm fido_credman_set_dev_rk ,
 .Nm fido_credman_del_dev_rk ,
 .Nm fido_credman_get_dev_rp
-.Nd FIDO 2 credential management API
+.Nd FIDO2 credential management API
 .Sh SYNOPSIS
 .In fido.h
 .In fido/credman.h
@@ -307,7 +307,7 @@ The
 .Fn fido_credman_set_dev_rk ,
 .Fn fido_credman_del_dev_rk ,
 and
-.Fn  fido_credman_get_dev_rp
+.Fn fido_credman_get_dev_rp
 functions return
 .Dv FIDO_OK
 on success.
@@ -323,4 +323,4 @@ should have their return values checked for NULL.
 .Sh CAVEATS
 Resident credentials are called
 .Dq discoverable credentials
-in FIDO 2.1.
+in CTAP 2.1.
index c059fa1..65acddf 100644 (file)
@@ -2,15 +2,16 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: March 29 2022 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_ENABLE_ENTATTEST 3
 .Os
 .Sh NAME
 .Nm fido_dev_enable_entattest ,
 .Nm fido_dev_toggle_always_uv ,
 .Nm fido_dev_force_pin_change ,
-.Nm fido_dev_set_pin_minlen
-.Nd FIDO 2.1 configuration authenticator API
+.Nm fido_dev_set_pin_minlen ,
+.Nm fido_dev_set_pin_minlen_rpid
+.Nd CTAP 2.1 configuration authenticator API
 .Sh SYNOPSIS
 .In fido.h
 .In fido/config.h
 .Fn fido_dev_force_pin_change "fido_dev_t *dev" "const char *pin"
 .Ft int
 .Fn fido_dev_set_pin_minlen "fido_dev_t *dev" "size_t len" "const char *pin"
+.Ft int
+.Fn fido_dev_set_pin_minlen_rpid "fido_dev_t *dev" "const char * const *rpid" "size_t n" "const char *pin"
 .Sh DESCRIPTION
 The functions described in this page allow configuration of a
-FIDO 2.1 authenticator.
+CTAP 2.1 authenticator.
 .Pp
 The
 .Fn fido_dev_enable_entattest
@@ -77,6 +80,24 @@ to
 .Fa len .
 Minimum PIN lengths may only be increased.
 .Pp
+The
+.Fn fido_dev_set_pin_minlen_rpid
+function sets the list of relying party identifiers
+.Pq RP IDs
+that are allowed to obtain the minimum PIN length of
+.Fa dev
+through the CTAP 2.1
+.Dv FIDO_EXT_MINPINLEN
+extension.
+The list of RP identifiers is denoted by
+.Fa rpid ,
+a vector of
+.Fa n
+NUL-terminated UTF-8 strings.
+A copy of
+.Fa rpid
+is made, and no reference to it or its contents is kept.
+.Pp
 Configuration settings are reflected in the payload returned by the
 authenticator in response to a
 .Xr fido_dev_get_cbor_info 3
@@ -86,13 +107,15 @@ The error codes returned by
 .Fn fido_dev_enable_entattest ,
 .Fn fido_dev_toggle_always_uv ,
 .Fn fido_dev_force_pin_change ,
+.Fn fido_dev_set_pin_minlen ,
 and
-.Fn fido_dev_set_pin_minlen
+.Fn fido_dev_set_pin_minlen_rpid
 are defined in
 .In fido/err.h .
 On success,
 .Dv FIDO_OK
 is returned.
 .Sh SEE ALSO
+.Xr fido_cred_pin_minlen 3 ,
 .Xr fido_dev_get_cbor_info 3 ,
 .Xr fido_dev_reset 3
index 3ea8157..e0b7eca 100644 (file)
@@ -2,20 +2,20 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_GET_ASSERT 3
 .Os
 .Sh NAME
 .Nm fido_dev_get_assert
-.Nd obtains an assertion from a FIDO device
+.Nd obtains an assertion from a FIDO2 device
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
-.Fn fido_dev_get_assert "fido_dev_t *dev" " fido_assert_t *assert" "const char *pin"
+.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
+function asks the FIDO2 device represented by
 .Fa dev
 for an assertion according to the following parameters defined in
 .Fa assert :
index 4a9b990..3329cb2 100644 (file)
@@ -2,13 +2,13 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: September 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_GET_TOUCH_BEGIN 3
 .Os
 .Sh NAME
 .Nm fido_dev_get_touch_begin ,
 .Nm fido_dev_get_touch_status
-.Nd asynchronously wait for touch on a FIDO 2 authenticator
+.Nd asynchronously wait for touch on a FIDO2 authenticator
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
@@ -17,7 +17,7 @@
 .Fn fido_dev_get_touch_status "fido_dev_t *dev" "int *touched" "int ms"
 .Sh DESCRIPTION
 The functions described in this page allow an application to
-asynchronously wait for touch on a FIDO authenticator.
+asynchronously wait for touch on a FIDO2 authenticator.
 This is useful when multiple authenticators are present and
 the application needs to know which one to use.
 .Pp
index 7e0389c..adfb6ef 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: March 29 2022 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_INFO_MANIFEST 3
 .Os
 .Sh NAME
@@ -14,8 +14,9 @@
 .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
+.Nm fido_dev_info_product_string ,
+.Nm fido_dev_info_set
+.Nd FIDO2 device discovery functions
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
@@ -36,6 +37,8 @@
 .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"
+.Ft int
+.Fn fido_dev_info_set "fido_dev_info_t *devlist" "size_t i" "const char *path" "const char *manufacturer" "const char *product" "const fido_dev_io_t *io" "const fido_dev_transport_t *transport"
 .Sh DESCRIPTION
 The
 .Fn fido_dev_info_manifest
@@ -43,7 +46,7 @@ function fills
 .Fa devlist
 with up to
 .Fa ilen
-FIDO devices found by the underlying operating system.
+FIDO2 devices found by the underlying operating system.
 Currently only USB HID devices are supported.
 The number of discovered devices is returned in
 .Fa olen ,
@@ -112,17 +115,51 @@ The
 .Fn fido_dev_info_manufacturer_string
 function returns the manufacturer string of
 .Fa di .
+If
+.Fa di
+does not have an associated manufacturer string,
+.Fn fido_dev_info_manufacturer_string
+returns an empty string.
 .Pp
 The
 .Fn fido_dev_info_product_string
 function returns the product string of
 .Fa di .
+If
+.Fa di
+does not have an associated product string,
+.Fn fido_dev_info_product_string
+returns an empty string.
 .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 .
+.Pp
+The
+.Fn fido_dev_info_set
+function initializes an entry in a device list allocated by
+.Fn fido_dev_info_new
+with the specified path, manufacturer, and product strings, and with
+the specified I/O handlers and, optionally, transport functions, as
+described in
+.Xr fido_dev_set_io_functions 3 .
+The
+.Fa io
+argument must be specified; the
+.Fa transport
+argument may be
+.Dv NULL .
+The path, I/O handlers, and transport functions will be used
+automatically by
+.Xr fido_dev_new_with_info 3
+and
+.Xr fido_dev_open_with_info 3 .
+An application can use this, for example, to substitute mock FIDO2
+devices in testing for the real ones that
+.Fn fido_dev_info_manifest
+would discover.
 .Sh RETURN VALUES
 The
 .Fn fido_dev_info_manifest
@@ -132,6 +169,14 @@ If a discovery error occurs, the
 .Fa olen
 pointer is set to 0.
 .Pp
+On success, the
+.Fn fido_dev_info_set
+function returns
+.Dv FIDO_OK .
+On error, a different error code defined in
+.In fido/err.h
+is returned.
+.Pp
 The pointers returned by
 .Fn fido_dev_info_ptr ,
 .Fn fido_dev_info_path ,
index cd9d0cc..9bcfe48 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_LARGEBLOB_GET 3
 .Os
 .Sh NAME
@@ -11,7 +11,7 @@
 .Nm fido_dev_largeblob_remove ,
 .Nm fido_dev_largeblob_get_array ,
 .Nm fido_dev_largeblob_set_array
-.Nd FIDO 2 large blob API
+.Nd FIDO2 large blob API
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
@@ -29,10 +29,10 @@ The
 .Dq largeBlobs
 API of
 .Em libfido2
-allows binary blobs residing on a FIDO 2.1 authenticator to be
+allows binary blobs residing on a CTAP 2.1 authenticator to be
 read, written, and inspected.
 .Dq largeBlobs
-is a FIDO 2.1 extension.
+is a CTAP 2.1 extension.
 .Pp
 .Dq largeBlobs
 are stored as elements of a CBOR array.
@@ -58,9 +58,9 @@ The
 .Dq largeBlobs
 CBOR array is opaque to the authenticator.
 Management of the array is left at the discretion of FIDO2 clients.
-For further details on FIDO 2.1's
+For further details on CTAP 2.1's
 .Dq largeBlobs
-extension, please refer to the FIDO 2.1 spec.
+extension, please refer to the CTAP 2.1 spec.
 .Pp
 The
 .Fn fido_dev_largeblob_get
index 158f5ea..da1d035 100644 (file)
@@ -2,20 +2,20 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_MAKE_CRED 3
 .Os
 .Sh NAME
 .Nm fido_dev_make_cred
-.Nd generates a new credential on a FIDO device
+.Nd generates a new credential on a FIDO2 device
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
-.Fn fido_dev_make_cred "fido_dev_t *dev" " fido_cred_t *cred" "const char *pin"
+.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
+function asks the FIDO2 device represented by
 .Fa dev
 to generate a new credential according to the following parameters
 defined in
index def6a99..ff6e16f 100644 (file)
@@ -2,14 +2,16 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_OPEN 3
 .Os
 .Sh NAME
 .Nm fido_dev_open ,
+.Nm fido_dev_open_with_info ,
 .Nm fido_dev_close ,
 .Nm fido_dev_cancel ,
 .Nm fido_dev_new ,
+.Nm fido_dev_new_with_info ,
 .Nm fido_dev_free ,
 .Nm fido_dev_force_fido2 ,
 .Nm fido_dev_force_u2f ,
 .Nm fido_dev_is_winhello ,
 .Nm fido_dev_supports_credman ,
 .Nm fido_dev_supports_cred_prot ,
+.Nm fido_dev_supports_permissions ,
 .Nm fido_dev_supports_pin ,
-.Nm fido_dev_has_pin ,
 .Nm fido_dev_supports_uv ,
+.Nm fido_dev_has_pin ,
 .Nm fido_dev_has_uv ,
 .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
+.Nd FIDO2 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_open_with_info "fido_dev_t *dev"
+.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 fido_dev_t *
+.Fn fido_dev_new_with_info "const fido_dev_info_t *"
 .Ft void
 .Fn fido_dev_free "fido_dev_t **dev_p"
 .Ft void
 .Ft bool
 .Fn fido_dev_supports_cred_prot "const fido_dev_t *dev"
 .Ft bool
-.Fn fido_dev_supports_pin "const fido_dev_t *dev"
+.Fn fido_dev_supports_permissions "const fido_dev_t *dev"
 .Ft bool
-.Fn fido_dev_has_pin "const fido_dev_t *dev"
+.Fn fido_dev_supports_pin "const fido_dev_t *dev"
 .Ft bool
 .Fn fido_dev_supports_uv "const fido_dev_t *dev"
 .Ft bool
+.Fn fido_dev_has_pin "const fido_dev_t *dev"
+.Ft bool
 .Fn fido_dev_has_uv "const fido_dev_t *dev"
 .Ft uint8_t
 .Fn fido_dev_protocol "const fido_dev_t *dev"
@@ -92,6 +101,13 @@ flag was set in
 .Xr fido_init 3 .
 .Pp
 The
+.Fn fido_dev_open_with_info
+function opens
+.Fa dev
+as previously allocated using
+.Fn fido_dev_new_with_info .
+.Pp
+The
 .Fn fido_dev_close
 function closes the device represented by
 .Fa dev .
@@ -113,6 +129,18 @@ function returns a pointer to a newly allocated, empty
 If memory cannot be allocated, NULL is returned.
 .Pp
 The
+.Fn fido_dev_new_with_info
+function returns a pointer to a newly allocated
+.Vt fido_dev_t
+with
+.Vt fido_dev_info_t
+parameters, for use with
+.Xr fido_dev_info_manifest 3
+and
+.Fn fido_dev_open_with_info .
+If memory cannot be allocated, NULL is returned.
+.Pp
+The
 .Fn fido_dev_free
 function releases the memory backing
 .Fa *dev_p ,
@@ -134,12 +162,18 @@ is a NOP.
 The
 .Fn fido_dev_force_fido2
 function can be used to force CTAP2 communication with
-.Fa dev .
+.Fa dev ,
+where
+.Fa dev
+is an open device.
 .Pp
 The
 .Fn fido_dev_force_u2f
 function can be used to force CTAP1 (U2F) communication with
-.Fa dev .
+.Fa dev ,
+where
+.Fa dev
+is an open device.
 .Pp
 The
 .Fn fido_dev_is_fido2
@@ -147,7 +181,7 @@ function returns
 .Dv true
 if
 .Fa dev
-is a FIDO 2 device.
+is a FIDO2 device.
 .Pp
 The
 .Fn fido_dev_is_winhello
@@ -163,7 +197,7 @@ function returns
 .Dv true
 if
 .Fa dev
-supports FIDO 2.1 Credential Management.
+supports CTAP 2.1 Credential Management.
 .Pp
 The
 .Fn fido_dev_supports_cred_prot
@@ -171,23 +205,23 @@ function returns
 .Dv true
 if
 .Fa dev
-supports FIDO 2.1 Credential Protection.
+supports CTAP 2.1 Credential Protection.
 .Pp
 The
-.Fn fido_dev_supports_pin
+.Fn fido_dev_supports_permissions
 function returns
 .Dv true
 if
 .Fa dev
-supports FIDO 2.0 Client PINs.
+supports CTAP 2.1 UV token permissions.
 .Pp
 The
-.Fn fido_dev_has_pin
+.Fn fido_dev_supports_pin
 function returns
 .Dv true
 if
 .Fa dev
-has a FIDO 2.0 Client PIN set.
+supports CTAP 2.0 Client PINs.
 .Pp
 The
 .Fn fido_dev_supports_uv
@@ -198,6 +232,14 @@ if
 supports a built-in user verification method.
 .Pp
 The
+.Fn fido_dev_has_pin
+function returns
+.Dv true
+if
+.Fa dev
+has a CTAP 2.0 Client PIN set.
+.Pp
+The
 .Fn fido_dev_has_uv
 function returns
 .Dv true
@@ -236,7 +278,8 @@ functions above, please refer to the FIDO Client to Authenticator
 Protocol (CTAP) specification.
 .Sh RETURN VALUES
 On success,
-.Fn fido_dev_open
+.Fn fido_dev_open ,
+.Fn fido_dev_open_with_info ,
 and
 .Fn fido_dev_close
 return
index 6775b73..cf0680b 100644 (file)
@@ -1,14 +1,17 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 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: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_SET_IO_FUNCTIONS 3
 .Os
 .Sh NAME
 .Nm fido_dev_set_io_functions ,
-.Nm fido_dev_set_sigmask
-.Nd FIDO 2 device I/O interface
+.Nm fido_dev_set_sigmask ,
+.Nm fido_dev_set_timeout ,
+.Nm fido_dev_set_transport_functions ,
+.Nm fido_dev_io_handle
+.Nd FIDO2 device I/O interface
 .Sh SYNOPSIS
 .In fido.h
 .Bd -literal
@@ -29,11 +32,28 @@ typedef int fido_sigset_t;
 #else
 typedef sigset_t fido_sigset_t;
 #endif
+
+typedef int   fido_dev_rx_t(struct fido_dev *,
+                  uint8_t, unsigned char *, size_t, int);
+typedef int   fido_dev_tx_t(struct fido_dev *,
+                  uint8_t, const unsigned char *, size_t);
+
+typedef struct fido_dev_transport {
+       fido_dev_rx_t *rx;
+       fido_dev_tx_t *tx;
+} fido_dev_transport_t;
 .Ed
+.Pp
 .Ft int
 .Fn fido_dev_set_io_functions "fido_dev_t *dev" "const fido_dev_io_t *io"
 .Ft int
 .Fn fido_dev_set_sigmask "fido_dev_t *dev" "const fido_sigset_t *sigmask"
+.Ft int
+.Fn fido_dev_set_timeout "fido_dev_t *dev" "int ms"
+.Ft int
+.Fn fido_dev_set_transport_functions "fido_dev_t *dev" "const fido_dev_transport_t *t"
+.Ft void *
+.Fn fido_dev_io_handle "const fido_dev_t *dev"
 .Sh DESCRIPTION
 The
 .Fn fido_dev_set_io_functions
@@ -122,13 +142,97 @@ No references to
 .Fa sigmask
 are held by
 .Fn fido_dev_set_sigmask .
+.Pp
+The
+.Fn fido_dev_set_timeout
+function informs
+.Em libfido2
+not to block for more than
+.Fa ms
+milliseconds while communicating with
+.Fa dev .
+If a timeout occurs, the corresponding
+.Em fido_dev_*
+function will fail with
+.Dv FIDO_ERR_RX .
+If
+.Fa ms
+is -1,
+then
+.Em libfido2
+may block indefinitely.
+This is the default behaviour.
+When using the Windows Hello backend,
+.Fa ms
+is used as a guidance and may be overwritten by the platform.
+.Pp
+The
+.Fn fido_dev_set_transport_functions
+function sets the transport functions used by
+.Em libfido2
+to talk to
+.Fa dev .
+While the I/O handlers are responsible for sending and receiving
+transmission units of initialization and continuation packets already
+formatted by
+.Em libfido2 ,
+the transport handlers are responsible for sending and receiving
+the CTAPHID commands and data directly, as defined in the FIDO Client
+to Authenticator Protocol (CTAP) standard.
+They are defined as follows:
+.Bl -tag -width Ds
+.It Vt fido_dev_tx_t
+Receives a device, a CTAPHID command to transmit, a data buffer to
+transmit, and the length of the data buffer.
+On success, 0 is returned.
+On error, -1 is returned.
+.It Vt fido_dev_rx_t
+Receives a device, a CTAPHID command whose response the caller expects
+to receive, a data buffer to receive into, the size of the data buffer
+determining the maximum length of a response, and the maximum number of
+milliseconds to wait for a response.
+On success, the number of bytes read into the data buffer is returned.
+On error, -1 is returned.
+.El
+.Pp
+When transport functions are specified,
+.Em libfido2
+will use them instead of the
+.Dv read
+and
+.Dv write
+functions of the I/O handlers.
+However, the I/O handlers must still be specified to open and close the
+device.
+.Pp
+The
+.Fn fido_dev_io_handle
+function returns the opaque pointer returned by the
+.Dv open
+function of the I/O handlers.
+This is useful mainly for the transport functions, which unlike the I/O
+handlers are passed the
+.Vt fido_dev_t
+pointer instead of the opaque I/O handle.
 .Sh RETURN VALUES
 On success,
-.Fn fido_dev_set_io_functions
+.Fn fido_dev_set_io_functions ,
+.Fn fido_dev_set_transport_functions ,
+.Fn fido_dev_set_sigmask ,
 and
-.Fn fido_dev_set_sigmask
+.Fn fido_dev_set_timeout
 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_open 3
+.Rs
+.%D 2021-06-15
+.%O Proposed Standard, Version 2.1
+.%Q FIDO Alliance
+.%R Client to Authenticator Protocol (CTAP)
+.%U https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html
+.Re
index a269541..50244fb 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_DEV_SET_PIN 3
 .Os
 .Sh NAME
@@ -10,7 +10,7 @@
 .Nm fido_dev_get_retry_count ,
 .Nm fido_dev_get_uv_retry_count ,
 .Nm fido_dev_reset
-.Nd FIDO 2 device management functions
+.Nd FIDO2 device management functions
 .Sh SYNOPSIS
 .In fido.h
 .Ft int
index cd95c15..a54dcbc 100644 (file)
@@ -2,16 +2,23 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_INIT 3
 .Os
 .Sh NAME
-.Nm fido_init
-.Nd initialise the FIDO 2 library
+.Nm fido_init ,
+.Nm fido_set_log_handler
+.Nd initialise the FIDO2 library
 .Sh SYNOPSIS
 .In fido.h
+.Bd -literal
+typedef void fido_log_handler_t(const char *);
+.Ed
+.Pp
 .Ft void
 .Fn fido_init "int flags"
+.Ft void
+.Fn fido_set_log_handler "fido_log_handler_t *handler"
 .Sh DESCRIPTION
 The
 .Fn fido_init
@@ -43,8 +50,21 @@ then
 .Em libfido2
 will not fallback to U2F in
 .Xr fido_dev_open 3
-if a device claims to be FIDO2 but fails to respond to a
-FIDO2 command.
+if a device claims to support FIDO2 but fails to respond to
+a CTAP 2.0 greeting.
+.Pp
+The
+.Fn fido_set_log_handler
+function causes
+.Fa handler
+to be called for each log line generated in the context of the
+executing thread.
+Lines passed to
+.Fa handler
+include a trailing newline character and are not printed by
+.Em libfido2
+on
+.Em stderr .
 .Sh SEE ALSO
 .Xr fido_assert_new 3 ,
 .Xr fido_cred_new 3 ,
index fa1919b..3ab956f 100644 (file)
@@ -2,12 +2,12 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt FIDO_STRERR 3
 .Os
 .Sh NAME
 .Nm fido_strerr
-.Nd FIDO 2 error codes
+.Nd FIDO2 error codes
 .Sh SYNOPSIS
 .In fido.h
 .Ft const char *
index a11e012..580ee51 100644 (file)
@@ -1,17 +1,18 @@
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 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: February 7 2020 $
+.Dd $Mdocdate: August 29 2022 $
 .Dt RS256_PK_NEW 3
 .Os
 .Sh NAME
 .Nm rs256_pk_new ,
 .Nm rs256_pk_free ,
 .Nm rs256_pk_from_RSA ,
+.Nm rs256_pk_from_EVP_PKEY ,
 .Nm rs256_pk_from_ptr ,
 .Nm rs256_pk_to_EVP_PKEY
-.Nd FIDO 2 COSE RS256 API
+.Nd FIDO2 COSE RS256 API
 .Sh SYNOPSIS
 .In openssl/rsa.h
 .In fido/rs256.h
@@ -20,6 +21,8 @@
 .Ft void
 .Fn rs256_pk_free "rs256_pk_t **pkp"
 .Ft int
+.Fn rs256_pk_from_EVP_PKEY "rs256_pk_t *pk" "const EVP_PKEY *pkey"
+.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"
@@ -69,6 +72,16 @@ may be NULL, in which case
 is a NOP.
 .Pp
 The
+.Fn rs256_pk_from_EVP_PKEY
+function fills
+.Fa pk
+with the contents of
+.Fa pkey .
+No references to
+.Fa pkey
+are kept.
+.Pp
+The
 .Fn rs256_pk_from_RSA
 function fills
 .Fa pk
@@ -106,7 +119,8 @@ If an error occurs,
 returns NULL.
 .Sh RETURN VALUES
 The
-.Fn rs256_pk_from_RSA
+.Fn rs256_pk_from_EVP_PKEY ,
+.Fn rs256_pk_from_RSA ,
 and
 .Fn rs256_pk_from_ptr
 functions return
index 3066b97..9c15516 100644 (file)
@@ -1,2 +1,2 @@
-major=5
+major=6
 minor=0
index b36f8e3..949af91 100644 (file)
@@ -4,7 +4,6 @@
  * license that can be found in the LICENSE file.
  */
 
-#include <openssl/ecdsa.h>
 #include <openssl/sha.h>
 
 #include "fido.h"
@@ -79,7 +78,7 @@ parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 
 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)
+    const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin, int *ms)
 {
        fido_blob_t      f;
        fido_opt_t       uv = assert->uv;
@@ -127,7 +126,7 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
        if (pin != NULL || (uv == FIDO_OPT_TRUE &&
            fido_dev_supports_permissions(dev))) {
                if ((r = cbor_add_uv_params(dev, cmd, &assert->cdh, pk, ecdh,
-                   pin, assert->rp_id, &argv[5], &argv[6])) != FIDO_OK) {
+                   pin, assert->rp_id, &argv[5], &argv[6], ms)) != FIDO_OK) {
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
                        goto fail;
                }
@@ -144,7 +143,7 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
 
        /* frame and transmit */
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -159,7 +158,7 @@ fail:
 }
 
 static int
-fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
+fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -200,11 +199,11 @@ fido_dev_get_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
 }
 
 static int
-fido_get_next_assert_tx(fido_dev_t *dev)
+fido_get_next_assert_tx(fido_dev_t *dev, int *ms)
 {
        const unsigned char cbor[] = { CTAP_CBOR_NEXT_ASSERT };
 
-       if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) {
+       if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                return (FIDO_ERR_TX);
        }
@@ -213,7 +212,7 @@ fido_get_next_assert_tx(fido_dev_t *dev)
 }
 
 static int
-fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
+fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -243,16 +242,17 @@ fido_get_next_assert_rx(fido_dev_t *dev, fido_assert_t *assert, int ms)
 
 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)
+    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 ||
+       if ((r = fido_dev_get_assert_tx(dev, assert, pk, ecdh, pin,
+           ms)) != 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 ||
+               if ((r = fido_get_next_assert_tx(dev, ms)) != FIDO_OK ||
                    (r = fido_get_next_assert_rx(dev, assert, ms)) != FIDO_OK)
                        return (r);
                assert->stmt_len++;
@@ -286,11 +286,12 @@ 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              ms = dev->timeout_ms;
        int              r;
 
 #ifdef USE_WINHELLO
        if (dev->flags & FIDO_DEV_WINHELLO)
-               return (fido_winhello_get_assert(dev, assert, pin));
+               return (fido_winhello_get_assert(dev, assert, pin, ms));
 #endif
 
        if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
@@ -302,19 +303,19 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
        if (fido_dev_is_fido2(dev) == false) {
                if (pin != NULL || assert->ext.mask != 0)
                        return (FIDO_ERR_UNSUPPORTED_OPTION);
-               return (u2f_authenticate(dev, assert, -1));
+               return (u2f_authenticate(dev, assert, &ms));
        }
 
        if (pin != NULL || (assert->uv == FIDO_OPT_TRUE &&
            fido_dev_supports_permissions(dev)) ||
            (assert->ext.mask & FIDO_EXT_HMAC_SECRET)) {
-               if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+               if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_do_ecdh", __func__);
                        goto fail;
                }
        }
 
-       r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
+       r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, &ms);
        if (r == FIDO_OK && (assert->ext.mask & FIDO_EXT_HMAC_SECRET))
                if (decrypt_hmac_secrets(dev, assert, ecdh) < 0) {
                        fido_log_debug("%s: decrypt_hmac_secrets", __func__);
@@ -372,7 +373,8 @@ fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
        unsigned char           *authdata_ptr = NULL;
        size_t                   authdata_len;
        struct cbor_load_result  cbor;
-       SHA256_CTX               ctx;
+       const EVP_MD            *md = NULL;
+       EVP_MD_CTX              *ctx = NULL;
        int                      ok = -1;
 
        if ((item = cbor_load(authdata_cbor->ptr, authdata_cbor->len,
@@ -386,10 +388,13 @@ fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
        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) {
+               if (dgst->len < SHA256_DIGEST_LENGTH ||
+                   (md = EVP_sha256()) == NULL ||
+                   (ctx = EVP_MD_CTX_new()) == NULL ||
+                   EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
+                   EVP_DigestUpdate(ctx, authdata_ptr, authdata_len) != 1 ||
+                   EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
+                   EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
                        fido_log_debug("%s: sha256", __func__);
                        goto fail;
                }
@@ -411,122 +416,7 @@ fail:
        if (item != NULL)
                cbor_decref(&item);
 
-       return (ok);
-}
-
-int
-fido_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) {
-               fido_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) {
-               fido_log_debug("%s: pk -> ec", __func__);
-               goto fail;
-       }
-
-       if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
-           (int)sig->len, ec) != 1) {
-               fido_log_debug("%s: ECDSA_verify", __func__);
-               goto fail;
-       }
-
-       ok = 0;
-fail:
-       if (pkey != NULL)
-               EVP_PKEY_free(pkey);
-
-       return (ok);
-}
-
-int
-fido_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) {
-               fido_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) {
-               fido_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) {
-               fido_log_debug("%s: RSA_verify", __func__);
-               goto fail;
-       }
-
-       ok = 0;
-fail:
-       if (pkey != NULL)
-               EVP_PKEY_free(pkey);
-
-       return (ok);
-}
-
-int
-fido_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) {
-               fido_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) {
-               fido_log_debug("%s: pk -> pkey", __func__);
-               goto fail;
-       }
-
-       if ((mdctx = EVP_MD_CTX_new()) == NULL) {
-               fido_log_debug("%s: EVP_MD_CTX_new", __func__);
-               goto fail;
-       }
-
-       if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
-               fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
-               goto fail;
-       }
-
-       if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
-           dgst->len) != 1) {
-               fido_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);
+       EVP_MD_CTX_free(ctx);
 
        return (ok);
 }
@@ -589,13 +479,13 @@ fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
 
        switch (cose_alg) {
        case COSE_ES256:
-               ok = fido_verify_sig_es256(&dgst, pk, &stmt->sig);
+               ok = es256_pk_verify_sig(&dgst, pk, &stmt->sig);
                break;
        case COSE_RS256:
-               ok = fido_verify_sig_rs256(&dgst, pk, &stmt->sig);
+               ok = rs256_pk_verify_sig(&dgst, pk, &stmt->sig);
                break;
        case COSE_EDDSA:
-               ok = fido_verify_sig_eddsa(&dgst, pk, &stmt->sig);
+               ok = eddsa_pk_verify_sig(&dgst, pk, &stmt->sig);
                break;
        default:
                fido_log_debug("%s: unsupported cose_alg %d", __func__,
index c3474cc..33e0a8d 100644 (file)
@@ -22,7 +22,7 @@ parse_authkey(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 }
 
 static int
-fido_dev_authkey_tx(fido_dev_t *dev)
+fido_dev_authkey_tx(fido_dev_t *dev, int *ms)
 {
        fido_blob_t      f;
        cbor_item_t     *argv[2];
@@ -43,7 +43,7 @@ fido_dev_authkey_tx(fido_dev_t *dev)
 
        /* frame and transmit */
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
-           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -58,13 +58,13 @@ fail:
 }
 
 static int
-fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms)
+fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
 
        fido_log_debug("%s: dev=%p, authkey=%p, ms=%d", __func__, (void *)dev,
-           (void *)authkey, ms);
+           (void *)authkey, *ms);
 
        memset(authkey, 0, sizeof(*authkey));
 
@@ -79,11 +79,11 @@ fido_dev_authkey_rx(fido_dev_t *dev, es256_pk_t *authkey, int ms)
 }
 
 static int
-fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int ms)
+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 ||
+       if ((r = fido_dev_authkey_tx(dev, ms)) != FIDO_OK ||
            (r = fido_dev_authkey_rx(dev, authkey, ms)) != FIDO_OK)
                return (r);
 
@@ -91,7 +91,7 @@ fido_dev_authkey_wait(fido_dev_t *dev, es256_pk_t *authkey, int ms)
 }
 
 int
-fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey)
+fido_dev_authkey(fido_dev_t *dev, es256_pk_t *authkey, int *ms)
 {
-       return (fido_dev_authkey_wait(dev, authkey, -1));
+       return (fido_dev_authkey_wait(dev, authkey, ms));
 }
index 06bc32e..8c52de5 100644 (file)
@@ -58,7 +58,7 @@ fail:
 
 static int
 bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc,
-    const char *pin, const fido_blob_t *token)
+    const char *pin, const fido_blob_t *token, int *ms)
 {
        cbor_item_t     *argv[5];
        es256_pk_t      *pk = NULL;
@@ -90,12 +90,12 @@ bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc,
 
        /* pinProtocol, pinAuth */
        if (pin) {
-               if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+               if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_do_ecdh", __func__);
                        goto fail;
                }
                if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
-                   NULL, &argv[4], &argv[3])) != FIDO_OK) {
+                   NULL, &argv[4], &argv[3], ms)) != FIDO_OK) {
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
                        goto fail;
                }
@@ -109,7 +109,7 @@ bio_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **sub_argv, size_t sub_argc,
 
        /* framing and transmission */
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -231,7 +231,7 @@ bio_parse_template_array(const cbor_item_t *key, const cbor_item_t *val,
 }
 
 static int
-bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
+bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -256,11 +256,11 @@ bio_rx_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta, int ms)
 
 static int
 bio_get_template_array_wait(fido_dev_t *dev, fido_bio_template_array_t *ta,
-    const char *pin, int ms)
+    const char *pin, int *ms)
 {
        int r;
 
-       if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL)) != FIDO_OK ||
+       if ((r = bio_tx(dev, CMD_ENUM, NULL, 0, pin, NULL, ms)) != FIDO_OK ||
            (r = bio_rx_template_array(dev, ta, ms)) != FIDO_OK)
                return (r);
 
@@ -271,15 +271,17 @@ int
 fido_bio_dev_get_template_array(fido_dev_t *dev, fido_bio_template_array_t *ta,
     const char *pin)
 {
+       int ms = dev->timeout_ms;
+
        if (pin == NULL)
                return (FIDO_ERR_INVALID_ARGUMENT);
 
-       return (bio_get_template_array_wait(dev, ta, pin, -1));
+       return (bio_get_template_array_wait(dev, ta, pin, &ms));
 }
 
 static int
 bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
-    const char *pin, int ms)
+    const char *pin, int *ms)
 {
        cbor_item_t     *argv[2];
        int              r = FIDO_ERR_INTERNAL;
@@ -292,7 +294,8 @@ bio_set_template_name_wait(fido_dev_t *dev, const fido_bio_template_t *t,
                goto fail;
        }
 
-       if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL)) != FIDO_OK ||
+       if ((r = bio_tx(dev, CMD_SET_NAME, argv, 2, pin, NULL,
+           ms)) != FIDO_OK ||
            (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
                fido_log_debug("%s: tx/rx", __func__);
                goto fail;
@@ -309,10 +312,12 @@ int
 fido_bio_dev_set_template_name(fido_dev_t *dev, const fido_bio_template_t *t,
     const char *pin)
 {
+       int ms = dev->timeout_ms;
+
        if (pin == NULL || t->name == NULL)
                return (FIDO_ERR_INVALID_ARGUMENT);
 
-       return (bio_set_template_name_wait(dev, t, pin, -1));
+       return (bio_set_template_name_wait(dev, t, pin, &ms));
 }
 
 static void
@@ -378,7 +383,7 @@ bio_parse_template_id(const cbor_item_t *key, const cbor_item_t *val,
 
 static int
 bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
-    fido_bio_enroll_t *e, int ms)
+    fido_bio_enroll_t *e, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -411,7 +416,7 @@ bio_rx_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
 
 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)
+    fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
 {
        cbor_item_t     *argv[3];
        const uint8_t    cmd = CMD_ENROLL_BEGIN;
@@ -419,12 +424,12 @@ bio_enroll_begin_wait(fido_dev_t *dev, fido_bio_template_t *t,
 
        memset(&argv, 0, sizeof(argv));
 
-       if ((argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
+       if ((argv[2] = cbor_build_uint(timo_ms)) == NULL) {
                fido_log_debug("%s: cbor encode", __func__);
                goto fail;
        }
 
-       if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
+       if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
            (r = bio_rx_enroll_begin(dev, t, e, ms)) != FIDO_OK) {
                fido_log_debug("%s: tx/rx", __func__);
                goto fail;
@@ -444,6 +449,7 @@ fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
        es256_pk_t      *pk = NULL;
        fido_blob_t     *ecdh = NULL;
        fido_blob_t     *token = NULL;
+       int              ms = dev->timeout_ms;
        int              r;
 
        if (pin == NULL || e->token != NULL)
@@ -454,13 +460,13 @@ fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
                goto fail;
        }
 
-       if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+       if ((r = fido_do_ecdh(dev, &pk, &ecdh, &ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_do_ecdh", __func__);
                goto fail;
        }
 
        if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
-           pk, NULL, token)) != FIDO_OK) {
+           pk, NULL, token, &ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_dev_get_uv_token", __func__);
                goto fail;
        }
@@ -475,11 +481,11 @@ fail:
        if (r != FIDO_OK)
                return (r);
 
-       return (bio_enroll_begin_wait(dev, t, e, timo_ms, -1));
+       return (bio_enroll_begin_wait(dev, t, e, timo_ms, &ms));
 }
 
 static int
-bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
+bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -505,7 +511,7 @@ bio_rx_enroll_continue(fido_dev_t *dev, fido_bio_enroll_t *e, int ms)
 
 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)
+    fido_bio_enroll_t *e, uint32_t timo_ms, int *ms)
 {
        cbor_item_t     *argv[3];
        const uint8_t    cmd = CMD_ENROLL_NEXT;
@@ -514,12 +520,12 @@ bio_enroll_continue_wait(fido_dev_t *dev, const fido_bio_template_t *t,
        memset(&argv, 0, sizeof(argv));
 
        if ((argv[0] = fido_blob_encode(&t->id)) == NULL ||
-           (argv[2] = cbor_build_uint32(timo_ms)) == NULL) {
+           (argv[2] = cbor_build_uint(timo_ms)) == NULL) {
                fido_log_debug("%s: cbor encode", __func__);
                goto fail;
        }
 
-       if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token)) != FIDO_OK ||
+       if ((r = bio_tx(dev, cmd, argv, 3, NULL, e->token, ms)) != FIDO_OK ||
            (r = bio_rx_enroll_continue(dev, e, ms)) != FIDO_OK) {
                fido_log_debug("%s: tx/rx", __func__);
                goto fail;
@@ -536,19 +542,21 @@ 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)
 {
+       int ms = dev->timeout_ms;
+
        if (e->token == NULL)
                return (FIDO_ERR_INVALID_ARGUMENT);
 
-       return (bio_enroll_continue_wait(dev, t, e, timo_ms, -1));
+       return (bio_enroll_continue_wait(dev, t, e, timo_ms, &ms));
 }
 
 static int
-bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
+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 ||
+       if ((r = bio_tx(dev, cmd, NULL, 0, NULL, NULL, ms)) != FIDO_OK ||
            (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
                fido_log_debug("%s: tx/rx", __func__);
                return (r);
@@ -560,12 +568,14 @@ bio_enroll_cancel_wait(fido_dev_t *dev, int ms)
 int
 fido_bio_dev_enroll_cancel(fido_dev_t *dev)
 {
-       return (bio_enroll_cancel_wait(dev, -1));
+       int ms = dev->timeout_ms;
+
+       return (bio_enroll_cancel_wait(dev, &ms));
 }
 
 static int
 bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
-    const char *pin, int ms)
+    const char *pin, int *ms)
 {
        cbor_item_t     *argv[1];
        const uint8_t    cmd = CMD_ENROLL_REMOVE;
@@ -578,7 +588,7 @@ bio_enroll_remove_wait(fido_dev_t *dev, const fido_bio_template_t *t,
                goto fail;
        }
 
-       if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL)) != FIDO_OK ||
+       if ((r = bio_tx(dev, cmd, argv, 1, pin, NULL, ms)) != FIDO_OK ||
            (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
                fido_log_debug("%s: tx/rx", __func__);
                goto fail;
@@ -595,7 +605,9 @@ 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));
+       int ms = dev->timeout_ms;
+
+       return (bio_enroll_remove_wait(dev, t, pin, &ms));
 }
 
 static void
@@ -640,7 +652,7 @@ bio_parse_info(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 }
 
 static int
-bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
+bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -664,11 +676,12 @@ bio_rx_info(fido_dev_t *dev, fido_bio_info_t *i, int ms)
 }
 
 static int
-bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
+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 ||
+       if ((r = bio_tx(dev, CMD_GET_INFO, NULL, 0, NULL, NULL,
+           ms)) != FIDO_OK ||
            (r = bio_rx_info(dev, i, ms)) != FIDO_OK) {
                fido_log_debug("%s: tx/rx", __func__);
                return (r);
@@ -680,7 +693,9 @@ bio_get_info_wait(fido_dev_t *dev, fido_bio_info_t *i, int ms)
 int
 fido_bio_dev_get_info(fido_dev_t *dev, fido_bio_info_t *i)
 {
-       return (bio_get_info_wait(dev, i, -1));
+       int ms = dev->timeout_ms;
+
+       return (bio_get_info_wait(dev, i, &ms));
 }
 
 const char *
index 5c1b115..8b7edec 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 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.
  */
@@ -560,6 +560,32 @@ fail:
        return (NULL);
 }
 
+cbor_item_t *
+cbor_encode_str_array(const fido_str_array_t *a)
+{
+       cbor_item_t     *array = NULL;
+       cbor_item_t     *entry = NULL;
+
+       if ((array = cbor_new_definite_array(a->len)) == NULL)
+               goto fail;
+
+       for (size_t i = 0; i < a->len; i++) {
+               if ((entry = cbor_build_string(a->ptr[i])) == NULL ||
+                   cbor_array_push(array, entry) == false)
+                       goto fail;
+               cbor_decref(&entry);
+       }
+
+       return (array);
+fail:
+       if (entry != NULL)
+               cbor_decref(&entry);
+       if (array != NULL)
+               cbor_decref(&array);
+
+       return (NULL);
+}
+
 static int
 cbor_encode_largeblob_key_ext(cbor_item_t *map)
 {
@@ -584,6 +610,8 @@ cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob)
                size++;
        if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
                size++;
+       if (ext->mask & FIDO_EXT_MINPINLEN)
+               size++;
 
        if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
                return (NULL);
@@ -615,6 +643,12 @@ cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob)
                        return (NULL);
                }
        }
+       if (ext->mask & FIDO_EXT_MINPINLEN) {
+               if (cbor_add_bool(item, "minPinLength", FIDO_OPT_TRUE) < 0) {
+                       cbor_decref(&item);
+                       return (NULL);
+               }
+       }
 
        return (item);
 }
@@ -662,7 +696,6 @@ cbor_encode_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
        uint8_t          prot;
        fido_blob_t      key;
 
-
        key.ptr = secret->ptr;
        key.len = secret->len;
 
@@ -706,11 +739,7 @@ cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
        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      key;
        uint8_t          prot;
        size_t           outlen;
@@ -726,19 +755,6 @@ cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
        if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
                key.len = 32;
 
-#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, new_pin_enc->ptr, new_pin_enc->len) == 0 ||
-           HMAC_Update(&ctx, pin_hash_enc->ptr, pin_hash_enc->len) == 0 ||
-           HMAC_Final(&ctx, dgst, &dgst_len) == 0 ||
-           dgst_len != SHA256_DIGEST_LENGTH) {
-               fido_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 ||
@@ -749,7 +765,6 @@ cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
                fido_log_debug("%s: HMAC", __func__);
                goto fail;
        }
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
 
        outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
 
@@ -759,10 +774,7 @@ cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
        }
 
 fail:
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-       if (ctx != NULL)
-               HMAC_CTX_free(ctx);
-#endif
+       HMAC_CTX_free(ctx);
 
        return (item);
 }
@@ -775,6 +787,7 @@ cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item,
        cbor_item_t             *argv[4];
        struct cbor_pair         pair;
        fido_blob_t             *enc = NULL;
+       uint8_t                  prot;
        int                      r;
 
        memset(argv, 0, sizeof(argv));
@@ -801,11 +814,17 @@ cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item,
                goto fail;
        }
 
+       if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
+               fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
+               r = FIDO_ERR_INTERNAL;
+               goto fail;
+       }
+
        /* XXX not pin, but salt */
        if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
            (argv[1] = fido_blob_encode(enc)) == NULL ||
            (argv[2] = cbor_encode_pin_auth(dev, ecdh, enc)) == NULL ||
-           (argv[3] = cbor_encode_pin_opt(dev)) == NULL) {
+           (prot != 1 && (argv[3] = cbor_build_uint8(prot)) == NULL)) {
                fido_log_debug("%s: cbor encode", __func__);
                r = FIDO_ERR_INTERNAL;
                goto fail;
@@ -896,7 +915,7 @@ cbor_decode_fmt(const cbor_item_t *item, char **fmt)
        }
 
        if (strcmp(type, "packed") && strcmp(type, "fido-u2f") &&
-           strcmp(type, "none")) {
+           strcmp(type, "none") && strcmp(type, "tpm")) {
                fido_log_debug("%s: type=%s", __func__, type);
                free(type);
                return (-1);
@@ -1141,6 +1160,14 @@ decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                }
                if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
                        authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
+       } else if (strcmp(type, "minPinLength") == 0) {
+               if (cbor_isa_uint(val) == false ||
+                   cbor_int_get_width(val) != CBOR_INT_8) {
+                       fido_log_debug("%s: cbor type", __func__);
+                       goto out;
+               }
+               authdata_ext->mask |= FIDO_EXT_MINPINLEN;
+               authdata_ext->minpinlen = cbor_get_uint8(val);
        }
 
        ok = 0;
@@ -1365,7 +1392,6 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 {
        fido_attstmt_t  *attstmt = arg;
        char            *name = NULL;
-       int              cose_alg = 0;
        int              ok = -1;
 
        if (cbor_string_copy(key, &name) < 0) {
@@ -1380,10 +1406,11 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                        fido_log_debug("%s: alg", __func__);
                        goto out;
                }
-               if ((cose_alg = -(int)cbor_get_int(val) - 1) != COSE_ES256 &&
-                   cose_alg != COSE_RS256 && cose_alg != COSE_EDDSA) {
-                       fido_log_debug("%s: unsupported cose_alg=%d", __func__,
-                           cose_alg);
+               attstmt->alg = -(int)cbor_get_int(val) - 1;
+               if (attstmt->alg != COSE_ES256 && attstmt->alg != COSE_RS256 &&
+                   attstmt->alg != COSE_EDDSA && attstmt->alg != COSE_RS1) {
+                       fido_log_debug("%s: unsupported attstmt->alg=%d",
+                           __func__, attstmt->alg);
                        goto out;
                }
        } else if (!strcmp(name, "sig")) {
@@ -1398,6 +1425,16 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                        fido_log_debug("%s: x5c", __func__);
                        goto out;
                }
+       } else if (!strcmp(name, "certInfo")) {
+               if (fido_blob_decode(val, &attstmt->certinfo) < 0) {
+                       fido_log_debug("%s: certinfo", __func__);
+                       goto out;
+               }
+       } else if (!strcmp(name, "pubArea")) {
+               if (fido_blob_decode(val, &attstmt->pubarea) < 0) {
+                       fido_log_debug("%s: pubarea", __func__);
+                       goto out;
+               }
        }
 
        ok = 0;
@@ -1410,6 +1447,8 @@ out:
 int
 cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
 {
+       size_t alloc_len;
+
        if (cbor_isa_map(item) == false ||
            cbor_map_is_definite(item) == false ||
            cbor_map_iter(item, attstmt, decode_attstmt_entry) < 0) {
@@ -1417,6 +1456,13 @@ cbor_decode_attstmt(const cbor_item_t *item, fido_attstmt_t *attstmt)
                return (-1);
        }
 
+       if (attstmt->cbor.ptr != NULL ||
+           (attstmt->cbor.len = cbor_serialize_alloc(item,
+           &attstmt->cbor.ptr, &alloc_len)) == 0) {
+               fido_log_debug("%s: cbor_serialize_alloc", __func__);
+               return (-1);
+       }
+
        return (0);
 }
 
index ee5501b..074bca8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Copyright (c) 2020-2022 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.
  */
 
 #define BOUND (1024UL * 1024UL)
 
+/* zlib inflate (raw + headers) */
 static int
-do_compress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz, int decomp)
+rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
 {
        u_long ilen, olen;
-       int r;
+       int z;
 
        memset(out, 0, sizeof(*out));
+
        if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
-           origsiz > ULONG_MAX || (olen = decomp ? (u_long)origsiz :
-           compressBound(ilen)) > BOUND)
+           origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) {
+               fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
+                   in->len, origsiz);
                return FIDO_ERR_INVALID_ARGUMENT;
+       }
+
        if ((out->ptr = calloc(1, olen)) == NULL)
                return FIDO_ERR_INTERNAL;
        out->len = olen;
-       if (decomp)
-               r = uncompress(out->ptr, &olen, in->ptr, ilen);
-       else
-               r = compress(out->ptr, &olen, in->ptr, ilen);
-       if (r != Z_OK || olen > SIZE_MAX || olen > out->len) {
+
+       if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK ||
+           olen > SIZE_MAX || olen != out->len) {
+               fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu",
+                   __func__, z, olen, out->len);
                fido_blob_reset(out);
                return FIDO_ERR_COMPRESS;
        }
-       out->len = olen;
 
        return FIDO_OK;
 }
 
+/* raw inflate */
+static int
+rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
+{
+       z_stream zs;
+       u_int ilen, olen;
+       int r, z;
+
+       memset(&zs, 0, sizeof(zs));
+       memset(out, 0, sizeof(*out));
+
+       if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND ||
+           origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) {
+               fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
+                   in->len, origsiz);
+               return FIDO_ERR_INVALID_ARGUMENT;
+       }
+       if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) {
+               fido_log_debug("%s: inflateInit2: %d", __func__, z);
+               return FIDO_ERR_COMPRESS;
+       }
+
+       if ((out->ptr = calloc(1, olen)) == NULL) {
+               r = FIDO_ERR_INTERNAL;
+               goto fail;
+       }
+       out->len = olen;
+       zs.next_in = in->ptr;
+       zs.avail_in = ilen;
+       zs.next_out = out->ptr;
+       zs.avail_out = olen;
+
+       if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
+               fido_log_debug("%s: inflate: %d", __func__, z);
+               r = FIDO_ERR_COMPRESS;
+               goto fail;
+       }
+       if (zs.avail_out != 0) {
+               fido_log_debug("%s: %u != 0", __func__, zs.avail_out);
+               r = FIDO_ERR_COMPRESS;
+               goto fail;
+       }
+
+       r = FIDO_OK;
+fail:
+       if ((z = inflateEnd(&zs)) != Z_OK) {
+               fido_log_debug("%s: inflateEnd: %d", __func__, z);
+               r = FIDO_ERR_COMPRESS;
+       }
+       if (r != FIDO_OK)
+               fido_blob_reset(out);
+
+       return r;
+}
+
+/* raw deflate */
+static int
+rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in)
+{
+       z_stream zs;
+       u_int ilen, olen;
+       int r, z;
+
+       memset(&zs, 0, sizeof(zs));
+       memset(out, 0, sizeof(*out));
+
+       if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) {
+               fido_log_debug("%s: in->len=%zu", __func__, in->len);
+               return FIDO_ERR_INVALID_ARGUMENT;
+       }
+       if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+           -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
+               fido_log_debug("%s: deflateInit2: %d", __func__, z);
+               return FIDO_ERR_COMPRESS;
+       }
+
+       olen = BOUND;
+       if ((out->ptr = calloc(1, olen)) == NULL) {
+               r = FIDO_ERR_INTERNAL;
+               goto fail;
+       }
+       out->len = olen;
+       zs.next_in = in->ptr;
+       zs.avail_in = ilen;
+       zs.next_out = out->ptr;
+       zs.avail_out = olen;
+
+       if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) {
+               fido_log_debug("%s: inflate: %d", __func__, z);
+               r = FIDO_ERR_COMPRESS;
+               goto fail;
+       }
+       if (zs.avail_out >= out->len) {
+               fido_log_debug("%s: %u > %zu", __func__, zs.avail_out,
+                   out->len);
+               r = FIDO_ERR_COMPRESS;
+               goto fail;
+       }
+       out->len -= zs.avail_out;
+
+       r = FIDO_OK;
+fail:
+       if ((z = deflateEnd(&zs)) != Z_OK) {
+               fido_log_debug("%s: deflateEnd: %d", __func__, z);
+               r = FIDO_ERR_COMPRESS;
+       }
+       if (r != FIDO_OK)
+               fido_blob_reset(out);
+
+       return r;
+}
+
 int
 fido_compress(fido_blob_t *out, const fido_blob_t *in)
 {
-       return do_compress(out, in, 0, 0);
+       return rfc1951_deflate(out, in);
 }
 
 int
 fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
 {
-       return do_compress(out, in, origsiz, 1);
+       if (rfc1950_inflate(out, in, origsiz) == FIDO_OK)
+               return FIDO_OK; /* backwards compat with libfido2 < 1.11 */
+       return rfc1951_inflate(out, in, origsiz);
 }
index 0dda161..2baaab0 100644 (file)
@@ -39,7 +39,7 @@ config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac)
 
 static int
 config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc,
-    const char *pin)
+    const char *pin, int *ms)
 {
        cbor_item_t *argv[4];
        es256_pk_t *pk = NULL;
@@ -68,12 +68,12 @@ config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc,
                        fido_log_debug("%s: config_prepare_hmac", __func__);
                        goto fail;
                }
-               if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+               if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_do_ecdh", __func__);
                        goto fail;
                }
                if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
-                   NULL, &argv[3], &argv[2])) != FIDO_OK) {
+                   NULL, &argv[3], &argv[2], ms)) != FIDO_OK) {
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
                        goto fail;
                }
@@ -81,7 +81,7 @@ config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc,
 
        /* framing and transmission */
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -99,11 +99,12 @@ fail:
 }
 
 static int
-config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int ms)
+config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int *ms)
 {
        int r;
 
-       if ((r = config_tx(dev, CMD_ENABLE_ENTATTEST, NULL, 0, pin)) != FIDO_OK)
+       if ((r = config_tx(dev, CMD_ENABLE_ENTATTEST, NULL, 0, pin,
+           ms)) != FIDO_OK)
                return r;
 
        return fido_rx_cbor_status(dev, ms);
@@ -112,15 +113,18 @@ config_enable_entattest_wait(fido_dev_t *dev, const char *pin, int ms)
 int
 fido_dev_enable_entattest(fido_dev_t *dev, const char *pin)
 {
-       return (config_enable_entattest_wait(dev, pin, -1));
+       int ms = dev->timeout_ms;
+
+       return (config_enable_entattest_wait(dev, pin, &ms));
 }
 
 static int
-config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int ms)
+config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int *ms)
 {
        int r;
 
-       if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin)) != FIDO_OK)
+       if ((r = config_tx(dev, CMD_TOGGLE_ALWAYS_UV, NULL, 0, pin,
+           ms)) != FIDO_OK)
                return r;
 
        return (fido_rx_cbor_status(dev, ms));
@@ -129,18 +133,21 @@ config_toggle_always_uv_wait(fido_dev_t *dev, const char *pin, int ms)
 int
 fido_dev_toggle_always_uv(fido_dev_t *dev, const char *pin)
 {
-       return config_toggle_always_uv_wait(dev, pin, -1);
+       int ms = dev->timeout_ms;
+
+       return config_toggle_always_uv_wait(dev, pin, &ms);
 }
 
 static int
-config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, const char *pin)
+config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force,
+    const fido_str_array_t *rpid, const char *pin, int *ms)
 {
        cbor_item_t *argv[3];
        int r;
 
        memset(argv, 0, sizeof(argv));
 
-       if ((!len && !force) || len > UINT8_MAX) {
+       if ((rpid == NULL && len == 0 && !force) || len > UINT8_MAX) {
                r = FIDO_ERR_INVALID_ARGUMENT;
                goto fail;
        }
@@ -149,13 +156,18 @@ config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, const char *pin)
                r = FIDO_ERR_INTERNAL;
                goto fail;
        }
+       if (rpid != NULL && (argv[1] = cbor_encode_str_array(rpid)) == NULL) {
+               fido_log_debug("%s: cbor_encode_str_array", __func__);
+               r = FIDO_ERR_INTERNAL;
+               goto fail;
+       }
        if (force && (argv[2] = cbor_build_bool(true)) == NULL) {
                fido_log_debug("%s: cbor_build_bool", __func__);
                r = FIDO_ERR_INTERNAL;
                goto fail;
        }
        if ((r = config_tx(dev, CMD_SET_PIN_MINLEN, argv, nitems(argv),
-           pin)) != FIDO_OK) {
+           pin, ms)) != FIDO_OK) {
                fido_log_debug("%s: config_tx", __func__);
                goto fail;
        }
@@ -167,12 +179,13 @@ fail:
 }
 
 static int
-config_pin_minlen(fido_dev_t *dev, size_t len, bool force, const char *pin,
-    int ms)
+config_pin_minlen(fido_dev_t *dev, size_t len, bool force,
+    const fido_str_array_t *rpid, const char *pin, int *ms)
 {
        int r;
 
-       if ((r = config_pin_minlen_tx(dev, len, force, pin)) != FIDO_OK)
+       if ((r = config_pin_minlen_tx(dev, len, force, rpid, pin,
+           ms)) != FIDO_OK)
                return r;
 
        return fido_rx_cbor_status(dev, ms);
@@ -181,11 +194,36 @@ config_pin_minlen(fido_dev_t *dev, size_t len, bool force, const char *pin,
 int
 fido_dev_set_pin_minlen(fido_dev_t *dev, size_t len, const char *pin)
 {
-       return config_pin_minlen(dev, len, false, pin, -1);
+       int ms = dev->timeout_ms;
+
+       return config_pin_minlen(dev, len, false, NULL, pin, &ms);
 }
 
 int
 fido_dev_force_pin_change(fido_dev_t *dev, const char *pin)
 {
-       return config_pin_minlen(dev, 0, true, pin, -1);
+       int ms = dev->timeout_ms;
+
+       return config_pin_minlen(dev, 0, true, NULL, pin, &ms);
+}
+
+int
+fido_dev_set_pin_minlen_rpid(fido_dev_t *dev, const char * const *rpid,
+    size_t n, const char *pin)
+{
+       fido_str_array_t sa;
+       int ms = dev->timeout_ms;
+       int r;
+
+       memset(&sa, 0, sizeof(sa));
+       if (fido_str_array_pack(&sa, rpid, n) < 0) {
+               fido_log_debug("%s: fido_str_array_pack", __func__);
+               r = FIDO_ERR_INTERNAL;
+               goto fail;
+       }
+       r = config_pin_minlen(dev, 0, false, &sa, pin, &ms);
+fail:
+       fido_str_array_free(&sa);
+
+       return r;
 }
index 5e65b08..6da502c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 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.h"
 #include "fido/es256.h"
 
+#ifndef FIDO_MAXMSG_CRED
+#define FIDO_MAXMSG_CRED       4096
+#endif
+
 static int
 parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 {
@@ -43,7 +47,8 @@ parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 }
 
 static int
-fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
+fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
+    int *ms)
 {
        fido_blob_t      f;
        fido_blob_t     *ecdh = NULL;
@@ -92,12 +97,12 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
        /* user verification */
        if (pin != NULL || (uv == FIDO_OPT_TRUE &&
            fido_dev_supports_permissions(dev))) {
-               if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+               if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_do_ecdh", __func__);
                        goto fail;
                }
                if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh,
-                   pin, cred->rp.id, &argv[7], &argv[8])) != FIDO_OK) {
+                   pin, cred->rp.id, &argv[7], &argv[8], ms)) != FIDO_OK) {
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
                        goto fail;
                }
@@ -114,7 +119,7 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
 
        /* framing and transmission */
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -131,42 +136,55 @@ fail:
 }
 
 static int
-fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
+fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int *ms)
 {
-       unsigned char   reply[FIDO_MAXMSG];
-       int             reply_len;
-       int             r;
+       unsigned char   *reply;
+       int              reply_len;
+       int              r;
 
        fido_cred_reset_rx(cred);
 
-       if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
+       if ((reply = malloc(FIDO_MAXMSG_CRED)) == NULL) {
+               r = FIDO_ERR_INTERNAL;
+               goto fail;
+       }
+
+       if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, reply, FIDO_MAXMSG_CRED,
            ms)) < 0) {
                fido_log_debug("%s: fido_rx", __func__);
-               return (FIDO_ERR_RX);
+               r = FIDO_ERR_RX;
+               goto fail;
        }
 
        if ((r = cbor_parse_reply(reply, (size_t)reply_len, cred,
            parse_makecred_reply)) != FIDO_OK) {
                fido_log_debug("%s: parse_makecred_reply", __func__);
-               return (r);
+               goto fail;
        }
 
        if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
            fido_blob_is_empty(&cred->attcred.id)) {
-               fido_cred_reset_rx(cred);
-               return (FIDO_ERR_INVALID_CBOR);
+               r = FIDO_ERR_INVALID_CBOR;
+               goto fail;
        }
 
-       return (FIDO_OK);
+       r = FIDO_OK;
+fail:
+       free(reply);
+
+       if (r != FIDO_OK)
+               fido_cred_reset_rx(cred);
+
+       return (r);
 }
 
 static int
 fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
-    int ms)
+    int *ms)
 {
        int  r;
 
-       if ((r = fido_dev_make_cred_tx(dev, cred, pin)) != FIDO_OK ||
+       if ((r = fido_dev_make_cred_tx(dev, cred, pin, ms)) != FIDO_OK ||
            (r = fido_dev_make_cred_rx(dev, cred, ms)) != FIDO_OK)
                return (r);
 
@@ -176,18 +194,20 @@ fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
 int
 fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
 {
+       int ms = dev->timeout_ms;
+
 #ifdef USE_WINHELLO
        if (dev->flags & FIDO_DEV_WINHELLO)
-               return (fido_winhello_make_cred(dev, cred, pin));
+               return (fido_winhello_make_cred(dev, cred, pin, ms));
 #endif
        if (fido_dev_is_fido2(dev) == false) {
                if (pin != NULL || cred->rk == FIDO_OPT_TRUE ||
                    cred->ext.mask != 0)
                        return (FIDO_ERR_UNSUPPORTED_OPTION);
-               return (u2f_register(dev, cred, -1));
+               return (u2f_register(dev, cred, &ms));
        }
 
-       return (fido_dev_make_cred_wait(dev, cred, pin, -1));
+       return (fido_dev_make_cred_wait(dev, cred, pin, &ms));
 }
 
 static int
@@ -225,66 +245,81 @@ 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) {
+       const uint8_t    zero = 0;
+       const uint8_t    four = 4; /* uncompressed point */
+       const EVP_MD    *md = NULL;
+       EVP_MD_CTX      *ctx = NULL;
+       int              ok = -1;
+
+       if (dgst->len != SHA256_DIGEST_LENGTH ||
+           (md = EVP_sha256()) == NULL ||
+           (ctx = EVP_MD_CTX_new()) == NULL ||
+           EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
+           EVP_DigestUpdate(ctx, &zero, sizeof(zero)) != 1 ||
+           EVP_DigestUpdate(ctx, rp_id, rp_id_len) != 1 ||
+           EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
+           EVP_DigestUpdate(ctx, id->ptr, id->len) != 1 ||
+           EVP_DigestUpdate(ctx, &four, sizeof(four)) != 1 ||
+           EVP_DigestUpdate(ctx, pk->x, sizeof(pk->x)) != 1 ||
+           EVP_DigestUpdate(ctx, pk->y, sizeof(pk->y)) != 1 ||
+           EVP_DigestFinal_ex(ctx, dgst->ptr, NULL) != 1) {
                fido_log_debug("%s: sha256", __func__);
-               return (-1);
+               goto fail;
        }
 
-       return (0);
+       ok = 0;
+fail:
+       EVP_MD_CTX_free(ctx);
+
+       return (ok);
 }
 
 static int
-verify_sig(const fido_blob_t *dgst, const fido_blob_t *x5c,
-    const fido_blob_t *sig)
+verify_attstmt(const fido_blob_t *dgst, const fido_attstmt_t *attstmt)
 {
        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) {
-               fido_log_debug("%s: dgst->len=%zu, x5c->len=%zu, sig->len=%zu",
-                   __func__, dgst->len, x5c->len, sig->len);
+       if (attstmt->x5c.len > INT_MAX) {
+               fido_log_debug("%s: x5c.len=%zu", __func__, attstmt->x5c.len);
                return (-1);
        }
 
        /* fetch key from x509 */
-       if ((rawcert = BIO_new_mem_buf(x5c->ptr, (int)x5c->len)) == NULL ||
+       if ((rawcert = BIO_new_mem_buf(attstmt->x5c.ptr,
+           (int)attstmt->x5c.len)) == NULL ||
            (cert = d2i_X509_bio(rawcert, NULL)) == NULL ||
-           (pkey = X509_get_pubkey(cert)) == NULL ||
-           (ec = EVP_PKEY_get0_EC_KEY(pkey)) == NULL) {
+           (pkey = X509_get_pubkey(cert)) == NULL) {
                fido_log_debug("%s: x509 key", __func__);
                goto fail;
        }
 
-       if (ECDSA_verify(0, dgst->ptr, (int)dgst->len, sig->ptr,
-           (int)sig->len, ec) != 1) {
-               fido_log_debug("%s: ECDSA_verify", __func__);
-               goto fail;
+       switch (attstmt->alg) {
+       case COSE_UNSPEC:
+       case COSE_ES256:
+               ok = es256_verify_sig(dgst, pkey, &attstmt->sig);
+               break;
+       case COSE_RS256:
+               ok = rs256_verify_sig(dgst, pkey, &attstmt->sig);
+               break;
+       case COSE_RS1:
+               ok = rs1_verify_sig(dgst, pkey, &attstmt->sig);
+               break;
+       case COSE_EDDSA:
+               ok = eddsa_verify_sig(dgst, pkey, &attstmt->sig);
+               break;
+       default:
+               fido_log_debug("%s: unknown alg %d", __func__, attstmt->alg);
+               break;
        }
 
-       ok = 0;
 fail:
-       if (rawcert != NULL)
-               BIO_free(rawcert);
-       if (cert != NULL)
-               X509_free(cert);
-       if (pkey != NULL)
-               EVP_PKEY_free(pkey);
+       BIO_free(rawcert);
+       X509_free(cert);
+       EVP_PKEY_free(pkey);
 
        return (ok);
 }
@@ -348,14 +383,21 @@ fido_cred_verify(const fido_cred_t *cred)
                        r = FIDO_ERR_INTERNAL;
                        goto out;
                }
+       } else if (!strcmp(cred->fmt, "tpm")) {
+               if (fido_get_signed_hash_tpm(&dgst, &cred->cdh,
+                   &cred->authdata_raw, &cred->attstmt, &cred->attcred) < 0) {
+                       fido_log_debug("%s: fido_get_signed_hash_tpm", __func__);
+                       r = FIDO_ERR_INTERNAL;
+                       goto out;
+               }
        } else {
                fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
                r = FIDO_ERR_INVALID_ARGUMENT;
                goto out;
        }
 
-       if (verify_sig(&dgst, &cred->attstmt.x5c, &cred->attstmt.sig) < 0) {
-               fido_log_debug("%s: verify_sig", __func__);
+       if (verify_attstmt(&dgst, &cred->attstmt) < 0) {
+               fido_log_debug("%s: verify_attstmt", __func__);
                r = FIDO_ERR_INVALID_SIG;
                goto out;
        }
@@ -435,15 +477,15 @@ fido_cred_verify_self(const fido_cred_t *cred)
 
        switch (cred->attcred.type) {
        case COSE_ES256:
-               ok = fido_verify_sig_es256(&dgst, &cred->attcred.pubkey.es256,
+               ok = es256_pk_verify_sig(&dgst, &cred->attcred.pubkey.es256,
                    &cred->attstmt.sig);
                break;
        case COSE_RS256:
-               ok = fido_verify_sig_rs256(&dgst, &cred->attcred.pubkey.rs256,
+               ok = rs256_pk_verify_sig(&dgst, &cred->attcred.pubkey.rs256,
                    &cred->attstmt.sig);
                break;
        case COSE_EDDSA:
-               ok = fido_verify_sig_eddsa(&dgst, &cred->attcred.pubkey.eddsa,
+               ok = eddsa_pk_verify_sig(&dgst, &cred->attcred.pubkey.eddsa,
                    &cred->attstmt.sig);
                break;
        default:
@@ -482,6 +524,18 @@ fido_cred_clean_authdata(fido_cred_t *cred)
        memset(&cred->attcred, 0, sizeof(cred->attcred));
 }
 
+static void
+fido_cred_clean_attstmt(fido_attstmt_t *attstmt)
+{
+       fido_blob_reset(&attstmt->certinfo);
+       fido_blob_reset(&attstmt->pubarea);
+       fido_blob_reset(&attstmt->cbor);
+       fido_blob_reset(&attstmt->x5c);
+       fido_blob_reset(&attstmt->sig);
+
+       memset(attstmt, 0, sizeof(*attstmt));
+}
+
 void
 fido_cred_reset_tx(fido_cred_t *cred)
 {
@@ -513,8 +567,7 @@ fido_cred_reset_rx(fido_cred_t *cred)
        free(cred->fmt);
        cred->fmt = NULL;
        fido_cred_clean_authdata(cred);
-       fido_blob_reset(&cred->attstmt.x5c);
-       fido_blob_reset(&cred->attstmt.sig);
+       fido_cred_clean_attstmt(&cred->attstmt);
        fido_blob_reset(&cred->largeblob_key);
 }
 
@@ -568,7 +621,6 @@ fail:
                fido_cred_clean_authdata(cred);
 
        return (r);
-
 }
 
 int
@@ -610,7 +662,6 @@ fail:
                fido_cred_clean_authdata(cred);
 
        return (r);
-
 }
 
 int
@@ -640,6 +691,39 @@ fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
        return (FIDO_OK);
 }
 
+int
+fido_cred_set_attstmt(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+       cbor_item_t             *item = NULL;
+       struct cbor_load_result  cbor;
+       int                      r = FIDO_ERR_INVALID_ARGUMENT;
+
+       fido_cred_clean_attstmt(&cred->attstmt);
+
+       if (ptr == NULL || len == 0)
+               goto fail;
+
+       if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
+               fido_log_debug("%s: cbor_load", __func__);
+               goto fail;
+       }
+
+       if (cbor_decode_attstmt(item, &cred->attstmt) < 0) {
+               fido_log_debug("%s: cbor_decode_attstmt", __func__);
+               goto fail;
+       }
+
+       r = FIDO_OK;
+fail:
+       if (item != NULL)
+               cbor_decref(&item);
+
+       if (r != FIDO_OK)
+               fido_cred_clean_attstmt(&cred->attstmt);
+
+       return (r);
+}
+
 int
 fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
 {
@@ -833,6 +917,19 @@ fido_cred_set_prot(fido_cred_t *cred, int prot)
        return (FIDO_OK);
 }
 
+int
+fido_cred_set_pin_minlen(fido_cred_t *cred, size_t len)
+{
+       if (len == 0)
+               cred->ext.mask &= ~FIDO_EXT_MINPINLEN;
+       else
+               cred->ext.mask |= FIDO_EXT_MINPINLEN;
+
+       cred->ext.minpinlen = len;
+
+       return (FIDO_OK);
+}
+
 int
 fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len)
 {
@@ -856,7 +953,7 @@ fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
                return (FIDO_ERR_INVALID_ARGUMENT);
 
        if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") &&
-           strcmp(fmt, "none"))
+           strcmp(fmt, "none") && strcmp(fmt, "tpm"))
                return (FIDO_ERR_INVALID_ARGUMENT);
 
        if ((cred->fmt = strdup(fmt)) == NULL)
@@ -955,6 +1052,18 @@ fido_cred_authdata_raw_len(const fido_cred_t *cred)
        return (cred->authdata_raw.len);
 }
 
+const unsigned char *
+fido_cred_attstmt_ptr(const fido_cred_t *cred)
+{
+       return (cred->attstmt.cbor.ptr);
+}
+
+size_t
+fido_cred_attstmt_len(const fido_cred_t *cred)
+{
+       return (cred->attstmt.cbor.len);
+}
+
 const unsigned char *
 fido_cred_pubkey_ptr(const fido_cred_t *cred)
 {
@@ -1031,6 +1140,12 @@ fido_cred_prot(const fido_cred_t *cred)
        return (cred->ext.prot);
 }
 
+size_t
+fido_cred_pin_minlen(const fido_cred_t *cred)
+{
+       return (cred->ext.minpinlen);
+}
+
 const char *
 fido_cred_fmt(const fido_cred_t *cred)
 {
index e48ca45..8d2649a 100644 (file)
@@ -112,7 +112,7 @@ fail:
 
 static int
 credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
-    const char *rp_id, fido_opt_t uv)
+    const char *rp_id, fido_opt_t uv, int *ms)
 {
        fido_blob_t      f;
        fido_blob_t     *ecdh = NULL;
@@ -144,12 +144,12 @@ credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
                        fido_log_debug("%s: credman_prepare_hmac", __func__);
                        goto fail;
                }
-               if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+               if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_do_ecdh", __func__);
                        goto fail;
                }
                if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
-                   rp_id, &argv[3], &argv[2])) != FIDO_OK) {
+                   rp_id, &argv[3], &argv[2], ms)) != FIDO_OK) {
                        fido_log_debug("%s: cbor_add_uv_params", __func__);
                        goto fail;
                }
@@ -157,7 +157,7 @@ credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
 
        /* framing and transmission */
        if (cbor_build_frame(cmd, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -198,7 +198,7 @@ credman_parse_metadata(const cbor_item_t *key, const cbor_item_t *val,
 }
 
 static int
-credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms)
+credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -223,12 +223,12 @@ credman_rx_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata, int ms)
 
 static int
 credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
-    const char *pin, int ms)
+    const char *pin, int *ms)
 {
        int r;
 
        if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL,
-           FIDO_OPT_TRUE)) != FIDO_OK ||
+           FIDO_OPT_TRUE, ms)) != FIDO_OK ||
            (r = credman_rx_metadata(dev, metadata, ms)) != FIDO_OK)
                return (r);
 
@@ -239,7 +239,9 @@ int
 fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
     const char *pin)
 {
-       return (credman_get_metadata_wait(dev, metadata, pin, -1));
+       int ms = dev->timeout_ms;
+
+       return (credman_get_metadata_wait(dev, metadata, pin, &ms));
 }
 
 static int
@@ -321,7 +323,7 @@ credman_parse_rk_count(const cbor_item_t *key, const cbor_item_t *val,
 }
 
 static int
-credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
+credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -360,7 +362,7 @@ credman_rx_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
 }
 
 static int
-credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
+credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -390,7 +392,7 @@ credman_rx_next_rk(fido_dev_t *dev, fido_credman_rk_t *rk, int ms)
 
 static int
 credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
-    const char *pin, int ms)
+    const char *pin, int *ms)
 {
        fido_blob_t     rp_dgst;
        uint8_t         dgst[SHA256_DIGEST_LENGTH];
@@ -405,13 +407,13 @@ credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
        rp_dgst.len = sizeof(dgst);
 
        if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id,
-           FIDO_OPT_TRUE)) != FIDO_OK ||
+           FIDO_OPT_TRUE, ms)) != 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, NULL,
-                   FIDO_OPT_FALSE)) != FIDO_OK ||
+                   FIDO_OPT_FALSE, ms)) != FIDO_OK ||
                    (r = credman_rx_next_rk(dev, rk, ms)) != FIDO_OK)
                        return (r);
                rk->n_rx++;
@@ -424,12 +426,14 @@ int
 fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
     fido_credman_rk_t *rk, const char *pin)
 {
-       return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
+       int ms = dev->timeout_ms;
+
+       return (credman_get_rk_wait(dev, rp_id, rk, pin, &ms));
 }
 
 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)
+    size_t cred_id_len, const char *pin, int *ms)
 {
        fido_blob_t cred;
        int r;
@@ -440,7 +444,7 @@ credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
                return (FIDO_ERR_INVALID_ARGUMENT);
 
        if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL,
-           FIDO_OPT_TRUE)) != FIDO_OK ||
+           FIDO_OPT_TRUE, ms)) != FIDO_OK ||
            (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
                goto fail;
 
@@ -455,7 +459,9 @@ int
 fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
     size_t cred_id_len, const char *pin)
 {
-       return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
+       int ms = dev->timeout_ms;
+
+       return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, &ms));
 }
 
 static int
@@ -526,7 +532,7 @@ credman_parse_rp_count(const cbor_item_t *key, const cbor_item_t *val,
 }
 
 static int
-credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
+credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -565,7 +571,7 @@ credman_rx_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
 }
 
 static int
-credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
+credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -595,18 +601,18 @@ credman_rx_next_rp(fido_dev_t *dev, fido_credman_rp_t *rp, int ms)
 
 static int
 credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
-    int ms)
+    int *ms)
 {
        int r;
 
        if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL,
-           FIDO_OPT_TRUE)) != FIDO_OK ||
+           FIDO_OPT_TRUE, ms)) != 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, NULL,
-                   FIDO_OPT_FALSE)) != FIDO_OK ||
+                   FIDO_OPT_FALSE, ms)) != FIDO_OK ||
                    (r = credman_rx_next_rp(dev, rp, ms)) != FIDO_OK)
                        return (r);
                rp->n_rx++;
@@ -618,17 +624,19 @@ credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
 int
 fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
 {
-       return (credman_get_rp_wait(dev, rp, pin, -1));
+       int ms = dev->timeout_ms;
+
+       return (credman_get_rp_wait(dev, rp, pin, &ms));
 }
 
 static int
 credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
-    int ms)
+    int *ms)
 {
        int r;
 
        if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL,
-           FIDO_OPT_TRUE)) != FIDO_OK ||
+           FIDO_OPT_TRUE, ms)) != FIDO_OK ||
            (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
                return (r);
 
@@ -638,7 +646,9 @@ credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
 int
 fido_credman_set_dev_rk(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
 {
-       return (credman_set_dev_rk_wait(dev, cred, pin, -1));
+       int ms = dev->timeout_ms;
+
+       return (credman_set_dev_rk_wait(dev, cred, pin, &ms));
 }
 
 fido_credman_rk_t *
index a003854..635e417 100644 (file)
@@ -1,37 +1,17 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2022 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 "fido.h"
 
 #ifndef TLS
 #define TLS
 #endif
 
-typedef struct dev_manifest_func_node {
-       dev_manifest_func_t manifest_func;
-       struct dev_manifest_func_node *next;
-} dev_manifest_func_node_t;
-
-static TLS dev_manifest_func_node_t *manifest_funcs = NULL;
 static TLS bool disable_u2f_fallback;
 
-static void
-find_manifest_func_node(dev_manifest_func_t f, dev_manifest_func_node_t **curr,
-    dev_manifest_func_node_t **prev)
-{
-       *prev = NULL;
-       *curr = manifest_funcs;
-
-       while (*curr != NULL && (*curr)->manifest_func != f) {
-               *prev = *curr;
-               *curr = (*curr)->next;
-       }
-}
-
 #ifdef FIDO_FUZZ
 static void
 set_random_report_len(fido_dev_t *dev)
@@ -63,13 +43,15 @@ fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
 
        for (size_t i = 0; i < len; i++)
                if (strcmp(ptr[i], "clientPin") == 0) {
-                       dev->flags |= val[i] ? FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET;
+                       dev->flags |= val[i] ?
+                           FIDO_DEV_PIN_SET : FIDO_DEV_PIN_UNSET;
                } else if (strcmp(ptr[i], "credMgmt") == 0 ||
                           strcmp(ptr[i], "credentialMgmtPreview") == 0) {
                        if (val[i])
                                dev->flags |= FIDO_DEV_CREDMAN;
                } else if (strcmp(ptr[i], "uv") == 0) {
-                       dev->flags |= val[i] ? FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET;
+                       dev->flags |= val[i] ?
+                           FIDO_DEV_UV_SET : FIDO_DEV_UV_UNSET;
                } else if (strcmp(ptr[i], "pinUvAuthToken") == 0) {
                        if (val[i])
                                dev->flags |= FIDO_DEV_TOKEN_PERMS;
@@ -106,7 +88,7 @@ fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
 }
 
 static int
-fido_dev_open_tx(fido_dev_t *dev, const char *path)
+fido_dev_open_tx(fido_dev_t *dev, const char *path, int *ms)
 {
        int r;
 
@@ -161,7 +143,8 @@ fido_dev_open_tx(fido_dev_t *dev, const char *path)
                goto fail;
        }
 
-       if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce)) < 0) {
+       if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce),
+           ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -176,7 +159,7 @@ fail:
 }
 
 static int
-fido_dev_open_rx(fido_dev_t *dev, int ms)
+fido_dev_open_rx(fido_dev_t *dev, int *ms)
 {
        fido_cbor_info_t        *info = NULL;
        int                      reply_len;
@@ -241,7 +224,7 @@ fail:
 }
 
 static int
-fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
+fido_dev_open_wait(fido_dev_t *dev, const char *path, int *ms)
 {
        int r;
 
@@ -249,121 +232,80 @@ fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
        if (strcmp(path, FIDO_WINHELLO_PATH) == 0)
                return (fido_winhello_open(dev));
 #endif
-       if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK ||
+       if ((r = fido_dev_open_tx(dev, path, ms)) != FIDO_OK ||
            (r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
                return (r);
 
        return (FIDO_OK);
 }
 
-int
-fido_dev_register_manifest_func(const dev_manifest_func_t f)
-{
-       dev_manifest_func_node_t *prev, *curr, *n;
-
-       find_manifest_func_node(f, &curr, &prev);
-       if (curr != NULL)
-               return (FIDO_OK);
-
-       if ((n = calloc(1, sizeof(*n))) == NULL) {
-               fido_log_debug("%s: calloc", __func__);
-               return (FIDO_ERR_INTERNAL);
-       }
-
-       n->manifest_func = f;
-       n->next = manifest_funcs;
-       manifest_funcs = n;
-
-       return (FIDO_OK);
-}
-
-void
-fido_dev_unregister_manifest_func(const dev_manifest_func_t f)
+static void
+run_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen,
+    const char *type, int (*manifest)(fido_dev_info_t *, size_t, size_t *))
 {
-       dev_manifest_func_node_t *prev, *curr;
+       size_t ndevs = 0;
+       int r;
 
-       find_manifest_func_node(f, &curr, &prev);
-       if (curr == NULL)
+       if (*olen >= ilen) {
+               fido_log_debug("%s: skipping %s", __func__, type);
                return;
-       if (prev != NULL)
-               prev->next = curr->next;
-       else
-               manifest_funcs = curr->next;
-
-       free(curr);
+       }
+       if ((r = manifest(devlist + *olen, ilen - *olen, &ndevs)) != FIDO_OK)
+               fido_log_debug("%s: %s: 0x%x", __func__, type, r);
+       fido_log_debug("%s: found %zu %s device%s", __func__, ndevs, type,
+           ndevs == 1 ? "" : "s");
+       *olen += ndevs;
 }
 
 int
 fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
 {
-       dev_manifest_func_node_t        *curr = NULL;
-       dev_manifest_func_t              m_func;
-       size_t                           curr_olen;
-       int                              r;
-
        *olen = 0;
 
-       if (fido_dev_register_manifest_func(fido_hid_manifest) != FIDO_OK)
-               return (FIDO_ERR_INTERNAL);
-#ifdef NFC_LINUX
-       if (fido_dev_register_manifest_func(fido_nfc_manifest) != FIDO_OK)
-               return (FIDO_ERR_INTERNAL);
+       run_manifest(devlist, ilen, olen, "hid", fido_hid_manifest);
+#ifdef USE_NFC
+       run_manifest(devlist, ilen, olen, "nfc", fido_nfc_manifest);
+#endif
+#ifdef USE_PCSC
+       run_manifest(devlist, ilen, olen, "pcsc", fido_pcsc_manifest);
 #endif
 #ifdef USE_WINHELLO
-       if (fido_dev_register_manifest_func(fido_winhello_manifest) != FIDO_OK)
-               return (FIDO_ERR_INTERNAL);
+       run_manifest(devlist, ilen, olen, "winhello", fido_winhello_manifest);
 #endif
 
-       for (curr = manifest_funcs; curr != NULL; curr = curr->next) {
-               curr_olen = 0;
-               m_func = curr->manifest_func;
-               r = m_func(devlist + *olen, ilen - *olen, &curr_olen);
-               if (r != FIDO_OK)
-                       return (r);
-               *olen += curr_olen;
-               if (*olen == ilen)
-                       break;
-       }
-
        return (FIDO_OK);
 }
 
 int
 fido_dev_open_with_info(fido_dev_t *dev)
 {
+       int ms = dev->timeout_ms;
+
        if (dev->path == NULL)
                return (FIDO_ERR_INVALID_ARGUMENT);
 
-       return (fido_dev_open_wait(dev, dev->path, -1));
+       return (fido_dev_open_wait(dev, dev->path, &ms));
 }
 
 int
 fido_dev_open(fido_dev_t *dev, const char *path)
 {
-#ifdef NFC_LINUX
-       /*
-        * this is a hack to get existing applications up and running with nfc;
-        * it will *NOT* be part of a libfido2 release. to support nfc in your
-        * application, please change it to use fido_dev_open_with_info().
-        */
-       if (strncmp(path, "/sys", strlen("/sys")) == 0 && strlen(path) > 4 &&
-           path[strlen(path) - 4] == 'n' && path[strlen(path) - 3] == 'f' &&
-           path[strlen(path) - 2] == 'c') {
-               dev->io_own = true;
-               dev->io = (fido_dev_io_t) {
-                       fido_nfc_open,
-                       fido_nfc_close,
-                       fido_nfc_read,
-                       fido_nfc_write,
-               };
-               dev->transport = (fido_dev_transport_t) {
-                       fido_nfc_rx,
-                       fido_nfc_tx,
-               };
+       int ms = dev->timeout_ms;
+
+#ifdef USE_NFC
+       if (fido_is_nfc(path) && fido_dev_set_nfc(dev) < 0) {
+               fido_log_debug("%s: fido_dev_set_nfc", __func__);
+               return FIDO_ERR_INTERNAL;
+       }
+#endif
+#ifdef USE_PCSC
+       if (fido_is_pcsc(path) && fido_dev_set_pcsc(dev) < 0) {
+               fido_log_debug("%s: fido_dev_set_pcsc", __func__);
+               return FIDO_ERR_INTERNAL;
        }
 #endif
 
-       return (fido_dev_open_wait(dev, path, -1));
+       return (fido_dev_open_wait(dev, path, &ms));
 }
 
 int
@@ -386,130 +328,36 @@ fido_dev_close(fido_dev_t *dev)
 int
 fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask)
 {
-       if (dev->io_own || dev->io_handle == NULL || sigmask == NULL)
+       if (dev->io_handle == NULL || sigmask == NULL)
                return (FIDO_ERR_INVALID_ARGUMENT);
 
-#ifdef NFC_LINUX
-       if (dev->transport.rx == fido_nfc_rx)
+#ifdef USE_NFC
+       if (dev->transport.rx == fido_nfc_rx && dev->io.read == fido_nfc_read)
                return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
 #endif
-       return (fido_hid_set_sigmask(dev->io_handle, sigmask));
+       if (dev->transport.rx == NULL && dev->io.read == fido_hid_read)
+               return (fido_hid_set_sigmask(dev->io_handle, sigmask));
+
+       return (FIDO_ERR_INVALID_ARGUMENT);
 }
 
 int
 fido_dev_cancel(fido_dev_t *dev)
 {
+       int ms = dev->timeout_ms;
+
 #ifdef USE_WINHELLO
        if (dev->flags & FIDO_DEV_WINHELLO)
                return (fido_winhello_cancel(dev));
 #endif
        if (fido_dev_is_fido2(dev) == false)
                return (FIDO_ERR_INVALID_ARGUMENT);
-       if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0) < 0)
+       if (fido_tx(dev, CTAP_CMD_CANCEL, NULL, 0, &ms) < 0)
                return (FIDO_ERR_TX);
 
        return (FIDO_OK);
 }
 
-int
-fido_dev_get_touch_begin(fido_dev_t *dev)
-{
-       fido_blob_t      f;
-       cbor_item_t     *argv[9];
-       const char      *clientdata = FIDO_DUMMY_CLIENTDATA;
-       const uint8_t    user_id = FIDO_DUMMY_USER_ID;
-       unsigned char    cdh[SHA256_DIGEST_LENGTH];
-       fido_rp_t        rp;
-       fido_user_t      user;
-       int              r = FIDO_ERR_INTERNAL;
-
-       memset(&f, 0, sizeof(f));
-       memset(argv, 0, sizeof(argv));
-       memset(cdh, 0, sizeof(cdh));
-       memset(&rp, 0, sizeof(rp));
-       memset(&user, 0, sizeof(user));
-
-       if (fido_dev_is_fido2(dev) == false)
-               return (u2f_get_touch_begin(dev));
-
-       if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) {
-               fido_log_debug("%s: sha256", __func__);
-               return (FIDO_ERR_INTERNAL);
-       }
-
-       if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL ||
-           (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) {
-               fido_log_debug("%s: strdup", __func__);
-               goto fail;
-       }
-
-       if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) {
-               fido_log_debug("%s: fido_blob_set", __func__);
-               goto fail;
-       }
-
-       if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL ||
-           (argv[1] = cbor_encode_rp_entity(&rp)) == NULL ||
-           (argv[2] = cbor_encode_user_entity(&user)) == NULL ||
-           (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) {
-               fido_log_debug("%s: cbor encode", __func__);
-               goto fail;
-       }
-
-       if (fido_dev_supports_pin(dev)) {
-               if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
-                   (argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
-                       fido_log_debug("%s: cbor encode", __func__);
-                       goto fail;
-               }
-       }
-
-       if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
-               fido_log_debug("%s: fido_tx", __func__);
-               r = FIDO_ERR_TX;
-               goto fail;
-       }
-
-       r = FIDO_OK;
-fail:
-       cbor_vector_free(argv, nitems(argv));
-       free(f.ptr);
-       free(rp.id);
-       free(user.name);
-       free(user.id.ptr);
-
-       return (r);
-}
-
-int
-fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms)
-{
-       int r;
-
-       *touched = 0;
-
-       if (fido_dev_is_fido2(dev) == false)
-               return (u2f_get_touch_status(dev, touched, ms));
-
-       switch ((r = fido_rx_cbor_status(dev, ms))) {
-       case FIDO_ERR_PIN_AUTH_INVALID:
-       case FIDO_ERR_PIN_INVALID:
-       case FIDO_ERR_PIN_NOT_SET:
-       case FIDO_ERR_SUCCESS:
-               *touched = 1;
-               break;
-       case FIDO_ERR_RX:
-               /* ignore */
-               break;
-       default:
-               fido_log_debug("%s: fido_rx_cbor_status", __func__);
-               return (r);
-       }
-
-       return (FIDO_OK);
-}
-
 int
 fido_dev_set_io_functions(fido_dev_t *dev, const fido_dev_io_t *io)
 {
@@ -544,6 +392,13 @@ fido_dev_set_transport_functions(fido_dev_t *dev, const fido_dev_transport_t *t)
        return (FIDO_OK);
 }
 
+void *
+fido_dev_io_handle(const fido_dev_t *dev)
+{
+
+       return (dev->io_handle);
+}
+
 void
 fido_init(int flags)
 {
@@ -562,6 +417,7 @@ fido_dev_new(void)
                return (NULL);
 
        dev->cid = CTAP_CID_BROADCAST;
+       dev->timeout_ms = -1;
        dev->io = (fido_dev_io_t) {
                &fido_hid_open,
                &fido_hid_close,
@@ -593,6 +449,7 @@ fido_dev_new_with_info(const fido_dev_info_t *di)
        dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
        dev->transport = di->transport;
        dev->cid = CTAP_CID_BROADCAST;
+       dev->timeout_ms = -1;
 
        if ((dev->path = strdup(di->path)) == NULL) {
                fido_log_debug("%s: strdup", __func__);
@@ -730,3 +587,14 @@ fido_dev_maxmsgsize(const fido_dev_t *dev)
 {
        return (dev->maxmsgsize);
 }
+
+int
+fido_dev_set_timeout(fido_dev_t *dev, int ms)
+{
+       if (ms < -1)
+               return (FIDO_ERR_INVALID_ARGUMENT);
+
+       dev->timeout_ms = ms;
+
+       return (FIDO_OK);
+}
index 3ea47ae..9c4f2b9 100644 (file)
@@ -8,14 +8,14 @@
 #include <openssl/sha.h>
 #if defined(LIBRESSL_VERSION_NUMBER)
 #include <openssl/hkdf.h>
-#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+#else
 #include <openssl/kdf.h>
 #endif
 
 #include "fido.h"
 #include "fido/es256.h"
 
-#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L
+#if defined(LIBRESSL_VERSION_NUMBER)
 static int
 hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret)
 {
@@ -56,7 +56,7 @@ hkdf_sha256(uint8_t *key, char *info, fido_blob_t *secret)
            EVP_PKEY_CTX_set_hkdf_md(ctx, md) < 1 ||
            EVP_PKEY_CTX_set1_hkdf_salt(ctx, salt, sizeof(salt)) < 1 ||
            EVP_PKEY_CTX_set1_hkdf_key(ctx, secret->ptr, (int)secret->len) < 1 ||
-           EVP_PKEY_CTX_add1_hkdf_info(ctx, info, (int)strlen(info)) < 1) {
+           EVP_PKEY_CTX_add1_hkdf_info(ctx, (void *)info, (int)strlen(info)) < 1) {
                fido_log_debug("%s: EVP_PKEY_CTX", __func__);
                goto fail;
        }
@@ -74,7 +74,7 @@ fail:
 
        return ok;
 }
-#endif /* defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L */
+#endif /* defined(LIBRESSL_VERSION_NUMBER) */
 
 static int
 kdf(uint8_t prot, fido_blob_t *key, /* const */ fido_blob_t *secret)
@@ -164,7 +164,7 @@ fail:
 }
 
 int
-fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
+fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh, int *ms)
 {
        es256_sk_t *sk = NULL; /* our private key */
        es256_pk_t *ak = NULL; /* authenticator's public key */
@@ -182,7 +182,7 @@ fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
                goto fail;
        }
        if ((ak = es256_pk_new()) == NULL ||
-           fido_dev_authkey(dev, ak) != FIDO_OK) {
+           fido_dev_authkey(dev, ak, ms) != FIDO_OK) {
                fido_log_debug("%s: fido_dev_authkey", __func__);
                r = FIDO_ERR_INTERNAL;
                goto fail;
index 89b84c5..a94ae30 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Copyright (c) 2019-2021 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.
  */
@@ -10,7 +10,7 @@
 #include "fido.h"
 #include "fido/eddsa.h"
 
-#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
+#if defined(LIBRESSL_VERSION_NUMBER)
 EVP_PKEY *
 EVP_PKEY_new_raw_public_key(int type, ENGINE *e, const unsigned char *key,
     size_t keylen)
@@ -37,7 +37,9 @@ EVP_PKEY_get_raw_public_key(const EVP_PKEY *pkey, unsigned char *pub,
 
        return (0);
 }
+#endif /* LIBRESSL_VERSION_NUMBER */
 
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3040000f
 int
 EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
     const unsigned char *tbs, size_t tbslen)
@@ -52,23 +54,7 @@ EVP_DigestVerify(EVP_MD_CTX *ctx, const unsigned char *sigret, size_t siglen,
 
        return (0);
 }
-#endif /* LIBRESSL_VERSION_NUMBER || OPENSSL_VERSION_NUMBER < 0x10101000L */
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-EVP_MD_CTX *
-EVP_MD_CTX_new(void)
-{
-       fido_log_debug("%s: unimplemented", __func__);
-
-       return (NULL);
-}
-
-void
-EVP_MD_CTX_free(EVP_MD_CTX *ctx)
-{
-       (void)ctx;
-}
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+#endif /* LIBRESSL_VERSION_NUMBER < 0x3040000f */
 
 static int
 decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
@@ -136,11 +122,20 @@ eddsa_pk_free(eddsa_pk_t **pkp)
 int
 eddsa_pk_from_ptr(eddsa_pk_t *pk, const void *ptr, size_t len)
 {
+       EVP_PKEY *pkey;
+
        if (len < sizeof(*pk))
                return (FIDO_ERR_INVALID_ARGUMENT);
 
        memcpy(pk, ptr, sizeof(*pk));
 
+       if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL) {
+               fido_log_debug("%s: eddsa_pk_to_EVP_PKEY", __func__);
+               return (FIDO_ERR_INVALID_ARGUMENT);
+       }
+
+       EVP_PKEY_free(pkey);
+
        return (FIDO_OK);
 }
 
@@ -161,6 +156,8 @@ eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
 {
        size_t len = 0;
 
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519)
+               return (FIDO_ERR_INVALID_ARGUMENT);
        if (EVP_PKEY_get_raw_public_key(pkey, NULL, &len) != 1 ||
            len != sizeof(pk->x))
                return (FIDO_ERR_INTERNAL);
@@ -170,3 +167,65 @@ eddsa_pk_from_EVP_PKEY(eddsa_pk_t *pk, const EVP_PKEY *pkey)
 
        return (FIDO_OK);
 }
+
+int
+eddsa_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
+    const fido_blob_t *sig)
+{
+       EVP_MD_CTX      *mdctx = NULL;
+       int              ok = -1;
+
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_ED25519) {
+               fido_log_debug("%s: EVP_PKEY_base_id", __func__);
+               goto fail;
+       }
+
+       /* EVP_DigestVerify needs ints */
+       if (dgst->len > INT_MAX || sig->len > INT_MAX) {
+               fido_log_debug("%s: dgst->len=%zu, sig->len=%zu", __func__,
+                   dgst->len, sig->len);
+               return (-1);
+       }
+
+       if ((mdctx = EVP_MD_CTX_new()) == NULL) {
+               fido_log_debug("%s: EVP_MD_CTX_new", __func__);
+               goto fail;
+       }
+
+       if (EVP_DigestVerifyInit(mdctx, NULL, NULL, NULL, pkey) != 1) {
+               fido_log_debug("%s: EVP_DigestVerifyInit", __func__);
+               goto fail;
+       }
+
+       if (EVP_DigestVerify(mdctx, sig->ptr, sig->len, dgst->ptr,
+           dgst->len) != 1) {
+               fido_log_debug("%s: EVP_DigestVerify", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_MD_CTX_free(mdctx);
+
+       return (ok);
+}
+
+int
+eddsa_pk_verify_sig(const fido_blob_t *dgst, const eddsa_pk_t *pk,
+    const fido_blob_t *sig)
+{
+       EVP_PKEY        *pkey;
+       int              ok = -1;
+
+       if ((pkey = eddsa_pk_to_EVP_PKEY(pk)) == NULL ||
+           eddsa_verify_sig(dgst, pkey, sig) < 0) {
+               fido_log_debug("%s: eddsa_verify_sig", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_PKEY_free(pkey);
+
+       return (ok);
+}
index 9cdb48e..4d6e86f 100644 (file)
@@ -1,15 +1,24 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2022 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/ecdsa.h>
 #include <openssl/obj_mac.h>
 
 #include "fido.h"
 #include "fido/es256.h"
 
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#define get0_EC_KEY(x) EVP_PKEY_get0_EC_KEY((x))
+#else
+#define get0_EC_KEY(x) EVP_PKEY_get0((x))
+#endif
+
+static const int es256_nid = NID_X9_62_prime256v1;
+
 static int
 decode_coord(const cbor_item_t *item, void *xy, size_t xy_len)
 {
@@ -169,7 +178,8 @@ es256_pk_free(es256_pk_t **pkp)
 int
 es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len)
 {
-       const uint8_t *p = ptr;
+       const uint8_t   *p = ptr;
+       EVP_PKEY        *pkey;
 
        if (len < sizeof(*pk))
                return (FIDO_ERR_INVALID_ARGUMENT);
@@ -179,6 +189,14 @@ es256_pk_from_ptr(es256_pk_t *pk, const void *ptr, size_t len)
        else
                memcpy(pk, ptr, sizeof(*pk)); /* libfido2 x||y format */
 
+       if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL) {
+               fido_log_debug("%s: es256_pk_to_EVP_PKEY", __func__);
+               explicit_bzero(pk, sizeof(*pk));
+               return (FIDO_ERR_INVALID_ARGUMENT);
+       }
+
+       EVP_PKEY_free(pkey);
+
        return (FIDO_OK);
 }
 
@@ -207,13 +225,12 @@ es256_sk_create(es256_sk_t *key)
        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_CTX_set_ec_paramgen_curve_nid(pctx, es256_nid) <= 0 ||
            EVP_PKEY_paramgen(pctx, &p) <= 0) {
                fido_log_debug("%s: EVP_PKEY_paramgen", __func__);
                goto fail;
@@ -257,7 +274,6 @@ es256_pk_to_EVP_PKEY(const es256_pk_t *k)
        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)
@@ -275,7 +291,7 @@ es256_pk_to_EVP_PKEY(const es256_pk_t *k)
                goto fail;
        }
 
-       if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
+       if ((ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL ||
            (g = EC_KEY_get0_group(ec)) == NULL) {
                fido_log_debug("%s: EC_KEY init", __func__);
                goto fail;
@@ -323,12 +339,15 @@ es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec)
        BIGNUM          *x = NULL;
        BIGNUM          *y = NULL;
        const EC_POINT  *q = NULL;
-       const EC_GROUP  *g = NULL;
+       EC_GROUP        *g = NULL;
+       size_t           dx;
+       size_t           dy;
        int              ok = FIDO_ERR_INTERNAL;
-       int              n;
+       int              nx;
+       int              ny;
 
        if ((q = EC_KEY_get0_public_key(ec)) == NULL ||
-           (g = EC_KEY_get0_group(ec)) == NULL ||
+           (g = EC_GROUP_new_by_curve_name(es256_nid)) == NULL ||
            (bnctx = BN_CTX_new()) == NULL)
                goto fail;
 
@@ -338,22 +357,33 @@ es256_pk_from_EC_KEY(es256_pk_t *pk, const EC_KEY *ec)
            (y = BN_CTX_get(bnctx)) == NULL)
                goto fail;
 
+       if (EC_POINT_is_on_curve(g, q, bnctx) != 1) {
+               fido_log_debug("%s: EC_POINT_is_on_curve", __func__);
+               ok = FIDO_ERR_INVALID_ARGUMENT;
+               goto fail;
+       }
+
        if (EC_POINT_get_affine_coordinates_GFp(g, q, x, y, bnctx) == 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)) {
+           (nx = BN_num_bytes(x)) < 0 || (size_t)nx > sizeof(pk->x) ||
+           (ny = BN_num_bytes(y)) < 0 || (size_t)ny > sizeof(pk->y)) {
                fido_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)) {
+       dx = sizeof(pk->x) - (size_t)nx;
+       dy = sizeof(pk->y) - (size_t)ny;
+
+       if ((nx = BN_bn2bin(x, pk->x + dx)) < 0 || (size_t)nx > sizeof(pk->x) ||
+           (ny = BN_bn2bin(y, pk->y + dy)) < 0 || (size_t)ny > sizeof(pk->y)) {
                fido_log_debug("%s: BN_bn2bin", __func__);
                goto fail;
        }
 
        ok = FIDO_OK;
 fail:
+       EC_GROUP_free(g);
+
        if (bnctx != NULL) {
                BN_CTX_end(bnctx);
                BN_CTX_free(bnctx);
@@ -362,6 +392,18 @@ fail:
        return (ok);
 }
 
+int
+es256_pk_from_EVP_PKEY(es256_pk_t *pk, const EVP_PKEY *pkey)
+{
+       const EC_KEY *ec;
+
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC ||
+           (ec = get0_EC_KEY(pkey)) == NULL)
+               return (FIDO_ERR_INVALID_ARGUMENT);
+
+       return (es256_pk_from_EC_KEY(pk, ec));
+}
+
 EVP_PKEY *
 es256_sk_to_EVP_PKEY(const es256_sk_t *k)
 {
@@ -369,7 +411,6 @@ es256_sk_to_EVP_PKEY(const es256_sk_t *k)
        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)
@@ -383,7 +424,7 @@ es256_sk_to_EVP_PKEY(const es256_sk_t *k)
                goto fail;
        }
 
-       if ((ec = EC_KEY_new_by_curve_name(nid)) == NULL ||
+       if ((ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL ||
            EC_KEY_set_private_key(ec, d) == 0) {
                fido_log_debug("%s: EC_KEY_set_private_key", __func__);
                goto fail;
@@ -422,11 +463,10 @@ es256_derive_pk(const es256_sk_t *sk, es256_pk_t *pk)
        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 ||
+           (ec = EC_KEY_new_by_curve_name(es256_nid)) == NULL ||
            (g = EC_KEY_get0_group(ec)) == NULL ||
            (q = EC_POINT_new(g)) == NULL) {
                fido_log_debug("%s: get", __func__);
@@ -451,3 +491,50 @@ fail:
 
        return (ok);
 }
+
+int
+es256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
+    const fido_blob_t *sig)
+{
+       EVP_PKEY_CTX    *pctx = NULL;
+       int              ok = -1;
+
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
+               fido_log_debug("%s: EVP_PKEY_base_id", __func__);
+               goto fail;
+       }
+
+       if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
+           EVP_PKEY_verify_init(pctx) != 1 ||
+           EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
+           dgst->len) != 1) {
+               fido_log_debug("%s: EVP_PKEY_verify", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_PKEY_CTX_free(pctx);
+
+       return (ok);
+}
+
+int
+es256_pk_verify_sig(const fido_blob_t *dgst, const es256_pk_t *pk,
+    const fido_blob_t *sig)
+{
+       EVP_PKEY        *pkey;
+       int              ok = -1;
+
+       if ((pkey = es256_pk_to_EVP_PKEY(pk)) == NULL ||
+           es256_verify_sig(dgst, pkey, sig) < 0) {
+               fido_log_debug("%s: es256_verify_sig", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_PKEY_free(pkey);
+
+       return (ok);
+}
index 8d3810f..df9a667 100644 (file)
@@ -5,6 +5,7 @@ _eddsa_pk_new
 _eddsa_pk_to_EVP_PKEY
 _es256_pk_free
 _es256_pk_from_EC_KEY
+_es256_pk_from_EVP_PKEY
 _es256_pk_from_ptr
 _es256_pk_new
 _es256_pk_to_EVP_PKEY
@@ -82,10 +83,11 @@ _fido_cbor_info_algorithm_type
 _fido_cbor_info_extensions_len
 _fido_cbor_info_extensions_ptr
 _fido_cbor_info_free
-_fido_cbor_info_maxmsgsiz
 _fido_cbor_info_maxcredbloblen
 _fido_cbor_info_maxcredcntlst
 _fido_cbor_info_maxcredidlen
+_fido_cbor_info_maxlargeblob
+_fido_cbor_info_maxmsgsiz
 _fido_cbor_info_fwversion
 _fido_cbor_info_new
 _fido_cbor_info_options_len
@@ -97,6 +99,8 @@ _fido_cbor_info_transports_len
 _fido_cbor_info_transports_ptr
 _fido_cbor_info_versions_len
 _fido_cbor_info_versions_ptr
+_fido_cred_attstmt_len
+_fido_cred_attstmt_ptr
 _fido_cred_authdata_len
 _fido_cred_authdata_ptr
 _fido_cred_authdata_raw_len
@@ -136,11 +140,13 @@ _fido_credman_rp_name
 _fido_credman_rp_new
 _fido_credman_set_dev_rk
 _fido_cred_new
+_fido_cred_pin_minlen
 _fido_cred_prot
 _fido_cred_pubkey_len
 _fido_cred_pubkey_ptr
 _fido_cred_rp_id
 _fido_cred_rp_name
+_fido_cred_set_attstmt
 _fido_cred_set_authdata
 _fido_cred_set_authdata_raw
 _fido_cred_set_blob
@@ -150,6 +156,7 @@ _fido_cred_set_extensions
 _fido_cred_set_fmt
 _fido_cred_set_id
 _fido_cred_set_options
+_fido_cred_set_pin_minlen
 _fido_cred_set_prot
 _fido_cred_set_rk
 _fido_cred_set_rp
@@ -193,20 +200,26 @@ _fido_dev_info_path
 _fido_dev_info_product
 _fido_dev_info_product_string
 _fido_dev_info_ptr
+_fido_dev_info_set
 _fido_dev_info_vendor
+_fido_dev_io_handle
 _fido_dev_is_fido2
 _fido_dev_is_winhello
 _fido_dev_major
 _fido_dev_make_cred
 _fido_dev_minor
 _fido_dev_new
+_fido_dev_new_with_info
 _fido_dev_open
+_fido_dev_open_with_info
 _fido_dev_protocol
 _fido_dev_reset
 _fido_dev_set_io_functions
 _fido_dev_set_pin
 _fido_dev_set_pin_minlen
+_fido_dev_set_pin_minlen_rpid
 _fido_dev_set_sigmask
+_fido_dev_set_timeout
 _fido_dev_set_transport_functions
 _fido_dev_supports_cred_prot
 _fido_dev_supports_credman
@@ -224,6 +237,7 @@ _fido_set_log_handler
 _fido_strerr
 _rs256_pk_free
 _rs256_pk_from_ptr
+_rs256_pk_from_EVP_PKEY
 _rs256_pk_from_RSA
 _rs256_pk_new
 _rs256_pk_to_EVP_PKEY
index 3be3323..84536d5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2022 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.
  */
@@ -51,6 +51,7 @@ cbor_item_t *cbor_encode_pubkey(const fido_blob_t *);
 cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *);
 cbor_item_t *cbor_encode_pubkey_param(int);
 cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *);
+cbor_item_t *cbor_encode_str_array(const fido_str_array_t *);
 cbor_item_t *cbor_encode_user_entity(const fido_user_t *);
 cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
 
@@ -86,7 +87,7 @@ int cbor_parse_reply(const unsigned char *, size_t, void *,
     int(*)(const cbor_item_t *, const cbor_item_t *, void *));
 int cbor_add_uv_params(fido_dev_t *, uint8_t, const fido_blob_t *,
     const es256_pk_t *, const fido_blob_t *, const char *, const char *,
-    cbor_item_t **, cbor_item_t **);
+    cbor_item_t **, cbor_item_t **, int *);
 void cbor_vector_free(cbor_item_t **, size_t);
 int cbor_array_append(cbor_item_t **, cbor_item_t *);
 int cbor_array_drop(cbor_item_t **, size_t);
@@ -117,6 +118,7 @@ size_t fido_hid_report_in_len(void *);
 size_t fido_hid_report_out_len(void *);
 
 /* nfc i/o */
+bool fido_is_nfc(const char *);
 void *fido_nfc_open(const char *);
 void  fido_nfc_close(void *);
 int fido_nfc_read(void *, unsigned char *, size_t, int);
@@ -124,20 +126,31 @@ int fido_nfc_write(void *, const unsigned char *, size_t);
 int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
 int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
 int fido_nfc_set_sigmask(void *, const fido_sigset_t *);
+int fido_dev_set_nfc(fido_dev_t *);
+
+/* pcsc i/o */
+bool fido_is_pcsc(const char *);
+void *fido_pcsc_open(const char *);
+void  fido_pcsc_close(void *);
+int fido_pcsc_read(void *, unsigned char *, size_t, int);
+int fido_pcsc_write(void *, const unsigned char *, size_t);
+int fido_pcsc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
+int fido_pcsc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
+int fido_dev_set_pcsc(fido_dev_t *);
 
 /* windows hello */
 int fido_winhello_manifest(fido_dev_info_t *, size_t, size_t *);
 int fido_winhello_open(fido_dev_t *);
 int fido_winhello_close(fido_dev_t *);
 int fido_winhello_cancel(fido_dev_t *);
-int fido_winhello_get_assert(fido_dev_t *, fido_assert_t *, const char *);
+int fido_winhello_get_assert(fido_dev_t *, fido_assert_t *, const char *, int);
 int fido_winhello_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
-int fido_winhello_make_cred(fido_dev_t *, fido_cred_t *, const char *);
+int fido_winhello_make_cred(fido_dev_t *, fido_cred_t *, const char *, int);
 
 /* generic i/o */
-int fido_rx_cbor_status(fido_dev_t *, int);
-int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int);
-int fido_tx(fido_dev_t *, uint8_t, const void *, size_t);
+int fido_rx_cbor_status(fido_dev_t *, int *);
+int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int *);
+int fido_tx(fido_dev_t *, uint8_t, const void *, size_t, int *);
 
 /* log */
 #ifdef FIDO_NO_DIAGNOSTIC
@@ -163,20 +176,28 @@ void fido_log_error(int, const char *, ...);
 #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);
-int u2f_get_touch_begin(fido_dev_t *);
-int u2f_get_touch_status(fido_dev_t *, int *, int);
+int u2f_register(fido_dev_t *, fido_cred_t *, int *);
+int u2f_authenticate(fido_dev_t *, fido_assert_t *, int *);
+int u2f_get_touch_begin(fido_dev_t *, int *);
+int u2f_get_touch_status(fido_dev_t *, int *, int *);
 
 /* unexposed fido ops */
 uint8_t fido_dev_get_pin_protocol(const fido_dev_t *);
-int fido_dev_authkey(fido_dev_t *, es256_pk_t *);
-int fido_dev_get_cbor_info_wait(fido_dev_t *, fido_cbor_info_t *, int);
+int fido_dev_authkey(fido_dev_t *, es256_pk_t *, int *);
+int fido_dev_get_cbor_info_wait(fido_dev_t *, fido_cbor_info_t *, int *);
 int fido_dev_get_uv_token(fido_dev_t *, uint8_t, const char *,
-    const fido_blob_t *, const es256_pk_t *, const char *, fido_blob_t *);
+    const fido_blob_t *, const es256_pk_t *, const char *, fido_blob_t *,
+    int *);
 uint64_t fido_dev_maxmsgsize(const fido_dev_t *);
-int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **);
-bool fido_dev_supports_permissions(const fido_dev_t *);
+int fido_do_ecdh(fido_dev_t *, es256_pk_t **, fido_blob_t **, int *);
+
+/* types */
+void fido_algo_array_free(fido_algo_array_t *);
+void fido_byte_array_free(fido_byte_array_t *);
+void fido_opt_array_free(fido_opt_array_t *);
+void fido_str_array_free(fido_str_array_t *);
+void fido_algo_free(fido_algo_t *);
+int fido_str_array_pack(fido_str_array_t *, const char * const *, size_t);
 
 /* misc */
 void fido_assert_reset_rx(fido_assert_t *);
@@ -189,25 +210,30 @@ int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t);
 int fido_check_rp_id(const char *, const unsigned char *);
 int fido_get_random(void *, size_t);
 int fido_sha256(fido_blob_t *, const u_char *, size_t);
+int fido_time_now(struct timespec *);
+int fido_time_delta(const struct timespec *, int *);
+int fido_to_uint64(const char *, int, uint64_t *);
 
 /* crypto */
-int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *,
+int es256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
+int rs256_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
+int eddsa_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
+int rs1_verify_sig(const fido_blob_t *, EVP_PKEY *, const fido_blob_t *);
+int es256_pk_verify_sig(const fido_blob_t *, const es256_pk_t *,
     const fido_blob_t *);
-int fido_verify_sig_rs256(const fido_blob_t *, const rs256_pk_t *,
+int rs256_pk_verify_sig(const fido_blob_t *, const rs256_pk_t *,
     const fido_blob_t *);
-int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_pk_t *,
+int eddsa_pk_verify_sig(const fido_blob_t *, const eddsa_pk_t *,
     const fido_blob_t *);
 int fido_get_signed_hash(int, fido_blob_t *, const fido_blob_t *,
     const fido_blob_t *);
+int fido_get_signed_hash_tpm(fido_blob_t *, const fido_blob_t *,
+    const fido_blob_t *, const fido_attstmt_t *, const fido_attcred_t *);
 
 /* device manifest functions */
 int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *);
 int fido_nfc_manifest(fido_dev_info_t *, size_t, size_t *);
-
-/* device manifest registration */
-typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *);
-int fido_dev_register_manifest_func(const dev_manifest_func_t);
-void fido_dev_unregister_manifest_func(const dev_manifest_func_t);
+int fido_pcsc_manifest(fido_dev_info_t *, size_t, size_t *);
 
 /* fuzzing instrumentation */
 #ifdef FIDO_FUZZ
@@ -232,6 +258,8 @@ uint32_t uniform_random(uint32_t);
 #define FIDO_DUMMY_USER_NAME   "dummy"
 #define FIDO_DUMMY_USER_ID     1
 #define FIDO_WINHELLO_PATH     "windows://hello"
+#define FIDO_NFC_PREFIX                "nfc:"
+#define FIDO_PCSC_PREFIX       "pcsc:"
 
 #ifdef __cplusplus
 } /* extern "C" */
index ef98130..f1cd026 100644 (file)
@@ -41,6 +41,7 @@ fido_dev_t *fido_dev_new(void);
 fido_dev_t *fido_dev_new_with_info(const fido_dev_info_t *);
 fido_dev_info_t *fido_dev_info_new(size_t);
 fido_cbor_info_t *fido_cbor_info_new(void);
+void *fido_dev_io_handle(const fido_dev_t *);
 
 void fido_assert_free(fido_assert_t **);
 void fido_cbor_info_free(fido_cbor_info_t **);
@@ -87,16 +88,17 @@ 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_aaguid_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_attstmt_ptr(const fido_cred_t *);
 const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
 const unsigned char *fido_cred_authdata_raw_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_aaguid_ptr(const fido_cred_t *);
-const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_largeblob_key_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_user_id_ptr(const fido_cred_t *);
 const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
-const unsigned char *fido_cred_largeblob_key_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 *,
@@ -120,6 +122,7 @@ int fido_assert_verify(const fido_assert_t *, size_t, int, const void *);
 int fido_cbor_info_algorithm_cose(const fido_cbor_info_t *, size_t);
 int fido_cred_exclude(fido_cred_t *, const unsigned char *, size_t);
 int fido_cred_prot(const fido_cred_t *);
+int fido_cred_set_attstmt(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_blob(fido_cred_t *, const unsigned char *, size_t);
@@ -129,6 +132,7 @@ int fido_cred_set_extensions(fido_cred_t *, int);
 int fido_cred_set_fmt(fido_cred_t *, const char *);
 int fido_cred_set_id(fido_cred_t *, const unsigned char *, size_t);
 int fido_cred_set_options(fido_cred_t *, bool, bool);
+int fido_cred_set_pin_minlen(fido_cred_t *, size_t);
 int fido_cred_set_prot(fido_cred_t *, int);
 int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
 int fido_cred_set_rp(fido_cred_t *, const char *, const char *);
@@ -141,7 +145,9 @@ int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
 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 *);
+#ifdef _FIDO_SIGSET_DEFINED
 int fido_dev_set_sigmask(fido_dev_t *, const fido_sigset_t *);
+#endif
 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 *);
@@ -151,6 +157,8 @@ int fido_dev_get_uv_retry_count(fido_dev_t *, int *);
 int fido_dev_get_touch_begin(fido_dev_t *);
 int fido_dev_get_touch_status(fido_dev_t *, int *, int);
 int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
+int fido_dev_info_set(fido_dev_info_t *, size_t, const char *, const char *,
+    const char *, const fido_dev_io_t *, const fido_dev_transport_t *);
 int fido_dev_make_cred(fido_dev_t *, fido_cred_t *, const char *);
 int fido_dev_open_with_info(fido_dev_t *);
 int fido_dev_open(fido_dev_t *, const char *);
@@ -158,6 +166,7 @@ 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 *);
 int fido_dev_set_transport_functions(fido_dev_t *, const fido_dev_transport_t *);
+int fido_dev_set_timeout(fido_dev_t *, int);
 
 size_t fido_assert_authdata_len(const fido_assert_t *, size_t);
 size_t fido_assert_clientdata_hash_len(const fido_assert_t *);
@@ -175,16 +184,18 @@ 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_transports_len(const fido_cbor_info_t *);
 size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
+size_t fido_cred_aaguid_len(const fido_cred_t *);
+size_t fido_cred_attstmt_len(const fido_cred_t *);
 size_t fido_cred_authdata_len(const fido_cred_t *);
 size_t fido_cred_authdata_raw_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_aaguid_len(const fido_cred_t *);
-size_t fido_cred_user_id_len(const fido_cred_t *);
+size_t fido_cred_largeblob_key_len(const fido_cred_t *);
+size_t fido_cred_pin_minlen(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_user_id_len(const fido_cred_t *);
 size_t fido_cred_x5c_len(const fido_cred_t *);
-size_t fido_cred_largeblob_key_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);
@@ -197,19 +208,21 @@ 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 *);
 uint64_t fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *);
 uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *);
 uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *);
+uint64_t fido_cbor_info_maxlargeblob(const fido_cbor_info_t *);
+uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
 uint64_t fido_cbor_info_fwversion(const fido_cbor_info_t *);
 
 bool fido_dev_has_pin(const fido_dev_t *);
 bool fido_dev_has_uv(const fido_dev_t *);
 bool fido_dev_is_fido2(const fido_dev_t *);
 bool fido_dev_is_winhello(const fido_dev_t *);
-bool fido_dev_supports_pin(const fido_dev_t *);
-bool fido_dev_supports_cred_prot(const fido_dev_t *);
 bool fido_dev_supports_credman(const fido_dev_t *);
+bool fido_dev_supports_cred_prot(const fido_dev_t *);
+bool fido_dev_supports_permissions(const fido_dev_t *);
+bool fido_dev_supports_pin(const fido_dev_t *);
 bool fido_dev_supports_uv(const fido_dev_t *);
 
 int fido_dev_largeblob_get(fido_dev_t *, const unsigned char *, size_t,
index 869927d..d8134a3 100644 (file)
@@ -26,6 +26,8 @@ int fido_dev_enable_entattest(fido_dev_t *, const char *);
 int fido_dev_force_pin_change(fido_dev_t *, const char *);
 int fido_dev_toggle_always_uv(fido_dev_t *, const char *);
 int fido_dev_set_pin_minlen(fido_dev_t *, size_t, const char *);
+int fido_dev_set_pin_minlen_rpid(fido_dev_t *, const char * const *, size_t,
+    const char *);
 
 #ifdef __cplusplus
 } /* extern "C" */
index 4a81017..083721c 100644 (file)
@@ -31,19 +31,14 @@ int eddsa_pk_from_ptr(eddsa_pk_t *, const void *, size_t);
 
 #ifdef _FIDO_INTERNAL
 
-#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10101000L
+#if defined(LIBRESSL_VERSION_NUMBER)
 #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 /* LIBRESSL_VERSION_NUMBER */
 
 #endif /* _FIDO_INTERNAL */
 
index 80f4db3..683494d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 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.
  */
@@ -27,6 +27,7 @@ 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_EVP_PKEY(es256_pk_t *, const EVP_PKEY *);
 int es256_pk_from_ptr(es256_pk_t *, const void *, size_t);
 
 #ifdef _FIDO_INTERNAL
index 025bb57..7c6db98 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 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.
  */
 #define FIDO_CAP_NMSG  0x08 /* if set, device doesn't support CTAP_CMD_MSG */
 
 /* Supported COSE algorithms. */
+#define        COSE_UNSPEC     0
 #define        COSE_ES256      -7
 #define        COSE_EDDSA      -8
 #define        COSE_ECDH_ES256 -25
 #define        COSE_RS256      -257
+#define        COSE_RS1        -65535
 
 /* Supported COSE types. */
 #define COSE_KTY_OKP   1
 #define FIDO_EXT_CRED_PROTECT  0x02
 #define FIDO_EXT_LARGEBLOB_KEY 0x04
 #define FIDO_EXT_CRED_BLOB     0x08
+#define FIDO_EXT_MINPINLEN     0x10
 
 /* Supported credential protection policies. */
 #define FIDO_CRED_PROT_UV_OPTIONAL             0x01
 #define FIDO_EXT_ASSERT_MASK   (FIDO_EXT_HMAC_SECRET|FIDO_EXT_LARGEBLOB_KEY| \
                                 FIDO_EXT_CRED_BLOB)
 #define FIDO_EXT_CRED_MASK     (FIDO_EXT_HMAC_SECRET|FIDO_EXT_CRED_PROTECT| \
-                                FIDO_EXT_LARGEBLOB_KEY|FIDO_EXT_CRED_BLOB)
+                                FIDO_EXT_LARGEBLOB_KEY|FIDO_EXT_CRED_BLOB| \
+                                FIDO_EXT_MINPINLEN)
 #endif /* _FIDO_INTERNAL */
 
 #endif /* !_FIDO_PARAM_H */
index 2b08d59..0398161 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 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.
  */
@@ -26,6 +26,7 @@ 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_EVP_PKEY(rs256_pk_t *, const EVP_PKEY *);
 int rs256_pk_from_RSA(rs256_pk_t *, const RSA *);
 int rs256_pk_from_ptr(rs256_pk_t *, const void *, size_t);
 
index 00b6058..593a6a6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2022 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.
  */
@@ -48,10 +48,14 @@ typedef enum {
 
 typedef void fido_log_handler_t(const char *);
 
+#undef  _FIDO_SIGSET_DEFINED
+#define _FIDO_SIGSET_DEFINED
 #ifdef _WIN32
 typedef int fido_sigset_t;
-#else
+#elif defined(SIG_BLOCK)
 typedef sigset_t fido_sigset_t;
+#else
+#undef _FIDO_SIGSET_DEFINED
 #endif
 
 #ifdef _FIDO_INTERNAL
@@ -107,8 +111,12 @@ typedef struct fido_attcred {
 } fido_attcred_t;
 
 typedef struct fido_attstmt {
-       fido_blob_t x5c; /* attestation certificate */
-       fido_blob_t sig; /* attestation signature */
+       fido_blob_t certinfo; /* tpm attestation TPMS_ATTEST structure */
+       fido_blob_t pubarea;  /* tpm attestation TPMT_PUBLIC structure */
+       fido_blob_t cbor;     /* cbor-encoded attestation statement */
+       fido_blob_t x5c;      /* attestation certificate */
+       fido_blob_t sig;      /* attestation signature */
+       int         alg;      /* attestation algorithm (cose) */
 } fido_attstmt_t;
 
 typedef struct fido_rp {
@@ -124,8 +132,9 @@ typedef struct fido_user {
 } fido_user_t;
 
 typedef struct fido_cred_ext {
-       int mask; /* enabled extensions */
-       int prot; /* protection policy */
+       int    mask;      /* enabled extensions */
+       int    prot;      /* protection policy */
+       size_t minpinlen; /* minimum pin length */
 } fido_cred_ext_t;
 
 typedef struct fido_cred {
@@ -146,13 +155,13 @@ typedef struct fido_cred {
        fido_attcred_t    attcred;       /* returned credential (key + id) */
        fido_attstmt_t    attstmt;       /* attestation statement (x509 + sig) */
        fido_blob_t       largeblob_key; /* decoded large blob key */
-       fido_blob_t       blob;          /* FIDO 2.1 credBlob */
+       fido_blob_t       blob;          /* CTAP 2.1 credBlob */
 } fido_cred_t;
 
 typedef struct fido_assert_extattr {
        int         mask;            /* decoded extensions */
        fido_blob_t hmac_secret_enc; /* hmac secret, encrypted */
-       fido_blob_t blob;            /* decoded FIDO 2.1 credBlob */
+       fido_blob_t blob;            /* decoded CTAP 2.1 credBlob */
 } fido_assert_extattr_t;
 
 typedef struct _fido_assert_stmt {
@@ -211,18 +220,19 @@ typedef struct fido_algo_array {
 } fido_algo_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 */
-       fido_str_array_t  transports;    /* list of supported transports */
-       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_algo_array_t algorithms;    /* list of supported algorithms */
-       uint64_t          maxcredcntlst; /* max number of credentials in list */
-       uint64_t          maxcredidlen;  /* max credential ID length */
-       uint64_t          fwversion;     /* firmware version */
+       fido_str_array_t  versions;       /* supported versions: fido2|u2f */
+       fido_str_array_t  extensions;     /* list of supported extensions */
+       fido_str_array_t  transports;     /* list of supported transports */
+       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_algo_array_t algorithms;     /* list of supported algorithms */
+       uint64_t          maxcredcntlst;  /* max credentials in list */
+       uint64_t          maxcredidlen;   /* max credential ID length */
+       uint64_t          fwversion;      /* firmware version */
        uint64_t          maxcredbloblen; /* max credBlob length */
+       uint64_t          maxlargeblob;   /* max largeBlob array length */
 } fido_cbor_info_t;
 
 typedef struct fido_dev_info {
@@ -260,6 +270,7 @@ typedef struct fido_dev {
        int                   flags;      /* internal flags; see FIDO_DEV_* */
        fido_dev_transport_t  transport;  /* transport functions */
        uint64_t              maxmsgsize; /* max message size */
+       int                   timeout_ms; /* read timeout in ms */
 } fido_dev_t;
 
 #else
index a3768ad..926272b 100644 (file)
@@ -121,6 +121,15 @@ fido_dev_info_new(size_t n)
        return (calloc(n, sizeof(fido_dev_info_t)));
 }
 
+static void
+fido_dev_info_reset(fido_dev_info_t *di)
+{
+       free(di->path);
+       free(di->manufacturer);
+       free(di->product);
+       memset(di, 0, sizeof(*di));
+}
+
 void
 fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
 {
@@ -129,13 +138,8 @@ fido_dev_info_free(fido_dev_info_t **devlist_p, size_t n)
        if (devlist_p == NULL || (devlist = *devlist_p) == NULL)
                return;
 
-       for (size_t i = 0; i < n; i++) {
-               const fido_dev_info_t *di;
-               di = &devlist[i];
-               free(di->path);
-               free(di->manufacturer);
-               free(di->product);
-       }
+       for (size_t i = 0; i < n; i++)
+               fido_dev_info_reset(&devlist[i]);
 
        free(devlist);
 
@@ -148,6 +152,44 @@ fido_dev_info_ptr(const fido_dev_info_t *devlist, size_t i)
        return (&devlist[i]);
 }
 
+int
+fido_dev_info_set(fido_dev_info_t *devlist, size_t i,
+    const char *path, const char *manufacturer, const char *product,
+    const fido_dev_io_t *io, const fido_dev_transport_t *transport)
+{
+       char *path_copy = NULL, *manu_copy = NULL, *prod_copy = NULL;
+       int r;
+
+       if (path == NULL || manufacturer == NULL || product == NULL ||
+           io == NULL) {
+               r = FIDO_ERR_INVALID_ARGUMENT;
+               goto out;
+       }
+
+       if ((path_copy = strdup(path)) == NULL ||
+           (manu_copy = strdup(manufacturer)) == NULL ||
+           (prod_copy = strdup(product)) == NULL) {
+               r = FIDO_ERR_INTERNAL;
+               goto out;
+       }
+
+       fido_dev_info_reset(&devlist[i]);
+       devlist[i].path = path_copy;
+       devlist[i].manufacturer = manu_copy;
+       devlist[i].product = prod_copy;
+       devlist[i].io = *io;
+       if (transport)
+               devlist[i].transport = *transport;
+       r = FIDO_OK;
+out:
+       if (r != FIDO_OK) {
+               free(prod_copy);
+               free(manu_copy);
+               free(path_copy);
+       }
+       return (r);
+}
+
 const char *
 fido_dev_info_path(const fido_dev_info_t *di)
 {
index 4b2aff9..946b2dc 100644 (file)
@@ -58,8 +58,7 @@ fido_hid_unix_wait(int fd, int ms, const fido_sigset_t *sigmask)
        pfd.fd = fd;
 
 #ifdef FIDO_FUZZ
-       if (ms < 0)
-               return (0);
+       return (0);
 #endif
        if (ms > -1) {
                ts.tv_sec = ms / 1000;
index 57bc8de..ea26f22 100644 (file)
@@ -186,14 +186,6 @@ out:
        return (ok);
 }
 
-static void
-free_algo(fido_algo_t *a)
-{
-       free(a->type);
-       a->type = NULL;
-       a->cose = 0;
-}
-
 static int
 decode_algorithm(const cbor_item_t *item, void *arg)
 {
@@ -210,7 +202,7 @@ decode_algorithm(const cbor_item_t *item, void *arg)
 
        if (cbor_map_iter(item, &aa->ptr[i], decode_algorithm_entry) < 0) {
                fido_log_debug("%s: decode_algorithm_entry", __func__);
-               free_algo(&aa->ptr[i]);
+               fido_algo_free(&aa->ptr[i]);
                return (-1);
        }
 
@@ -276,6 +268,8 @@ parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                return (decode_string_array(val, &ci->transports));
        case 10: /* algorithms */
                return (decode_algorithms(val, &ci->algorithms));
+       case 11: /* maxSerializedLargeBlobArray */
+               return (cbor_decode_uint64(val, &ci->maxlargeblob));
        case 14: /* fwVersion */
                return (cbor_decode_uint64(val, &ci->fwversion));
        case 15: /* maxCredBlobLen */
@@ -287,13 +281,13 @@ parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 }
 
 static int
-fido_dev_get_cbor_info_tx(fido_dev_t *dev)
+fido_dev_get_cbor_info_tx(fido_dev_t *dev, int *ms)
 {
        const unsigned char cbor[] = { CTAP_CBOR_GETINFO };
 
        fido_log_debug("%s: dev=%p", __func__, (void *)dev);
 
-       if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) {
+       if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                return (FIDO_ERR_TX);
        }
@@ -302,13 +296,13 @@ fido_dev_get_cbor_info_tx(fido_dev_t *dev)
 }
 
 static int
-fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
+fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
 
        fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
-           (void *)ci, ms);
+           (void *)ci, *ms);
 
        fido_cbor_info_reset(ci);
 
@@ -323,7 +317,7 @@ fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
 }
 
 int
-fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
+fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int *ms)
 {
        int r;
 
@@ -331,7 +325,7 @@ fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
        if (dev->flags & FIDO_DEV_WINHELLO)
                return (fido_winhello_get_cbor_info(dev, ci));
 #endif
-       if ((r = fido_dev_get_cbor_info_tx(dev)) != FIDO_OK ||
+       if ((r = fido_dev_get_cbor_info_tx(dev, ms)) != FIDO_OK ||
            (r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
                return (r);
 
@@ -341,7 +335,9 @@ fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
 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));
+       int ms = dev->timeout_ms;
+
+       return (fido_dev_get_cbor_info_wait(dev, ci, &ms));
 }
 
 /*
@@ -354,58 +350,15 @@ 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;
-}
-
-static void
-free_algo_array(fido_algo_array_t *aa)
-{
-       for (size_t i = 0; i < aa->len; i++)
-               free_algo(&aa->ptr[i]);
-
-       free(aa->ptr);
-       aa->ptr = NULL;
-       aa->len = 0;
-}
-
 void
 fido_cbor_info_reset(fido_cbor_info_t *ci)
 {
-       free_str_array(&ci->versions);
-       free_str_array(&ci->extensions);
-       free_str_array(&ci->transports);
-       free_opt_array(&ci->options);
-       free_byte_array(&ci->protocols);
-       free_algo_array(&ci->algorithms);
+       fido_str_array_free(&ci->versions);
+       fido_str_array_free(&ci->extensions);
+       fido_str_array_free(&ci->transports);
+       fido_opt_array_free(&ci->options);
+       fido_byte_array_free(&ci->protocols);
+       fido_algo_array_free(&ci->algorithms);
 }
 
 void
@@ -510,6 +463,12 @@ fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci)
        return (ci->maxcredidlen);
 }
 
+uint64_t
+fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci)
+{
+       return (ci->maxlargeblob);
+}
+
 uint64_t
 fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
 {
index e259420..70f777f 100644 (file)
@@ -30,7 +30,24 @@ struct frame {
 #endif
 
 static int
-tx_empty(fido_dev_t *d, uint8_t cmd)
+tx_pkt(fido_dev_t *d, const void *pkt, size_t len, int *ms)
+{
+       struct timespec ts;
+       int n;
+
+       if (fido_time_now(&ts) != 0)
+               return (-1);
+
+       n = d->io.write(d->io_handle, pkt, len);
+
+       if (fido_time_delta(&ts, ms) != 0)
+               return (-1);
+
+       return (n);
+}
+
+static int
+tx_empty(fido_dev_t *d, uint8_t cmd, int *ms)
 {
        struct frame    *fp;
        unsigned char    pkt[sizeof(*fp) + 1];
@@ -42,15 +59,15 @@ tx_empty(fido_dev_t *d, uint8_t cmd)
        fp->cid = d->cid;
        fp->body.init.cmd = CTAP_FRAME_INIT | cmd;
 
-       if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
-           len)) < 0 || (size_t)n != len)
+       if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
+           (size_t)n != len)
                return (-1);
 
        return (0);
 }
 
 static size_t
-tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
+tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
 {
        struct frame    *fp;
        unsigned char    pkt[sizeof(*fp) + 1];
@@ -69,15 +86,15 @@ tx_preamble(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
        count = MIN(count, d->tx_len - CTAP_INIT_HEADER_LEN);
        memcpy(&fp->body.init.data, buf, count);
 
-       if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
-           len)) < 0 || (size_t)n != len)
+       if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
+           (size_t)n != len)
                return (0);
 
        return (count);
 }
 
 static size_t
-tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
+tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count, int *ms)
 {
        struct frame    *fp;
        unsigned char    pkt[sizeof(*fp) + 1];
@@ -94,19 +111,19 @@ tx_frame(fido_dev_t *d, uint8_t seq, const void *buf, size_t count)
        count = MIN(count, d->tx_len - CTAP_CONT_HEADER_LEN);
        memcpy(&fp->body.cont.data, buf, count);
 
-       if (len > sizeof(pkt) || (n = d->io.write(d->io_handle, pkt,
-           len)) < 0 || (size_t)n != len)
+       if (len > sizeof(pkt) || (n = tx_pkt(d, pkt, len, ms)) < 0 ||
+           (size_t)n != len)
                return (0);
 
        return (count);
 }
 
 static int
-tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
+tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count, int *ms)
 {
        size_t n, sent;
 
-       if ((sent = tx_preamble(d, cmd, buf, count)) == 0) {
+       if ((sent = tx_preamble(d, cmd, buf, count, ms)) == 0) {
                fido_log_debug("%s: tx_preamble", __func__);
                return (-1);
        }
@@ -116,7 +133,8 @@ tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
                        fido_log_debug("%s: seq & 0x80", __func__);
                        return (-1);
                }
-               if ((n = tx_frame(d, seq++, buf + sent, count - sent)) == 0) {
+               if ((n = tx_frame(d, seq++, buf + sent, count - sent,
+                   ms)) == 0) {
                        fido_log_debug("%s: tx_frame", __func__);
                        return (-1);
                }
@@ -125,38 +143,59 @@ tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
        return (0);
 }
 
+static int
+transport_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
+{
+       struct timespec ts;
+       int n;
+
+       if (fido_time_now(&ts) != 0)
+               return (-1);
+
+       n = d->transport.tx(d, cmd, buf, count);
+
+       if (fido_time_delta(&ts, ms) != 0)
+               return (-1);
+
+       return (n);
+}
+
 int
-fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
+fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count, int *ms)
 {
        fido_log_debug("%s: dev=%p, cmd=0x%02x", __func__, (void *)d, cmd);
        fido_log_xxd(buf, count, "%s", __func__);
 
        if (d->transport.tx != NULL)
-               return (d->transport.tx(d, cmd, buf, count));
+               return (transport_tx(d, cmd, buf, count, ms));
        if (d->io_handle == NULL || d->io.write == NULL || count > UINT16_MAX) {
                fido_log_debug("%s: invalid argument", __func__);
                return (-1);
        }
 
-       return (count == 0 ? tx_empty(d, cmd) : tx(d, cmd, buf, count));
+       return (count == 0 ? tx_empty(d, cmd, ms) : tx(d, cmd, buf, count, ms));
 }
 
 static int
-rx_frame(fido_dev_t *d, struct frame *fp, int ms)
+rx_frame(fido_dev_t *d, struct frame *fp, int *ms)
 {
+       struct timespec ts;
        int n;
 
        memset(fp, 0, sizeof(*fp));
 
+       if (fido_time_now(&ts) != 0)
+               return (-1);
+
        if (d->rx_len > sizeof(*fp) || (n = d->io.read(d->io_handle,
-           (unsigned char *)fp, d->rx_len, ms)) < 0 || (size_t)n != d->rx_len)
+           (unsigned char *)fp, d->rx_len, *ms)) < 0 || (size_t)n != d->rx_len)
                return (-1);
 
-       return (0);
+       return (fido_time_delta(&ts, ms));
 }
 
 static int
-rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
+rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int *ms)
 {
        do {
                if (rx_frame(d, fp, ms) < 0)
@@ -185,7 +224,7 @@ rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
 }
 
 static int
-rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
+rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int *ms)
 {
        struct frame f;
        size_t r, payload_len, init_data_len, cont_data_len;
@@ -252,16 +291,33 @@ rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
        return ((int)r);
 }
 
+static int
+transport_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
+{
+       struct timespec ts;
+       int n;
+
+       if (fido_time_now(&ts) != 0)
+               return (-1);
+
+       n = d->transport.rx(d, cmd, buf, count, *ms);
+
+       if (fido_time_delta(&ts, ms) != 0)
+               return (-1);
+
+       return (n);
+}
+
 int
-fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
+fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int *ms)
 {
        int n;
 
        fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
-           cmd, ms);
+           cmd, *ms);
 
        if (d->transport.rx != NULL)
-               return (d->transport.rx(d, cmd, buf, count, ms));
+               return (transport_rx(d, cmd, buf, count, ms));
        if (d->io_handle == NULL || d->io.read == NULL || count > UINT16_MAX) {
                fido_log_debug("%s: invalid argument", __func__);
                return (-1);
@@ -273,7 +329,7 @@ fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
 }
 
 int
-fido_rx_cbor_status(fido_dev_t *d, int ms)
+fido_rx_cbor_status(fido_dev_t *d, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
index a11aae3..a490227 100644 (file)
@@ -59,6 +59,6 @@ iso7816_ptr(const iso7816_apdu_t *apdu)
 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);
+       return apdu->alloc_len - offsetof(iso7816_apdu_t, header) -
+           (sizeof(iso7816_apdu_t) - offsetof(iso7816_apdu_t, payload));
 }
index 5f5363a..9bfad1f 100644 (file)
@@ -27,14 +27,13 @@ struct iso7816_header {
        uint8_t lc3;
 })
 
-PACKED_TYPE(iso7816_apdu_t,
-struct iso7816_apdu {
+typedef struct iso7816_apdu {
        size_t            alloc_len;
        uint16_t          payload_len;
        uint8_t          *payload_ptr;
        iso7816_header_t  header;
        uint8_t           payload[];
-})
+} iso7816_apdu_t;
 
 const unsigned char *iso7816_ptr(const iso7816_apdu_t *);
 int iso7816_add(iso7816_apdu_t *, const void *, size_t);
index fa453f5..c817317 100644 (file)
@@ -153,7 +153,7 @@ fail:
 }
 
 static int
-largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count)
+largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count, int *ms)
 {
        fido_blob_t f;
        cbor_item_t *argv[3];
@@ -169,7 +169,7 @@ largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count)
                goto fail;
        }
        if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -198,7 +198,7 @@ parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val,
 }
 
 static int
-largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int ms)
+largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int *ms)
 {
        unsigned char reply[FIDO_MAXMSG];
        int reply_len, r;
@@ -419,7 +419,7 @@ largeblob_array_check(const fido_blob_t *array)
 }
 
 static int
-largeblob_get_array(fido_dev_t *dev, cbor_item_t **item)
+largeblob_get_array(fido_dev_t *dev, cbor_item_t **item, int *ms)
 {
        fido_blob_t *array, *chunk = NULL;
        size_t n;
@@ -432,8 +432,8 @@ largeblob_get_array(fido_dev_t *dev, cbor_item_t **item)
                return FIDO_ERR_INTERNAL;
        do {
                fido_blob_free(&chunk);
-               if ((r = largeblob_get_tx(dev, array->len, n)) != FIDO_OK ||
-                   (r = largeblob_get_rx(dev, &chunk, -1)) != FIDO_OK) {
+               if ((r = largeblob_get_tx(dev, array->len, n, ms)) != FIDO_OK ||
+                   (r = largeblob_get_rx(dev, &chunk, ms)) != FIDO_OK) {
                        fido_log_debug("%s: largeblob_get_wait %zu/%zu",
                            __func__, array->len, n);
                        goto fail;
@@ -491,7 +491,7 @@ prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac)
 
 static int
 largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk,
-    size_t chunk_len, size_t offset, size_t totalsiz)
+    size_t chunk_len, size_t offset, size_t totalsiz, int *ms)
 {
        fido_blob_t *hmac = NULL, f;
        cbor_item_t *argv[6];
@@ -518,7 +518,7 @@ largeblob_set_tx(fido_dev_t *dev, const fido_blob_t *token, const u_char *chunk,
                }
        }
        if (cbor_build_frame(CTAP_CBOR_LARGEBLOB, argv, nitems(argv), &f) < 0 ||
-           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -534,7 +534,8 @@ fail:
 }
 
 static int
-largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token)
+largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token,
+    int *ms)
 {
        es256_pk_t *pk = NULL;
        fido_blob_t *ecdh = NULL;
@@ -542,12 +543,12 @@ largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token)
 
        if ((*token = fido_blob_new()) == NULL)
                return FIDO_ERR_INTERNAL;
-       if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+       if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_do_ecdh", __func__);
                goto fail;
        }
        if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_LARGEBLOB, pin, ecdh, pk,
-           NULL, *token)) != FIDO_OK) {
+           NULL, *token, ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_dev_get_uv_token", __func__);
                goto fail;
        }
@@ -564,7 +565,8 @@ fail:
 }
 
 static int
-largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin)
+largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin,
+    int *ms)
 {
        unsigned char dgst[SHA256_DIGEST_LENGTH];
        fido_blob_t cbor, *token = NULL;
@@ -600,7 +602,8 @@ largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin)
        }
        totalsize = cbor.len + sizeof(dgst) - 16; /* the first 16 bytes only */
        if (pin != NULL || fido_dev_supports_permissions(dev)) {
-               if ((r = largeblob_get_uv_token(dev, pin, &token)) != FIDO_OK) {
+               if ((r = largeblob_get_uv_token(dev, pin, &token,
+                   ms)) != FIDO_OK) {
                        fido_log_debug("%s: largeblob_get_uv_token", __func__);
                        goto fail;
                }
@@ -609,15 +612,15 @@ largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin)
                if ((chunklen = cbor.len - offset) > maxchunklen)
                        chunklen = maxchunklen;
                if ((r = largeblob_set_tx(dev, token, cbor.ptr + offset,
-                   chunklen, offset, totalsize)) != FIDO_OK ||
-                   (r = fido_rx_cbor_status(dev, -1)) != FIDO_OK) {
+                   chunklen, offset, totalsize, ms)) != FIDO_OK ||
+                   (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
                        fido_log_debug("%s: body", __func__);
                        goto fail;
                }
        }
        if ((r = largeblob_set_tx(dev, token, dgst, sizeof(dgst) - 16, cbor.len,
-           totalsize)) != FIDO_OK ||
-           (r = fido_rx_cbor_status(dev, -1)) != FIDO_OK) {
+           totalsize, ms)) != FIDO_OK ||
+           (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK) {
                fido_log_debug("%s: dgst", __func__);
                goto fail;
        }
@@ -632,13 +635,13 @@ fail:
 
 static int
 largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
-    const char *pin)
+    const char *pin, int *ms)
 {
        cbor_item_t *array = NULL;
        size_t idx;
        int r;
 
-       if ((r = largeblob_get_array(dev, &array)) != FIDO_OK) {
+       if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
                fido_log_debug("%s: largeblob_get_array", __func__);
                goto fail;
        }
@@ -661,7 +664,7 @@ largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
                goto fail;
        }
 
-       if ((r = largeblob_set_array(dev, array, pin)) != FIDO_OK) {
+       if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
                fido_log_debug("%s: largeblob_set_array", __func__);
                goto fail;
        }
@@ -675,13 +678,14 @@ fail:
 }
 
 static int
-largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin)
+largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin,
+    int *ms)
 {
        cbor_item_t *array = NULL;
        size_t idx;
        int r;
 
-       if ((r = largeblob_get_array(dev, &array)) != FIDO_OK) {
+       if ((r = largeblob_get_array(dev, &array, ms)) != FIDO_OK) {
                fido_log_debug("%s: largeblob_get_array", __func__);
                goto fail;
        }
@@ -694,7 +698,7 @@ largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin)
                r = FIDO_ERR_INTERNAL;
                goto fail;
        }
-       if ((r = largeblob_set_array(dev, array, pin)) != FIDO_OK) {
+       if ((r = largeblob_set_array(dev, array, pin, ms)) != FIDO_OK) {
                fido_log_debug("%s: largeblob_set_array", __func__);
                goto fail;
        }
@@ -713,6 +717,7 @@ fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
 {
        cbor_item_t *item = NULL;
        fido_blob_t key, body;
+       int ms = dev->timeout_ms;
        int r;
 
        memset(&key, 0, sizeof(key));
@@ -733,7 +738,7 @@ fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
                fido_log_debug("%s: fido_blob_set", __func__);
                return FIDO_ERR_INTERNAL;
        }
-       if ((r = largeblob_get_array(dev, &item)) != FIDO_OK) {
+       if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
                fido_log_debug("%s: largeblob_get_array", __func__);
                goto fail;
        }
@@ -759,6 +764,7 @@ fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
 {
        cbor_item_t *item = NULL;
        fido_blob_t key, body;
+       int ms = dev->timeout_ms;
        int r;
 
        memset(&key, 0, sizeof(key));
@@ -784,7 +790,7 @@ fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
                r = FIDO_ERR_INTERNAL;
                goto fail;
        }
-       if ((r = largeblob_add(dev, &key, item, pin)) != FIDO_OK)
+       if ((r = largeblob_add(dev, &key, item, pin, &ms)) != FIDO_OK)
                fido_log_debug("%s: largeblob_add", __func__);
 fail:
        if (item != NULL)
@@ -801,6 +807,7 @@ fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
     size_t key_len, const char *pin)
 {
        fido_blob_t key;
+       int ms = dev->timeout_ms;
        int r;
 
        memset(&key, 0, sizeof(key));
@@ -813,7 +820,7 @@ fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
                fido_log_debug("%s: fido_blob_set", __func__);
                return FIDO_ERR_INTERNAL;
        }
-       if ((r = largeblob_drop(dev, &key, pin)) != FIDO_OK)
+       if ((r = largeblob_drop(dev, &key, pin, &ms)) != FIDO_OK)
                fido_log_debug("%s: largeblob_drop", __func__);
 
        fido_blob_reset(&key);
@@ -827,6 +834,7 @@ fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
 {
        cbor_item_t *item = NULL;
        fido_blob_t cbor;
+       int ms = dev->timeout_ms;
        int r;
 
        memset(&cbor, 0, sizeof(cbor));
@@ -838,7 +846,7 @@ fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
        }
        *cbor_ptr = NULL;
        *cbor_len = 0;
-       if ((r = largeblob_get_array(dev, &item)) != FIDO_OK) {
+       if ((r = largeblob_get_array(dev, &item, &ms)) != FIDO_OK) {
                fido_log_debug("%s: largeblob_get_array", __func__);
                return r;
        }
@@ -861,6 +869,7 @@ fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
 {
        cbor_item_t *item = NULL;
        struct cbor_load_result cbor_result;
+       int ms = dev->timeout_ms;
        int r;
 
        if (cbor_ptr == NULL || cbor_len == 0) {
@@ -872,7 +881,7 @@ fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
                fido_log_debug("%s: cbor_load", __func__);
                return FIDO_ERR_INVALID_ARGUMENT;
        }
-       if ((r = largeblob_set_array(dev, item, pin)) != FIDO_OK)
+       if ((r = largeblob_set_array(dev, item, pin, &ms)) != FIDO_OK)
                fido_log_debug("%s: largeblob_set_array", __func__);
 
        cbor_decref(&item);
diff --git a/lib/libfido2/src/nfc.c b/lib/libfido2/src/nfc.c
new file mode 100644 (file)
index 0000000..8e1221b
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2020-2022 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 <stdio.h>
+#include <string.h>
+
+#include "fido.h"
+#include "fido/param.h"
+#include "iso7816.h"
+
+#define TX_CHUNK_SIZE  240
+
+static const uint8_t aid[] = { 0xa0, 0x00, 0x00, 0x06, 0x47, 0x2f, 0x00, 0x01 };
+static const uint8_t v_u2f[] = { 'U', '2', 'F', '_', 'V', '2' };
+static const uint8_t v_fido[] = { 'F', 'I', 'D', 'O', '_', '2', '_', '0' };
+
+static int
+tx_short_apdu(fido_dev_t *d, const iso7816_header_t *h, const uint8_t *payload,
+    uint8_t payload_len, uint8_t cla_flags)
+{
+       uint8_t apdu[5 + UINT8_MAX + 1];
+       uint8_t sw[2];
+       size_t apdu_len;
+       int ok = -1;
+
+       memset(&apdu, 0, sizeof(apdu));
+       apdu[0] = h->cla | cla_flags;
+       apdu[1] = h->ins;
+       apdu[2] = h->p1;
+       apdu[3] = h->p2;
+       apdu[4] = payload_len;
+       memcpy(&apdu[5], payload, payload_len);
+       apdu_len = (size_t)(5 + payload_len + 1);
+
+       if (d->io.write(d->io_handle, apdu, apdu_len) < 0) {
+               fido_log_debug("%s: write", __func__);
+               goto fail;
+       }
+
+       if (cla_flags & 0x10) {
+               if (d->io.read(d->io_handle, sw, sizeof(sw), -1) != 2) {
+                       fido_log_debug("%s: read", __func__);
+                       goto fail;
+               }
+               if ((sw[0] << 8 | sw[1]) != SW_NO_ERROR) {
+                       fido_log_debug("%s: unexpected sw", __func__);
+                       goto fail;
+               }
+       }
+
+       ok = 0;
+fail:
+       explicit_bzero(apdu, sizeof(apdu));
+
+       return ok;
+}
+
+static int
+nfc_do_tx(fido_dev_t *d, const uint8_t *apdu_ptr, size_t apdu_len)
+{
+       iso7816_header_t h;
+
+       if (fido_buf_read(&apdu_ptr, &apdu_len, &h, sizeof(h)) < 0) {
+               fido_log_debug("%s: header", __func__);
+               return -1;
+       }
+       if (apdu_len < 2) {
+               fido_log_debug("%s: apdu_len %zu", __func__, apdu_len);
+               return -1;
+       }
+
+       apdu_len -= 2; /* trim le1 le2 */
+
+       while (apdu_len > TX_CHUNK_SIZE) {
+               if (tx_short_apdu(d, &h, apdu_ptr, TX_CHUNK_SIZE, 0x10) < 0) {
+                       fido_log_debug("%s: chain", __func__);
+                       return -1;
+               }
+               apdu_ptr += TX_CHUNK_SIZE;
+               apdu_len -= TX_CHUNK_SIZE;
+       }
+
+       if (tx_short_apdu(d, &h, apdu_ptr, (uint8_t)apdu_len, 0) < 0) {
+               fido_log_debug("%s: tx_short_apdu", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+fido_nfc_tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
+{
+       iso7816_apdu_t *apdu = NULL;
+       const uint8_t *ptr;
+       size_t len;
+       int ok = -1;
+
+       switch (cmd) {
+       case CTAP_CMD_INIT: /* select */
+               if ((apdu = iso7816_new(0, 0xa4, 0x04, sizeof(aid))) == NULL ||
+                   iso7816_add(apdu, aid, sizeof(aid)) < 0) {
+                       fido_log_debug("%s: iso7816", __func__);
+                       goto fail;
+               }
+               break;
+       case CTAP_CMD_CBOR: /* wrap cbor */
+               if (count > UINT16_MAX || (apdu = iso7816_new(0x80, 0x10, 0x00,
+                   (uint16_t)count)) == NULL ||
+                   iso7816_add(apdu, buf, count) < 0) {
+                       fido_log_debug("%s: iso7816", __func__);
+                       goto fail;
+               }
+               break;
+       case CTAP_CMD_MSG: /* already an apdu */
+               break;
+       default:
+               fido_log_debug("%s: cmd=%02x", __func__, cmd);
+               goto fail;
+       }
+
+       if (apdu != NULL) {
+               ptr = iso7816_ptr(apdu);
+               len = iso7816_len(apdu);
+       } else {
+               ptr = buf;
+               len = count;
+       }
+
+       if (nfc_do_tx(d, ptr, len) < 0) {
+               fido_log_debug("%s: nfc_do_tx", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       iso7816_free(&apdu);
+
+       return ok;
+}
+
+static int
+rx_init(fido_dev_t *d, unsigned char *buf, size_t count, int ms)
+{
+       fido_ctap_info_t *attr = (fido_ctap_info_t *)buf;
+       uint8_t f[64];
+       int n;
+
+       if (count != sizeof(*attr)) {
+               fido_log_debug("%s: count=%zu", __func__, count);
+               return -1;
+       }
+
+       memset(attr, 0, sizeof(*attr));
+
+       if ((n = d->io.read(d->io_handle, f, sizeof(f), ms)) < 2 ||
+           (f[n - 2] << 8 | f[n - 1]) != SW_NO_ERROR) {
+               fido_log_debug("%s: read", __func__);
+               return -1;
+       }
+
+       n -= 2;
+
+       if (n == sizeof(v_u2f) && memcmp(f, v_u2f, sizeof(v_u2f)) == 0)
+               attr->flags = FIDO_CAP_CBOR;
+       else if (n == sizeof(v_fido) && memcmp(f, v_fido, sizeof(v_fido)) == 0)
+               attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG;
+       else {
+               fido_log_debug("%s: unknown version string", __func__);
+#ifdef FIDO_FUZZ
+               attr->flags = FIDO_CAP_CBOR | FIDO_CAP_NMSG;
+#else
+               return -1;
+#endif
+       }
+
+       memcpy(&attr->nonce, &d->nonce, sizeof(attr->nonce)); /* XXX */
+
+       return (int)count;
+}
+
+static int
+tx_get_response(fido_dev_t *d, uint8_t count)
+{
+       uint8_t apdu[5];
+
+       memset(apdu, 0, sizeof(apdu));
+       apdu[1] = 0xc0; /* GET_RESPONSE */
+       apdu[4] = count;
+
+       if (d->io.write(d->io_handle, apdu, sizeof(apdu)) < 0) {
+               fido_log_debug("%s: write", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+rx_apdu(fido_dev_t *d, uint8_t sw[2], unsigned char **buf, size_t *count, int *ms)
+{
+       uint8_t f[256 + 2];
+       struct timespec ts;
+       int n, ok = -1;
+
+       if (fido_time_now(&ts) != 0)
+               goto fail;
+
+       if ((n = d->io.read(d->io_handle, f, sizeof(f), *ms)) < 2) {
+               fido_log_debug("%s: read", __func__);
+               goto fail;
+       }
+
+       if (fido_time_delta(&ts, ms) != 0)
+               goto fail;
+
+       if (fido_buf_write(buf, count, f, (size_t)(n - 2)) < 0) {
+               fido_log_debug("%s: fido_buf_write", __func__);
+               goto fail;
+       }
+
+       memcpy(sw, f + n - 2, 2);
+
+       ok = 0;
+fail:
+       explicit_bzero(f, sizeof(f));
+
+       return ok;
+}
+
+static int
+rx_msg(fido_dev_t *d, unsigned char *buf, size_t count, int ms)
+{
+       uint8_t sw[2];
+       const size_t bufsiz = count;
+
+       if (rx_apdu(d, sw, &buf, &count, &ms) < 0) {
+               fido_log_debug("%s: preamble", __func__);
+               return -1;
+       }
+
+       while (sw[0] == SW1_MORE_DATA)
+               if (tx_get_response(d, sw[1]) < 0 ||
+                   rx_apdu(d, sw, &buf, &count, &ms) < 0) {
+                       fido_log_debug("%s: chain", __func__);
+                       return -1;
+               }
+
+       if (fido_buf_write(&buf, &count, sw, sizeof(sw)) < 0) {
+               fido_log_debug("%s: sw", __func__);
+               return -1;
+       }
+
+       if (bufsiz - count > INT_MAX) {
+               fido_log_debug("%s: bufsiz", __func__);
+               return -1;
+       }
+
+       return (int)(bufsiz - count);
+}
+
+static int
+rx_cbor(fido_dev_t *d, unsigned char *buf, size_t count, int ms)
+{
+       int r;
+
+       if ((r = rx_msg(d, buf, count, ms)) < 2)
+               return -1;
+
+       return r - 2;
+}
+
+int
+fido_nfc_rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
+{
+       switch (cmd) {
+       case CTAP_CMD_INIT:
+               return rx_init(d, buf, count, ms);
+       case CTAP_CMD_CBOR:
+               return rx_cbor(d, buf, count, ms);
+       case CTAP_CMD_MSG:
+               return rx_msg(d, buf, count, ms);
+       default:
+               fido_log_debug("%s: cmd=%02x", __func__, cmd);
+               return -1;
+       }
+}
+
+#ifdef USE_NFC
+bool
+fido_is_nfc(const char *path)
+{
+       return strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) == 0;
+}
+
+int
+fido_dev_set_nfc(fido_dev_t *d)
+{
+       if (d->io_handle != NULL) {
+               fido_log_debug("%s: device open", __func__);
+               return -1;
+       }
+       d->io_own = true;
+       d->io = (fido_dev_io_t) {
+               fido_nfc_open,
+               fido_nfc_close,
+               fido_nfc_read,
+               fido_nfc_write,
+       };
+       d->transport = (fido_dev_transport_t) {
+               fido_nfc_rx,
+               fido_nfc_tx,
+       };
+
+       return 0;
+}
+#endif /* USE_NFC */
index d3104e0..30eeb08 100644 (file)
@@ -146,7 +146,7 @@ encode_uv_permission(uint8_t cmd)
 
 static int
 ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
-    const es256_pk_t *pk)
+    const es256_pk_t *pk, int *ms)
 {
        fido_blob_t      f;
        fido_blob_t     *p = NULL;
@@ -185,7 +185,7 @@ ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
        }
 
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
-           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -203,7 +203,7 @@ fail:
 
 static int
 ctap21_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
-    const es256_pk_t *pk, uint8_t cmd, const char *rpid)
+    const es256_pk_t *pk, uint8_t cmd, const char *rpid, int *ms)
 {
        fido_blob_t      f;
        fido_blob_t     *p = NULL;
@@ -248,7 +248,7 @@ ctap21_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
        }
 
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
-           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s:  fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -281,7 +281,7 @@ parse_uv_token(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 
 static int
 uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token,
-    int ms)
+    int *ms)
 {
        fido_blob_t     *aes_token = NULL;
        unsigned char    reply[FIDO_MAXMSG];
@@ -322,16 +322,16 @@ fail:
 static int
 uv_token_wait(fido_dev_t *dev, uint8_t cmd, const char *pin,
     const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
-    fido_blob_t *token, int ms)
+    fido_blob_t *token, int *ms)
 {
        int r;
 
        if (ecdh == NULL || pk == NULL)
                return (FIDO_ERR_INVALID_ARGUMENT);
        if (fido_dev_supports_permissions(dev))
-               r = ctap21_uv_token_tx(dev, pin, ecdh, pk, cmd, rpid);
+               r = ctap21_uv_token_tx(dev, pin, ecdh, pk, cmd, rpid, ms);
        else
-               r = ctap20_uv_token_tx(dev, pin, ecdh, pk);
+               r = ctap20_uv_token_tx(dev, pin, ecdh, pk, ms);
        if (r != FIDO_OK)
                return (r);
 
@@ -341,13 +341,14 @@ uv_token_wait(fido_dev_t *dev, uint8_t cmd, const char *pin,
 int
 fido_dev_get_uv_token(fido_dev_t *dev, uint8_t cmd, const char *pin,
     const fido_blob_t *ecdh, const es256_pk_t *pk, const char *rpid,
-    fido_blob_t *token)
+    fido_blob_t *token, int *ms)
 {
-       return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, -1));
+       return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, ms));
 }
 
 static int
-fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
+fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin,
+    int *ms)
 {
        fido_blob_t      f;
        fido_blob_t     *ppine = NULL;
@@ -368,7 +369,7 @@ fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
                goto fail;
        }
 
-       if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+       if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_do_ecdh", __func__);
                goto fail;
        }
@@ -397,7 +398,7 @@ fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
        }
 
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
-           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -418,7 +419,7 @@ fail:
 }
 
 static int
-fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
+fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin, int *ms)
 {
        fido_blob_t      f;
        fido_blob_t     *ppine = NULL;
@@ -430,7 +431,7 @@ fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
        memset(&f, 0, sizeof(f));
        memset(argv, 0, sizeof(argv));
 
-       if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+       if ((r = fido_do_ecdh(dev, &pk, &ecdh, ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_do_ecdh", __func__);
                goto fail;
        }
@@ -451,7 +452,7 @@ fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
        }
 
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
-           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -470,17 +471,18 @@ fail:
 
 static int
 fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
-    int ms)
+    int *ms)
 {
        int r;
 
        if (oldpin != NULL) {
-               if ((r = fido_dev_change_pin_tx(dev, pin, oldpin)) != FIDO_OK) {
+               if ((r = fido_dev_change_pin_tx(dev, pin, oldpin,
+                   ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_dev_change_pin_tx", __func__);
                        return (r);
                }
        } else {
-               if ((r = fido_dev_set_pin_tx(dev, pin)) != FIDO_OK) {
+               if ((r = fido_dev_set_pin_tx(dev, pin, ms)) != FIDO_OK) {
                        fido_log_debug("%s: fido_dev_set_pin_tx", __func__);
                        return (r);
                }
@@ -502,7 +504,9 @@ fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
 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));
+       int ms = dev->timeout_ms;
+
+       return (fido_dev_set_pin_wait(dev, pin, oldpin, &ms));
 }
 
 static int
@@ -542,7 +546,7 @@ parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 }
 
 static int
-fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd)
+fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd, int *ms)
 {
        fido_blob_t      f;
        cbor_item_t     *argv[2];
@@ -558,7 +562,7 @@ fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd)
        }
 
        if (cbor_build_frame(CTAP_CBOR_CLIENT_PIN, argv, nitems(argv),
-           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+           &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -573,7 +577,7 @@ fail:
 }
 
 static int
-fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
+fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -597,11 +601,11 @@ fido_dev_get_pin_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
 }
 
 static int
-fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
+fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
 {
        int r;
 
-       if ((r = fido_dev_get_retry_count_tx(dev, 1)) != FIDO_OK ||
+       if ((r = fido_dev_get_retry_count_tx(dev, 1, ms)) != FIDO_OK ||
            (r = fido_dev_get_pin_retry_count_rx(dev, retries, ms)) != FIDO_OK)
                return (r);
 
@@ -611,11 +615,13 @@ fido_dev_get_pin_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
 int
 fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
 {
-       return (fido_dev_get_pin_retry_count_wait(dev, retries, -1));
+       int ms = dev->timeout_ms;
+
+       return (fido_dev_get_pin_retry_count_wait(dev, retries, &ms));
 }
 
 static int
-fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
+fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -639,11 +645,11 @@ fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
 }
 
 static int
-fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
+fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int *ms)
 {
        int r;
 
-       if ((r = fido_dev_get_retry_count_tx(dev, 7)) != FIDO_OK ||
+       if ((r = fido_dev_get_retry_count_tx(dev, 7, ms)) != FIDO_OK ||
            (r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK)
                return (r);
 
@@ -653,13 +659,15 @@ fido_dev_get_uv_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
 int
 fido_dev_get_uv_retry_count(fido_dev_t *dev, int *retries)
 {
-       return (fido_dev_get_uv_retry_count_wait(dev, retries, -1));
+       int ms = dev->timeout_ms;
+
+       return (fido_dev_get_uv_retry_count_wait(dev, retries, &ms));
 }
 
 int
 cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data,
     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin,
-    const char *rpid, cbor_item_t **auth, cbor_item_t **opt)
+    const char *rpid, cbor_item_t **auth, cbor_item_t **opt, int *ms)
 {
        fido_blob_t     *token = NULL;
        int              r;
@@ -670,7 +678,7 @@ cbor_add_uv_params(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *hmac_data,
        }
 
        if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid,
-           token)) != FIDO_OK) {
+           token, ms)) != FIDO_OK) {
                fido_log_debug("%s: fido_dev_get_uv_token", __func__);
                goto fail;
        }
index 11380ce..c5fe6df 100644 (file)
@@ -7,11 +7,11 @@
 #include "fido.h"
 
 static int
-fido_dev_reset_tx(fido_dev_t *dev)
+fido_dev_reset_tx(fido_dev_t *dev, int *ms)
 {
        const unsigned char cbor[] = { CTAP_CBOR_RESET };
 
-       if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor)) < 0) {
+       if (fido_tx(dev, CTAP_CMD_CBOR, cbor, sizeof(cbor), ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                return (FIDO_ERR_TX);
        }
@@ -20,11 +20,11 @@ fido_dev_reset_tx(fido_dev_t *dev)
 }
 
 static int
-fido_dev_reset_wait(fido_dev_t *dev, int ms)
+fido_dev_reset_wait(fido_dev_t *dev, int *ms)
 {
        int r;
 
-       if ((r = fido_dev_reset_tx(dev)) != FIDO_OK ||
+       if ((r = fido_dev_reset_tx(dev, ms)) != FIDO_OK ||
            (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
                return (r);
 
@@ -39,5 +39,7 @@ fido_dev_reset_wait(fido_dev_t *dev, int ms)
 int
 fido_dev_reset(fido_dev_t *dev)
 {
-       return (fido_dev_reset_wait(dev, -1));
+       int ms = dev->timeout_ms;
+
+       return (fido_dev_reset_wait(dev, &ms));
 }
diff --git a/lib/libfido2/src/rs1.c b/lib/libfido2/src/rs1.c
new file mode 100644 (file)
index 0000000..134068b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2021 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/rsa.h>
+#include <openssl/obj_mac.h>
+
+#include "fido.h"
+
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL
+static EVP_MD *
+rs1_get_EVP_MD(void)
+{
+       const EVP_MD *from;
+       EVP_MD *to = NULL;
+
+       if ((from = EVP_sha1()) != NULL && (to = malloc(sizeof(*to))) != NULL)
+               memcpy(to, from, sizeof(*to));
+
+       return (to);
+}
+
+static void
+rs1_free_EVP_MD(EVP_MD *md)
+{
+       freezero(md, sizeof(*md));
+}
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000
+static EVP_MD *
+rs1_get_EVP_MD(void)
+{
+       return (EVP_MD_fetch(NULL, "SHA-1", NULL));
+}
+
+static void
+rs1_free_EVP_MD(EVP_MD *md)
+{
+       EVP_MD_free(md);
+}
+#else
+static EVP_MD *
+rs1_get_EVP_MD(void)
+{
+       const EVP_MD *md;
+
+       if ((md = EVP_sha1()) == NULL)
+               return (NULL);
+
+       return (EVP_MD_meth_dup(md));
+}
+
+static void
+rs1_free_EVP_MD(EVP_MD *md)
+{
+       EVP_MD_meth_free(md);
+}
+#endif /* LIBRESSL_VERSION_NUMBER */
+
+int
+rs1_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
+    const fido_blob_t *sig)
+{
+       EVP_PKEY_CTX    *pctx = NULL;
+       EVP_MD          *md = NULL;
+       int              ok = -1;
+
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
+               fido_log_debug("%s: EVP_PKEY_base_id", __func__);
+               goto fail;
+       }
+
+       if ((md = rs1_get_EVP_MD()) == NULL) {
+               fido_log_debug("%s: rs1_get_EVP_MD", __func__);
+               goto fail;
+       }
+
+       if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
+           EVP_PKEY_verify_init(pctx) != 1 ||
+           EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 ||
+           EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
+               fido_log_debug("%s: EVP_PKEY_CTX", __func__);
+               goto fail;
+       }
+
+       if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
+           dgst->len) != 1) {
+               fido_log_debug("%s: EVP_PKEY_verify", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_PKEY_CTX_free(pctx);
+       rs1_free_EVP_MD(md);
+
+       return (ok);
+}
index c6d87a3..95bae16 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2022 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.h"
 #include "fido/rs256.h"
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static int
-RSA_bits(const RSA *r)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#define get0_RSA(x)    EVP_PKEY_get0_RSA((x))
+#else
+#define get0_RSA(x)    EVP_PKEY_get0((x))
+#endif
+
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL
+static EVP_MD *
+rs256_get_EVP_MD(void)
 {
-       return (BN_num_bits(r->n));
+       const EVP_MD *from;
+       EVP_MD *to = NULL;
+
+       if ((from = EVP_sha256()) != NULL && (to = malloc(sizeof(*to))) != NULL)
+               memcpy(to, from, sizeof(*to));
+
+       return (to);
 }
 
-static int
-RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+static void
+rs256_free_EVP_MD(EVP_MD *md)
+{
+       freezero(md, sizeof(*md));
+}
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000
+static EVP_MD *
+rs256_get_EVP_MD(void)
 {
-       r->n = n;
-       r->e = e;
-       r->d = d;
+       return (EVP_MD_fetch(NULL, "SHA2-256", NULL));
+}
 
-       return (1);
+static void
+rs256_free_EVP_MD(EVP_MD *md)
+{
+       EVP_MD_free(md);
+}
+#else
+static EVP_MD *
+rs256_get_EVP_MD(void)
+{
+       const EVP_MD *md;
+
+       if ((md = EVP_sha256()) == NULL)
+               return (NULL);
+
+       return (EVP_MD_meth_dup(md));
 }
 
 static void
-RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+rs256_free_EVP_MD(EVP_MD *md)
 {
-       *n = r->n;
-       *e = r->e;
-       *d = r->d;
+       EVP_MD_meth_free(md);
 }
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+#endif /* LIBRESSL_VERSION_NUMBER */
 
 static int
 decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
@@ -105,11 +134,20 @@ rs256_pk_free(rs256_pk_t **pkp)
 int
 rs256_pk_from_ptr(rs256_pk_t *pk, const void *ptr, size_t len)
 {
+       EVP_PKEY *pkey;
+
        if (len < sizeof(*pk))
                return (FIDO_ERR_INVALID_ARGUMENT);
 
        memcpy(pk, ptr, sizeof(*pk));
 
+       if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL) {
+               fido_log_debug("%s: rs256_pk_to_EVP_PKEY", __func__);
+               return (FIDO_ERR_INVALID_ARGUMENT);
+       }
+
+       EVP_PKEY_free(pkey);
+
        return (FIDO_OK);
 }
 
@@ -140,6 +178,11 @@ rs256_pk_to_EVP_PKEY(const rs256_pk_t *k)
        n = NULL;
        e = NULL;
 
+       if (RSA_bits(rsa) != 2048) {
+               fido_log_debug("%s: invalid key length", __func__);
+               goto fail;
+       }
+
        if ((pkey = EVP_PKEY_new()) == NULL ||
            EVP_PKEY_assign_RSA(pkey, rsa) == 0) {
                fido_log_debug("%s: EVP_PKEY_assign_RSA", __func__);
@@ -198,3 +241,75 @@ rs256_pk_from_RSA(rs256_pk_t *pk, const RSA *rsa)
 
        return (FIDO_OK);
 }
+
+int
+rs256_pk_from_EVP_PKEY(rs256_pk_t *pk, const EVP_PKEY *pkey)
+{
+       const RSA *rsa;
+
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA ||
+           (rsa = get0_RSA(pkey)) == NULL)
+               return (FIDO_ERR_INVALID_ARGUMENT);
+
+       return (rs256_pk_from_RSA(pk, rsa));
+}
+
+int
+rs256_verify_sig(const fido_blob_t *dgst, EVP_PKEY *pkey,
+    const fido_blob_t *sig)
+{
+       EVP_PKEY_CTX    *pctx = NULL;
+       EVP_MD          *md = NULL;
+       int              ok = -1;
+
+       if (EVP_PKEY_base_id(pkey) != EVP_PKEY_RSA) {
+               fido_log_debug("%s: EVP_PKEY_base_id", __func__);
+               goto fail;
+       }
+
+       if ((md = rs256_get_EVP_MD()) == NULL) {
+               fido_log_debug("%s: rs256_get_EVP_MD", __func__);
+               goto fail;
+       }
+
+       if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL ||
+           EVP_PKEY_verify_init(pctx) != 1 ||
+           EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PADDING) != 1 ||
+           EVP_PKEY_CTX_set_signature_md(pctx, md) != 1) {
+               fido_log_debug("%s: EVP_PKEY_CTX", __func__);
+               goto fail;
+       }
+
+       if (EVP_PKEY_verify(pctx, sig->ptr, sig->len, dgst->ptr,
+           dgst->len) != 1) {
+               fido_log_debug("%s: EVP_PKEY_verify", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_PKEY_CTX_free(pctx);
+       rs256_free_EVP_MD(md);
+
+       return (ok);
+}
+
+int
+rs256_pk_verify_sig(const fido_blob_t *dgst, const rs256_pk_t *pk,
+    const fido_blob_t *sig)
+{
+       EVP_PKEY        *pkey;
+       int              ok = -1;
+
+       if ((pkey = rs256_pk_to_EVP_PKEY(pk)) == NULL ||
+           rs256_verify_sig(dgst, pkey, sig) < 0) {
+               fido_log_debug("%s: rs256_verify_sig", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_PKEY_free(pkey);
+
+       return (ok);
+}
diff --git a/lib/libfido2/src/time.c b/lib/libfido2/src/time.c
new file mode 100644 (file)
index 0000000..b82b618
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021 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 <errno.h>
+#include "fido.h"
+
+static int
+timespec_to_ms(const struct timespec *ts)
+{
+       int64_t x, y;
+
+       if (ts->tv_sec < 0 || ts->tv_nsec < 0 ||
+           ts->tv_nsec >= 1000000000LL)
+               return -1;
+
+       if ((uint64_t)ts->tv_sec >= INT64_MAX / 1000LL)
+               return -1;
+
+       x = ts->tv_sec * 1000LL;
+       y = ts->tv_nsec / 1000000LL;
+
+       if (INT64_MAX - x < y || x + y > INT_MAX)
+               return -1;
+
+       return (int)(x + y);
+}
+
+int
+fido_time_now(struct timespec *ts_now)
+{
+       if (clock_gettime(CLOCK_MONOTONIC, ts_now) != 0) {
+               fido_log_error(errno, "%s: clock_gettime", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+fido_time_delta(const struct timespec *ts_start, int *ms_remain)
+{
+       struct timespec ts_end, ts_delta;
+       int ms;
+
+       if (*ms_remain < 0)
+               return 0;
+
+       if (clock_gettime(CLOCK_MONOTONIC, &ts_end) != 0) {
+               fido_log_error(errno, "%s: clock_gettime", __func__);
+               return -1;
+       }
+
+       if (timespeccmp(&ts_end, ts_start, <)) {
+               fido_log_debug("%s: timespeccmp", __func__);
+               return -1;
+       }
+
+       timespecsub(&ts_end, ts_start, &ts_delta);
+
+       if ((ms = timespec_to_ms(&ts_delta)) < 0) {
+               fido_log_debug("%s: timespec_to_ms", __func__);
+               return -1;
+       }
+
+       if (ms > *ms_remain)
+               ms = *ms_remain;
+
+       *ms_remain -= ms;
+
+       return 0;
+}
diff --git a/lib/libfido2/src/touch.c b/lib/libfido2/src/touch.c
new file mode 100644 (file)
index 0000000..66b1c34
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018-2022 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 "fido.h"
+
+int
+fido_dev_get_touch_begin(fido_dev_t *dev)
+{
+       fido_blob_t      f;
+       cbor_item_t     *argv[9];
+       const char      *clientdata = FIDO_DUMMY_CLIENTDATA;
+       const uint8_t    user_id = FIDO_DUMMY_USER_ID;
+       unsigned char    cdh[SHA256_DIGEST_LENGTH];
+       fido_rp_t        rp;
+       fido_user_t      user;
+       int              ms = dev->timeout_ms;
+       int              r = FIDO_ERR_INTERNAL;
+
+       memset(&f, 0, sizeof(f));
+       memset(argv, 0, sizeof(argv));
+       memset(cdh, 0, sizeof(cdh));
+       memset(&rp, 0, sizeof(rp));
+       memset(&user, 0, sizeof(user));
+
+       if (fido_dev_is_fido2(dev) == false)
+               return (u2f_get_touch_begin(dev, &ms));
+
+       if (SHA256((const void *)clientdata, strlen(clientdata), cdh) != cdh) {
+               fido_log_debug("%s: sha256", __func__);
+               return (FIDO_ERR_INTERNAL);
+       }
+
+       if ((rp.id = strdup(FIDO_DUMMY_RP_ID)) == NULL ||
+           (user.name = strdup(FIDO_DUMMY_USER_NAME)) == NULL) {
+               fido_log_debug("%s: strdup", __func__);
+               goto fail;
+       }
+
+       if (fido_blob_set(&user.id, &user_id, sizeof(user_id)) < 0) {
+               fido_log_debug("%s: fido_blob_set", __func__);
+               goto fail;
+       }
+
+       if ((argv[0] = cbor_build_bytestring(cdh, sizeof(cdh))) == NULL ||
+           (argv[1] = cbor_encode_rp_entity(&rp)) == NULL ||
+           (argv[2] = cbor_encode_user_entity(&user)) == NULL ||
+           (argv[3] = cbor_encode_pubkey_param(COSE_ES256)) == NULL) {
+               fido_log_debug("%s: cbor encode", __func__);
+               goto fail;
+       }
+
+       if (fido_dev_supports_pin(dev)) {
+               if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
+                   (argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
+                       fido_log_debug("%s: cbor encode", __func__);
+                       goto fail;
+               }
+       }
+
+       if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
+           fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len, &ms) < 0) {
+               fido_log_debug("%s: fido_tx", __func__);
+               r = FIDO_ERR_TX;
+               goto fail;
+       }
+
+       r = FIDO_OK;
+fail:
+       cbor_vector_free(argv, nitems(argv));
+       free(f.ptr);
+       free(rp.id);
+       free(user.name);
+       free(user.id.ptr);
+
+       return (r);
+}
+
+int
+fido_dev_get_touch_status(fido_dev_t *dev, int *touched, int ms)
+{
+       int r;
+
+       *touched = 0;
+
+       if (fido_dev_is_fido2(dev) == false)
+               return (u2f_get_touch_status(dev, touched, &ms));
+
+       switch ((r = fido_rx_cbor_status(dev, &ms))) {
+       case FIDO_ERR_PIN_AUTH_INVALID:
+       case FIDO_ERR_PIN_INVALID:
+       case FIDO_ERR_PIN_NOT_SET:
+       case FIDO_ERR_SUCCESS:
+               *touched = 1;
+               break;
+       case FIDO_ERR_RX:
+               /* ignore */
+               break;
+       default:
+               fido_log_debug("%s: fido_rx_cbor_status", __func__);
+               return (r);
+       }
+
+       return (FIDO_OK);
+}
diff --git a/lib/libfido2/src/tpm.c b/lib/libfido2/src/tpm.c
new file mode 100644 (file)
index 0000000..74244f8
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2021 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.
+ */
+
+/*
+ * Trusted Platform Module (TPM) 2.0 attestation support. Documentation
+ * references are relative to revision 01.38 of the TPM 2.0 specification.
+ */
+
+#include <openssl/sha.h>
+
+#include "packed.h"
+#include "fido.h"
+
+/* Part 1, 4.89: TPM_GENERATED_VALUE */
+#define TPM_MAGIC      0xff544347
+
+/* Part 2, 6.3: TPM_ALG_ID */
+#define TPM_ALG_RSA    0x0001
+#define TPM_ALG_SHA256 0x000b
+#define TPM_ALG_NULL   0x0010
+#define TPM_ALG_ECC    0x0023
+
+/* Part 2, 6.4: TPM_ECC_CURVE */
+#define TPM_ECC_P256   0x0003
+
+/* Part 2, 6.9: TPM_ST_ATTEST_CERTIFY */
+#define TPM_ST_CERTIFY 0x8017
+
+/* Part 2, 8.3: TPMA_OBJECT */
+#define TPMA_RESERVED  0xfff8f309      /* reserved bits; must be zero */
+#define TPMA_FIXED     0x00000002      /* object has fixed hierarchy */
+#define TPMA_CLEAR     0x00000004      /* object persists */
+#define TPMA_FIXED_P   0x00000010      /* object has fixed parent */
+#define TPMA_SENSITIVE 0x00000020      /* data originates within tpm */
+#define TPMA_SIGN      0x00040000      /* object may sign */
+
+/* Part 2, 10.4.2: TPM2B_DIGEST */
+PACKED_TYPE(tpm_sha256_digest_t,
+struct tpm_sha256_digest {
+       uint16_t size; /* sizeof(body) */
+       uint8_t  body[32];
+})
+
+/* Part 2, 10.4.3: TPM2B_DATA */
+PACKED_TYPE(tpm_sha1_data_t,
+struct tpm_sha1_data {
+       uint16_t size; /* sizeof(body */
+       uint8_t  body[20];
+})
+
+/* Part 2, 10.5.3: TPM2B_NAME */
+PACKED_TYPE(tpm_sha256_name_t,
+struct tpm_sha256_name {
+       uint16_t size; /* sizeof(alg) + sizeof(body) */
+       uint16_t alg;  /* TPM_ALG_SHA256 */
+       uint8_t  body[32];
+})
+
+/* Part 2, 10.11.1: TPMS_CLOCK_INFO */
+PACKED_TYPE(tpm_clock_info_t,
+struct tpm_clock_info {
+       uint64_t timestamp_ms;
+       uint32_t reset_count;   /* obfuscated by tpm */
+       uint32_t restart_count; /* obfuscated by tpm */
+       uint8_t  safe;          /* 1 if timestamp_ms is current */
+})
+
+/* Part 2, 10.12.8 TPMS_ATTEST */
+PACKED_TYPE(tpm_sha1_attest_t,
+struct tpm_sha1_attest {
+       uint32_t          magic;     /* TPM_MAGIC */
+       uint16_t          type;      /* TPM_ST_ATTEST_CERTIFY */
+       tpm_sha256_name_t signer;    /* full tpm path of signing key */
+       tpm_sha1_data_t   data;      /* signed sha1 */
+       tpm_clock_info_t  clock;
+       uint64_t          fwversion; /* obfuscated by tpm */
+       tpm_sha256_name_t name;      /* sha256 of tpm_rs256_pubarea_t */
+       tpm_sha256_name_t qual_name; /* full tpm path of attested key */
+})
+
+/* Part 2, 11.2.4.5: TPM2B_PUBLIC_KEY_RSA */
+PACKED_TYPE(tpm_rs256_key_t,
+struct tpm_rs256_key {
+       uint16_t size; /* sizeof(body) */
+       uint8_t  body[256];
+})
+
+/* Part 2, 11.2.5.1: TPM2B_ECC_PARAMETER */
+PACKED_TYPE(tpm_es256_coord_t,
+struct tpm_es256_coord {
+       uint16_t size; /* sizeof(body) */
+       uint8_t  body[32];
+})
+
+/* Part 2, 11.2.5.2: TPMS_ECC_POINT */
+PACKED_TYPE(tpm_es256_point_t,
+struct tpm_es256_point {
+       tpm_es256_coord_t x;
+       tpm_es256_coord_t y;
+})
+
+/* Part 2, 12.2.3.5: TPMS_RSA_PARMS */
+PACKED_TYPE(tpm_rs256_param_t,
+struct tpm_rs256_param {
+       uint16_t symmetric; /* TPM_ALG_NULL */
+       uint16_t scheme;    /* TPM_ALG_NULL */
+       uint16_t keybits;   /* 2048 */
+       uint32_t exponent;  /* zero (meaning 2^16 + 1) */
+})
+
+/* Part 2, 12.2.3.6: TPMS_ECC_PARMS */
+PACKED_TYPE(tpm_es256_param_t,
+struct tpm_es256_param {
+       uint16_t symmetric; /* TPM_ALG_NULL */
+       uint16_t scheme;    /* TPM_ALG_NULL */
+       uint16_t curve_id;  /* TPM_ECC_P256 */
+       uint16_t kdf;       /* TPM_ALG_NULL */
+})
+
+/* Part 2, 12.2.4: TPMT_PUBLIC */
+PACKED_TYPE(tpm_rs256_pubarea_t,
+struct tpm_rs256_pubarea {
+       uint16_t            alg;    /* TPM_ALG_RSA */
+       uint16_t            hash;   /* TPM_ALG_SHA256 */
+       uint32_t            attr;
+       tpm_sha256_digest_t policy; /* must be present? */
+       tpm_rs256_param_t   param;
+       tpm_rs256_key_t     key;
+})
+
+/* Part 2, 12.2.4: TPMT_PUBLIC */
+PACKED_TYPE(tpm_es256_pubarea_t,
+struct tpm_es256_pubarea {
+       uint16_t            alg;    /* TPM_ALG_ECC */
+       uint16_t            hash;   /* TPM_ALG_SHA256 */
+       uint32_t            attr;
+       tpm_sha256_digest_t policy; /* must be present? */
+       tpm_es256_param_t   param;
+       tpm_es256_point_t   point;
+})
+
+static int
+get_signed_sha1(tpm_sha1_data_t *dgst, const fido_blob_t *authdata,
+    const fido_blob_t *clientdata)
+{
+       const EVP_MD    *md = NULL;
+       EVP_MD_CTX      *ctx = NULL;
+       int              ok = -1;
+
+       if ((dgst->size = sizeof(dgst->body)) != SHA_DIGEST_LENGTH ||
+           (md = EVP_sha1()) == NULL ||
+           (ctx = EVP_MD_CTX_new()) == NULL ||
+           EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
+           EVP_DigestUpdate(ctx, authdata->ptr, authdata->len) != 1 ||
+           EVP_DigestUpdate(ctx, clientdata->ptr, clientdata->len) != 1 ||
+           EVP_DigestFinal_ex(ctx, dgst->body, NULL) != 1) {
+               fido_log_debug("%s: sha1", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       EVP_MD_CTX_free(ctx);
+
+       return (ok);
+}
+
+static int
+get_signed_name(tpm_sha256_name_t *name, const fido_blob_t *pubarea)
+{
+       name->alg = TPM_ALG_SHA256;
+       name->size = sizeof(name->alg) + sizeof(name->body);
+       if (sizeof(name->body) != SHA256_DIGEST_LENGTH ||
+           SHA256(pubarea->ptr, pubarea->len, name->body) != name->body) {
+               fido_log_debug("%s: sha256", __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+static void
+bswap_rs256_pubarea(tpm_rs256_pubarea_t *x)
+{
+       x->alg = htobe16(x->alg);
+       x->hash = htobe16(x->hash);
+       x->attr = htobe32(x->attr);
+       x->policy.size = htobe16(x->policy.size);
+       x->param.symmetric = htobe16(x->param.symmetric);
+       x->param.scheme = htobe16(x->param.scheme);
+       x->param.keybits = htobe16(x->param.keybits);
+       x->key.size = htobe16(x->key.size);
+}
+
+static void
+bswap_es256_pubarea(tpm_es256_pubarea_t *x)
+{
+       x->alg = htobe16(x->alg);
+       x->hash = htobe16(x->hash);
+       x->attr = htobe32(x->attr);
+       x->policy.size = htobe16(x->policy.size);
+       x->param.symmetric = htobe16(x->param.symmetric);
+       x->param.scheme = htobe16(x->param.scheme);
+       x->param.curve_id = htobe16(x->param.curve_id);
+       x->param.kdf = htobe16(x->param.kdf);
+       x->point.x.size = htobe16(x->point.x.size);
+       x->point.y.size = htobe16(x->point.y.size);
+}
+
+static void
+bswap_sha1_certinfo(tpm_sha1_attest_t *x)
+{
+       x->magic = htobe32(x->magic);
+       x->type = htobe16(x->type);
+       x->signer.size = htobe16(x->signer.size);
+       x->data.size = htobe16(x->data.size);
+       x->name.alg = htobe16(x->name.alg);
+       x->name.size = htobe16(x->name.size);
+}
+
+static int
+check_rs256_pubarea(const fido_blob_t *buf, const rs256_pk_t *pk)
+{
+       const tpm_rs256_pubarea_t       *actual;
+       tpm_rs256_pubarea_t              expected;
+       int                              ok;
+
+       if (buf->len != sizeof(*actual)) {
+               fido_log_debug("%s: buf->len=%zu", __func__, buf->len);
+               return -1;
+       }
+       actual = (const void *)buf->ptr;
+
+       memset(&expected, 0, sizeof(expected));
+       expected.alg = TPM_ALG_RSA;
+       expected.hash = TPM_ALG_SHA256;
+       expected.attr = be32toh(actual->attr);
+       expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR);
+       expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN);
+       expected.policy = actual->policy;
+       expected.policy.size = sizeof(expected.policy.body);
+       expected.param.symmetric = TPM_ALG_NULL;
+       expected.param.scheme = TPM_ALG_NULL;
+       expected.param.keybits = 2048;
+       expected.param.exponent = 0; /* meaning 2^16+1 */
+       expected.key.size = sizeof(expected.key.body);
+       memcpy(&expected.key.body, &pk->n, sizeof(expected.key.body));
+       bswap_rs256_pubarea(&expected);
+
+       ok = timingsafe_bcmp(&expected, actual, sizeof(expected));
+       explicit_bzero(&expected, sizeof(expected));
+
+       return ok != 0 ? -1 : 0;
+}
+
+static int
+check_es256_pubarea(const fido_blob_t *buf, const es256_pk_t *pk)
+{
+       const tpm_es256_pubarea_t       *actual;
+       tpm_es256_pubarea_t              expected;
+       int                              ok;
+
+       if (buf->len != sizeof(*actual)) {
+               fido_log_debug("%s: buf->len=%zu", __func__, buf->len);
+               return -1;
+       }
+       actual = (const void *)buf->ptr;
+
+       memset(&expected, 0, sizeof(expected));
+       expected.alg = TPM_ALG_ECC;
+       expected.hash = TPM_ALG_SHA256;
+       expected.attr = be32toh(actual->attr);
+       expected.attr &= ~(TPMA_RESERVED|TPMA_CLEAR);
+       expected.attr |= (TPMA_FIXED|TPMA_FIXED_P|TPMA_SENSITIVE|TPMA_SIGN);
+       expected.policy = actual->policy;
+       expected.policy.size = sizeof(expected.policy.body);
+       expected.param.symmetric = TPM_ALG_NULL;
+       expected.param.scheme = TPM_ALG_NULL; /* TCG Alg. Registry, 5.2.4 */
+       expected.param.curve_id = TPM_ECC_P256;
+       expected.param.kdf = TPM_ALG_NULL;
+       expected.point.x.size = sizeof(expected.point.x.body);
+       expected.point.y.size = sizeof(expected.point.y.body);
+       memcpy(&expected.point.x.body, &pk->x, sizeof(expected.point.x.body));
+       memcpy(&expected.point.y.body, &pk->y, sizeof(expected.point.y.body));
+       bswap_es256_pubarea(&expected);
+
+       ok = timingsafe_bcmp(&expected, actual, sizeof(expected));
+       explicit_bzero(&expected, sizeof(expected));
+
+       return ok != 0 ? -1 : 0;
+}
+
+static int
+check_sha1_certinfo(const fido_blob_t *buf, const fido_blob_t *clientdata_hash,
+    const fido_blob_t *authdata_raw, const fido_blob_t *pubarea)
+{
+       const tpm_sha1_attest_t *actual;
+       tpm_sha1_attest_t        expected;
+       tpm_sha1_data_t          signed_data;
+       tpm_sha256_name_t        signed_name;
+       int                      ok = -1;
+
+       memset(&signed_data, 0, sizeof(signed_data));
+       memset(&signed_name, 0, sizeof(signed_name));
+
+       if (get_signed_sha1(&signed_data, authdata_raw, clientdata_hash) < 0 ||
+           get_signed_name(&signed_name, pubarea) < 0) {
+               fido_log_debug("%s: get_signed_sha1/name", __func__);
+               goto fail;
+       }
+       if (buf->len != sizeof(*actual)) {
+               fido_log_debug("%s: buf->len=%zu", __func__, buf->len);
+               goto fail;
+       }
+       actual = (const void *)buf->ptr;
+
+       memset(&expected, 0, sizeof(expected));
+       expected.magic = TPM_MAGIC;
+       expected.type = TPM_ST_CERTIFY;
+       expected.signer = actual->signer;
+       expected.signer.size = sizeof(expected.signer.alg) +
+           sizeof(expected.signer.body);
+       expected.data = signed_data;
+       expected.clock = actual->clock;
+       expected.clock.safe = 1;
+       expected.fwversion = actual->fwversion;
+       expected.name = signed_name;
+       expected.qual_name = actual->qual_name;
+       bswap_sha1_certinfo(&expected);
+
+       ok = timingsafe_bcmp(&expected, actual, sizeof(expected));
+fail:
+       explicit_bzero(&expected, sizeof(expected));
+       explicit_bzero(&signed_data, sizeof(signed_data));
+       explicit_bzero(&signed_name, sizeof(signed_name));
+
+       return ok != 0 ? -1 : 0;
+}
+
+int
+fido_get_signed_hash_tpm(fido_blob_t *dgst, const fido_blob_t *clientdata_hash,
+    const fido_blob_t *authdata_raw, const fido_attstmt_t *attstmt,
+    const fido_attcred_t *attcred)
+{
+       const fido_blob_t *pubarea = &attstmt->pubarea;
+       const fido_blob_t *certinfo = &attstmt->certinfo;
+
+       if (attstmt->alg != COSE_RS1) {
+               fido_log_debug("%s: unsupported alg %d", __func__,
+                   attstmt->alg);
+               return -1;
+       }
+
+       switch (attcred->type) {
+       case COSE_ES256:
+               if (check_es256_pubarea(pubarea, &attcred->pubkey.es256) < 0) {
+                       fido_log_debug("%s: check_es256_pubarea", __func__);
+                       return -1;
+               }
+               break;
+       case COSE_RS256:
+               if (check_rs256_pubarea(pubarea, &attcred->pubkey.rs256) < 0) {
+                       fido_log_debug("%s: check_rs256_pubarea", __func__);
+                       return -1;
+               }
+               break;
+       default:
+               fido_log_debug("%s: unsupported type %d", __func__,
+                   attcred->type);
+               return -1;
+       }
+
+       if (check_sha1_certinfo(certinfo, clientdata_hash, authdata_raw,
+           pubarea) < 0) {
+               fido_log_debug("%s: check_sha1_certinfo", __func__);
+               return -1;
+       }
+
+       if (dgst->len < SHA_DIGEST_LENGTH ||
+           SHA1(certinfo->ptr, certinfo->len, dgst->ptr) != dgst->ptr) {
+               fido_log_debug("%s: sha1", __func__);
+               return -1;
+       }
+       dgst->len = SHA_DIGEST_LENGTH;
+
+       return 0;
+}
diff --git a/lib/libfido2/src/types.c b/lib/libfido2/src/types.c
new file mode 100644 (file)
index 0000000..54c0ca5
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2018-2021 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.h"
+
+void
+fido_str_array_free(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;
+}
+
+void
+fido_opt_array_free(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;
+}
+
+void
+fido_byte_array_free(fido_byte_array_t *ba)
+{
+       free(ba->ptr);
+
+       ba->ptr = NULL;
+       ba->len = 0;
+}
+
+void
+fido_algo_free(fido_algo_t *a)
+{
+       free(a->type);
+       a->type = NULL;
+       a->cose = 0;
+}
+
+void
+fido_algo_array_free(fido_algo_array_t *aa)
+{
+       for (size_t i = 0; i < aa->len; i++)
+               fido_algo_free(&aa->ptr[i]);
+
+       free(aa->ptr);
+       aa->ptr = NULL;
+       aa->len = 0;
+}
+
+int
+fido_str_array_pack(fido_str_array_t *sa, const char * const *v, size_t n)
+{
+       if ((sa->ptr = calloc(n, sizeof(char *))) == NULL) {
+               fido_log_debug("%s: calloc", __func__);
+               return -1;
+       }
+       for (size_t i = 0; i < n; i++) {
+               if ((sa->ptr[i] = strdup(v[i])) == NULL) {
+                       fido_log_debug("%s: strdup", __func__);
+                       return -1;
+               }
+               sa->len++;
+       }
+
+       return 0;
+}
diff --git a/lib/libfido2/src/types.h b/lib/libfido2/src/types.h
deleted file mode 100644 (file)
index 42ed1b7..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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 */
index c5fbe0c..6ebfcc7 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 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.
  */
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#include <errno.h>
 
 #include "fido.h"
 #include "fido/es256.h"
 
+#define U2F_PACE_MS (100)
+
 #if defined(_MSC_VER)
 static int
 usleep(unsigned int usec)
@@ -24,6 +27,28 @@ usleep(unsigned int usec)
 }
 #endif
 
+static int
+delay_ms(unsigned int ms, int *ms_remain)
+{
+       if (*ms_remain > -1 && (unsigned int)*ms_remain < ms)
+               ms = (unsigned int)*ms_remain;
+
+       if (ms > UINT_MAX / 1000) {
+               fido_log_debug("%s: ms=%u", __func__, ms);
+               return (-1);
+       }
+
+       if (usleep(ms * 1000) < 0) {
+               fido_log_error(errno, "%s: usleep", __func__);
+               return (-1);
+       }
+
+       if (*ms_remain > -1)
+               *ms_remain -= (int)ms;
+
+       return (0);
+}
+
 static int
 sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
 {
@@ -115,7 +140,7 @@ authdata_fake(const char *rp_id, uint8_t flags, uint32_t sigcount,
 
 /* TODO: use u2f_get_touch_begin & u2f_get_touch_status instead */
 static int
-send_dummy_register(fido_dev_t *dev, int ms)
+send_dummy_register(fido_dev_t *dev, int *ms)
 {
        iso7816_apdu_t  *apdu = NULL;
        unsigned char    challenge[SHA256_DIGEST_LENGTH];
@@ -123,10 +148,6 @@ send_dummy_register(fido_dev_t *dev, int ms)
        unsigned char    reply[FIDO_MAXMSG];
        int              r;
 
-#ifdef FIDO_FUZZ
-       ms = 0; /* XXX */
-#endif
-
        /* dummy challenge & application */
        memset(&challenge, 0xff, sizeof(challenge));
        memset(&application, 0xff, sizeof(application));
@@ -142,7 +163,7 @@ send_dummy_register(fido_dev_t *dev, int ms)
 
        do {
                if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
-                   iso7816_len(apdu)) < 0) {
+                   iso7816_len(apdu), ms) < 0) {
                        fido_log_debug("%s: fido_tx", __func__);
                        r = FIDO_ERR_TX;
                        goto fail;
@@ -152,8 +173,8 @@ send_dummy_register(fido_dev_t *dev, int ms)
                        r = FIDO_ERR_RX;
                        goto fail;
                }
-               if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
-                       fido_log_debug("%s: usleep", __func__);
+               if (delay_ms(U2F_PACE_MS, ms) != 0) {
+                       fido_log_debug("%s: delay_ms", __func__);
                        r = FIDO_ERR_RX;
                        goto fail;
                }
@@ -168,7 +189,7 @@ fail:
 
 static int
 key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
-    int *found, int ms)
+    int *found, int *ms)
 {
        iso7816_apdu_t  *apdu = NULL;
        unsigned char    challenge[SHA256_DIGEST_LENGTH];
@@ -208,7 +229,7 @@ key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
        }
 
        if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
-           iso7816_len(apdu)) < 0) {
+           iso7816_len(apdu), ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -274,7 +295,7 @@ parse_auth_reply(fido_blob_t *sig, fido_blob_t *ad, const char *rp_id,
 
 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 fido_blob_t *key_id, fido_blob_t *sig, fido_blob_t *ad, int *ms)
 {
        iso7816_apdu_t  *apdu = NULL;
        unsigned char    rp_id_hash[SHA256_DIGEST_LENGTH];
@@ -284,7 +305,7 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
        int              r;
 
 #ifdef FIDO_FUZZ
-       ms = 0; /* XXX */
+       *ms = 0; /* XXX */
 #endif
 
        if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
@@ -317,7 +338,7 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
 
        do {
                if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
-                   iso7816_len(apdu)) < 0) {
+                   iso7816_len(apdu), ms) < 0) {
                        fido_log_debug("%s: fido_tx", __func__);
                        r = FIDO_ERR_TX;
                        goto fail;
@@ -328,8 +349,8 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
                        r = FIDO_ERR_RX;
                        goto fail;
                }
-               if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
-                       fido_log_debug("%s: usleep", __func__);
+               if (delay_ms(U2F_PACE_MS, ms) != 0) {
+                       fido_log_debug("%s: delay_ms", __func__);
                        r = FIDO_ERR_RX;
                        goto fail;
                }
@@ -390,6 +411,71 @@ fail:
        return (ok);
 }
 
+static int
+encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
+    const fido_blob_t *sig, fido_blob_t *out)
+{
+       cbor_item_t             *item = NULL;
+       cbor_item_t             *x5c_cbor = NULL;
+       const uint8_t            alg_cbor = (uint8_t)(-cose_alg - 1);
+       struct cbor_pair         kv[3];
+       size_t                   alloc_len;
+       int                      ok = -1;
+
+       memset(&kv, 0, sizeof(kv));
+       memset(out, 0, sizeof(*out));
+
+       if ((item = cbor_new_definite_map(3)) == NULL) {
+               fido_log_debug("%s: cbor_new_definite_map", __func__);
+               goto fail;
+       }
+
+       if ((kv[0].key = cbor_build_string("alg")) == NULL ||
+           (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
+           !cbor_map_add(item, kv[0])) {
+               fido_log_debug("%s: alg", __func__);
+               goto fail;
+       }
+
+       if ((kv[1].key = cbor_build_string("sig")) == NULL ||
+           (kv[1].value = fido_blob_encode(sig)) == NULL ||
+           !cbor_map_add(item, kv[1])) {
+               fido_log_debug("%s: sig", __func__);
+               goto fail;
+       }
+
+       if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
+           (kv[2].value = cbor_new_definite_array(1)) == NULL ||
+           (x5c_cbor = fido_blob_encode(x5c)) == NULL ||
+           !cbor_array_push(kv[2].value, x5c_cbor) ||
+           !cbor_map_add(item, kv[2])) {
+               fido_log_debug("%s: x5c", __func__);
+               goto fail;
+       }
+
+       if ((out->len = cbor_serialize_alloc(item, &out->ptr,
+           &alloc_len)) == 0) {
+               fido_log_debug("%s: cbor_serialize_alloc", __func__);
+               goto fail;
+       }
+
+       ok = 0;
+fail:
+       if (item != NULL)
+               cbor_decref(&item);
+       if (x5c_cbor != NULL)
+               cbor_decref(&x5c_cbor);
+
+       for (size_t i = 0; i < nitems(kv); i++) {
+               if (kv[i].key)
+                       cbor_decref(&kv[i].key);
+               if (kv[i].value)
+                       cbor_decref(&kv[i].value);
+       }
+
+       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)
@@ -476,6 +562,7 @@ 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;
+       fido_blob_t      stmt;
        uint8_t          dummy;
        uint8_t          pubkey[65];
        uint8_t          kh_len = 0;
@@ -485,6 +572,7 @@ parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
        memset(&x5c, 0, sizeof(x5c));
        memset(&sig, 0, sizeof(sig));
        memset(&ad, 0, sizeof(ad));
+       memset(&stmt, 0, sizeof(stmt));
        r = FIDO_ERR_RX;
 
        /* status word */
@@ -518,6 +606,12 @@ parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
                goto fail;
        }
 
+       /* attstmt */
+       if (encode_cred_attstmt(COSE_ES256, &x5c, &sig, &stmt) < 0) {
+               fido_log_debug("%s: encode_cred_attstmt", __func__);
+               goto fail;
+       }
+
        /* authdata */
        if (encode_cred_authdata(cred->rp.id, kh, kh_len, pubkey,
            sizeof(pubkey), &ad) < 0) {
@@ -527,8 +621,7 @@ parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
 
        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) {
+           fido_cred_set_attstmt(cred, stmt.ptr, stmt.len) != FIDO_OK) {
                fido_log_debug("%s: fido_cred_set", __func__);
                r = FIDO_ERR_INTERNAL;
                goto fail;
@@ -540,12 +633,13 @@ fail:
        fido_blob_reset(&x5c);
        fido_blob_reset(&sig);
        fido_blob_reset(&ad);
+       fido_blob_reset(&stmt);
 
        return (r);
 }
 
 int
-u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
+u2f_register(fido_dev_t *dev, fido_cred_t *cred, int *ms)
 {
        iso7816_apdu_t  *apdu = NULL;
        unsigned char    rp_id_hash[SHA256_DIGEST_LENGTH];
@@ -554,10 +648,6 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
        int              found;
        int              r;
 
-#ifdef FIDO_FUZZ
-       ms = 0; /* XXX */
-#endif
-
        if (cred->rk == FIDO_OPT_TRUE || cred->uv == FIDO_OPT_TRUE) {
                fido_log_debug("%s: rk=%d, uv=%d", __func__, cred->rk,
                    cred->uv);
@@ -606,7 +696,7 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
 
        do {
                if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
-                   iso7816_len(apdu)) < 0) {
+                   iso7816_len(apdu), ms) < 0) {
                        fido_log_debug("%s: fido_tx", __func__);
                        r = FIDO_ERR_TX;
                        goto fail;
@@ -617,8 +707,8 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
                        r = FIDO_ERR_RX;
                        goto fail;
                }
-               if (usleep((unsigned)(ms == -1 ? 100 : ms) * 1000) < 0) {
-                       fido_log_debug("%s: usleep", __func__);
+               if (delay_ms(U2F_PACE_MS, ms) != 0) {
+                       fido_log_debug("%s: delay_ms", __func__);
                        r = FIDO_ERR_RX;
                        goto fail;
                }
@@ -637,7 +727,7 @@ fail:
 
 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_assert_t *fa, size_t idx, int *ms)
 {
        fido_blob_t     sig;
        fido_blob_t     ad;
@@ -692,7 +782,7 @@ fail:
 }
 
 int
-u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
+u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int *ms)
 {
        size_t  nfound = 0;
        size_t  nauth_ok = 0;
@@ -739,7 +829,7 @@ u2f_authenticate(fido_dev_t *dev, fido_assert_t *fa, int ms)
 }
 
 int
-u2f_get_touch_begin(fido_dev_t *dev)
+u2f_get_touch_begin(fido_dev_t *dev, int *ms)
 {
        iso7816_apdu_t  *apdu = NULL;
        const char      *clientdata = FIDO_DUMMY_CLIENTDATA;
@@ -769,12 +859,12 @@ u2f_get_touch_begin(fido_dev_t *dev)
        }
 
        if (dev->attr.flags & FIDO_CAP_WINK) {
-               fido_tx(dev, CTAP_CMD_WINK, NULL, 0);
-               fido_rx(dev, CTAP_CMD_WINK, &reply, sizeof(reply), 200);
+               fido_tx(dev, CTAP_CMD_WINK, NULL, 0, ms);
+               fido_rx(dev, CTAP_CMD_WINK, &reply, sizeof(reply), ms);
        }
 
        if (fido_tx(dev, CTAP_CMD_MSG, iso7816_ptr(apdu),
-           iso7816_len(apdu)) < 0) {
+           iso7816_len(apdu), ms) < 0) {
                fido_log_debug("%s: fido_tx", __func__);
                r = FIDO_ERR_TX;
                goto fail;
@@ -788,7 +878,7 @@ fail:
 }
 
 int
-u2f_get_touch_status(fido_dev_t *dev, int *touched, int ms)
+u2f_get_touch_status(fido_dev_t *dev, int *touched, int *ms)
 {
        unsigned char   reply[FIDO_MAXMSG];
        int             reply_len;
@@ -802,7 +892,7 @@ u2f_get_touch_status(fido_dev_t *dev, int *touched, int ms)
 
        switch ((reply[reply_len - 2] << 8) | reply[reply_len - 1]) {
        case SW_CONDITIONS_NOT_SATISFIED:
-               if ((r = u2f_get_touch_begin(dev)) != FIDO_OK) {
+               if ((r = u2f_get_touch_begin(dev, ms)) != FIDO_OK) {
                        fido_log_debug("%s: u2f_get_touch_begin", __func__);
                        return (r);
                }
diff --git a/lib/libfido2/src/util.c b/lib/libfido2/src/util.c
new file mode 100644 (file)
index 0000000..61e120c
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2022 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 <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "fido.h"
+
+int
+fido_to_uint64(const char *str, int base, uint64_t *out)
+{
+       char *ep;
+       unsigned long long ull;
+
+       errno = 0;
+       ull = strtoull(str, &ep, base);
+       if (str == ep || *ep != '\0')
+               return -1;
+       else if (ull == ULLONG_MAX && errno == ERANGE)
+               return -1;
+       else if (ull > UINT64_MAX)
+               return -1;
+       *out = (uint64_t)ull;
+
+       return 0;
+}