diff options
Diffstat (limited to 'lib/libfido2')
72 files changed, 3400 insertions, 1243 deletions
diff --git a/lib/libfido2/Makefile b/lib/libfido2/Makefile index fc699ff62d4..740e995b584 100644 --- a/lib/libfido2/Makefile +++ b/lib/libfido2/Makefile @@ -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 index 00000000000..f5bd414297d --- /dev/null +++ b/lib/libfido2/NEWS @@ -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. diff --git a/lib/libfido2/README.openbsd b/lib/libfido2/README.openbsd index 90f48bcae32..7b632008281 100644 --- a/lib/libfido2/README.openbsd +++ b/lib/libfido2/README.openbsd @@ -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: diff --git a/lib/libfido2/man/eddsa_pk_new.3 b/lib/libfido2/man/eddsa_pk_new.3 index 09dacc7d750..8a956b29285 100644 --- a/lib/libfido2/man/eddsa_pk_new.3 +++ b/lib/libfido2/man/eddsa_pk_new.3 @@ -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 diff --git a/lib/libfido2/man/es256_pk_new.3 b/lib/libfido2/man/es256_pk_new.3 index 6f46e0d813c..0bfd0344d9c 100644 --- a/lib/libfido2/man/es256_pk_new.3 +++ b/lib/libfido2/man/es256_pk_new.3 @@ -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 diff --git a/lib/libfido2/man/fido_assert_allow_cred.3 b/lib/libfido2/man/fido_assert_allow_cred.3 index e5b8b180a91..8beea2faa85 100644 --- a/lib/libfido2/man/fido_assert_allow_cred.3 +++ b/lib/libfido2/man/fido_assert_allow_cred.3 @@ -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 diff --git a/lib/libfido2/man/fido_assert_new.3 b/lib/libfido2/man/fido_assert_new.3 index d14fd01d68e..32be4ac4efd 100644 --- a/lib/libfido2/man/fido_assert_new.3 +++ b/lib/libfido2/man/fido_assert_new.3 @@ -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 * @@ -85,9 +85,12 @@ .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 diff --git a/lib/libfido2/man/fido_assert_set_authdata.3 b/lib/libfido2/man/fido_assert_set_authdata.3 index b5d070f5f1f..d4ee263439d 100644 --- a/lib/libfido2/man/fido_assert_set_authdata.3 +++ b/lib/libfido2/man/fido_assert_set_authdata.3 @@ -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 diff --git a/lib/libfido2/man/fido_assert_verify.3 b/lib/libfido2/man/fido_assert_verify.3 index 04380b224d6..1e004b9a218 100644 --- a/lib/libfido2/man/fido_assert_verify.3 +++ b/lib/libfido2/man/fido_assert_verify.3 @@ -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: diff --git a/lib/libfido2/man/fido_bio_dev_get_info.3 b/lib/libfido2/man/fido_bio_dev_get_info.3 index df0fb692514..1054ff7fed2 100644 --- a/lib/libfido2/man/fido_bio_dev_get_info.3 +++ b/lib/libfido2/man/fido_bio_dev_get_info.3 @@ -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 diff --git a/lib/libfido2/man/fido_bio_enroll_new.3 b/lib/libfido2/man/fido_bio_enroll_new.3 index 603421abcdf..d5f488905af 100644 --- a/lib/libfido2/man/fido_bio_enroll_new.3 +++ b/lib/libfido2/man/fido_bio_enroll_new.3 @@ -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 diff --git a/lib/libfido2/man/fido_bio_info_new.3 b/lib/libfido2/man/fido_bio_info_new.3 index 5712db298f6..41320365b07 100644 --- a/lib/libfido2/man/fido_bio_info_new.3 +++ b/lib/libfido2/man/fido_bio_info_new.3 @@ -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 diff --git a/lib/libfido2/man/fido_bio_template.3 b/lib/libfido2/man/fido_bio_template.3 index da012b4fb2c..603747458e2 100644 --- a/lib/libfido2/man/fido_bio_template.3 +++ b/lib/libfido2/man/fido_bio_template.3 @@ -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 diff --git a/lib/libfido2/man/fido_cbor_info_new.3 b/lib/libfido2/man/fido_cbor_info_new.3 index 93a4affd426..b70249aea6b 100644 --- a/lib/libfido2/man/fido_cbor_info_new.3 +++ b/lib/libfido2/man/fido_cbor_info_new.3 @@ -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 @@ -26,10 +26,12 @@ .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 . diff --git a/lib/libfido2/man/fido_cred_exclude.3 b/lib/libfido2/man/fido_cred_exclude.3 index d27c0aabc8b..f42411cf9cf 100644 --- a/lib/libfido2/man/fido_cred_exclude.3 +++ b/lib/libfido2/man/fido_cred_exclude.3 @@ -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 diff --git a/lib/libfido2/man/fido_cred_new.3 b/lib/libfido2/man/fido_cred_new.3 index f78f6a6f04c..6e25c25ba0e 100644 --- a/lib/libfido2/man/fido_cred_new.3 +++ b/lib/libfido2/man/fido_cred_new.3 @@ -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 , @@ -34,18 +36,21 @@ .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 @@ -96,6 +103,8 @@ .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 @@ -103,7 +112,7 @@ .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 , diff --git a/lib/libfido2/man/fido_cred_set_authdata.3 b/lib/libfido2/man/fido_cred_set_authdata.3 index dea4b7dec0a..39359e9a45c 100644 --- a/lib/libfido2/man/fido_cred_set_authdata.3 +++ b/lib/libfido2/man/fido_cred_set_authdata.3 @@ -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 , @@ -17,12 +18,13 @@ .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 diff --git a/lib/libfido2/man/fido_cred_verify.3 b/lib/libfido2/man/fido_cred_verify.3 index 47c14948e94..9db299e64cf 100644 --- a/lib/libfido2/man/fido_cred_verify.3 +++ b/lib/libfido2/man/fido_cred_verify.3 @@ -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. diff --git a/lib/libfido2/man/fido_credman_metadata_new.3 b/lib/libfido2/man/fido_credman_metadata_new.3 index 7fb310909fd..a414b44c8b0 100644 --- a/lib/libfido2/man/fido_credman_metadata_new.3 +++ b/lib/libfido2/man/fido_credman_metadata_new.3 @@ -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. diff --git a/lib/libfido2/man/fido_dev_enable_entattest.3 b/lib/libfido2/man/fido_dev_enable_entattest.3 index c059fa10fd4..65acddfd3ec 100644 --- a/lib/libfido2/man/fido_dev_enable_entattest.3 +++ b/lib/libfido2/man/fido_dev_enable_entattest.3 @@ -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 @@ -22,9 +23,11 @@ .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 diff --git a/lib/libfido2/man/fido_dev_get_assert.3 b/lib/libfido2/man/fido_dev_get_assert.3 index 3ea8157c5db..e0b7eca79d1 100644 --- a/lib/libfido2/man/fido_dev_get_assert.3 +++ b/lib/libfido2/man/fido_dev_get_assert.3 @@ -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 : diff --git a/lib/libfido2/man/fido_dev_get_touch_begin.3 b/lib/libfido2/man/fido_dev_get_touch_begin.3 index 4a9b9908cbd..3329cb27dea 100644 --- a/lib/libfido2/man/fido_dev_get_touch_begin.3 +++ b/lib/libfido2/man/fido_dev_get_touch_begin.3 @@ -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 diff --git a/lib/libfido2/man/fido_dev_info_manifest.3 b/lib/libfido2/man/fido_dev_info_manifest.3 index 7e0389ca621..adfb6eff098 100644 --- a/lib/libfido2/man/fido_dev_info_manifest.3 +++ b/lib/libfido2/man/fido_dev_info_manifest.3 @@ -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 , diff --git a/lib/libfido2/man/fido_dev_largeblob_get.3 b/lib/libfido2/man/fido_dev_largeblob_get.3 index cd9d0cce2bf..9bcfe48efe6 100644 --- a/lib/libfido2/man/fido_dev_largeblob_get.3 +++ b/lib/libfido2/man/fido_dev_largeblob_get.3 @@ -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 diff --git a/lib/libfido2/man/fido_dev_make_cred.3 b/lib/libfido2/man/fido_dev_make_cred.3 index 158f5ea5a8e..da1d035644c 100644 --- a/lib/libfido2/man/fido_dev_make_cred.3 +++ b/lib/libfido2/man/fido_dev_make_cred.3 @@ -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 diff --git a/lib/libfido2/man/fido_dev_open.3 b/lib/libfido2/man/fido_dev_open.3 index def6a997941..ff6e16fe6f5 100644 --- a/lib/libfido2/man/fido_dev_open.3 +++ b/lib/libfido2/man/fido_dev_open.3 @@ -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 , @@ -17,26 +19,31 @@ .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 @@ -52,12 +59,14 @@ .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 diff --git a/lib/libfido2/man/fido_dev_set_io_functions.3 b/lib/libfido2/man/fido_dev_set_io_functions.3 index 6775b73c1da..cf0680b5024 100644 --- a/lib/libfido2/man/fido_dev_set_io_functions.3 +++ b/lib/libfido2/man/fido_dev_set_io_functions.3 @@ -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 diff --git a/lib/libfido2/man/fido_dev_set_pin.3 b/lib/libfido2/man/fido_dev_set_pin.3 index a269541b2fb..50244fbb247 100644 --- a/lib/libfido2/man/fido_dev_set_pin.3 +++ b/lib/libfido2/man/fido_dev_set_pin.3 @@ -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 diff --git a/lib/libfido2/man/fido_init.3 b/lib/libfido2/man/fido_init.3 index cd95c15d158..a54dcbc6427 100644 --- a/lib/libfido2/man/fido_init.3 +++ b/lib/libfido2/man/fido_init.3 @@ -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 , diff --git a/lib/libfido2/man/fido_strerr.3 b/lib/libfido2/man/fido_strerr.3 index fa1919b3a92..3ab956faa38 100644 --- a/lib/libfido2/man/fido_strerr.3 +++ b/lib/libfido2/man/fido_strerr.3 @@ -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 * diff --git a/lib/libfido2/man/rs256_pk_new.3 b/lib/libfido2/man/rs256_pk_new.3 index a11e012229d..580ee513155 100644 --- a/lib/libfido2/man/rs256_pk_new.3 +++ b/lib/libfido2/man/rs256_pk_new.3 @@ -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 diff --git a/lib/libfido2/shlib_version b/lib/libfido2/shlib_version index 3066b9771e7..9c1551636c5 100644 --- a/lib/libfido2/shlib_version +++ b/lib/libfido2/shlib_version @@ -1,2 +1,2 @@ -major=5 +major=6 minor=0 diff --git a/lib/libfido2/src/assert.c b/lib/libfido2/src/assert.c index b36f8e32466..949af919d25 100644 --- a/lib/libfido2/src/assert.c +++ b/lib/libfido2/src/assert.c @@ -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__, diff --git a/lib/libfido2/src/authkey.c b/lib/libfido2/src/authkey.c index c3474ccafc0..33e0a8d44bd 100644 --- a/lib/libfido2/src/authkey.c +++ b/lib/libfido2/src/authkey.c @@ -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)); } diff --git a/lib/libfido2/src/bio.c b/lib/libfido2/src/bio.c index 06bc32eea7e..8c52de5d76c 100644 --- a/lib/libfido2/src/bio.c +++ b/lib/libfido2/src/bio.c @@ -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 * diff --git a/lib/libfido2/src/cbor.c b/lib/libfido2/src/cbor.c index 5c1b11583e7..8b7edece3d8 100644 --- a/lib/libfido2/src/cbor.c +++ b/lib/libfido2/src/cbor.c @@ -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); } diff --git a/lib/libfido2/src/compress.c b/lib/libfido2/src/compress.c index ee5501b4a4a..074bca87648 100644 --- a/lib/libfido2/src/compress.c +++ b/lib/libfido2/src/compress.c @@ -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. */ @@ -9,41 +9,159 @@ #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); } diff --git a/lib/libfido2/src/config.c b/lib/libfido2/src/config.c index 0dda16163bc..2baaab0fd62 100644 --- a/lib/libfido2/src/config.c +++ b/lib/libfido2/src/config.c @@ -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; } diff --git a/lib/libfido2/src/cred.c b/lib/libfido2/src/cred.c index 5e65b08293b..6da502c8d90 100644 --- a/lib/libfido2/src/cred.c +++ b/lib/libfido2/src/cred.c @@ -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. */ @@ -10,6 +10,10 @@ #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 @@ -641,6 +692,39 @@ fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len) } 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) { fido_blob_t id_blob; @@ -834,6 +918,19 @@ fido_cred_set_prot(fido_cred_t *cred, int prot) } 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) { if (ptr == NULL || len == 0) @@ -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) @@ -956,6 +1053,18 @@ fido_cred_authdata_raw_len(const fido_cred_t *cred) } 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) { const void *ptr; @@ -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) { diff --git a/lib/libfido2/src/credman.c b/lib/libfido2/src/credman.c index e48ca4543b1..8d2649a144f 100644 --- a/lib/libfido2/src/credman.c +++ b/lib/libfido2/src/credman.c @@ -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 * diff --git a/lib/libfido2/src/dev.c b/lib/libfido2/src/dev.c index a003854f89d..635e4171475 100644 --- a/lib/libfido2/src/dev.c +++ b/lib/libfido2/src/dev.c @@ -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,131 +328,37 @@ 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) { if (dev->io_handle != NULL) { @@ -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); +} diff --git a/lib/libfido2/src/ecdh.c b/lib/libfido2/src/ecdh.c index 3ea47ae6457..9c4f2b99e1a 100644 --- a/lib/libfido2/src/ecdh.c +++ b/lib/libfido2/src/ecdh.c @@ -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; diff --git a/lib/libfido2/src/eddsa.c b/lib/libfido2/src/eddsa.c index 89b84c5a6bd..a94ae302391 100644 --- a/lib/libfido2/src/eddsa.c +++ b/lib/libfido2/src/eddsa.c @@ -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); +} diff --git a/lib/libfido2/src/es256.c b/lib/libfido2/src/es256.c index 9cdb48e4832..4d6e86f8a26 100644 --- a/lib/libfido2/src/es256.c +++ b/lib/libfido2/src/es256.c @@ -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); +} diff --git a/lib/libfido2/src/export.llvm b/lib/libfido2/src/export.llvm index 8d3810f92ce..df9a667f747 100644 --- a/lib/libfido2/src/export.llvm +++ b/lib/libfido2/src/export.llvm @@ -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 diff --git a/lib/libfido2/src/extern.h b/lib/libfido2/src/extern.h index 3be33236f2b..84536d58b6f 100644 --- a/lib/libfido2/src/extern.h +++ b/lib/libfido2/src/extern.h @@ -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" */ diff --git a/lib/libfido2/src/fido.h b/lib/libfido2/src/fido.h index ef98130b09e..f1cd026b1d3 100644 --- a/lib/libfido2/src/fido.h +++ b/lib/libfido2/src/fido.h @@ -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, diff --git a/lib/libfido2/src/fido/config.h b/lib/libfido2/src/fido/config.h index 869927df914..d8134a3c7b6 100644 --- a/lib/libfido2/src/fido/config.h +++ b/lib/libfido2/src/fido/config.h @@ -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" */ diff --git a/lib/libfido2/src/fido/eddsa.h b/lib/libfido2/src/fido/eddsa.h index 4a810179b6f..083721cc3d3 100644 --- a/lib/libfido2/src/fido/eddsa.h +++ b/lib/libfido2/src/fido/eddsa.h @@ -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 */ diff --git a/lib/libfido2/src/fido/es256.h b/lib/libfido2/src/fido/es256.h index 80f4db39c7b..683494dadfe 100644 --- a/lib/libfido2/src/fido/es256.h +++ b/lib/libfido2/src/fido/es256.h @@ -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 diff --git a/lib/libfido2/src/fido/param.h b/lib/libfido2/src/fido/param.h index 025bb57dd81..7c6db98cfd5 100644 --- a/lib/libfido2/src/fido/param.h +++ b/lib/libfido2/src/fido/param.h @@ -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. */ @@ -82,10 +82,12 @@ #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 @@ -101,6 +103,7 @@ #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 @@ -111,7 +114,8 @@ #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 */ diff --git a/lib/libfido2/src/fido/rs256.h b/lib/libfido2/src/fido/rs256.h index 2b08d59980c..03981619178 100644 --- a/lib/libfido2/src/fido/rs256.h +++ b/lib/libfido2/src/fido/rs256.h @@ -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); diff --git a/lib/libfido2/src/fido/types.h b/lib/libfido2/src/fido/types.h index 00b6058c7e1..593a6a6b481 100644 --- a/lib/libfido2/src/fido/types.h +++ b/lib/libfido2/src/fido/types.h @@ -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 diff --git a/lib/libfido2/src/hid.c b/lib/libfido2/src/hid.c index a3768ad3cae..926272b6b3e 100644 --- a/lib/libfido2/src/hid.c +++ b/lib/libfido2/src/hid.c @@ -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) { diff --git a/lib/libfido2/src/hid_unix.c b/lib/libfido2/src/hid_unix.c index 4b2aff9d67f..946b2dc3b65 100644 --- a/lib/libfido2/src/hid_unix.c +++ b/lib/libfido2/src/hid_unix.c @@ -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; diff --git a/lib/libfido2/src/info.c b/lib/libfido2/src/info.c index 57bc8de4406..ea26f22297a 100644 --- a/lib/libfido2/src/info.c +++ b/lib/libfido2/src/info.c @@ -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 @@ -511,6 +464,12 @@ fido_cbor_info_maxcredidlen(const fido_cbor_info_t *ci) } 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) { return (ci->fwversion); diff --git a/lib/libfido2/src/io.c b/lib/libfido2/src/io.c index e2594203efb..70f777fb49a 100644 --- a/lib/libfido2/src/io.c +++ b/lib/libfido2/src/io.c @@ -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; diff --git a/lib/libfido2/src/iso7816.c b/lib/libfido2/src/iso7816.c index a11aae3e99d..a4902277c6d 100644 --- a/lib/libfido2/src/iso7816.c +++ b/lib/libfido2/src/iso7816.c @@ -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)); } diff --git a/lib/libfido2/src/iso7816.h b/lib/libfido2/src/iso7816.h index 5f5363a63a5..9bfad1fbab9 100644 --- a/lib/libfido2/src/iso7816.h +++ b/lib/libfido2/src/iso7816.h @@ -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); diff --git a/lib/libfido2/src/largeblob.c b/lib/libfido2/src/largeblob.c index fa453f5de33..c8173170766 100644 --- a/lib/libfido2/src/largeblob.c +++ b/lib/libfido2/src/largeblob.c @@ -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 index 00000000000..8e1221bb505 --- /dev/null +++ b/lib/libfido2/src/nfc.c @@ -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 */ diff --git a/lib/libfido2/src/pin.c b/lib/libfido2/src/pin.c index d3104e0ca6e..30eeb086a6e 100644 --- a/lib/libfido2/src/pin.c +++ b/lib/libfido2/src/pin.c @@ -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; } diff --git a/lib/libfido2/src/reset.c b/lib/libfido2/src/reset.c index 11380cea090..c5fe6dfe7ac 100644 --- a/lib/libfido2/src/reset.c +++ b/lib/libfido2/src/reset.c @@ -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 index 00000000000..134068b1674 --- /dev/null +++ b/lib/libfido2/src/rs1.c @@ -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); +} diff --git a/lib/libfido2/src/rs256.c b/lib/libfido2/src/rs256.c index c6d87a3ea22..95bae167a17 100644 --- a/lib/libfido2/src/rs256.c +++ b/lib/libfido2/src/rs256.c @@ -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. */ @@ -11,31 +11,60 @@ #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 index 00000000000..b82b6187449 --- /dev/null +++ b/lib/libfido2/src/time.c @@ -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 index 00000000000..66b1c3478c0 --- /dev/null +++ b/lib/libfido2/src/touch.c @@ -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 index 00000000000..74244f8cbf0 --- /dev/null +++ b/lib/libfido2/src/tpm.c @@ -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 index 00000000000..54c0ca58286 --- /dev/null +++ b/lib/libfido2/src/types.c @@ -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 index 42ed1b7cc89..00000000000 --- a/lib/libfido2/src/types.h +++ /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 */ diff --git a/lib/libfido2/src/u2f.c b/lib/libfido2/src/u2f.c index c5fbe0cfbb6..6ebfcc7bb84 100644 --- a/lib/libfido2/src/u2f.c +++ b/lib/libfido2/src/u2f.c @@ -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. */ @@ -10,10 +10,13 @@ #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) @@ -25,6 +28,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) { sig->len = *len; /* consume the whole buffer */ @@ -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; } @@ -391,6 +412,71 @@ fail: } 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 index 00000000000..61e120cfd06 --- /dev/null +++ b/lib/libfido2/src/util.c @@ -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; +} |