-# $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
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
--- /dev/null
+* 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.
-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:
.\" 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
.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
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
-.\" 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
.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"
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
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
.\" 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
.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
-.\" 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
.Nm fido_assert_id_len ,
.Nm fido_assert_sigcount ,
.Nm fido_assert_flags
-.Nd FIDO 2 assertion API
+.Nd FIDO2 assertion API
.Sh SYNOPSIS
.In fido.h
.Ft fido_assert_t *
.Ft uint8_t
.Fn fido_assert_flags "const fido_assert_t *assert" "size_t idx"
.Sh DESCRIPTION
-FIDO 2 assertions are abstracted in
-.Em libfido2
-by the
+A FIDO2 assertion is a collection of statements, each statement a
+map between a challenge, a credential, a signature, and ancillary
+attributes.
+In
+.Em libfido2 ,
+a FIDO2 assertion is abstracted by the
.Vt fido_assert_t
type.
The functions described in this page allow a
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
(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
-.\" 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
.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
} 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
.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
.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
.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 ,
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
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
.\" 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
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:
.\" 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
.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
.\" 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
.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
.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
.\" 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
.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
.\" 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
.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
.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
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2022 Yubico AB. All rights reserved.
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
.Dt FIDO_CBOR_INFO_NEW 3
.Os
.Sh NAME
.Nm fido_cbor_info_versions_len ,
.Nm fido_cbor_info_options_len ,
.Nm fido_cbor_info_maxmsgsiz ,
+.Nm fido_cbor_info_maxcredbloblen ,
.Nm fido_cbor_info_maxcredcntlst ,
.Nm fido_cbor_info_maxcredidlen ,
+.Nm fido_cbor_info_maxlargeblob ,
.Nm fido_cbor_info_fwversion
-.Nd FIDO 2 CBOR Info API
+.Nd FIDO2 CBOR Info API
.Sh SYNOPSIS
.In fido.h
.Ft fido_cbor_info_t *
.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
.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 .
.\" 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
.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
-.\" 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 ,
.Nm fido_cred_sig_ptr ,
.Nm fido_cred_user_id_ptr ,
.Nm fido_cred_x5c_ptr ,
+.Nm fido_cred_attstmt_ptr ,
.Nm fido_cred_authdata_len ,
.Nm fido_cred_authdata_raw_len ,
.Nm fido_cred_clientdata_hash_len ,
.Nm fido_cred_sig_len ,
.Nm fido_cred_user_id_len ,
.Nm fido_cred_x5c_len ,
+.Nm fido_cred_attstmt_len ,
.Nm fido_cred_type ,
.Nm fido_cred_flags ,
.Nm fido_cred_sigcount
-.Nd FIDO 2 credential API
+.Nd FIDO2 credential API
.Sh SYNOPSIS
.In fido.h
.Ft fido_cred_t *
.Fn fido_cred_new "void"
.Ft void
.Fn fido_cred_free "fido_cred_t **cred_p"
+.Ft size_t
+.Fn fido_cred_pin_minlen "const fido_cred_t *cred"
.Ft int
-.Fn fido_cred_prot "fido_cred_t *cred"
+.Fn fido_cred_prot "const fido_cred_t *cred"
.Ft const char *
.Fn fido_cred_fmt "const fido_cred_t *cred"
.Ft const char *
.Fn fido_cred_user_id_ptr "const fido_cred_t *cred"
.Ft const unsigned char *
.Fn fido_cred_x5c_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
+.Fn fido_cred_attstmt_ptr "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_authdata_len "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_user_id_len "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_x5c_len "const fido_cred_t *cred"
+.Ft size_t
+.Fn fido_cred_attstmt_len "const fido_cred_t *cred"
.Ft int
.Fn fido_cred_type "const fido_cred_t *cred"
.Ft uint8_t
.Ft uint32_t
.Fn fido_cred_sigcount "const fido_cred_t *cred"
.Sh DESCRIPTION
-FIDO 2 credentials are abstracted in
+FIDO2 credentials are abstracted in
.Em libfido2
by the
.Vt fido_cred_t
.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
.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
.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
.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 ,
-.\" Copyright (c) 2018 Yubico AB. All rights reserved.
+.\" Copyright (c) 2018-2021 Yubico AB. All rights reserved.
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
.Dt FIDO_CRED_SET_AUTHDATA 3
.Os
.Sh NAME
.Nm fido_cred_set_authdata ,
.Nm fido_cred_set_authdata_raw ,
+.Nm fido_cred_set_attstmt ,
.Nm fido_cred_set_x509 ,
.Nm fido_cred_set_sig ,
.Nm fido_cred_set_id ,
.Nm fido_cred_set_user ,
.Nm fido_cred_set_extensions ,
.Nm fido_cred_set_blob ,
+.Nm fido_cred_set_pin_minlen ,
.Nm fido_cred_set_prot ,
.Nm fido_cred_set_rk ,
.Nm fido_cred_set_uv ,
.Nm fido_cred_set_fmt ,
.Nm fido_cred_set_type
-.Nd set parameters of a FIDO 2 credential
+.Nd set parameters of a FIDO2 credential
.Sh SYNOPSIS
.In fido.h
.Bd -literal
.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"
.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"
.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 ,
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
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
.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.
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 .
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
-.\" 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.
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.
.\" 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
.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
.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.
.Sh CAVEATS
Resident credentials are called
.Dq discoverable credentials
-in FIDO 2.1.
+in CTAP 2.1.
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: March 29 2022 $
+.Dd $Mdocdate: August 29 2022 $
.Dt FIDO_DEV_ENABLE_ENTATTEST 3
.Os
.Sh NAME
.Nm fido_dev_enable_entattest ,
.Nm fido_dev_toggle_always_uv ,
.Nm fido_dev_force_pin_change ,
-.Nm fido_dev_set_pin_minlen
-.Nd FIDO 2.1 configuration authenticator API
+.Nm fido_dev_set_pin_minlen ,
+.Nm fido_dev_set_pin_minlen_rpid
+.Nd CTAP 2.1 configuration authenticator API
.Sh SYNOPSIS
.In fido.h
.In fido/config.h
.Fn fido_dev_force_pin_change "fido_dev_t *dev" "const char *pin"
.Ft int
.Fn fido_dev_set_pin_minlen "fido_dev_t *dev" "size_t len" "const char *pin"
+.Ft int
+.Fn fido_dev_set_pin_minlen_rpid "fido_dev_t *dev" "const char * const *rpid" "size_t n" "const char *pin"
.Sh DESCRIPTION
The functions described in this page allow configuration of a
-FIDO 2.1 authenticator.
+CTAP 2.1 authenticator.
.Pp
The
.Fn fido_dev_enable_entattest
.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
.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
.\" 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 :
.\" 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
.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
.\" 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
.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
.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
.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 ,
.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
.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 ,
.\" 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
.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
.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.
.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
.\" 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
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: October 26 2021 $
+.Dd $Mdocdate: August 29 2022 $
.Dt FIDO_DEV_OPEN 3
.Os
.Sh NAME
.Nm fido_dev_open ,
+.Nm fido_dev_open_with_info ,
.Nm fido_dev_close ,
.Nm fido_dev_cancel ,
.Nm fido_dev_new ,
+.Nm fido_dev_new_with_info ,
.Nm fido_dev_free ,
.Nm fido_dev_force_fido2 ,
.Nm fido_dev_force_u2f ,
.Nm fido_dev_is_winhello ,
.Nm fido_dev_supports_credman ,
.Nm fido_dev_supports_cred_prot ,
+.Nm fido_dev_supports_permissions ,
.Nm fido_dev_supports_pin ,
-.Nm fido_dev_has_pin ,
.Nm fido_dev_supports_uv ,
+.Nm fido_dev_has_pin ,
.Nm fido_dev_has_uv ,
.Nm fido_dev_protocol ,
.Nm fido_dev_build ,
.Nm fido_dev_flags ,
.Nm fido_dev_major ,
.Nm fido_dev_minor
-.Nd FIDO 2 device open/close and related functions
+.Nd FIDO2 device open/close and related functions
.Sh SYNOPSIS
.In fido.h
.Ft int
.Fn fido_dev_open "fido_dev_t *dev" "const char *path"
.Ft int
+.Fn fido_dev_open_with_info "fido_dev_t *dev"
+.Ft int
.Fn fido_dev_close "fido_dev_t *dev"
.Ft int
.Fn fido_dev_cancel "fido_dev_t *dev"
.Ft fido_dev_t *
.Fn fido_dev_new "void"
+.Ft fido_dev_t *
+.Fn fido_dev_new_with_info "const fido_dev_info_t *"
.Ft void
.Fn fido_dev_free "fido_dev_t **dev_p"
.Ft void
.Ft bool
.Fn fido_dev_supports_cred_prot "const fido_dev_t *dev"
.Ft bool
-.Fn fido_dev_supports_pin "const fido_dev_t *dev"
+.Fn fido_dev_supports_permissions "const fido_dev_t *dev"
.Ft bool
-.Fn fido_dev_has_pin "const fido_dev_t *dev"
+.Fn fido_dev_supports_pin "const fido_dev_t *dev"
.Ft bool
.Fn fido_dev_supports_uv "const fido_dev_t *dev"
.Ft bool
+.Fn fido_dev_has_pin "const fido_dev_t *dev"
+.Ft bool
.Fn fido_dev_has_uv "const fido_dev_t *dev"
.Ft uint8_t
.Fn fido_dev_protocol "const fido_dev_t *dev"
.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 .
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 ,
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
.Dv true
if
.Fa dev
-is a FIDO 2 device.
+is a FIDO2 device.
.Pp
The
.Fn fido_dev_is_winhello
.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
.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
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
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
-.\" 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
#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
.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
.\" 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
.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
.\" 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
.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 ,
.\" 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 *
-.\" 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
.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"
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
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
-major=5
+major=6
minor=0
* license that can be found in the LICENSE file.
*/
-#include <openssl/ecdsa.h>
#include <openssl/sha.h>
#include "fido.h"
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;
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;
}
/* 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;
}
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;
}
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);
}
}
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;
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++;
{
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) {
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__);
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,
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;
}
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);
}
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__,
}
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];
/* 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;
}
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));
}
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);
}
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));
}
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;
/* 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;
}
/* 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;
}
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;
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);
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;
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;
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
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;
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;
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;
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)
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;
}
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;
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;
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;
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);
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;
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;
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
}
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;
}
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);
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 *
/*
- * 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.
*/
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)
{
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);
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);
}
uint8_t prot;
fido_blob_t key;
-
key.ptr = secret->ptr;
key.len = secret->len;
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;
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 ||
fido_log_debug("%s: HMAC", __func__);
goto fail;
}
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
}
fail:
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
- if (ctx != NULL)
- HMAC_CTX_free(ctx);
-#endif
+ HMAC_CTX_free(ctx);
return (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));
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;
}
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);
}
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;
{
fido_attstmt_t *attstmt = arg;
char *name = NULL;
- int cose_alg = 0;
int ok = -1;
if (cbor_string_copy(key, &name) < 0) {
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")) {
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;
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) {
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);
}
/*
- * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#define BOUND (1024UL * 1024UL)
+/* zlib inflate (raw + headers) */
static int
-do_compress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz, int decomp)
+rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
{
u_long ilen, olen;
- int r;
+ int z;
memset(out, 0, sizeof(*out));
+
if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
- origsiz > ULONG_MAX || (olen = decomp ? (u_long)origsiz :
- compressBound(ilen)) > BOUND)
+ origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) {
+ fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
+ in->len, origsiz);
return FIDO_ERR_INVALID_ARGUMENT;
+ }
+
if ((out->ptr = calloc(1, olen)) == NULL)
return FIDO_ERR_INTERNAL;
out->len = olen;
- if (decomp)
- r = uncompress(out->ptr, &olen, in->ptr, ilen);
- else
- r = compress(out->ptr, &olen, in->ptr, ilen);
- if (r != Z_OK || olen > SIZE_MAX || olen > out->len) {
+
+ if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK ||
+ olen > SIZE_MAX || olen != out->len) {
+ fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu",
+ __func__, z, olen, out->len);
fido_blob_reset(out);
return FIDO_ERR_COMPRESS;
}
- out->len = olen;
return FIDO_OK;
}
+/* raw inflate */
+static int
+rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
+{
+ z_stream zs;
+ u_int ilen, olen;
+ int r, z;
+
+ memset(&zs, 0, sizeof(zs));
+ memset(out, 0, sizeof(*out));
+
+ if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND ||
+ origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) {
+ fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
+ in->len, origsiz);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) {
+ fido_log_debug("%s: inflateInit2: %d", __func__, z);
+ return FIDO_ERR_COMPRESS;
+ }
+
+ if ((out->ptr = calloc(1, olen)) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ out->len = olen;
+ zs.next_in = in->ptr;
+ zs.avail_in = ilen;
+ zs.next_out = out->ptr;
+ zs.avail_out = olen;
+
+ if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
+ fido_log_debug("%s: inflate: %d", __func__, z);
+ r = FIDO_ERR_COMPRESS;
+ goto fail;
+ }
+ if (zs.avail_out != 0) {
+ fido_log_debug("%s: %u != 0", __func__, zs.avail_out);
+ r = FIDO_ERR_COMPRESS;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if ((z = inflateEnd(&zs)) != Z_OK) {
+ fido_log_debug("%s: inflateEnd: %d", __func__, z);
+ r = FIDO_ERR_COMPRESS;
+ }
+ if (r != FIDO_OK)
+ fido_blob_reset(out);
+
+ return r;
+}
+
+/* raw deflate */
+static int
+rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in)
+{
+ z_stream zs;
+ u_int ilen, olen;
+ int r, z;
+
+ memset(&zs, 0, sizeof(zs));
+ memset(out, 0, sizeof(*out));
+
+ if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) {
+ fido_log_debug("%s: in->len=%zu", __func__, in->len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
+ fido_log_debug("%s: deflateInit2: %d", __func__, z);
+ return FIDO_ERR_COMPRESS;
+ }
+
+ olen = BOUND;
+ if ((out->ptr = calloc(1, olen)) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ out->len = olen;
+ zs.next_in = in->ptr;
+ zs.avail_in = ilen;
+ zs.next_out = out->ptr;
+ zs.avail_out = olen;
+
+ if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) {
+ fido_log_debug("%s: inflate: %d", __func__, z);
+ r = FIDO_ERR_COMPRESS;
+ goto fail;
+ }
+ if (zs.avail_out >= out->len) {
+ fido_log_debug("%s: %u > %zu", __func__, zs.avail_out,
+ out->len);
+ r = FIDO_ERR_COMPRESS;
+ goto fail;
+ }
+ out->len -= zs.avail_out;
+
+ r = FIDO_OK;
+fail:
+ if ((z = deflateEnd(&zs)) != Z_OK) {
+ fido_log_debug("%s: deflateEnd: %d", __func__, z);
+ r = FIDO_ERR_COMPRESS;
+ }
+ if (r != FIDO_OK)
+ fido_blob_reset(out);
+
+ return r;
+}
+
int
fido_compress(fido_blob_t *out, const fido_blob_t *in)
{
- return do_compress(out, in, 0, 0);
+ return rfc1951_deflate(out, in);
}
int
fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
{
- return do_compress(out, in, origsiz, 1);
+ if (rfc1950_inflate(out, in, origsiz) == FIDO_OK)
+ return FIDO_OK; /* backwards compat with libfido2 < 1.11 */
+ return rfc1951_inflate(out, in, origsiz);
}
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;
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;
}
/* 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;
}
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);
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));
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;
}
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;
}
}
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);
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;
}
/*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include "fido.h"
#include "fido/es256.h"
+#ifndef FIDO_MAXMSG_CRED
+#define FIDO_MAXMSG_CRED 4096
+#endif
+
static int
parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
}
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;
/* 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;
}
/* 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;
}
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);
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
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);
}
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;
}
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:
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)
{
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);
}
fido_cred_clean_authdata(cred);
return (r);
-
}
int
fido_cred_clean_authdata(cred);
return (r);
-
}
int
return (FIDO_OK);
}
+int
+fido_cred_set_attstmt(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor;
+ int r = FIDO_ERR_INVALID_ARGUMENT;
+
+ fido_cred_clean_attstmt(&cred->attstmt);
+
+ if (ptr == NULL || len == 0)
+ goto fail;
+
+ if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
+ fido_log_debug("%s: cbor_load", __func__);
+ goto fail;
+ }
+
+ if (cbor_decode_attstmt(item, &cred->attstmt) < 0) {
+ fido_log_debug("%s: cbor_decode_attstmt", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ if (r != FIDO_OK)
+ fido_cred_clean_attstmt(&cred->attstmt);
+
+ return (r);
+}
+
int
fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
{
return (FIDO_OK);
}
+int
+fido_cred_set_pin_minlen(fido_cred_t *cred, size_t len)
+{
+ if (len == 0)
+ cred->ext.mask &= ~FIDO_EXT_MINPINLEN;
+ else
+ cred->ext.mask |= FIDO_EXT_MINPINLEN;
+
+ cred->ext.minpinlen = len;
+
+ return (FIDO_OK);
+}
+
int
fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len)
{
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)
return (cred->authdata_raw.len);
}
+const unsigned char *
+fido_cred_attstmt_ptr(const fido_cred_t *cred)
+{
+ return (cred->attstmt.cbor.ptr);
+}
+
+size_t
+fido_cred_attstmt_len(const fido_cred_t *cred)
+{
+ return (cred->attstmt.cbor.len);
+}
+
const unsigned char *
fido_cred_pubkey_ptr(const fido_cred_t *cred)
{
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)
{
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;
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;
}
/* 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;
}
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;
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);
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
}
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;
}
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;
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];
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++;
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;
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;
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
}
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;
}
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;
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++;
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);
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 *
/*
- * 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)
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;
}
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;
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;
}
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;
}
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;
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
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)
{
return (FIDO_OK);
}
+void *
+fido_dev_io_handle(const fido_dev_t *dev)
+{
+
+ return (dev->io_handle);
+}
+
void
fido_init(int flags)
{
return (NULL);
dev->cid = CTAP_CID_BROADCAST;
+ dev->timeout_ms = -1;
dev->io = (fido_dev_io_t) {
&fido_hid_open,
&fido_hid_close,
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__);
{
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);
+}
#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)
{
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;
}
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)
}
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 */
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;
/*
- * 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.
*/
#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)
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)
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)
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);
}
{
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);
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);
+}
/*
- * 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)
{
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);
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);
}
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;
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)
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;
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;
(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);
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)
{
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)
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;
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__);
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);
+}
_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
_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
_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
_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
_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
_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
_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
/*
- * 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.
*/
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);
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);
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);
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
#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 *);
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
#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" */
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 **);
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 *,
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);
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 *);
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 *);
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 *);
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 *);
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);
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,
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" */
#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 */
/*
- * 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.
*/
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
/*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#define FIDO_CAP_NMSG 0x08 /* if set, device doesn't support CTAP_CMD_MSG */
/* Supported COSE algorithms. */
+#define COSE_UNSPEC 0
#define COSE_ES256 -7
#define COSE_EDDSA -8
#define COSE_ECDH_ES256 -25
#define COSE_RS256 -257
+#define COSE_RS1 -65535
/* Supported COSE types. */
#define COSE_KTY_OKP 1
#define FIDO_EXT_CRED_PROTECT 0x02
#define FIDO_EXT_LARGEBLOB_KEY 0x04
#define FIDO_EXT_CRED_BLOB 0x08
+#define FIDO_EXT_MINPINLEN 0x10
/* Supported credential protection policies. */
#define FIDO_CRED_PROT_UV_OPTIONAL 0x01
#define FIDO_EXT_ASSERT_MASK (FIDO_EXT_HMAC_SECRET|FIDO_EXT_LARGEBLOB_KEY| \
FIDO_EXT_CRED_BLOB)
#define FIDO_EXT_CRED_MASK (FIDO_EXT_HMAC_SECRET|FIDO_EXT_CRED_PROTECT| \
- FIDO_EXT_LARGEBLOB_KEY|FIDO_EXT_CRED_BLOB)
+ FIDO_EXT_LARGEBLOB_KEY|FIDO_EXT_CRED_BLOB| \
+ FIDO_EXT_MINPINLEN)
#endif /* _FIDO_INTERNAL */
#endif /* !_FIDO_PARAM_H */
/*
- * 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.
*/
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);
/*
- * 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.
*/
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
} 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 {
} 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 {
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 {
} 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 {
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
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)
{
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);
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)
{
pfd.fd = fd;
#ifdef FIDO_FUZZ
- if (ms < 0)
- return (0);
+ return (0);
#endif
if (ms > -1) {
ts.tv_sec = ms / 1000;
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)
{
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);
}
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 */
}
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);
}
}
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);
}
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;
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);
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));
}
/*
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
return (ci->maxcredidlen);
}
+uint64_t
+fido_cbor_info_maxlargeblob(const fido_cbor_info_t *ci)
+{
+ return (ci->maxlargeblob);
+}
+
uint64_t
fido_cbor_info_fwversion(const fido_cbor_info_t *ci)
{
#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];
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];
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];
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);
}
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);
}
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)
}
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;
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);
}
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;
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));
}
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);
}
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];
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;
}
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;
}
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;
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;
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];
}
}
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;
}
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;
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;
}
}
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;
}
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;
}
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;
}
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;
}
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;
}
}
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;
}
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;
}
{
cbor_item_t *item = NULL;
fido_blob_t key, body;
+ int ms = dev->timeout_ms;
int r;
memset(&key, 0, sizeof(key));
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;
}
{
cbor_item_t *item = NULL;
fido_blob_t key, body;
+ int ms = dev->timeout_ms;
int r;
memset(&key, 0, sizeof(key));
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)
size_t key_len, const char *pin)
{
fido_blob_t key;
+ int ms = dev->timeout_ms;
int r;
memset(&key, 0, sizeof(key));
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);
{
cbor_item_t *item = NULL;
fido_blob_t cbor;
+ int ms = dev->timeout_ms;
int r;
memset(&cbor, 0, sizeof(cbor));
}
*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;
}
{
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) {
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);
--- /dev/null
+/*
+ * 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 */
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;
}
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;
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;
}
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;
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];
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);
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;
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 (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;
}
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;
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;
}
}
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;
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);
}
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
}
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];
}
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;
}
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;
}
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);
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;
}
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);
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;
}
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;
}
#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);
}
}
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);
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));
}
--- /dev/null
+/*
+ * 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);
+}
/*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2022 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#include "fido.h"
#include "fido/rs256.h"
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-static int
-RSA_bits(const RSA *r)
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#define get0_RSA(x) EVP_PKEY_get0_RSA((x))
+#else
+#define get0_RSA(x) EVP_PKEY_get0((x))
+#endif
+
+#if defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x3050200fL
+static EVP_MD *
+rs256_get_EVP_MD(void)
{
- return (BN_num_bits(r->n));
+ const EVP_MD *from;
+ EVP_MD *to = NULL;
+
+ if ((from = EVP_sha256()) != NULL && (to = malloc(sizeof(*to))) != NULL)
+ memcpy(to, from, sizeof(*to));
+
+ return (to);
}
-static int
-RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
+static void
+rs256_free_EVP_MD(EVP_MD *md)
+{
+ freezero(md, sizeof(*md));
+}
+#elif OPENSSL_VERSION_NUMBER >= 0x30000000
+static EVP_MD *
+rs256_get_EVP_MD(void)
{
- r->n = n;
- r->e = e;
- r->d = d;
+ return (EVP_MD_fetch(NULL, "SHA2-256", NULL));
+}
- return (1);
+static void
+rs256_free_EVP_MD(EVP_MD *md)
+{
+ EVP_MD_free(md);
+}
+#else
+static EVP_MD *
+rs256_get_EVP_MD(void)
+{
+ const EVP_MD *md;
+
+ if ((md = EVP_sha256()) == NULL)
+ return (NULL);
+
+ return (EVP_MD_meth_dup(md));
}
static void
-RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
+rs256_free_EVP_MD(EVP_MD *md)
{
- *n = r->n;
- *e = r->e;
- *d = r->d;
+ EVP_MD_meth_free(md);
}
-#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+#endif /* LIBRESSL_VERSION_NUMBER */
static int
decode_bignum(const cbor_item_t *item, void *ptr, size_t len)
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);
}
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__);
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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
+++ /dev/null
-/*
- * 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 */
/*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
+#include <errno.h>
#include "fido.h"
#include "fido/es256.h"
+#define U2F_PACE_MS (100)
+
#if defined(_MSC_VER)
static int
usleep(unsigned int usec)
}
#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)
{
/* 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];
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));
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;
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;
}
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];
}
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;
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];
int r;
#ifdef FIDO_FUZZ
- ms = 0; /* XXX */
+ *ms = 0; /* XXX */
#endif
if (cdh->len != SHA256_DIGEST_LENGTH || key_id->len > UINT8_MAX ||
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;
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;
}
return (ok);
}
+static int
+encode_cred_attstmt(int cose_alg, const fido_blob_t *x5c,
+ const fido_blob_t *sig, fido_blob_t *out)
+{
+ cbor_item_t *item = NULL;
+ cbor_item_t *x5c_cbor = NULL;
+ const uint8_t alg_cbor = (uint8_t)(-cose_alg - 1);
+ struct cbor_pair kv[3];
+ size_t alloc_len;
+ int ok = -1;
+
+ memset(&kv, 0, sizeof(kv));
+ memset(out, 0, sizeof(*out));
+
+ if ((item = cbor_new_definite_map(3)) == NULL) {
+ fido_log_debug("%s: cbor_new_definite_map", __func__);
+ goto fail;
+ }
+
+ if ((kv[0].key = cbor_build_string("alg")) == NULL ||
+ (kv[0].value = cbor_build_negint8(alg_cbor)) == NULL ||
+ !cbor_map_add(item, kv[0])) {
+ fido_log_debug("%s: alg", __func__);
+ goto fail;
+ }
+
+ if ((kv[1].key = cbor_build_string("sig")) == NULL ||
+ (kv[1].value = fido_blob_encode(sig)) == NULL ||
+ !cbor_map_add(item, kv[1])) {
+ fido_log_debug("%s: sig", __func__);
+ goto fail;
+ }
+
+ if ((kv[2].key = cbor_build_string("x5c")) == NULL ||
+ (kv[2].value = cbor_new_definite_array(1)) == NULL ||
+ (x5c_cbor = fido_blob_encode(x5c)) == NULL ||
+ !cbor_array_push(kv[2].value, x5c_cbor) ||
+ !cbor_map_add(item, kv[2])) {
+ fido_log_debug("%s: x5c", __func__);
+ goto fail;
+ }
+
+ if ((out->len = cbor_serialize_alloc(item, &out->ptr,
+ &alloc_len)) == 0) {
+ fido_log_debug("%s: cbor_serialize_alloc", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+ if (x5c_cbor != NULL)
+ cbor_decref(&x5c_cbor);
+
+ for (size_t i = 0; i < nitems(kv); i++) {
+ if (kv[i].key)
+ cbor_decref(&kv[i].key);
+ if (kv[i].value)
+ cbor_decref(&kv[i].value);
+ }
+
+ return (ok);
+}
+
static int
encode_cred_authdata(const char *rp_id, const uint8_t *kh, uint8_t kh_len,
const uint8_t *pubkey, size_t pubkey_len, fido_blob_t *out)
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;
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 */
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) {
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;
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];
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);
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;
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;
}
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;
}
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;
}
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;
}
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;
}
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;
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);
}
--- /dev/null
+/*
+ * 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;
+}