-Copyright (c) 2018 Yubico AB. All rights reserved.
+Copyright (c) 2018-2021 Yubico AB. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
-# $OpenBSD: Makefile,v 1.6 2020/08/11 08:44:53 djm Exp $
+# $OpenBSD: Makefile,v 1.7 2021/10/26 21:36:23 djm Exp $
.PATH: ${.CURDIR}/man ${.CURDIR}/src
CFLAGS+= -I${.CURDIR}/src -std=c99
-CFLAGS+= -DHAVE_ARC4RANDOM_BUF -D_FIDO_INTERNAL -DHAVE_UNISTD_H
+CFLAGS+= -D_FIDO_INTERNAL -DHAVE_UNISTD_H
WARNINGS= yes
CDIAGFLAGS+= -Wall -Wextra
CDIAGFLAGS+= -Werror
LDADD+= -L${BSDOBJDIR}/lib/libcbor -lcbor
+LDADD+= -L${BSDOBJDIR}/lib/libz -lz
SYMBOL_LIST= Symbols.list
VERSION_SCRIPT= Symbols.map
CLEANFILES= ${SYMBOL_LIST} ${VERSION_SCRIPT}
LIB= fido2
-SRCS= aes256.c assert.c authkey.c bio.c blob.c buf.c cbor.c cred.c credman.c
-SRCS+= dev.c ecdh.c eddsa.c err.c es256.c hid.c hid_openbsd.c info.c io.c
-SRCS+= iso7816.c log.c pin.c reset.c rs256.c u2f.c
+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
-HDRS= fido.h fido/bio.h fido/credman.h fido/eddsa.h fido/err.h
+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
MAN= eddsa_pk_new.3 es256_pk_new.3 fido_assert_allow_cred.3 fido_assert_new.3
MAN+= fido_dev_get_assert.3 fido_dev_info_manifest.3 fido_dev_make_cred.3
MAN+= fido_dev_open.3 fido_dev_set_io_functions.3 fido_dev_set_pin.3
MAN+= fido_init.3 fido_strerr.3 rs256_pk_new.3 fido_dev_get_touch_begin.3
+MAN+= fido_dev_enable_entattest.3 fido_dev_largeblob_get.3
includes:
@for i in $(HDRS); do \
-This is an import of https://github.com/Yubico/libfido2 1.5.0 (20200901)
+This is an import of https://github.com/Yubico/libfido2 1.8.0 (20210722)
Local changes:
The CMake-based build system has been replaced with a fixed Makefile.
src/hid_openbsd.c has been adapted to use the fido(4) device.
+src/random.c has had CSPRNG interfaces other than arc4random_buf(4) removed
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_ASSERT_NEW 3
.Os
.Sh NAME
.Nm fido_assert_user_icon ,
.Nm fido_assert_user_name ,
.Nm fido_assert_authdata_ptr ,
+.Nm fido_assert_blob_ptr ,
.Nm fido_assert_clientdata_hash_ptr ,
.Nm fido_assert_hmac_secret_ptr ,
+.Nm fido_assert_largeblob_key_ptr ,
.Nm fido_assert_user_id_ptr ,
.Nm fido_assert_sig_ptr ,
.Nm fido_assert_id_ptr ,
.Nm fido_assert_authdata_len ,
+.Nm fido_assert_blob_len ,
.Nm fido_assert_clientdata_hash_len ,
.Nm fido_assert_hmac_secret_len ,
+.Nm fido_assert_largeblob_key_len ,
.Nm fido_assert_user_id_len ,
.Nm fido_assert_sig_len ,
.Nm fido_assert_id_len ,
.Ft const unsigned char *
.Fn fido_assert_clientdata_hash_ptr "const fido_assert_t *assert"
.Ft const unsigned char *
+.Fn fido_assert_blob_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
.Fn fido_assert_hmac_secret_ptr "const fido_assert_t *assert" "size_t idx"
.Ft const unsigned char *
+.Fn fido_assert_largeblob_key_ptr "const fido_assert_t *assert" "size_t idx"
+.Ft const unsigned char *
.Fn fido_assert_user_id_ptr "const fido_assert_t *assert" "size_t idx"
.Ft const unsigned char *
.Fn fido_assert_sig_ptr "const fido_assert_t *assert" "size_t idx"
.Ft size_t
.Fn fido_assert_clientdata_hash_len "const fido_assert_t *assert"
.Ft size_t
+.Fn fido_assert_blob_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
.Fn fido_assert_hmac_secret_len "const fido_assert_t *assert" "size_t idx"
.Ft size_t
+.Fn fido_assert_largeblob_key_len "const fido_assert_t *assert" "size_t idx"
+.Ft size_t
.Fn fido_assert_user_id_len "const fido_assert_t *assert" "size_t idx"
.Ft size_t
.Fn fido_assert_sig_len "const fido_assert_t *assert" "size_t idx"
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_sig_ptr ,
and
.Fn fido_assert_id_ptr
-functions return pointers to the user ID, authenticator data,
-hmac-secret, signature, and credential ID attributes of statement
+functions return pointers to the user ID, CBOR-encoded
+authenticator data, cred blob, hmac-secret,
+.Dq largeBlobKey ,
+signature, and credential ID attributes 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
The corresponding length can be obtained by
.Fn fido_assert_clientdata_hash_len .
.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_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_user_id_ptr ,
and
.Fn fido_assert_sig_ptr
.Xr fido_assert_allow_cred 3 ,
.Xr fido_assert_set_authdata 3 ,
.Xr fido_assert_verify 3 ,
-.Xr fido_dev_get_assert 3
+.Xr fido_dev_get_assert 3 ,
+.Xr fido_dev_largeblob_get 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: October 26 2021 $
.Dt FIDO_ASSERT_SET_AUTHDATA 3
.Os
.Sh NAME
.Nm fido_assert_set_authdata ,
.Nm fido_assert_set_authdata_raw ,
+.Nm fido_assert_set_clientdata ,
.Nm fido_assert_set_clientdata_hash ,
.Nm fido_assert_set_count ,
.Nm fido_assert_set_extensions ,
.Nm fido_assert_set_hmac_salt ,
+.Nm fido_assert_set_hmac_secret ,
.Nm fido_assert_set_up ,
.Nm fido_assert_set_uv ,
.Nm fido_assert_set_rp ,
.Ft int
.Fn fido_assert_set_authdata_raw "fido_assert_t *assert" " size_t idx" "const unsigned char *ptr" "size_t len"
.Ft int
+.Fn fido_assert_set_clientdata "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.Ft int
.Fn fido_assert_set_clientdata_hash "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
.Ft int
.Fn fido_assert_set_count "fido_assert_t *assert" "size_t n"
.Ft int
.Fn fido_assert_set_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"
+.Ft int
.Fn fido_assert_set_up "fido_assert_t *assert" "fido_opt_t up"
.Ft int
.Fn fido_assert_set_uv "fido_assert_t *assert" "fido_opt_t uv"
.Fn fido_assert_set_authdata_raw .
.Pp
The
-.Fn fido_assert_set_clientdata_hash
+.Fn fido_assert_set_clientdata_hash ,
+.Fn fido_assert_set_hmac_salt ,
and
-.Fn fido_assert_set_hmac_salt
+.Fn fido_assert_set_hmac_secret
functions set the client data hash and hmac-salt parts of
.Fa assert
to
is made, and no references to the passed pointer are kept.
.Pp
The
+.Fn fido_assert_set_clientdata
+function allows an application to set the client data hash of
+.Fa assert
+by specifying the assertion's unhashed client data.
+This is required by Windows Hello, which calculates the client data
+hash internally.
+For compatibility with Windows Hello, applications should use
+.Fn fido_assert_set_clientdata
+instead of
+.Fn fido_assert_set_clientdata_hash .
+.Pp
+The
.Fn fido_assert_set_rp
function sets the relying party
.Fa id
to the bitmask
.Fa flags .
At the moment, only the
-.Dv FIDO_EXT_HMAC_SECRET
-extension is supported.
+.Dv FIDO_EXT_CRED_BLOB ,
+.Dv FIDO_EXT_HMAC_SECRET ,
+and
+.Dv FIDO_EXT_LARGEBLOB_KEY
+extensions are supported.
If
.Fa flags
is zero, the extensions of
.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: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_BIO_DEV_GET_INFO 3
.Os
.Sh NAME
.Fa template
on
.Fa dev .
-.Pp
+.Sh RETURN VALUES
The error codes returned by
.Fn fido_bio_dev_get_info ,
.Fn fido_bio_dev_enroll_begin ,
.Xr fido_bio_enroll_new 3 ,
.Xr fido_bio_info_new 3 ,
.Xr fido_bio_template 3
-.Sh CAVEATS
-Biometric enrollment is a tentative feature of FIDO 2.1.
-Applications willing to strictly abide by FIDO 2.0 should refrain
-from using biometric enrollment.
-Applications using biometric enrollment should ensure it is
-supported by the authenticator prior to using the API.
-Since FIDO 2.1 hasn't been finalised, there is a chance the
-functionality and associated data structures may change.
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_BIO_TEMPLATE 3
.Os
.Sh NAME
has an
.Fa idx
(index) value of 0.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_bio_template_set_id
+and
+.Fn fido_bio_template_set_name
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
.Sh SEE ALSO
.Xr fido_bio_dev_get_info 3 ,
.Xr fido_bio_enroll_new 3
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_CBOR_INFO_NEW 3
.Os
.Sh NAME
.Nm fido_cbor_info_aaguid_ptr ,
.Nm fido_cbor_info_extensions_ptr ,
.Nm fido_cbor_info_protocols_ptr ,
+.Nm fido_cbor_info_transports_ptr ,
.Nm fido_cbor_info_versions_ptr ,
.Nm fido_cbor_info_options_name_ptr ,
.Nm fido_cbor_info_options_value_ptr ,
+.Nm fido_cbor_info_algorithm_type ,
+.Nm fido_cbor_info_algorithm_cose ,
+.Nm fido_cbor_info_algorithm_count ,
.Nm fido_cbor_info_aaguid_len ,
.Nm fido_cbor_info_extensions_len ,
.Nm fido_cbor_info_protocols_len ,
+.Nm fido_cbor_info_transports_len ,
.Nm fido_cbor_info_versions_len ,
.Nm fido_cbor_info_options_len ,
.Nm fido_cbor_info_maxmsgsiz ,
.Ft const uint8_t *
.Fn fido_cbor_info_protocols_ptr "const fido_cbor_info_t *ci"
.Ft char **
+.Fn fido_cbor_info_transports_ptr "const fido_cbor_info_t *ci"
+.Ft char **
.Fn fido_cbor_info_versions_ptr "const fido_cbor_info_t *ci"
.Ft char **
.Fn fido_cbor_info_options_name_ptr "const fido_cbor_info_t *ci"
.Ft const bool *
.Fn fido_cbor_info_options_value_ptr "const fido_cbor_info_t *ci"
+.Ft const char *
+.Fn fido_cbor_info_algorithm_type "const fido_cbor_info_t *ci" "size_t idx"
+.Ft int
+.Fn fido_cbor_info_algorithm_cose "const fido_cbor_info_t *ci" "size_t idx"
+.Ft size_t
+.Fn fido_cbor_info_algorithm_count "const fido_cbor_info_t *ci"
.Ft size_t
.Fn fido_cbor_info_aaguid_len "const fido_cbor_info_t *ci"
.Ft size_t
.Ft size_t
.Fn fido_cbor_info_protocols_len "const fido_cbor_info_t *ci"
.Ft size_t
+.Fn fido_cbor_info_transports_len "const fido_cbor_info_t *ci"
+.Ft size_t
.Fn fido_cbor_info_versions_len "const fido_cbor_info_t *ci"
.Ft size_t
.Fn fido_cbor_info_options_len "const fido_cbor_info_t *ci"
.Ft uint64_t
.Fn fido_cbor_info_maxmsgsiz "const fido_cbor_info_t *ci"
.Ft uint64_t
+.Fn fido_cbor_info_maxcredbloblen "const fido_cbor_info_t *ci"
+.Ft uint64_t
.Fn fido_cbor_info_maxcredcntlst "const fido_cbor_info_t *ci"
.Ft uint64_t
.Fn fido_cbor_info_maxcredidlen "const fido_cbor_info_t *ci"
.Fn fido_cbor_info_aaguid_ptr ,
.Fn fido_cbor_info_extensions_ptr ,
.Fn fido_cbor_info_protocols_ptr ,
+.Fn fido_cbor_info_transports_ptr ,
and
.Fn fido_cbor_info_versions_ptr
functions return pointers to the authenticator attestation GUID,
-supported extensions, PIN protocol and CTAP version strings of
+supported extensions, PIN protocol, transports, and CTAP version
+strings of
.Fa ci .
The corresponding length of a given attribute can be
obtained by
.Fn fido_cbor_info_aaguid_len ,
.Fn fido_cbor_info_extensions_len ,
.Fn fido_cbor_info_protocols_len ,
+.Fn fido_cbor_info_transports_len ,
or
.Fn fido_cbor_info_versions_len .
.Pp
.Fn fido_cbor_info_options_len .
.Pp
The
+.Fn fido_cbor_info_algorithm_count
+function returns the number of supported algorithms in
+.Fa ci .
+The
+.Fn fido_cbor_info_algorithm_cose
+function returns the COSE identifier of algorithm
+.Fa idx
+in
+.Fa ci ,
+or 0 if the COSE identifier is unknown or unset.
+The
+.Fn fido_cbor_info_algorithm_type
+function returns the type of algorithm
+.Fa idx
+in
+.Fa ci ,
+or NULL if the type is unset.
+Please note that the first algorithm in
+.Fa ci
+has an
+.Fa idx
+(index) value of 0.
+.Pp
+The
.Fn fido_cbor_info_maxmsgsiz
function returns the maximum message size attribute of
.Fa ci .
.Pp
The
+.Fn fido_cbor_info_maxcredbloblen
+function returns the maximum
+.Dq credBlob
+length in bytes supported by the authenticator as reported in
+.Fa ci .
+.Pp
+The
.Fn fido_cbor_info_maxcredcntlst
function returns the maximum supported number of credentials in
a single credential ID list as reported in
.Fn fido_cbor_info_aaguid_ptr ,
.Fn fido_cbor_info_extensions_ptr ,
.Fn fido_cbor_info_protocols_ptr ,
+.Fn fido_cbor_info_transports_ptr ,
.Fn fido_cbor_info_versions_ptr ,
.Fn fido_cbor_info_options_name_ptr ,
and
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_CRED_NEW 3
.Os
.Sh NAME
.Nm fido_cred_user_name ,
.Nm fido_cred_display_name ,
.Nm fido_cred_authdata_ptr ,
+.Nm fido_cred_authdata_raw_ptr ,
.Nm fido_cred_clientdata_hash_ptr ,
.Nm fido_cred_id_ptr ,
.Nm fido_cred_aaguid_ptr ,
+.Nm fido_cred_largeblob_key_ptr ,
.Nm fido_cred_pubkey_ptr ,
.Nm fido_cred_sig_ptr ,
.Nm fido_cred_user_id_ptr ,
.Nm fido_cred_x5c_ptr ,
.Nm fido_cred_authdata_len ,
+.Nm fido_cred_authdata_raw_len ,
.Nm fido_cred_clientdata_hash_len ,
.Nm fido_cred_id_len ,
.Nm fido_cred_aaguid_len ,
+.Nm fido_cred_largeblob_key_len ,
.Nm fido_cred_pubkey_len ,
.Nm fido_cred_sig_len ,
.Nm fido_cred_user_id_len ,
.Nm fido_cred_x5c_len ,
.Nm fido_cred_type ,
-.Nm fido_cred_flags
+.Nm fido_cred_flags ,
+.Nm fido_cred_sigcount
.Nd FIDO 2 credential API
.Sh SYNOPSIS
.In fido.h
.Ft const unsigned char *
.Fn fido_cred_authdata_ptr "const fido_cred_t *cred"
.Ft const unsigned char *
+.Fn fido_cred_authdata_raw_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
.Fn fido_cred_clientdata_hash_ptr "const fido_cred_t *cred"
.Ft const unsigned char *
.Fn fido_cred_id_ptr "const fido_cred_t *cred"
.Ft const unsigned char *
.Fn fido_cred_aaguid_ptr "const fido_cred_t *cred"
.Ft const unsigned char *
+.Fn fido_cred_largeblob_key_ptr "const fido_cred_t *cred"
+.Ft const unsigned char *
.Fn fido_cred_pubkey_ptr "const fido_cred_t *cred"
.Ft const unsigned char *
.Fn fido_cred_sig_ptr "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_authdata_len "const fido_cred_t *cred"
.Ft size_t
+.Fn fido_cred_authdata_raw_len "const fido_cred_t *cred"
+.Ft size_t
.Fn fido_cred_clientdata_hash_len "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_id_len "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_aaguid_len "const fido_cred_t *cred"
.Ft size_t
+.Fn fido_cred_largeblob_key_len "const fido_cred_t *cred"
+.Ft size_t
.Fn fido_cred_pubkey_len "const fido_cred_t *cred"
.Ft size_t
.Fn fido_cred_sig_len "const fido_cred_t *cred"
.Fn fido_cred_type "const fido_cred_t *cred"
.Ft uint8_t
.Fn fido_cred_flags "const fido_cred_t *cred"
+.Ft uint32_t
+.Fn fido_cred_sigcount "const fido_cred_t *cred"
.Sh DESCRIPTION
FIDO 2 credentials are abstracted in
.Em libfido2
.Pp
The
.Fn fido_cred_authdata_ptr ,
+.Fn fido_cred_authdata_raw_ptr ,
.Fn fido_cred_clientdata_hash_ptr ,
.Fn fido_cred_id_ptr ,
.Fn fido_cred_aaguid_ptr ,
+.Fn fido_cred_largeblob_key_ptr ,
.Fn fido_cred_pubkey_ptr ,
.Fn fido_cred_sig_ptr ,
.Fn fido_cred_user_id_ptr ,
and
.Fn fido_cred_x5c_ptr
-functions return pointers to the authenticator data, client data
-hash, ID, authenticator attestation GUID, public key, signature,
-user ID, and x509 certificate parts of
+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
.Fa cred ,
or NULL if the respective entry is not set.
.Pp
The corresponding length can be obtained by
.Fn fido_cred_authdata_len ,
+.Fn fido_cred_authdata_raw_len ,
.Fn fido_cred_clientdata_hash_len ,
.Fn fido_cred_id_len ,
.Fn fido_cred_aaguid_len ,
+.Fn fido_cred_largeblob_key_len ,
.Fn fido_cred_pubkey_len ,
.Fn fido_cred_sig_len ,
.Fn fido_cred_user_id_len ,
.Fn fido_cred_flags
function returns the authenticator data flags of
.Fa cred .
+.Pp
+The
+.Fn fido_cred_sigcount
+function returns the authenticator data signature counter of
+.Fa cred .
.Sh RETURN VALUES
The authenticator data returned by
.Fn fido_cred_authdata_ptr
is a CBOR-encoded byte string, as obtained from the authenticator.
+To obtain the decoded byte string, use
+.Fn fido_cred_authdata_raw_ptr .
.Pp
If not NULL, pointers returned by
.Fn fido_cred_fmt ,
.Fn fido_cred_clientdata_hash_ptr ,
.Fn fido_cred_id_ptr ,
.Fn fido_cred_aaguid_ptr ,
+.Fn fido_cred_largeblob_key_ptr ,
.Fn fido_cred_pubkey_ptr ,
.Fn fido_cred_sig_ptr ,
and
.Xr fido_cred_set_authdata 3 ,
.Xr fido_cred_verify 3 ,
.Xr fido_credman_metadata_new 3 ,
+.Xr fido_dev_largeblob_get 3 ,
.Xr fido_dev_make_cred 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: October 26 2021 $
.Dt FIDO_CRED_SET_AUTHDATA 3
.Os
.Sh NAME
.Nm fido_cred_set_authdata_raw ,
.Nm fido_cred_set_x509 ,
.Nm fido_cred_set_sig ,
+.Nm fido_cred_set_id ,
+.Nm fido_cred_set_clientdata ,
.Nm fido_cred_set_clientdata_hash ,
.Nm fido_cred_set_rp ,
.Nm fido_cred_set_user ,
.Nm fido_cred_set_extensions ,
+.Nm fido_cred_set_blob ,
.Nm fido_cred_set_prot ,
.Nm fido_cred_set_rk ,
.Nm fido_cred_set_uv ,
.Ft int
.Fn fido_cred_set_sig "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
.Ft int
+.Fn fido_cred_set_id "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
+.Fn fido_cred_set_clientdata "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
+.Ft int
.Fn fido_cred_set_clientdata_hash "fido_cred_t *cred" "const unsigned char *ptr" "size_t len"
.Ft int
.Fn fido_cred_set_rp "fido_cred_t *cred" "const char *id" "const char *name"
.Ft int
.Fn fido_cred_set_extensions "fido_cred_t *cred" "int flags"
.Ft int
+.Fn fido_cred_set_blob "fido_cred_t *cred" "const unsigned char *ptr" "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"
.Fn fido_cred_set_authdata ,
.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 and client data hash parts of
+signature, id, and client data hash parts of
.Fa cred
to
.Fa 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
+.Fn fido_cred_set_id .
+The latter is meant to be used in contexts where the
+credential's authenticator data is not available.
+.Pp
+The
+.Fn fido_cred_set_clientdata
+function allows an application to set the client data hash of
+.Fa cred
+by specifying the credential's unhashed client data.
+This is required by Windows Hello, which calculates the client data
+hash internally.
+For compatibility with Windows Hello, applications should use
+.Fn fido_cred_set_clientdata
+instead of
+.Fn fido_cred_set_clientdata_hash .
+.Pp
The
.Fn fido_cred_set_rp
function sets the relying party
to the bitmask
.Fa flags .
At the moment, only the
-.Dv FIDO_EXT_HMAC_SECRET
+.Dv FIDO_EXT_CRED_BLOB ,
+.Dv FIDO_EXT_CRED_PROTECT ,
+.Dv FIDO_EXT_HMAC_SECRET ,
and
-.Dv FIDO_EXT_CRED_PROTECT
+.Dv FIDO_EXT_LARGEBLOB_KEY
extensions are supported.
If
.Fa flags
are cleared.
.Pp
The
+.Fn fido_cred_set_blob
+function sets the
+.Dq credBlob
+to be stored with
+.Fa cred
+to the data pointed to by
+.Fa ptr ,
+which must be
+.Fa len
+bytes long.
+.Pp
+The
.Fn fido_cred_set_prot
function sets the protection of
.Fa cred
.Fn fido_cred_set_uv
functions set the
.Em rk
-(resident key)
+.Pq resident/discoverable key
and
.Em uv
-(user verification)
+.Pq user verification
attributes of
.Fa cred .
Both are
.Pp
The
.Fn fido_cred_set_fmt
-function sets the format of
+function sets the attestation format of
.Fa cred
to
.Fa fmt ,
where
.Fa fmt
-must be either
+must be
.Vt "packed"
-(the format used in FIDO 2)
-or
+.Pq the format used in FIDO2 ,
.Vt "fido-u2f"
-(the format used by U2F).
+.Pq the format used by U2F ,
+or
+.Vt "none" .
A copy of
.Fa fmt
is made, and no references to the passed pointer are kept.
.\" 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: October 26 2021 $
.Dt FIDO_CRED_VERIFY 3
.Os
.Sh NAME
.Nm fido_cred_verify
-.Nd verifies the signature of a FIDO 2 credential
+.Nd verifies the attestation signature of a FIDO 2 credential
.Sh SYNOPSIS
.In fido.h
.Ft int
.Sh DESCRIPTION
The
.Fn fido_cred_verify
-function verifies whether the signature contained in
+function verifies whether the attestation signature contained in
.Fa cred
matches the attributes of the credential.
Before using
The
.Fn fido_cred_verify
function verifies whether the client data hash, relying party ID,
-credential ID, type, and resident key and user verification
+credential ID, type, and resident/discoverable key and user verification
attributes of
.Fa cred
have been attested by the holder of the private counterpart of
.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.
-.\" 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.
.\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_CREDMAN_METADATA_NEW 3
.Os
.Sh NAME
.Nm fido_credman_rp_id_hash_len ,
.Nm fido_credman_get_dev_metadata ,
.Nm fido_credman_get_dev_rk ,
+.Nm fido_credman_set_dev_rk ,
.Nm fido_credman_del_dev_rk ,
.Nm fido_credman_get_dev_rp
.Nd FIDO 2 credential management API
.Ft int
.Fn fido_credman_get_dev_rk "fido_dev_t *dev" "const char *rp_id" "fido_credman_rk_t *rk" "const char *pin"
.Ft int
-.Fn fido_credman_del_dev_rk "fido_dev_t *dev" const unsigned char *cred_id" "size_t cred_id_len" "const char *pin"
+.Fn fido_credman_set_dev_rk "fido_dev_t *dev" "fido_cred_t *cred" "const char *pin"
+.Ft int
+.Fn fido_credman_del_dev_rk "fido_dev_t *dev" "const unsigned char *cred_id" "size_t cred_id_len" "const char *pin"
.Ft int
.Fn fido_credman_get_dev_rp "fido_dev_t *dev" "fido_credman_rp_t *rp" "const char *pin"
.Sh DESCRIPTION
The credential management API of
.Em libfido2
allows resident credentials on a FIDO2 authenticator to be listed,
-inspected, and removed.
+inspected, modified, and removed.
Please note that not all FIDO2 authenticators support credential
management.
To obtain information on what an authenticator supports, please
(index) value of 0.
.Pp
The
+.Fn fido_credman_set_dev_rk
+function updates the credential pointed to by
+.Fa cred
+in
+.Fa dev .
+The credential id and user id attributes of
+.Fa cred
+must be set.
+See
+.Xr fido_cred_set_id 3
+and
+.Xr fido_cred_set_user 3
+for details.
+Only a credential's user attributes (name, display name)
+may be updated at this time.
+.Pp
+The
.Fn fido_credman_del_dev_rk
function deletes the resident credential identified by
.Fa cred_id
The
.Fn fido_credman_get_dev_metadata ,
.Fn fido_credman_get_dev_rk ,
+.Fn fido_credman_set_dev_rk ,
.Fn fido_credman_del_dev_rk ,
and
.Fn fido_credman_get_dev_rp
should have their return values checked for NULL.
.Sh SEE ALSO
.Xr fido_cbor_info_new 3 ,
-.Xr fido_cred_new 3
+.Xr fido_cred_new 3 ,
+.Xr fido_dev_supports_credman 3
.Sh CAVEATS
-Credential management is a tentative feature of FIDO 2.1.
-Applications willing to strictly abide by FIDO 2.0 should refrain
-from using credential management.
-Applications using credential management should ensure it is
-supported by the authenticator prior to using the API.
-Since FIDO 2.1 hasn't been finalised, there is a chance the
-functionality and associated data structures may change.
+Resident credentials are called
+.Dq discoverable credentials
+in FIDO 2.1.
--- /dev/null
+.\" Copyright (c) 2020 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: October 26 2021 $
+.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
+.Sh SYNOPSIS
+.In fido.h
+.In fido/config.h
+.Ft int
+.Fn fido_dev_enable_entattest "fido_dev_t *dev" "const char *pin"
+.Ft int
+.Fn fido_dev_toggle_always_uv "fido_dev_t *dev" "const char *pin"
+.Ft int
+.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"
+.Sh DESCRIPTION
+The functions described in this page allow configuration of a
+FIDO 2.1 authenticator.
+.Pp
+The
+.Fn fido_dev_enable_entattest
+function enables the
+.Em Enterprise Attestation
+feature on
+.Fa dev .
+.Em Enterprise Attestation
+instructs the authenticator to include uniquely identifying
+information in subsequent attestation statements.
+The
+.Fa pin
+parameter may be NULL if
+.Fa dev
+does not have a PIN set.
+.Pp
+The
+.Fn fido_dev_toggle_always_uv
+function toggles the
+.Dq user verification always
+feature on
+.Fa dev .
+When set, this toggle enforces user verification at the
+authenticator level for all known credentials.
+If
+.Fa dev
+supports U2F (CTAP1) and the user verification methods supported by
+the authenticator do not allow protection of U2F credentials, the
+U2F subsystem will be disabled by the authenticator.
+The
+.Fa pin
+parameter may be NULL if
+.Fa dev
+does not have a PIN set.
+.Pp
+The
+.Fn fido_dev_force_pin_change
+instructs
+.Fa dev
+to require a PIN change.
+Subsequent PIN authentication attempts against
+.Fa dev
+will fail until its PIN is changed.
+.Pp
+The
+.Fn fido_dev_set_pin_minlen
+function sets the minimum PIN length of
+.Fa dev
+to
+.Fa len .
+Minimum PIN lengths may only be increased.
+.Pp
+Configuration settings are reflected in the payload returned by the
+authenticator in response to a
+.Xr fido_dev_get_cbor_info 3
+call.
+.Sh RETURN VALUES
+The error codes returned by
+.Fn fido_dev_enable_entattest ,
+.Fn fido_dev_toggle_always_uv ,
+.Fn fido_dev_force_pin_change ,
+and
+.Fn fido_dev_set_pin_minlen
+are defined in
+.In fido/err.h .
+On success,
+.Dv FIDO_OK
+is returned.
+.Sh SEE ALSO
+.Xr fido_dev_get_cbor_info 3 ,
+.Xr fido_dev_reset 3
--- /dev/null
+.\" Copyright (c) 2020 Yubico AB. All rights reserved.
+.\" Use of this source code is governed by a BSD-style
+.\" license that can be found in the LICENSE file.
+.\"
+.Dd $Mdocdate: October 26 2021 $
+.Dt FIDO_LARGEBLOB_GET 3
+.Os
+.Sh NAME
+.Nm fido_dev_largeblob_get ,
+.Nm fido_dev_largeblob_set ,
+.Nm fido_dev_largeblob_remove ,
+.Nm fido_dev_largeblob_get_array ,
+.Nm fido_dev_largeblob_set_array
+.Nd FIDO 2 large blob API
+.Sh SYNOPSIS
+.In fido.h
+.Ft int
+.Fn fido_dev_largeblob_get "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "unsigned char **blob_ptr" "size_t *blob_len"
+.Ft int
+.Fn fido_dev_largeblob_set "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const unsigned char *blob_ptr" "size_t blob_len" "const char *pin"
+.Ft int
+.Fn fido_dev_largeblob_remove "fido_dev_t *dev" "const unsigned char *key_ptr" "size_t key_len" "const char *pin"
+.Ft int
+.Fn fido_dev_largeblob_get_array "fido_dev_t *dev" "unsigned char **cbor_ptr" "size_t *cbor_len"
+.Ft int
+.Fn fido_dev_largeblob_set_array "fido_dev_t *dev" "const unsigned char *cbor_ptr" "size_t cbor_len" "const char *pin"
+.Sh DESCRIPTION
+The
+.Dq largeBlobs
+API of
+.Em libfido2
+allows binary blobs residing on a FIDO 2.1 authenticator to be
+read, written, and inspected.
+.Dq largeBlobs
+is a FIDO 2.1 extension.
+.Pp
+.Dq largeBlobs
+are stored as elements of a CBOR array.
+Confidentiality is ensured by encrypting each element with a
+distinct, credential-bound 256-bit AES-GCM key.
+The array is otherwise shared between different credentials and
+FIDO2 relying parties.
+.Pp
+Retrieval of a credential's encryption key is possible during
+enrollment with
+.Xr fido_cred_set_extensions 3
+and
+.Xr fido_cred_largeblob_key_ptr 3 ,
+during assertion with
+.Xr fido_assert_set_extensions 3
+and
+.Xr fido_assert_largeblob_key_ptr 3 ,
+or, in the case of a resident credential, via
+.Em libfido2's
+credential management API.
+.Pp
+The
+.Dq largeBlobs
+CBOR array is opaque to the authenticator.
+Management of the array is left at the discretion of FIDO2 clients.
+For further details on FIDO 2.1's
+.Dq largeBlobs
+extension, please refer to the FIDO 2.1 spec.
+.Pp
+The
+.Fn fido_dev_largeblob_get
+function retrieves the authenticator's
+.Dq largeBlobs
+CBOR array and, on success, returns the first blob
+.Pq iterating from array index zero
+that can be
+decrypted by
+.Fa key_ptr ,
+where
+.Fa key_ptr
+points to
+.Fa key_len
+bytes.
+On success,
+.Fn fido_dev_largeblob_get
+sets
+.Fa blob_ptr
+to the body of the decrypted blob, and
+.Fa blob_len
+to the length of the decrypted blob in bytes.
+It is the caller's responsibility to free
+.Fa blob_ptr .
+.Pp
+The
+.Fn fido_dev_largeblob_set
+function uses
+.Fa key_ptr
+to encrypt
+.Fa blob_ptr
+and inserts the result in the authenticator's
+.Dq largeBlobs
+CBOR array.
+Insertion happens at the end of the array if no existing element
+can be decrypted by
+.Fa key_ptr ,
+or at the position of the first element
+.Pq iterating from array index zero
+that can be decrypted by
+.Fa key_ptr .
+.Fa key_len
+holds the length of
+.Fa key_ptr
+in bytes, and
+.Fa blob_len
+the length of
+.Fa blob_ptr
+in bytes.
+A
+.Fa pin
+or equivalent user-verification gesture is required.
+.Pp
+The
+.Fn fido_dev_largeblob_remove
+function retrieves the authenticator's
+.Dq largeBlobs
+CBOR array and, on success, drops the first blob
+.Pq iterating from array index zero
+that can be decrypted by
+.Fa key_ptr ,
+where
+.Fa key_ptr
+points to
+.Fa key_len
+bytes.
+A
+.Fa pin
+or equivalent user-verification gesture is required.
+.Pp
+The
+.Fn fido_dev_largeblob_get_array
+function retrieves the authenticator's
+.Dq largeBlobs
+CBOR array and, on success,
+sets
+.Fa cbor_ptr
+to the body of the CBOR array, and
+.Fa cbor_len
+to its corresponding length in bytes.
+It is the caller's responsibility to free
+.Fa cbor_ptr .
+.Pp
+Finally, the
+.Fn fido_dev_largeblob_set_array
+function sets the authenticator's
+.Dq largeBlobs
+CBOR array to the data pointed to by
+.Fa cbor_ptr ,
+where
+.Fa cbor_ptr
+points to
+.Fa cbor_len
+bytes.
+A
+.Fa pin
+or equivalent user-verification gesture is required.
+.Sh RETURN VALUES
+The functions
+.Fn fido_dev_largeblob_set ,
+.Fn fido_dev_largeblob_get ,
+.Fn fido_dev_largeblob_remove ,
+.Fn fido_dev_largeblob_get_array ,
+and
+.Fn fido_dev_largeblob_set_array
+return
+.Dv FIDO_OK
+on success.
+On error, an error code defined in
+.In fido/err.h
+is returned.
+.Sh SEE ALSO
+.Xr fido_assert_largeblob_key_len 3 ,
+.Xr fido_assert_largeblob_key_ptr 3 ,
+.Xr fido_assert_set_extensions 3 ,
+.Xr fido_cred_largeblob_key_len 3 ,
+.Xr fido_cred_largeblob_key_ptr 3 ,
+.Xr fido_cred_set_extensions 3 ,
+.Xr fido_credman_dev_get_rk 3 ,
+.Xr fido_credman_dev_get_rp 3 ,
+.Xr fido_dev_get_assert 3 ,
+.Xr fido_dev_make_cred 3
+.Sh CAVEATS
+The
+.Dq largeBlobs
+extension is not meant to be used to store sensitive data.
+When retrieved, a credential's
+.Dq largeBlobs
+encryption key is transmitted in the clear, and an authenticator's
+.Dq largeBlobs
+CBOR array can be read without user interaction or verification.
.\" 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: October 26 2021 $
.Dt FIDO_DEV_MAKE_CRED 3
.Os
.Sh NAME
.It
.Nm list of excluded credential IDs ;
.It
-.Nm resident key and user verification attributes .
+.Nm resident/discoverable key and user verification attributes .
.El
.Pp
See
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: August 23 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_DEV_OPEN 3
.Os
.Sh NAME
.Nm fido_dev_force_fido2 ,
.Nm fido_dev_force_u2f ,
.Nm fido_dev_is_fido2 ,
+.Nm fido_dev_is_winhello ,
+.Nm fido_dev_supports_credman ,
.Nm fido_dev_supports_cred_prot ,
.Nm fido_dev_supports_pin ,
.Nm fido_dev_has_pin ,
+.Nm fido_dev_supports_uv ,
+.Nm fido_dev_has_uv ,
.Nm fido_dev_protocol ,
.Nm fido_dev_build ,
.Nm fido_dev_flags ,
.Ft bool
.Fn fido_dev_is_fido2 "const fido_dev_t *dev"
.Ft bool
+.Fn fido_dev_is_winhello "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_supports_credman "const fido_dev_t *dev"
+.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"
.Ft bool
.Fn fido_dev_has_pin "const fido_dev_t *dev"
+.Ft bool
+.Fn fido_dev_supports_uv "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"
.Ft uint8_t
.Fa dev
is a freshly allocated or otherwise closed
.Vt fido_dev_t .
+If
+.Fa dev
+claims to be FIDO2,
+.Em libfido2
+will attempt to speak FIDO2 to
+.Fa dev .
+If that fails,
+.Em libfido2
+will fallback to U2F unless the
+.Dv FIDO_DISABLE_U2F_FALLBACK
+flag was set in
+.Xr fido_init 3 .
.Pp
The
.Fn fido_dev_close
is a FIDO 2 device.
.Pp
The
+.Fn fido_dev_is_winhello
+function returns
+.Dv true
+if
+.Fa dev
+is a Windows Hello device.
+.Pp
+The
+.Fn fido_dev_supports_credman
+function returns
+.Dv true
+if
+.Fa dev
+supports FIDO 2.1 Credential Management.
+.Pp
+The
.Fn fido_dev_supports_cred_prot
function returns
.Dv true
has a FIDO 2.0 Client PIN set.
.Pp
The
+.Fn fido_dev_supports_uv
+function returns
+.Dv true
+if
+.Fa dev
+supports a built-in user verification method.
+.Pp
+The
+.Fn fido_dev_has_uv
+function returns
+.Dv true
+if
+.Fa dev
+supports built-in user verification and its user verification
+feature is configured.
+.Pp
+The
.Fn fido_dev_protocol
function returns the CTAPHID protocol version identifier of
.Fa dev .
is returned.
.Sh SEE ALSO
.Xr fido_dev_info_manifest 3 ,
-.Xr fido_dev_set_io_functions 3
+.Xr fido_dev_set_io_functions 3 ,
+.Xr fido_init 3
.\" Use of this source code is governed by a BSD-style
.\" license that can be found in the LICENSE file.
.\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
.Dt FIDO_DEV_SET_IO_FUNCTIONS 3
.Os
.Sh NAME
-.Nm fido_dev_set_io_functions
+.Nm fido_dev_set_io_functions ,
+.Nm fido_dev_set_sigmask
.Nd FIDO 2 device I/O interface
.Sh SYNOPSIS
.In fido.h
typedef void fido_dev_io_close_t(void *);
typedef int fido_dev_io_read_t(void *, unsigned char *, size_t, int);
typedef int fido_dev_io_write_t(void *, const unsigned char *, size_t);
-typedef int fido_dev_io_rx_t(struct fido_dev *, uint8_t, unsigned char *, size_t, int);
-typedef int fido_dev_io_tx_t(struct fido_dev *, uint8_t, const unsigned char *, size_t);
typedef struct fido_dev_io {
fido_dev_io_open_t *open;
fido_dev_io_close_t *close;
fido_dev_io_read_t *read;
fido_dev_io_write_t *write;
- fido_dev_io_rx_t *rx;
- fido_dev_io_tx_t *tx;
} fido_dev_io_t;
+
+#ifdef _WIN32
+typedef int fido_sigset_t;
+#else
+typedef sigset_t fido_sigset_t;
+#endif
.Ed
.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"
.Sh DESCRIPTION
The
-.Nm
-interface defines the I/O and transmission handlers used to talk to
-.Fa dev .
-Its usage is optional.
-By default,
+.Fn fido_dev_set_io_functions
+function sets the I/O handlers used by
.Em libfido2
-will use the operating system's native HID interface to talk CTAP2 to
-a FIDO device.
-.Pp
-A
-.Vt fido_dev_io_open_t
-function is expected to return a non-NULL opaque pointer on success,
-and NULL on error.
-The returned opaque pointer is never dereferenced by
-.Em libfido2 .
-.Pp
-A
-.Vt fido_dev_io_close_t
-function receives the opaque handle obtained from
-.Vt fido_dev_io_open_t .
-It is not expected to be idempotent.
-.Pp
-A
-.Vt fido_dev_io_read_t
-function reads a single HID report from
-.Fa dev .
-The first parameter taken is the opaque handle obtained from
-.Vt fido_dev_io_open_t .
-The read buffer is pointed to by the second parameter, and the
-third parameter holds its size.
-The last argument passed to
-.Vt fido_dev_io_read_t
-is the number of milliseconds the caller is willing to sleep,
-should the call need to block.
-If this value holds -1,
-.Vt fido_dev_io_read_t
-may block indefinitely.
-The number of bytes read is returned.
-On error, -1 is returned.
-.Pp
-A
-.Vt fido_dev_io_write_t
-function writes a single HID report to
-.Fa dev .
-The first parameter taken is the opaque handle returned by
-.Vt fido_dev_io_open_t .
-The write buffer is pointed to by the second parameter, and the
-third parameter holds its size.
-A
-.Vt fido_dev_io_write_t
-function may block.
-The number of bytes written is returned.
-On error, -1 is returned.
-.Pp
-A
-.Vt fido_dev_io_rx_t
-function receives a complete CTAP2 message from
-.Fa dev .
-The first parameter taken is a pointer to
+to talk to
.Fa dev .
-The second parameter holds the expected CTAP2 command byte.
-The read buffer is pointed to by the third parameter, and the
-fourth parameter holds its size.
-The last argument passed to
-.Vt fido_dev_io_rx_t
-is the number of milliseconds the caller is willing to sleep,
-should the call need to block.
+By default, these handlers are set to the operating system's native HID or NFC
+interfaces.
+They are defined as follows:
+.Bl -tag -width Ds
+.It Vt fido_dev_open_t
+Receives a
+.Vt const char *
+holding a path and opens the corresponding device, returning a
+non-NULL opaque pointer on success and NULL on error.
+.It Vt fido_dev_close_t
+Receives the opaque pointer returned by
+.Vt fido_dev_open_t
+and closes the device.
+.It Vt fido_dev_read_t
+Reads a single transmission unit (HID report, APDU) from a device.
+The first parameter is the opaque pointer returned by
+.Vt fido_dev_open_t .
+The second parameter is the read buffer, and the third parameter
+is the read buffer size.
+The fourth parameter is the number of milliseconds the caller is
+willing to sleep, should the call need to block.
If this value holds -1,
-.Vt fido_dev_io_rx_t
+.Vt fido_dev_read_t
may block indefinitely.
-The number of bytes read is returned.
+On success, the number of bytes read is returned.
On error, -1 is returned.
-.Pp
-A
-.Vt fido_dev_io_tx_t
-function transmits a complete CTAP2 message to
+.It Vt fido_dev_write_t
+Writes a single transmission unit (HID report, APDU) to
.Fa dev .
-The first parameter taken is a pointer to
-.Fa dev .
-The second parameter holds the CTAP2 command byte.
-The write buffer is pointed to by the third parameter, and the
-fourth parameter holds its size.
+The first parameter is the opaque pointer returned by
+.Vt fido_dev_open_t .
+The second parameter is the write buffer, and the third parameter
+is the number of bytes to be written.
A
-.Vt fido_dev_io_tx_t
-function may block.
-On success, 0 is returned.
+.Vt fido_dev_write_t
+may block.
+On success, the number of bytes written is returned.
On error, -1 is returned.
+.El
.Pp
When calling
.Fn fido_dev_set_io_functions ,
the
.Fa open ,
.Fa close ,
-.Fa read
+.Fa read ,
and
.Fa write
fields of
.Fa io
may not be NULL.
-Either
-.Fa rx
-or
-.Fa tx
-may be NULL, in which case
-.Em libfido2
-uses its corresponding CTAP2 HID transport method.
.Pp
No references to
.Fa io
are held by
.Fn fido_dev_set_io_functions .
+.Pp
+The
+.Fn fido_dev_set_sigmask
+function may be used to specify a non-NULL signal mask
+.Fa sigmask
+to be used while
+.Em libfido2's
+default I/O handlers wait on
+.Fa dev .
+On UNIX-like operating systems,
+.Vt fido_sigset_t
+is defined as
+.Vt sigset_t .
+On Windows,
+.Vt fido_sigset_t
+is defined as
+.Vt int
+and
+.Fn fido_dev_set_sigmask
+is a no-op.
+.Pp
+No references to
+.Fa sigmask
+are held by
+.Fn fido_dev_set_sigmask .
.Sh RETURN VALUES
On success,
.Fn fido_dev_set_io_functions
-returns
+and
+.Fn fido_dev_set_sigmask
+return
.Dv FIDO_OK .
On error, a different error code defined in
.In fido/err.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: October 26 2021 $
.Dt FIDO_DEV_SET_PIN 3
.Os
.Sh NAME
.Nm fido_dev_set_pin ,
.Nm fido_dev_get_retry_count ,
+.Nm fido_dev_get_uv_retry_count ,
.Nm fido_dev_reset
.Nd FIDO 2 device management functions
.Sh SYNOPSIS
.Ft int
.Fn fido_dev_get_retry_count "fido_dev_t *dev" "int *retries"
.Ft int
+.Fn fido_dev_get_uv_retry_count "fido_dev_t *dev" "int *retries"
+.Ft int
.Fn fido_dev_reset "fido_dev_t *dev"
.Sh DESCRIPTION
The
is an addressable pointer.
.Pp
The
+.Fn fido_dev_get_uv_retry_count
+function fills
+.Fa retries
+with the number of built-in UV retries left in
+.Fa dev
+before built-in UV is disabled, where
+.Fa retries
+is an addressable pointer.
+.Pp
+The
.Fn fido_dev_reset
function performs a reset on
.Fa dev ,
Please note that
.Fn fido_dev_set_pin ,
.Fn fido_dev_get_retry_count ,
+.Fn fido_dev_get_uv_retry_count ,
and
.Fn fido_dev_reset
are synchronous and will block if necessary.
The error codes returned by
.Fn fido_dev_set_pin ,
.Fn fido_dev_get_retry_count ,
+.Fn fido_dev_get_uv_retry_count ,
and
.Fn fido_dev_reset
are defined in
.\" 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: October 26 2021 $
.Dt FIDO_INIT 3
.Os
.Sh NAME
library.
Its invocation must precede that of any other
.Em libfido2
-function.
+function in the context of the executing thread.
+.Pp
If
.Dv FIDO_DEBUG
is set in
Alternatively, the
.Ev FIDO_DEBUG
environment variable may be set.
+.Pp
+If
+.Dv FIDO_DISABLE_U2F_FALLBACK
+is set in
+.Fa flags ,
+then
+.Em libfido2
+will not fallback to U2F in
+.Xr fido_dev_open 3
+if a device claims to be FIDO2 but fails to respond to a
+FIDO2 command.
.Sh SEE ALSO
.Xr fido_assert_new 3 ,
.Xr fido_cred_new 3 ,
-major=4
+major=5
minor=0
/*
- * Copyright (c) 2018 Yubico AB. All rights reserved.
+ * 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/evp.h>
-#include <string.h>
-
#include "fido.h"
-int
-aes256_cbc_enc(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
+static int
+aes256_cbc(const fido_blob_t *key, const u_char *iv, const fido_blob_t *in,
+ fido_blob_t *out, int encrypt)
{
- EVP_CIPHER_CTX *ctx = NULL;
- unsigned char iv[32];
- int len;
- int ok = -1;
-
- memset(iv, 0, sizeof(iv));
- out->ptr = NULL;
- out->len = 0;
-
- /* sanity check */
- if (in->len > INT_MAX || (in->len % 16) != 0 ||
- (out->ptr = calloc(1, in->len)) == NULL) {
- fido_log_debug("%s: in->len=%zu", __func__, in->len);
+ EVP_CIPHER_CTX *ctx = NULL;
+ const EVP_CIPHER *cipher;
+ int ok = -1;
+
+ memset(out, 0, sizeof(*out));
+
+ if (key->len != 32) {
+ fido_log_debug("%s: invalid key len %zu", __func__, key->len);
goto fail;
}
-
- if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
- !EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
- !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
- !EVP_EncryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
- len < 0 || (size_t)len != in->len) {
- fido_log_debug("%s: EVP_Encrypt", __func__);
+ if (in->len > UINT_MAX || in->len % 16 || in->len == 0) {
+ fido_log_debug("%s: invalid input len %zu", __func__, in->len);
+ goto fail;
+ }
+ out->len = in->len;
+ if ((out->ptr = calloc(1, out->len)) == NULL) {
+ fido_log_debug("%s: calloc", __func__);
+ goto fail;
+ }
+ if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
+ (cipher = EVP_aes_256_cbc()) == NULL) {
+ fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
+ goto fail;
+ }
+ if (EVP_CipherInit(ctx, cipher, key->ptr, iv, encrypt) == 0 ||
+ EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)out->len) < 0) {
+ fido_log_debug("%s: EVP_Cipher", __func__);
goto fail;
}
-
- out->len = (size_t)len;
ok = 0;
fail:
if (ctx != NULL)
EVP_CIPHER_CTX_free(ctx);
+ if (ok < 0)
+ fido_blob_reset(out);
- if (ok < 0) {
- free(out->ptr);
- out->ptr = NULL;
- out->len = 0;
- }
+ return ok;
+}
- return (ok);
+static int
+aes256_cbc_proto1(const fido_blob_t *key, const fido_blob_t *in,
+ fido_blob_t *out, int encrypt)
+{
+ u_char iv[16];
+
+ memset(&iv, 0, sizeof(iv));
+
+ return aes256_cbc(key, iv, in, out, encrypt);
}
-int
-aes256_cbc_dec(const fido_blob_t *key, const fido_blob_t *in, fido_blob_t *out)
+static int
+aes256_cbc_fips(const fido_blob_t *secret, const fido_blob_t *in,
+ fido_blob_t *out, int encrypt)
{
- EVP_CIPHER_CTX *ctx = NULL;
- unsigned char iv[32];
- int len;
- int ok = -1;
-
- memset(iv, 0, sizeof(iv));
- out->ptr = NULL;
- out->len = 0;
-
- /* sanity check */
- if (in->len > INT_MAX || (in->len % 16) != 0 ||
- (out->ptr = calloc(1, in->len)) == NULL) {
- fido_log_debug("%s: in->len=%zu", __func__, in->len);
- goto fail;
+ fido_blob_t key, cin, cout;
+ u_char iv[16];
+
+ memset(out, 0, sizeof(*out));
+
+ if (secret->len != 64) {
+ fido_log_debug("%s: invalid secret len %zu", __func__,
+ secret->len);
+ return -1;
}
+ if (in->len < sizeof(iv)) {
+ fido_log_debug("%s: invalid input len %zu", __func__, in->len);
+ return -1;
+ }
+ if (encrypt) {
+ if (fido_get_random(iv, sizeof(iv)) < 0) {
+ fido_log_debug("%s: fido_get_random", __func__);
+ return -1;
+ }
+ cin = *in;
+ } else {
+ memcpy(iv, in->ptr, sizeof(iv));
+ cin.ptr = in->ptr + sizeof(iv);
+ cin.len = in->len - sizeof(iv);
+ }
+ key.ptr = secret->ptr + 32;
+ key.len = secret->len - 32;
+ if (aes256_cbc(&key, iv, &cin, &cout, encrypt) < 0)
+ return -1;
+ if (encrypt) {
+ if (cout.len > SIZE_MAX - sizeof(iv) ||
+ (out->ptr = calloc(1, sizeof(iv) + cout.len)) == NULL) {
+ fido_blob_reset(&cout);
+ return -1;
+ }
+ out->len = sizeof(iv) + cout.len;
+ memcpy(out->ptr, iv, sizeof(iv));
+ memcpy(out->ptr + sizeof(iv), cout.ptr, cout.len);
+ fido_blob_reset(&cout);
+ } else
+ *out = cout;
+
+ return 0;
+}
- if ((ctx = EVP_CIPHER_CTX_new()) == NULL || key->len != 32 ||
- !EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->ptr, iv) ||
- !EVP_CIPHER_CTX_set_padding(ctx, 0) ||
- !EVP_DecryptUpdate(ctx, out->ptr, &len, in->ptr, (int)in->len) ||
- len < 0 || (size_t)len > in->len + 32) {
- fido_log_debug("%s: EVP_Decrypt", __func__);
+static int
+aes256_gcm(const fido_blob_t *key, const fido_blob_t *nonce,
+ const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out,
+ int encrypt)
+{
+ EVP_CIPHER_CTX *ctx = NULL;
+ const EVP_CIPHER *cipher;
+ size_t textlen;
+ int ok = -1;
+
+ memset(out, 0, sizeof(*out));
+
+ if (nonce->len != 12 || key->len != 32 || aad->len > UINT_MAX) {
+ fido_log_debug("%s: invalid params %zu, %zu, %zu", __func__,
+ nonce->len, key->len, aad->len);
+ goto fail;
+ }
+ if (in->len > UINT_MAX || in->len > SIZE_MAX - 16 || in->len < 16) {
+ fido_log_debug("%s: invalid input len %zu", __func__, in->len);
+ goto fail;
+ }
+ /* add tag to (on encrypt) or trim tag from the output (on decrypt) */
+ out->len = encrypt ? in->len + 16 : in->len - 16;
+ if ((out->ptr = calloc(1, out->len)) == NULL) {
+ fido_log_debug("%s: calloc", __func__);
+ goto fail;
+ }
+ if ((ctx = EVP_CIPHER_CTX_new()) == NULL ||
+ (cipher = EVP_aes_256_gcm()) == NULL) {
+ fido_log_debug("%s: EVP_CIPHER_CTX_new", __func__);
+ goto fail;
+ }
+ if (EVP_CipherInit(ctx, cipher, key->ptr, nonce->ptr, encrypt) == 0) {
+ fido_log_debug("%s: EVP_CipherInit", __func__);
goto fail;
}
- out->len = (size_t)len;
+ if (encrypt)
+ textlen = in->len;
+ else {
+ textlen = in->len - 16;
+ /* point openssl at the mac tag */
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16,
+ in->ptr + in->len - 16) == 0) {
+ fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
+ goto fail;
+ }
+ }
+ /* the last EVP_Cipher() will either compute or verify the mac tag */
+ if (EVP_Cipher(ctx, NULL, aad->ptr, (u_int)aad->len) < 0 ||
+ EVP_Cipher(ctx, out->ptr, in->ptr, (u_int)textlen) < 0 ||
+ EVP_Cipher(ctx, NULL, NULL, 0) < 0) {
+ fido_log_debug("%s: EVP_Cipher", __func__);
+ goto fail;
+ }
+ if (encrypt) {
+ /* append the mac tag */
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16,
+ out->ptr + out->len - 16) == 0) {
+ fido_log_debug("%s: EVP_CIPHER_CTX_ctrl", __func__);
+ goto fail;
+ }
+ }
ok = 0;
fail:
if (ctx != NULL)
EVP_CIPHER_CTX_free(ctx);
+ if (ok < 0)
+ fido_blob_reset(out);
- if (ok < 0) {
- free(out->ptr);
- out->ptr = NULL;
- out->len = 0;
- }
+ return ok;
+}
- return (ok);
+int
+aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *secret,
+ const fido_blob_t *in, fido_blob_t *out)
+{
+ return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
+ in, out, 1) : aes256_cbc_proto1(secret, in, out, 1);
+}
+
+int
+aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *secret,
+ const fido_blob_t *in, fido_blob_t *out)
+{
+ return fido_dev_get_pin_protocol(dev) == 2 ? aes256_cbc_fips(secret,
+ in, out, 0) : aes256_cbc_proto1(secret, in, out, 0);
+}
+
+int
+aes256_gcm_enc(const fido_blob_t *key, const fido_blob_t *nonce,
+ const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
+{
+ return aes256_gcm(key, nonce, aad, in, out, 1);
+}
+
+int
+aes256_gcm_dec(const fido_blob_t *key, const fido_blob_t *nonce,
+ const fido_blob_t *aad, const fido_blob_t *in, fido_blob_t *out)
+{
+ return aes256_gcm(key, nonce, aad, in, out, 0);
}
/*
- * 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 <openssl/ec.h>
#include <openssl/ecdsa.h>
-#include <openssl/evp.h>
#include <openssl/sha.h>
-#include <string.h>
#include "fido.h"
#include "fido/es256.h"
#include "fido/rs256.h"
return (cbor_decode_cred_id(val, &stmt->id));
case 2: /* authdata */
return (cbor_decode_assert_authdata(val, &stmt->authdata_cbor,
- &stmt->authdata, &stmt->authdata_ext,
- &stmt->hmac_secret_enc));
+ &stmt->authdata, &stmt->authdata_ext));
case 3: /* signature */
return (fido_blob_decode(val, &stmt->sig));
case 4: /* user attributes */
return (cbor_decode_user(val, &stmt->user));
+ case 7: /* large blob key */
+ return (fido_blob_decode(val, &stmt->largeblob_key));
default: /* ignore */
fido_log_debug("%s: cbor type", __func__);
return (0);
const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
{
fido_blob_t f;
+ fido_opt_t uv = assert->uv;
cbor_item_t *argv[7];
+ const uint8_t cmd = CTAP_CBOR_ASSERT;
int r;
memset(argv, 0, sizeof(argv));
}
}
- /* hmac-secret extension */
- if (assert->ext & FIDO_EXT_HMAC_SECRET)
- if ((argv[3] = cbor_encode_hmac_secret_param(ecdh, pk,
- &assert->hmac_salt)) == NULL) {
- fido_log_debug("%s: cbor_encode_hmac_secret_param",
- __func__);
+ if (assert->ext.mask)
+ if ((argv[3] = cbor_encode_assert_ext(dev, &assert->ext, ecdh,
+ pk)) == NULL) {
+ fido_log_debug("%s: cbor_encode_assert_ext", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
- /* options */
- if (assert->up != FIDO_OPT_OMIT || assert->uv != FIDO_OPT_OMIT)
- if ((argv[4] = cbor_encode_assert_options(assert->up,
- assert->uv)) == NULL) {
- fido_log_debug("%s: cbor_encode_assert_options",
- __func__);
- r = FIDO_ERR_INTERNAL;
+ /* user verification */
+ 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) {
+ fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
+ uv = FIDO_OPT_OMIT;
+ }
- /* pin authentication */
- if (pin) {
- if (pk == NULL || ecdh == NULL) {
- fido_log_debug("%s: pin=%p, pk=%p, ecdh=%p", __func__,
- (const void *)pin, (const void *)pk,
- (const void *)ecdh);
- r = FIDO_ERR_INVALID_ARGUMENT;
- goto fail;
- }
- if ((r = cbor_add_pin_params(dev, &assert->cdh, pk, ecdh, pin,
- &argv[5], &argv[6])) != FIDO_OK) {
- fido_log_debug("%s: cbor_add_pin_params", __func__);
+ /* options */
+ if (assert->up != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
+ if ((argv[4] = cbor_encode_assert_opt(assert->up, uv)) == NULL) {
+ fido_log_debug("%s: cbor_encode_assert_opt", __func__);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
- }
/* frame and transmit */
- if (cbor_build_frame(CTAP_CBOR_ASSERT, argv, nitems(argv), &f) < 0 ||
+ if (cbor_build_frame(cmd, 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;
}
static int
-decrypt_hmac_secrets(fido_assert_t *assert, const fido_blob_t *key)
+decrypt_hmac_secrets(const fido_dev_t *dev, fido_assert_t *assert,
+ const fido_blob_t *key)
{
for (size_t i = 0; i < assert->stmt_cnt; i++) {
fido_assert_stmt *stmt = &assert->stmt[i];
- if (stmt->hmac_secret_enc.ptr != NULL) {
- if (aes256_cbc_dec(key, &stmt->hmac_secret_enc,
+ if (stmt->authdata_ext.hmac_secret_enc.ptr != NULL) {
+ if (aes256_cbc_dec(dev, key,
+ &stmt->authdata_ext.hmac_secret_enc,
&stmt->hmac_secret) < 0) {
fido_log_debug("%s: aes256_cbc_dec %zu",
__func__, i);
es256_pk_t *pk = NULL;
int r;
+#ifdef USE_WINHELLO
+ if (dev->flags & FIDO_DEV_WINHELLO)
+ return (fido_winhello_get_assert(dev, assert, pin));
+#endif
+
if (assert->rp_id == NULL || assert->cdh.ptr == NULL) {
fido_log_debug("%s: rp_id=%p, cdh.ptr=%p", __func__,
(void *)assert->rp_id, (void *)assert->cdh.ptr);
}
if (fido_dev_is_fido2(dev) == false) {
- if (pin != NULL || assert->ext != 0)
+ if (pin != NULL || assert->ext.mask != 0)
return (FIDO_ERR_UNSUPPORTED_OPTION);
return (u2f_authenticate(dev, assert, -1));
}
- if (pin != NULL || assert->ext != 0) {
+ 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) {
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
r = fido_dev_get_assert_wait(dev, assert, pk, ecdh, pin, -1);
- if (r == FIDO_OK && assert->ext & FIDO_EXT_HMAC_SECRET)
- if (decrypt_hmac_secrets(assert, ecdh) < 0) {
+ 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__);
r = FIDO_ERR_INTERNAL;
goto fail;
static int
check_extensions(int authdata_ext, int ext)
{
+ /* XXX: largeBlobKey is not part of extensions map */
+ ext &= ~FIDO_EXT_LARGEBLOB_KEY;
if (authdata_ext != ext) {
fido_log_debug("%s: authdata_ext=0x%x != ext=0x%x", __func__,
authdata_ext, ext);
}
int
-fido_get_signed_hash(int cose_alg, fido_blob_t *dgst, const fido_blob_t *clientdata,
- const fido_blob_t *authdata_cbor)
+fido_get_signed_hash(int cose_alg, fido_blob_t *dgst,
+ const fido_blob_t *clientdata, const fido_blob_t *authdata_cbor)
{
cbor_item_t *item = NULL;
unsigned char *authdata_ptr = NULL;
goto out;
}
- if (check_extensions(stmt->authdata_ext, assert->ext) < 0) {
+ if (check_extensions(stmt->authdata_ext.mask, assert->ext.mask) < 0) {
fido_log_debug("%s: check_extensions", __func__);
r = FIDO_ERR_INVALID_PARAM;
goto out;
return (r);
}
+int
+fido_assert_set_clientdata(fido_assert_t *assert, const unsigned char *data,
+ size_t data_len)
+{
+ if (!fido_blob_is_empty(&assert->cdh) ||
+ fido_blob_set(&assert->cd, data, data_len) < 0) {
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+ if (fido_sha256(&assert->cdh, data, data_len) < 0) {
+ fido_blob_reset(&assert->cd);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ return (FIDO_OK);
+}
+
int
fido_assert_set_clientdata_hash(fido_assert_t *assert,
const unsigned char *hash, size_t hash_len)
{
- if (fido_blob_set(&assert->cdh, hash, hash_len) < 0)
+ if (!fido_blob_is_empty(&assert->cd) ||
+ fido_blob_set(&assert->cdh, hash, hash_len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
return (FIDO_OK);
size_t salt_len)
{
if ((salt_len != 32 && salt_len != 64) ||
- fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0)
+ fido_blob_set(&assert->ext.hmac_salt, salt, salt_len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+ return (FIDO_OK);
+}
+
+int
+fido_assert_set_hmac_secret(fido_assert_t *assert, size_t idx,
+ const unsigned char *secret, size_t secret_len)
+{
+ if (idx >= assert->stmt_len || (secret_len != 32 && secret_len != 64) ||
+ fido_blob_set(&assert->stmt[idx].hmac_secret, secret,
+ secret_len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
return (FIDO_OK);
int
fido_assert_set_extensions(fido_assert_t *assert, int ext)
{
- if (ext != 0 && ext != FIDO_EXT_HMAC_SECRET)
- return (FIDO_ERR_INVALID_ARGUMENT);
-
- assert->ext = ext;
+ if (ext == 0)
+ assert->ext.mask = 0;
+ else {
+ if ((ext & FIDO_EXT_ASSERT_MASK) != ext)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ assert->ext.mask |= ext;
+ }
return (FIDO_OK);
}
fido_assert_reset_tx(fido_assert_t *assert)
{
free(assert->rp_id);
- free(assert->cdh.ptr);
- free(assert->hmac_salt.ptr);
+ fido_blob_reset(&assert->cd);
+ fido_blob_reset(&assert->cdh);
+ fido_blob_reset(&assert->ext.hmac_salt);
fido_free_blob_array(&assert->allow_list);
-
- memset(&assert->cdh, 0, sizeof(assert->cdh));
- memset(&assert->hmac_salt, 0, sizeof(assert->hmac_salt));
+ memset(&assert->ext, 0, sizeof(assert->ext));
memset(&assert->allow_list, 0, sizeof(assert->allow_list));
-
assert->rp_id = NULL;
assert->up = FIDO_OPT_OMIT;
assert->uv = FIDO_OPT_OMIT;
- assert->ext = 0;
+}
+
+static void fido_assert_reset_extattr(fido_assert_extattr_t *ext)
+{
+ fido_blob_reset(&ext->hmac_secret_enc);
+ fido_blob_reset(&ext->blob);
+ memset(ext, 0, sizeof(*ext));
}
void
fido_assert_reset_rx(fido_assert_t *assert)
{
for (size_t i = 0; i < assert->stmt_cnt; i++) {
- free(assert->stmt[i].user.id.ptr);
free(assert->stmt[i].user.icon);
free(assert->stmt[i].user.name);
free(assert->stmt[i].user.display_name);
- free(assert->stmt[i].id.ptr);
- if (assert->stmt[i].hmac_secret.ptr != NULL) {
- explicit_bzero(assert->stmt[i].hmac_secret.ptr,
- assert->stmt[i].hmac_secret.len);
- }
- free(assert->stmt[i].hmac_secret.ptr);
- free(assert->stmt[i].hmac_secret_enc.ptr);
- free(assert->stmt[i].authdata_cbor.ptr);
- free(assert->stmt[i].sig.ptr);
+ fido_blob_reset(&assert->stmt[i].user.id);
+ fido_blob_reset(&assert->stmt[i].id);
+ fido_blob_reset(&assert->stmt[i].hmac_secret);
+ fido_blob_reset(&assert->stmt[i].authdata_cbor);
+ fido_blob_reset(&assert->stmt[i].largeblob_key);
+ fido_blob_reset(&assert->stmt[i].sig);
+ fido_assert_reset_extattr(&assert->stmt[i].authdata_ext);
memset(&assert->stmt[i], 0, sizeof(assert->stmt[i]));
}
-
free(assert->stmt);
-
assert->stmt = NULL;
assert->stmt_len = 0;
assert->stmt_cnt = 0;
if (assert_p == NULL || (assert = *assert_p) == NULL)
return;
-
fido_assert_reset_tx(assert);
fido_assert_reset_rx(assert);
-
free(assert);
-
*assert_p = NULL;
}
return (assert->stmt[idx].hmac_secret.len);
}
-static void
-fido_assert_clean_authdata(fido_assert_stmt *as)
+const unsigned char *
+fido_assert_largeblob_key_ptr(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (NULL);
+
+ return (assert->stmt[idx].largeblob_key.ptr);
+}
+
+size_t
+fido_assert_largeblob_key_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].largeblob_key.len);
+}
+
+const unsigned char *
+fido_assert_blob_ptr(const fido_assert_t *assert, size_t idx)
{
- free(as->authdata_cbor.ptr);
- free(as->hmac_secret_enc.ptr);
+ if (idx >= assert->stmt_len)
+ return (NULL);
- memset(&as->authdata_ext, 0, sizeof(as->authdata_ext));
- memset(&as->authdata_cbor, 0, sizeof(as->authdata_cbor));
- memset(&as->authdata, 0, sizeof(as->authdata));
- memset(&as->hmac_secret_enc, 0, sizeof(as->hmac_secret_enc));
+ return (assert->stmt[idx].authdata_ext.blob.ptr);
+}
+
+size_t
+fido_assert_blob_len(const fido_assert_t *assert, size_t idx)
+{
+ if (idx >= assert->stmt_len)
+ return (0);
+
+ return (assert->stmt[idx].authdata_ext.blob.len);
+}
+
+static void
+fido_assert_clean_authdata(fido_assert_stmt *stmt)
+{
+ fido_blob_reset(&stmt->authdata_cbor);
+ fido_assert_reset_extattr(&stmt->authdata_ext);
+ memset(&stmt->authdata, 0, sizeof(stmt->authdata));
}
int
}
if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
- &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
+ &stmt->authdata, &stmt->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}
if (cbor_decode_assert_authdata(item, &stmt->authdata_cbor,
- &stmt->authdata, &stmt->authdata_ext, &stmt->hmac_secret_enc) < 0) {
+ &stmt->authdata, &stmt->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_assert_authdata", __func__);
r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
return (r);
}
-static void
-fido_assert_clean_sig(fido_assert_stmt *as)
-{
- free(as->sig.ptr);
- as->sig.ptr = NULL;
- as->sig.len = 0;
-}
-
int
fido_assert_set_sig(fido_assert_t *a, size_t idx, const unsigned char *ptr,
size_t len)
{
- unsigned char *sig;
-
if (idx >= a->stmt_len || ptr == NULL || len == 0)
return (FIDO_ERR_INVALID_ARGUMENT);
-
- fido_assert_clean_sig(&a->stmt[idx]);
-
- if ((sig = malloc(len)) == NULL)
+ if (fido_blob_set(&a->stmt[idx].sig, ptr, len) < 0)
return (FIDO_ERR_INTERNAL);
- memcpy(sig, ptr, len);
- a->stmt[idx].sig.ptr = sig;
- a->stmt[idx].sig.len = len;
-
return (FIDO_OK);
}
* license that can be found in the LICENSE file.
*/
-#include <string.h>
#include "fido.h"
static int
memset(argv, 0, sizeof(argv));
/* add command parameters */
- if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
(argv[1] = cbor_build_uint8(2)) == NULL) {
fido_log_debug("%s: cbor_build", __func__);
r = FIDO_ERR_INTERNAL;
* license that can be found in the LICENSE file.
*/
-#include <string.h>
-
#include "fido.h"
#include "fido/bio.h"
#include "fido/es256.h"
}
static int
-bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
+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)
{
cbor_item_t *argv[5];
fido_blob_t *ecdh = NULL;
fido_blob_t f;
fido_blob_t hmac;
+ const uint8_t cmd = CTAP_CBOR_BIO_ENROLL_PRE;
int r = FIDO_ERR_INTERNAL;
memset(&f, 0, sizeof(f));
/* modality, subCommand */
if ((argv[0] = cbor_build_uint8(1)) == NULL ||
- (argv[1] = cbor_build_uint8(cmd)) == NULL) {
+ (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
goto fail;
}
/* subParams */
if (pin || token) {
- if (bio_prepare_hmac(cmd, sub_argv, sub_argc, &argv[2],
+ if (bio_prepare_hmac(subcmd, sub_argv, sub_argc, &argv[2],
&hmac) < 0) {
fido_log_debug("%s: bio_prepare_hmac", __func__);
goto fail;
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
- if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
- &argv[4], &argv[3])) != FIDO_OK) {
- fido_log_debug("%s: cbor_add_pin_params", __func__);
+ if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
+ NULL, &argv[4], &argv[3])) != FIDO_OK) {
+ fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
} else if (token) {
- if ((argv[3] = cbor_encode_pin_opt()) == NULL ||
- (argv[4] = cbor_encode_pin_auth(token, &hmac)) == NULL) {
+ if ((argv[3] = cbor_encode_pin_opt(dev)) == NULL ||
+ (argv[4] = cbor_encode_pin_auth(dev, token, &hmac)) == NULL) {
fido_log_debug("%s: encode pin", __func__);
goto fail;
}
}
/* framing and transmission */
- if (cbor_build_frame(CTAP_CBOR_BIO_ENROLL_PRE, argv, nitems(argv),
- &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ if (cbor_build_frame(cmd, 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;
bio_reset_template(fido_bio_template_t *t)
{
free(t->name);
- free(t->id.ptr);
t->name = NULL;
- memset(&t->id, 0, sizeof(t->id));
+ fido_blob_reset(&t->id);
}
static void
goto fail;
}
- if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
- fido_log_debug("%s: fido_dev_get_pin_token", __func__);
+ if ((r = fido_dev_get_uv_token(dev, CTAP_CBOR_BIO_ENROLL_PRE, pin, ecdh,
+ pk, NULL, token)) != FIDO_OK) {
+ fido_log_debug("%s: fido_dev_get_uv_token", __func__);
goto fail;
}
fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
size_t len)
{
- free(t->id.ptr);
- t->id.ptr = NULL;
- t->id.len = 0;
+ fido_blob_reset(&t->id);
if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
return (FIDO_ERR_INTERNAL);
* license that can be found in the LICENSE file.
*/
-#include <string.h>
#include "fido.h"
fido_blob_t *
fido_blob_new(void)
{
- return (calloc(1, sizeof(fido_blob_t)));
+ return calloc(1, sizeof(fido_blob_t));
}
-int
-fido_blob_set(fido_blob_t *b, const unsigned char *ptr, size_t len)
+void
+fido_blob_reset(fido_blob_t *b)
{
- if (b->ptr != NULL) {
- explicit_bzero(b->ptr, b->len);
- free(b->ptr);
- b->ptr = NULL;
- }
+ freezero(b->ptr, b->len);
+ explicit_bzero(b, sizeof(*b));
+}
- b->len = 0;
+int
+fido_blob_set(fido_blob_t *b, const u_char *ptr, size_t len)
+{
+ fido_blob_reset(b);
if (ptr == NULL || len == 0) {
fido_log_debug("%s: ptr=%p, len=%zu", __func__,
(const void *)ptr, len);
- return (-1);
+ return -1;
}
if ((b->ptr = malloc(len)) == NULL) {
fido_log_debug("%s: malloc", __func__);
- return (-1);
+ return -1;
}
memcpy(b->ptr, ptr, len);
b->len = len;
- return (0);
+ return 0;
+}
+
+int
+fido_blob_append(fido_blob_t *b, const u_char *ptr, size_t len)
+{
+ u_char *tmp;
+
+ if (ptr == NULL || len == 0) {
+ fido_log_debug("%s: ptr=%p, len=%zu", __func__,
+ (const void *)ptr, len);
+ return -1;
+ }
+ if (SIZE_MAX - b->len < len) {
+ fido_log_debug("%s: overflow", __func__);
+ return -1;
+ }
+ if ((tmp = realloc(b->ptr, b->len + len)) == NULL) {
+ fido_log_debug("%s: realloc", __func__);
+ return -1;
+ }
+ b->ptr = tmp;
+ memcpy(&b->ptr[b->len], ptr, len);
+ b->len += len;
+
+ return 0;
}
void
if (bp == NULL || (b = *bp) == NULL)
return;
- if (b->ptr) {
- explicit_bzero(b->ptr, b->len);
- free(b->ptr);
- }
-
- explicit_bzero(b, sizeof(*b));
+ fido_blob_reset(b);
free(b);
-
*bp = NULL;
}
for (size_t i = 0; i < array->len; i++) {
fido_blob_t *b = &array->ptr[i];
- if (b->ptr != NULL) {
- explicit_bzero(b->ptr, b->len);
- free(b->ptr);
- b->ptr = NULL;
- }
+ freezero(b->ptr, b->len);
+ b->ptr = NULL;
}
free(array->ptr);
fido_blob_encode(const fido_blob_t *b)
{
if (b == NULL || b->ptr == NULL)
- return (NULL);
+ return NULL;
- return (cbor_build_bytestring(b->ptr, b->len));
+ return cbor_build_bytestring(b->ptr, b->len);
}
int
fido_blob_decode(const cbor_item_t *item, fido_blob_t *b)
{
- return (cbor_bytestring_copy(item, &b->ptr, &b->len));
+ return cbor_bytestring_copy(item, &b->ptr, &b->len);
}
int
fido_blob_is_empty(const fido_blob_t *b)
{
- return (b->ptr == NULL || b->len == 0);
+ return b->ptr == NULL || b->len == 0;
+}
+
+int
+fido_blob_serialise(fido_blob_t *b, const cbor_item_t *item)
+{
+ size_t alloc;
+
+ if (!fido_blob_is_empty(b))
+ return -1;
+ if ((b->len = cbor_serialize_alloc(item, &b->ptr, &alloc)) == 0) {
+ b->ptr = NULL;
+ return -1;
+ }
+
+ return 0;
}
fido_blob_t *fido_blob_new(void);
int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
int fido_blob_is_empty(const fido_blob_t *);
-int fido_blob_set(fido_blob_t *, const unsigned char *, size_t);
+int fido_blob_set(fido_blob_t *, const u_char *, size_t);
+int fido_blob_append(fido_blob_t *, const u_char *, size_t);
void fido_blob_free(fido_blob_t **);
+void fido_blob_reset(fido_blob_t *);
void fido_free_blob_array(fido_blob_array_t *);
#ifdef __cplusplus
* license that can be found in the LICENSE file.
*/
-#include <string.h>
#include "fido.h"
int
* license that can be found in the LICENSE file.
*/
-#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
-
-#include <string.h>
#include "fido.h"
static int
return (NULL);
}
+static int
+cbor_encode_largeblob_key_ext(cbor_item_t *map)
+{
+ if (map == NULL ||
+ cbor_add_bool(map, "largeBlobKey", FIDO_OPT_TRUE) < 0)
+ return (-1);
+
+ return (0);
+}
+
cbor_item_t *
-cbor_encode_extensions(const fido_cred_ext_t *ext)
+cbor_encode_cred_ext(const fido_cred_ext_t *ext, const fido_blob_t *blob)
{
cbor_item_t *item = NULL;
size_t size = 0;
+ if (ext->mask & FIDO_EXT_CRED_BLOB)
+ size++;
if (ext->mask & FIDO_EXT_HMAC_SECRET)
size++;
if (ext->mask & FIDO_EXT_CRED_PROTECT)
size++;
+ if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
+ size++;
+
if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
return (NULL);
- if (ext->mask & FIDO_EXT_HMAC_SECRET) {
- if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
+ if (ext->mask & FIDO_EXT_CRED_BLOB) {
+ if (cbor_add_bytestring(item, "credBlob", blob->ptr,
+ blob->len) < 0) {
cbor_decref(&item);
return (NULL);
}
return (NULL);
}
}
+ if (ext->mask & FIDO_EXT_HMAC_SECRET) {
+ if (cbor_add_bool(item, "hmac-secret", FIDO_OPT_TRUE) < 0) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+ }
+ if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
+ if (cbor_encode_largeblob_key_ext(item) < 0) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+ }
return (item);
}
cbor_item_t *
-cbor_encode_options(fido_opt_t rk, fido_opt_t uv)
+cbor_encode_cred_opt(fido_opt_t rk, fido_opt_t uv)
{
cbor_item_t *item = NULL;
if ((item = cbor_new_definite_map(2)) == NULL)
return (NULL);
-
if ((rk != FIDO_OPT_OMIT && cbor_add_bool(item, "rk", rk) < 0) ||
(uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
cbor_decref(&item);
}
cbor_item_t *
-cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv)
+cbor_encode_assert_opt(fido_opt_t up, fido_opt_t uv)
{
cbor_item_t *item = NULL;
if ((item = cbor_new_definite_map(2)) == NULL)
return (NULL);
-
if ((up != FIDO_OPT_OMIT && cbor_add_bool(item, "up", up) < 0) ||
(uv != FIDO_OPT_OMIT && cbor_add_bool(item, "uv", uv) < 0)) {
cbor_decref(&item);
}
cbor_item_t *
-cbor_encode_pin_auth(const fido_blob_t *hmac_key, const fido_blob_t *data)
+cbor_encode_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
+ const fido_blob_t *data)
{
const EVP_MD *md = NULL;
unsigned char dgst[SHA256_DIGEST_LENGTH];
unsigned int dgst_len;
+ size_t outlen;
+ uint8_t prot;
+ fido_blob_t key;
- if ((md = EVP_sha256()) == NULL || HMAC(md, hmac_key->ptr,
- (int)hmac_key->len, data->ptr, data->len, dgst,
- &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
- return (NULL);
- return (cbor_build_bytestring(dgst, 16));
-}
+ key.ptr = secret->ptr;
+ key.len = secret->len;
-cbor_item_t *
-cbor_encode_pin_opt(void)
-{
- return (cbor_build_uint8(1));
-}
+ if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
+ fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
+ return (NULL);
+ }
-cbor_item_t *
-cbor_encode_pin_enc(const fido_blob_t *key, const fido_blob_t *pin)
-{
- fido_blob_t pe;
- cbor_item_t *item = NULL;
+ /* select hmac portion of the shared secret */
+ if (prot == CTAP_PIN_PROTOCOL2 && key.len > 32)
+ key.len = 32;
- if (aes256_cbc_enc(key, pin, &pe) < 0)
+ if ((md = EVP_sha256()) == NULL || HMAC(md, key.ptr,
+ (int)key.len, data->ptr, data->len, dgst,
+ &dgst_len) == NULL || dgst_len != SHA256_DIGEST_LENGTH)
return (NULL);
- item = cbor_build_bytestring(pe.ptr, pe.len);
- free(pe.ptr);
+ outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
- return (item);
+ return (cbor_build_bytestring(dgst, outlen));
}
-static int
-sha256(const unsigned char *data, size_t data_len, fido_blob_t *digest)
+cbor_item_t *
+cbor_encode_pin_opt(const fido_dev_t *dev)
{
- if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
- return (-1);
-
- digest->len = SHA256_DIGEST_LENGTH;
+ uint8_t prot;
- if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
- free(digest->ptr);
- digest->ptr = NULL;
- digest->len = 0;
- return (-1);
+ if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
+ fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
+ return (NULL);
}
- return (0);
+ return (cbor_build_uint8(prot));
}
cbor_item_t *
-cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
- const fido_blob_t *pin)
+cbor_encode_change_pin_auth(const fido_dev_t *dev, const fido_blob_t *secret,
+ const fido_blob_t *new_pin_enc, const fido_blob_t *pin_hash_enc)
{
unsigned char dgst[SHA256_DIGEST_LENGTH];
unsigned int dgst_len;
#else
HMAC_CTX *ctx = NULL;
#endif
- fido_blob_t *npe = NULL; /* new pin, encrypted */
- fido_blob_t *ph = NULL; /* pin hash */
- fido_blob_t *phe = NULL; /* pin hash, encrypted */
+ fido_blob_t key;
+ uint8_t prot;
+ size_t outlen;
- if ((npe = fido_blob_new()) == NULL ||
- (ph = fido_blob_new()) == NULL ||
- (phe = fido_blob_new()) == NULL)
- goto fail;
+ key.ptr = secret->ptr;
+ key.len = secret->len;
- if (aes256_cbc_enc(key, new_pin, npe) < 0) {
- fido_log_debug("%s: aes256_cbc_enc 1", __func__);
+ if ((prot = fido_dev_get_pin_protocol(dev)) == 0) {
+ fido_log_debug("%s: fido_dev_get_pin_protocol", __func__);
goto fail;
}
- if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
- fido_log_debug("%s: sha256", __func__);
- goto fail;
- }
-
- ph->len = 16; /* first 16 bytes */
-
- if (aes256_cbc_enc(key, ph, phe) < 0) {
- fido_log_debug("%s: aes256_cbc_enc 2", __func__);
- goto fail;
- }
+ 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, npe->ptr, (int)npe->len) == 0 ||
- HMAC_Update(&ctx, phe->ptr, (int)phe->len) == 0 ||
- HMAC_Final(&ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
+ 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 ||
- HMAC_Update(ctx, npe->ptr, npe->len) == 0 ||
- HMAC_Update(ctx, phe->ptr, phe->len) == 0 ||
- HMAC_Final(ctx, dgst, &dgst_len) == 0 || dgst_len != 32) {
+ 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;
}
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
- if ((item = cbor_build_bytestring(dgst, 16)) == NULL) {
+ outlen = (prot == CTAP_PIN_PROTOCOL1) ? 16 : dgst_len;
+
+ if ((item = cbor_build_bytestring(dgst, outlen)) == NULL) {
fido_log_debug("%s: cbor_build_bytestring", __func__);
goto fail;
}
fail:
- fido_blob_free(&npe);
- fido_blob_free(&ph);
- fido_blob_free(&phe);
-
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
if (ctx != NULL)
HMAC_CTX_free(ctx);
return (item);
}
-cbor_item_t *
-cbor_encode_set_pin_auth(const fido_blob_t *key, const fido_blob_t *pin)
-{
- const EVP_MD *md = NULL;
- unsigned char dgst[SHA256_DIGEST_LENGTH];
- unsigned int dgst_len;
- cbor_item_t *item = NULL;
- fido_blob_t *pe = NULL;
-
- if ((pe = fido_blob_new()) == NULL)
- goto fail;
-
- if (aes256_cbc_enc(key, pin, pe) < 0) {
- fido_log_debug("%s: aes256_cbc_enc", __func__);
- goto fail;
- }
-
- if ((md = EVP_sha256()) == NULL || key->len != 32 || HMAC(md, key->ptr,
- (int)key->len, pe->ptr, pe->len, dgst, &dgst_len) == NULL ||
- dgst_len != SHA256_DIGEST_LENGTH) {
- fido_log_debug("%s: HMAC", __func__);
- goto fail;
- }
-
- item = cbor_build_bytestring(dgst, 16);
-fail:
- fido_blob_free(&pe);
-
- return (item);
-}
-
-cbor_item_t *
-cbor_encode_pin_hash_enc(const fido_blob_t *shared, const fido_blob_t *pin)
-{
- cbor_item_t *item = NULL;
- fido_blob_t *ph = NULL;
- fido_blob_t *phe = NULL;
-
- if ((ph = fido_blob_new()) == NULL || (phe = fido_blob_new()) == NULL)
- goto fail;
-
- if (sha256(pin->ptr, pin->len, ph) < 0 || ph->len < 16) {
- fido_log_debug("%s: SHA256", __func__);
- goto fail;
- }
-
- ph->len = 16; /* first 16 bytes */
-
- if (aes256_cbc_enc(shared, ph, phe) < 0) {
- fido_log_debug("%s: aes256_cbc_enc", __func__);
- goto fail;
- }
-
- item = cbor_build_bytestring(phe->ptr, phe->len);
-fail:
- fido_blob_free(&ph);
- fido_blob_free(&phe);
-
- return (item);
-}
-
-cbor_item_t *
-cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
- const fido_blob_t *hmac_salt)
+static int
+cbor_encode_hmac_secret_param(const fido_dev_t *dev, cbor_item_t *item,
+ const fido_blob_t *ecdh, const es256_pk_t *pk, const fido_blob_t *salt)
{
- cbor_item_t *item = NULL;
cbor_item_t *param = NULL;
- cbor_item_t *argv[3];
+ cbor_item_t *argv[4];
struct cbor_pair pair;
+ fido_blob_t *enc = NULL;
+ int r;
memset(argv, 0, sizeof(argv));
memset(&pair, 0, sizeof(pair));
- if (ecdh == NULL || pk == NULL || hmac_salt->ptr == NULL) {
- fido_log_debug("%s: ecdh=%p, pk=%p, hmac_salt->ptr=%p",
- __func__, (const void *)ecdh, (const void *)pk,
- (const void *)hmac_salt->ptr);
+ if (item == NULL || ecdh == NULL || pk == NULL || salt->ptr == NULL) {
+ fido_log_debug("%s: ecdh=%p, pk=%p, salt->ptr=%p", __func__,
+ (const void *)ecdh, (const void *)pk,
+ (const void *)salt->ptr);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (salt->len != 32 && salt->len != 64) {
+ fido_log_debug("%s: salt->len=%zu", __func__, salt->len);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
- if (hmac_salt->len != 32 && hmac_salt->len != 64) {
- fido_log_debug("%s: hmac_salt->len=%zu", __func__,
- hmac_salt->len);
+ if ((enc = fido_blob_new()) == NULL ||
+ aes256_cbc_enc(dev, ecdh, salt, enc) < 0) {
+ fido_log_debug("%s: aes256_cbc_enc", __func__);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
/* XXX not pin, but salt */
if ((argv[0] = es256_pk_encode(pk, 1)) == NULL ||
- (argv[1] = cbor_encode_pin_enc(ecdh, hmac_salt)) == NULL ||
- (argv[2] = cbor_encode_set_pin_auth(ecdh, hmac_salt)) == 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) {
fido_log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
- if ((param = cbor_flatten_vector(argv, 3)) == NULL) {
+ if ((param = cbor_flatten_vector(argv, nitems(argv))) == NULL) {
fido_log_debug("%s: cbor_flatten_vector", __func__);
- goto fail;
- }
-
- if ((item = cbor_new_definite_map(1)) == NULL) {
- fido_log_debug("%s: cbor_new_definite_map", __func__);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
if ((pair.key = cbor_build_string("hmac-secret")) == NULL) {
fido_log_debug("%s: cbor_build", __func__);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
if (!cbor_map_add(item, pair)) {
fido_log_debug("%s: cbor_map_add", __func__);
- cbor_decref(&item);
- item = NULL;
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
+ r = FIDO_OK;
+
fail:
- for (size_t i = 0; i < 3; i++)
- if (argv[i] != NULL)
- cbor_decref(&argv[i]);
+ cbor_vector_free(argv, nitems(argv));
if (param != NULL)
cbor_decref(¶m);
if (pair.key != NULL)
cbor_decref(&pair.key);
+ fido_blob_free(&enc);
+
+ return (r);
+}
+
+cbor_item_t *
+cbor_encode_assert_ext(fido_dev_t *dev, const fido_assert_ext_t *ext,
+ const fido_blob_t *ecdh, const es256_pk_t *pk)
+{
+ cbor_item_t *item = NULL;
+ size_t size = 0;
+
+ if (ext->mask & FIDO_EXT_CRED_BLOB)
+ size++;
+ if (ext->mask & FIDO_EXT_HMAC_SECRET)
+ size++;
+ if (ext->mask & FIDO_EXT_LARGEBLOB_KEY)
+ size++;
+ if (size == 0 || (item = cbor_new_definite_map(size)) == NULL)
+ return (NULL);
+
+ if (ext->mask & FIDO_EXT_CRED_BLOB) {
+ if (cbor_add_bool(item, "credBlob", FIDO_OPT_TRUE) < 0) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+ }
+ if (ext->mask & FIDO_EXT_HMAC_SECRET) {
+ if (cbor_encode_hmac_secret_param(dev, item, ecdh, pk,
+ &ext->hmac_salt) < 0) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+ }
+ if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) {
+ if (cbor_encode_largeblob_key_ext(item) < 0) {
+ cbor_decref(&item);
+ return (NULL);
+ }
+ }
+
return (item);
}
return (-1);
}
- if (strcmp(type, "packed") && strcmp(type, "fido-u2f")) {
+ if (strcmp(type, "packed") && strcmp(type, "fido-u2f") &&
+ strcmp(type, "none")) {
fido_log_debug("%s: type=%s", __func__, type);
free(type);
return (-1);
uint16_t id_len;
int ok = -1;
- fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
- *len);
+ fido_log_xxd(*buf, *len, "%s", __func__);
if (fido_buf_read(buf, len, &attcred->aaguid,
sizeof(attcred->aaguid)) < 0) {
if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
- fido_log_xxd(*buf, *len);
goto fail;
}
}
static int
-decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+decode_cred_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
fido_cred_ext_t *authdata_ext = arg;
char *type = NULL;
}
authdata_ext->mask |= FIDO_EXT_CRED_PROTECT;
authdata_ext->prot = cbor_get_uint8(val);
+ } else if (strcmp(type, "credBlob") == 0) {
+ if (cbor_isa_float_ctrl(val) == false ||
+ cbor_float_get_width(val) != CBOR_FLOAT_0 ||
+ cbor_is_bool(val) == false) {
+ fido_log_debug("%s: cbor type", __func__);
+ goto out;
+ }
+ if (cbor_ctrl_value(val) == CBOR_CTRL_TRUE)
+ authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
}
ok = 0;
}
static int
-decode_extensions(const unsigned char **buf, size_t *len,
+decode_cred_extensions(const unsigned char **buf, size_t *len,
fido_cred_ext_t *authdata_ext)
{
cbor_item_t *item = NULL;
struct cbor_load_result cbor;
int ok = -1;
- fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
- *len);
- fido_log_xxd(*buf, *len);
-
memset(authdata_ext, 0, sizeof(*authdata_ext));
+ fido_log_xxd(*buf, *len, "%s", __func__);
+
if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
- fido_log_xxd(*buf, *len);
goto fail;
}
if (cbor_isa_map(item) == false ||
cbor_map_is_definite(item) == false ||
- cbor_map_iter(item, authdata_ext, decode_extension) < 0) {
+ cbor_map_iter(item, authdata_ext, decode_cred_extension) < 0) {
fido_log_debug("%s: cbor type", __func__);
goto fail;
}
}
static int
-decode_hmac_secret_aux(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+decode_assert_extension(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
{
- fido_blob_t *out = arg;
- char *type = NULL;
- int ok = -1;
+ fido_assert_extattr_t *authdata_ext = arg;
+ char *type = NULL;
+ int ok = -1;
- if (cbor_string_copy(key, &type) < 0 || strcmp(type, "hmac-secret")) {
+ if (cbor_string_copy(key, &type) < 0) {
fido_log_debug("%s: cbor type", __func__);
ok = 0; /* ignore */
goto out;
}
- ok = cbor_bytestring_copy(val, &out->ptr, &out->len);
+ if (strcmp(type, "hmac-secret") == 0) {
+ if (fido_blob_decode(val, &authdata_ext->hmac_secret_enc) < 0) {
+ fido_log_debug("%s: fido_blob_decode", __func__);
+ goto out;
+ }
+ authdata_ext->mask |= FIDO_EXT_HMAC_SECRET;
+ } else if (strcmp(type, "credBlob") == 0) {
+ if (fido_blob_decode(val, &authdata_ext->blob) < 0) {
+ fido_log_debug("%s: fido_blob_decode", __func__);
+ goto out;
+ }
+ authdata_ext->mask |= FIDO_EXT_CRED_BLOB;
+ }
+
+ ok = 0;
out:
free(type);
}
static int
-decode_hmac_secret(const unsigned char **buf, size_t *len, fido_blob_t *out)
+decode_assert_extensions(const unsigned char **buf, size_t *len,
+ fido_assert_extattr_t *authdata_ext)
{
cbor_item_t *item = NULL;
struct cbor_load_result cbor;
int ok = -1;
- fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)*buf,
- *len);
+ fido_log_xxd(*buf, *len, "%s", __func__);
if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
- fido_log_xxd(*buf, *len);
goto fail;
}
if (cbor_isa_map(item) == false ||
cbor_map_is_definite(item) == false ||
- cbor_map_size(item) != 1 ||
- cbor_map_iter(item, out, decode_hmac_secret_aux) < 0) {
+ cbor_map_iter(item, authdata_ext, decode_assert_extension) < 0) {
fido_log_debug("%s: cbor type", __func__);
goto fail;
}
buf = cbor_bytestring_handle(item);
len = cbor_bytestring_length(item);
-
- fido_log_debug("%s: buf=%p, len=%zu", __func__, (const void *)buf, len);
- fido_log_xxd(buf, len);
+ fido_log_xxd(buf, len, "%s", __func__);
if (fido_buf_read(&buf, &len, authdata, sizeof(*authdata)) < 0) {
fido_log_debug("%s: fido_buf_read", __func__);
if (authdata_ext != NULL) {
if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0 &&
- decode_extensions(&buf, &len, authdata_ext) < 0)
+ decode_cred_extensions(&buf, &len, authdata_ext) < 0)
return (-1);
}
int
cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
- fido_authdata_t *authdata, int *authdata_ext, fido_blob_t *hmac_secret_enc)
+ fido_authdata_t *authdata, fido_assert_extattr_t *authdata_ext)
{
const unsigned char *buf = NULL;
size_t len;
authdata->sigcount = be32toh(authdata->sigcount);
- *authdata_ext = 0;
if ((authdata->flags & CTAP_AUTHDATA_EXT_DATA) != 0) {
- /* XXX semantic leap: extensions -> hmac_secret */
- if (decode_hmac_secret(&buf, &len, hmac_secret_enc) < 0) {
- fido_log_debug("%s: decode_hmac_secret", __func__);
+ if (decode_assert_extensions(&buf, &len, authdata_ext) < 0) {
+ fido_log_debug("%s: decode_assert_extensions",
+ __func__);
return (-1);
}
- *authdata_ext = FIDO_EXT_HMAC_SECRET;
}
/* XXX we should probably ensure that len == 0 at this point */
if (x5c->len)
return (0); /* ignore */
- return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len));
+ return (fido_blob_decode(item, x5c));
}
static int
goto out;
}
} else if (!strcmp(name, "sig")) {
- if (cbor_bytestring_copy(val, &attstmt->sig.ptr,
- &attstmt->sig.len) < 0) {
+ if (fido_blob_decode(val, &attstmt->sig) < 0) {
fido_log_debug("%s: sig", __func__);
goto out;
}
}
if (!strcmp(name, "id"))
- if (cbor_bytestring_copy(val, &id->ptr, &id->len) < 0) {
+ if (fido_blob_decode(val, id) < 0) {
fido_log_debug("%s: cbor_bytestring_copy", __func__);
goto out;
}
goto out;
}
} else if (!strcmp(name, "id")) {
- if (cbor_bytestring_copy(val, &user->id.ptr, &user->id.len) < 0) {
+ if (fido_blob_decode(val, &user->id) < 0) {
fido_log_debug("%s: id", __func__);
goto out;
}
return (0);
}
+
+cbor_item_t *
+cbor_build_uint(const uint64_t value)
+{
+ if (value <= UINT8_MAX)
+ return cbor_build_uint8((uint8_t)value);
+ else if (value <= UINT16_MAX)
+ return cbor_build_uint16((uint16_t)value);
+ else if (value <= UINT32_MAX)
+ return cbor_build_uint32((uint32_t)value);
+
+ return cbor_build_uint64(value);
+}
+
+int
+cbor_array_append(cbor_item_t **array, cbor_item_t *item)
+{
+ cbor_item_t **v, *ret;
+ size_t n;
+
+ if ((v = cbor_array_handle(*array)) == NULL ||
+ (n = cbor_array_size(*array)) == SIZE_MAX ||
+ (ret = cbor_new_definite_array(n + 1)) == NULL)
+ return -1;
+ for (size_t i = 0; i < n; i++) {
+ if (cbor_array_push(ret, v[i]) == 0) {
+ cbor_decref(&ret);
+ return -1;
+ }
+ }
+ if (cbor_array_push(ret, item) == 0) {
+ cbor_decref(&ret);
+ return -1;
+ }
+ cbor_decref(array);
+ *array = ret;
+
+ return 0;
+}
+
+int
+cbor_array_drop(cbor_item_t **array, size_t idx)
+{
+ cbor_item_t **v, *ret;
+ size_t n;
+
+ if ((v = cbor_array_handle(*array)) == NULL ||
+ (n = cbor_array_size(*array)) == 0 || idx >= n ||
+ (ret = cbor_new_definite_array(n - 1)) == NULL)
+ return -1;
+ for (size_t i = 0; i < n; i++) {
+ if (i != idx && cbor_array_push(ret, v[i]) == 0) {
+ cbor_decref(&ret);
+ return -1;
+ }
+ }
+ cbor_decref(array);
+ *array = ret;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <zlib.h>
+#include "fido.h"
+
+#define BOUND (1024UL * 1024UL)
+
+static int
+do_compress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz, int decomp)
+{
+ u_long ilen, olen;
+ int r;
+
+ 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)
+ 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) {
+ fido_blob_reset(out);
+ return FIDO_ERR_COMPRESS;
+ }
+ out->len = olen;
+
+ return FIDO_OK;
+}
+
+int
+fido_compress(fido_blob_t *out, const fido_blob_t *in)
+{
+ return do_compress(out, in, 0, 0);
+}
+
+int
+fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
+{
+ return do_compress(out, in, origsiz, 1);
+}
--- /dev/null
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include "fido.h"
+#include "fido/config.h"
+#include "fido/es256.h"
+
+#define CMD_ENABLE_ENTATTEST 0x01
+#define CMD_TOGGLE_ALWAYS_UV 0x02
+#define CMD_SET_PIN_MINLEN 0x03
+
+static int
+config_prepare_hmac(uint8_t subcmd, const cbor_item_t *item, fido_blob_t *hmac)
+{
+ uint8_t prefix[32 + 2 * sizeof(uint8_t)], cbor[128];
+ size_t cbor_len;
+
+ memset(prefix, 0xff, sizeof(prefix));
+ prefix[sizeof(prefix) - 2] = CTAP_CBOR_CONFIG;
+ prefix[sizeof(prefix) - 1] = subcmd;
+
+ if ((cbor_len = cbor_serialize(item, cbor, sizeof(cbor))) == 0) {
+ fido_log_debug("%s: cbor_serialize", __func__);
+ return -1;
+ }
+ if ((hmac->ptr = malloc(cbor_len + sizeof(prefix))) == NULL) {
+ fido_log_debug("%s: malloc", __func__);
+ return -1;
+ }
+ memcpy(hmac->ptr, prefix, sizeof(prefix));
+ memcpy(hmac->ptr + sizeof(prefix), cbor, cbor_len);
+ hmac->len = cbor_len + sizeof(prefix);
+
+ return 0;
+}
+
+static int
+config_tx(fido_dev_t *dev, uint8_t subcmd, cbor_item_t **paramv, size_t paramc,
+ const char *pin)
+{
+ cbor_item_t *argv[4];
+ es256_pk_t *pk = NULL;
+ fido_blob_t *ecdh = NULL, f, hmac;
+ const uint8_t cmd = CTAP_CBOR_CONFIG;
+ int r = FIDO_ERR_INTERNAL;
+
+ memset(&f, 0, sizeof(f));
+ memset(&hmac, 0, sizeof(hmac));
+ memset(&argv, 0, sizeof(argv));
+
+ /* subCommand */
+ if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
+ fido_log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+
+ /* pinProtocol, pinAuth */
+ if (pin != NULL || (fido_dev_supports_permissions(dev) &&
+ fido_dev_has_uv(dev))) {
+ if ((argv[1] = cbor_flatten_vector(paramv, paramc)) == NULL) {
+ fido_log_debug("%s: cbor_flatten_vector", __func__);
+ goto fail;
+ }
+ if (config_prepare_hmac(subcmd, argv[1], &hmac) < 0) {
+ fido_log_debug("%s: config_prepare_hmac", __func__);
+ goto fail;
+ }
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != 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) {
+ 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_log_debug("%s: fido_tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ es256_pk_free(&pk);
+ fido_blob_free(&ecdh);
+ free(f.ptr);
+ free(hmac.ptr);
+
+ return r;
+}
+
+static int
+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)
+ 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));
+}
+
+static int
+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)
+ 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);
+}
+
+static int
+config_pin_minlen_tx(fido_dev_t *dev, size_t len, bool force, const char *pin)
+{
+ cbor_item_t *argv[3];
+ int r;
+
+ memset(argv, 0, sizeof(argv));
+
+ if ((!len && !force) || len > UINT8_MAX) {
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ if (len && (argv[0] = cbor_build_uint8((uint8_t)len)) == NULL) {
+ fido_log_debug("%s: cbor_encode_uint8", __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) {
+ fido_log_debug("%s: config_tx", __func__);
+ goto fail;
+ }
+
+fail:
+ cbor_vector_free(argv, nitems(argv));
+
+ return r;
+}
+
+static int
+config_pin_minlen(fido_dev_t *dev, size_t len, bool force, const char *pin,
+ int ms)
+{
+ int r;
+
+ if ((r = config_pin_minlen_tx(dev, len, force, pin)) != 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
+fido_dev_force_pin_change(fido_dev_t *dev, const char *pin)
+{
+ return config_pin_minlen(dev, 0, true, pin, -1);
+}
* license that can be found in the LICENSE file.
*/
-#include <openssl/ec.h>
-#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
-#include <string.h>
#include "fido.h"
#include "fido/es256.h"
case 1: /* fmt */
return (cbor_decode_fmt(val, &cred->fmt));
case 2: /* authdata */
+ if (fido_blob_decode(val, &cred->authdata_raw) < 0) {
+ fido_log_debug("%s: fido_blob_decode", __func__);
+ return (-1);
+ }
return (cbor_decode_cred_authdata(val, cred->type,
&cred->authdata_cbor, &cred->authdata, &cred->attcred,
&cred->authdata_ext));
case 3: /* attestation statement */
return (cbor_decode_attstmt(val, &cred->attstmt));
+ case 5: /* large blob key */
+ return (fido_blob_decode(val, &cred->largeblob_key));
default: /* ignore */
fido_log_debug("%s: cbor type", __func__);
return (0);
{
fido_blob_t f;
fido_blob_t *ecdh = NULL;
+ fido_opt_t uv = cred->uv;
es256_pk_t *pk = NULL;
cbor_item_t *argv[9];
+ const uint8_t cmd = CTAP_CBOR_MAKECRED;
int r;
memset(&f, 0, sizeof(f));
/* extensions */
if (cred->ext.mask)
- if ((argv[5] = cbor_encode_extensions(&cred->ext)) == NULL) {
- fido_log_debug("%s: cbor_encode_extensions", __func__);
+ if ((argv[5] = cbor_encode_cred_ext(&cred->ext,
+ &cred->blob)) == NULL) {
+ fido_log_debug("%s: cbor_encode_cred_ext", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
- /* options */
- if (cred->rk != FIDO_OPT_OMIT || cred->uv != FIDO_OPT_OMIT)
- if ((argv[6] = cbor_encode_options(cred->rk,
- cred->uv)) == NULL) {
- fido_log_debug("%s: cbor_encode_options", __func__);
- r = FIDO_ERR_INTERNAL;
- goto fail;
- }
-
- /* pin authentication */
- if (pin) {
+ /* user verification */
+ if (pin != NULL || (uv == FIDO_OPT_TRUE &&
+ fido_dev_supports_permissions(dev))) {
if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
- if ((r = cbor_add_pin_params(dev, &cred->cdh, pk, ecdh, pin,
- &argv[7], &argv[8])) != FIDO_OK) {
- fido_log_debug("%s: cbor_add_pin_params", __func__);
+ if ((r = cbor_add_uv_params(dev, cmd, &cred->cdh, pk, ecdh,
+ pin, cred->rp.id, &argv[7], &argv[8])) != FIDO_OK) {
+ fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
+ uv = FIDO_OPT_OMIT;
}
+ /* options */
+ if (cred->rk != FIDO_OPT_OMIT || uv != FIDO_OPT_OMIT)
+ if ((argv[6] = cbor_encode_cred_opt(cred->rk, uv)) == NULL) {
+ fido_log_debug("%s: cbor_encode_cred_opt", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
/* framing and transmission */
- if (cbor_build_frame(CTAP_CBOR_MAKECRED, argv, nitems(argv), &f) < 0 ||
+ if (cbor_build_frame(cmd, 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;
}
if (cred->fmt == NULL || fido_blob_is_empty(&cred->authdata_cbor) ||
- fido_blob_is_empty(&cred->attcred.id) ||
- fido_blob_is_empty(&cred->attstmt.sig)) {
+ fido_blob_is_empty(&cred->attcred.id)) {
fido_cred_reset_rx(cred);
return (FIDO_ERR_INVALID_CBOR);
}
}
static int
-fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int ms)
+fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
+ int ms)
{
int r;
int
fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
{
+#ifdef USE_WINHELLO
+ if (dev->flags & FIDO_DEV_WINHELLO)
+ return (fido_winhello_make_cred(dev, cred, pin));
+#endif
if (fido_dev_is_fido2(dev) == false) {
if (pin != NULL || cred->rk == FIDO_OPT_TRUE ||
cred->ext.mask != 0)
}
static int
-check_extensions(const fido_cred_ext_t *authdata_ext, const fido_cred_ext_t *ext)
+check_extensions(const fido_cred_ext_t *authdata_ext,
+ const fido_cred_ext_t *ext)
{
- return (timingsafe_bcmp(authdata_ext, ext, sizeof(*authdata_ext)));
+ fido_cred_ext_t tmp;
+
+ /* XXX: largeBlobKey is not part of the extensions map */
+ memcpy(&tmp, ext, sizeof(tmp));
+ tmp.mask &= ~FIDO_EXT_LARGEBLOB_KEY;
+
+ return (timingsafe_bcmp(authdata_ext, &tmp, sizeof(*authdata_ext)));
}
int
r = FIDO_ERR_INTERNAL;
goto out;
}
- } else {
+ } else if (!strcmp(cred->fmt, "fido-u2f")) {
if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
sizeof(cred->authdata.rp_id_hash), &cred->cdh,
&cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
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) {
r = FIDO_ERR_INTERNAL;
goto out;
}
- } else {
+ } else if (!strcmp(cred->fmt, "fido-u2f")) {
if (get_signed_hash_u2f(&dgst, cred->authdata.rp_id_hash,
sizeof(cred->authdata.rp_id_hash), &cred->cdh,
&cred->attcred.id, &cred->attcred.pubkey.es256) < 0) {
r = FIDO_ERR_INTERNAL;
goto out;
}
+ } else {
+ fido_log_debug("%s: unknown fmt %s", __func__, cred->fmt);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto out;
}
switch (cred->attcred.type) {
static void
fido_cred_clean_authdata(fido_cred_t *cred)
{
- free(cred->authdata_cbor.ptr);
- free(cred->attcred.id.ptr);
+ fido_blob_reset(&cred->authdata_cbor);
+ fido_blob_reset(&cred->authdata_raw);
+ fido_blob_reset(&cred->attcred.id);
memset(&cred->authdata_ext, 0, sizeof(cred->authdata_ext));
- memset(&cred->authdata_cbor, 0, sizeof(cred->authdata_cbor));
memset(&cred->authdata, 0, sizeof(cred->authdata));
memset(&cred->attcred, 0, sizeof(cred->attcred));
}
void
fido_cred_reset_tx(fido_cred_t *cred)
{
- free(cred->cdh.ptr);
+ fido_blob_reset(&cred->cd);
+ fido_blob_reset(&cred->cdh);
+ fido_blob_reset(&cred->user.id);
+ fido_blob_reset(&cred->blob);
+
free(cred->rp.id);
free(cred->rp.name);
- free(cred->user.id.ptr);
free(cred->user.icon);
free(cred->user.name);
free(cred->user.display_name);
fido_free_blob_array(&cred->excl);
- memset(&cred->cdh, 0, sizeof(cred->cdh));
memset(&cred->rp, 0, sizeof(cred->rp));
memset(&cred->user, 0, sizeof(cred->user));
memset(&cred->excl, 0, sizeof(cred->excl));
cred->uv = FIDO_OPT_OMIT;
}
-static void
-fido_cred_clean_x509(fido_cred_t *cred)
-{
- free(cred->attstmt.x5c.ptr);
- cred->attstmt.x5c.ptr = NULL;
- cred->attstmt.x5c.len = 0;
-}
-
-static void
-fido_cred_clean_sig(fido_cred_t *cred)
-{
- free(cred->attstmt.sig.ptr);
- cred->attstmt.sig.ptr = NULL;
- cred->attstmt.sig.len = 0;
-}
-
void
fido_cred_reset_rx(fido_cred_t *cred)
{
free(cred->fmt);
cred->fmt = NULL;
-
fido_cred_clean_authdata(cred);
- fido_cred_clean_x509(cred);
- fido_cred_clean_sig(cred);
+ fido_blob_reset(&cred->attstmt.x5c);
+ fido_blob_reset(&cred->attstmt.sig);
+ fido_blob_reset(&cred->largeblob_key);
}
void
if (cred_p == NULL || (cred = *cred_p) == NULL)
return;
-
fido_cred_reset_tx(cred);
fido_cred_reset_rx(cred);
-
free(cred);
-
*cred_p = NULL;
}
{
cbor_item_t *item = NULL;
struct cbor_load_result cbor;
- int r;
+ int r = FIDO_ERR_INVALID_ARGUMENT;
fido_cred_clean_authdata(cred);
- if (ptr == NULL || len == 0) {
- r = FIDO_ERR_INVALID_ARGUMENT;
+ if (ptr == NULL || len == 0)
goto fail;
- }
if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
fido_log_debug("%s: cbor_load", __func__);
- r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+
+ if (fido_blob_decode(item, &cred->authdata_raw) < 0) {
+ fido_log_debug("%s: fido_blob_decode", __func__);
goto fail;
}
if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
&cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
- r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}
fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
size_t len)
{
- cbor_item_t *item = NULL;
- int r;
+ cbor_item_t *item = NULL;
+ int r = FIDO_ERR_INVALID_ARGUMENT;
fido_cred_clean_authdata(cred);
- if (ptr == NULL || len == 0) {
- r = FIDO_ERR_INVALID_ARGUMENT;
+ if (ptr == NULL || len == 0)
+ goto fail;
+
+ if (fido_blob_set(&cred->authdata_raw, ptr, len) < 0) {
+ fido_log_debug("%s: fido_blob_set", __func__);
+ r = FIDO_ERR_INTERNAL;
goto fail;
}
if (cbor_decode_cred_authdata(item, cred->type, &cred->authdata_cbor,
&cred->authdata, &cred->attcred, &cred->authdata_ext) < 0) {
fido_log_debug("%s: cbor_decode_cred_authdata", __func__);
- r = FIDO_ERR_INVALID_ARGUMENT;
goto fail;
}
}
int
-fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+fido_cred_set_id(fido_cred_t *cred, const unsigned char *ptr, size_t len)
{
- unsigned char *x509;
+ if (fido_blob_set(&cred->attcred.id, ptr, len) < 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
- fido_cred_clean_x509(cred);
+ return (FIDO_OK);
+}
- if (ptr == NULL || len == 0)
+int
+fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+ if (fido_blob_set(&cred->attstmt.x5c, ptr, len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
- if ((x509 = malloc(len)) == NULL)
- return (FIDO_ERR_INTERNAL);
-
- memcpy(x509, ptr, len);
- cred->attstmt.x5c.ptr = x509;
- cred->attstmt.x5c.len = len;
return (FIDO_OK);
}
int
fido_cred_set_sig(fido_cred_t *cred, const unsigned char *ptr, size_t len)
{
- unsigned char *sig;
-
- fido_cred_clean_sig(cred);
-
- if (ptr == NULL || len == 0)
+ if (fido_blob_set(&cred->attstmt.sig, ptr, len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
- if ((sig = malloc(len)) == NULL)
- return (FIDO_ERR_INTERNAL);
-
- memcpy(sig, ptr, len);
- cred->attstmt.sig.ptr = sig;
- cred->attstmt.sig.len = len;
return (FIDO_OK);
}
return (FIDO_OK);
}
+int
+fido_cred_set_clientdata(fido_cred_t *cred, const unsigned char *data,
+ size_t data_len)
+{
+ if (!fido_blob_is_empty(&cred->cdh) ||
+ fido_blob_set(&cred->cd, data, data_len) < 0) {
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+ if (fido_sha256(&cred->cdh, data, data_len) < 0) {
+ fido_blob_reset(&cred->cd);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ return (FIDO_OK);
+}
+
int
fido_cred_set_clientdata_hash(fido_cred_t *cred, const unsigned char *hash,
size_t hash_len)
{
- if (fido_blob_set(&cred->cdh, hash, hash_len) < 0)
+ if (!fido_blob_is_empty(&cred->cd) ||
+ fido_blob_set(&cred->cdh, hash, hash_len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
return (FIDO_OK);
up->icon = NULL;
}
- if (user_id != NULL) {
- if ((up->id.ptr = malloc(user_id_len)) == NULL)
- goto fail;
- memcpy(up->id.ptr, user_id, user_id_len);
- up->id.len = user_id_len;
- }
+ if (user_id != NULL && fido_blob_set(&up->id, user_id, user_id_len) < 0)
+ goto fail;
if (name != NULL && (up->name = strdup(name)) == NULL)
goto fail;
if (display_name != NULL &&
if (ext == 0)
cred->ext.mask = 0;
else {
- if (ext != FIDO_EXT_HMAC_SECRET &&
- ext != FIDO_EXT_CRED_PROTECT)
+ if ((ext & FIDO_EXT_CRED_MASK) != ext)
return (FIDO_ERR_INVALID_ARGUMENT);
cred->ext.mask |= ext;
}
return (FIDO_OK);
}
+int
+fido_cred_set_blob(fido_cred_t *cred, const unsigned char *ptr, size_t len)
+{
+ if (ptr == NULL || len == 0)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ if (fido_blob_set(&cred->blob, ptr, len) < 0)
+ return (FIDO_ERR_INTERNAL);
+
+ cred->ext.mask |= FIDO_EXT_CRED_BLOB;
+
+ return (FIDO_OK);
+}
+
int
fido_cred_set_fmt(fido_cred_t *cred, const char *fmt)
{
if (fmt == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
- if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f"))
+ if (strcmp(fmt, "packed") && strcmp(fmt, "fido-u2f") &&
+ strcmp(fmt, "none"))
return (FIDO_ERR_INVALID_ARGUMENT);
if ((cred->fmt = strdup(fmt)) == NULL)
return (cred->authdata.flags);
}
+uint32_t
+fido_cred_sigcount(const fido_cred_t *cred)
+{
+ return (cred->authdata.sigcount);
+}
+
const unsigned char *
fido_cred_clientdata_hash_ptr(const fido_cred_t *cred)
{
return (cred->authdata_cbor.len);
}
+const unsigned char *
+fido_cred_authdata_raw_ptr(const fido_cred_t *cred)
+{
+ return (cred->authdata_raw.ptr);
+}
+
+size_t
+fido_cred_authdata_raw_len(const fido_cred_t *cred)
+{
+ return (cred->authdata_raw.len);
+}
+
const unsigned char *
fido_cred_pubkey_ptr(const fido_cred_t *cred)
{
{
return (cred->user.id.len);
}
+
+const unsigned char *
+fido_cred_largeblob_key_ptr(const fido_cred_t *cred)
+{
+ return (cred->largeblob_key.ptr);
+}
+
+size_t
+fido_cred_largeblob_key_len(const fido_cred_t *cred)
+{
+ return (cred->largeblob_key.len);
+}
/*
- * 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 <openssl/sha.h>
-#include <string.h>
-
#include "fido.h"
#include "fido/credman.h"
#include "fido/es256.h"
#define CMD_RK_BEGIN 0x04
#define CMD_RK_NEXT 0x05
#define CMD_DELETE_CRED 0x06
+#define CMD_UPDATE_CRED 0x07
static int
credman_grow_array(void **ptr, size_t *n_alloc, size_t *n_rx, size_t n,
}
static int
-credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param,
+credman_prepare_hmac(uint8_t cmd, const void *body, cbor_item_t **param,
fido_blob_t *hmac_data)
{
- cbor_item_t *param_cbor[2];
+ cbor_item_t *param_cbor[3];
+ const fido_cred_t *cred;
size_t n;
int ok = -1;
switch (cmd) {
case CMD_RK_BEGIN:
n = 1;
- param_cbor[n - 1] = fido_blob_encode(body);
+ if ((param_cbor[0] = fido_blob_encode(body)) == NULL) {
+ fido_log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
break;
case CMD_DELETE_CRED:
n = 2;
- param_cbor[n - 1] = cbor_encode_pubkey(body);
+ if ((param_cbor[1] = cbor_encode_pubkey(body)) == NULL) {
+ fido_log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+ break;
+ case CMD_UPDATE_CRED:
+ n = 3;
+ cred = body;
+ param_cbor[1] = cbor_encode_pubkey(&cred->attcred.id);
+ param_cbor[2] = cbor_encode_user_entity(&cred->user);
+ if (param_cbor[1] == NULL || param_cbor[2] == NULL) {
+ fido_log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
break;
default:
fido_log_debug("%s: unknown cmd=0x%02x", __func__, cmd);
return (-1);
}
- if (param_cbor[n - 1] == NULL) {
- fido_log_debug("%s: cbor encode", __func__);
- return (-1);
- }
if ((*param = cbor_flatten_vector(param_cbor, n)) == NULL) {
fido_log_debug("%s: cbor_flatten_vector", __func__);
goto fail;
}
static int
-credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
- const char *pin)
+credman_tx(fido_dev_t *dev, uint8_t subcmd, const void *param, const char *pin,
+ const char *rp_id, fido_opt_t uv)
{
fido_blob_t f;
fido_blob_t *ecdh = NULL;
fido_blob_t hmac;
es256_pk_t *pk = NULL;
cbor_item_t *argv[4];
+ const uint8_t cmd = CTAP_CBOR_CRED_MGMT_PRE;
int r = FIDO_ERR_INTERNAL;
memset(&f, 0, sizeof(f));
memset(&hmac, 0, sizeof(hmac));
memset(&argv, 0, sizeof(argv));
+ if (fido_dev_is_fido2(dev) == false) {
+ fido_log_debug("%s: fido_dev_is_fido2", __func__);
+ r = FIDO_ERR_INVALID_COMMAND;
+ goto fail;
+ }
+
/* subCommand */
- if ((argv[0] = cbor_build_uint8(cmd)) == NULL) {
+ if ((argv[0] = cbor_build_uint8(subcmd)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
goto fail;
}
/* pinProtocol, pinAuth */
- if (pin != NULL) {
- if (credman_prepare_hmac(cmd, param, &argv[1], &hmac) < 0) {
+ if (pin != NULL || uv == FIDO_OPT_TRUE) {
+ if (credman_prepare_hmac(subcmd, param, &argv[1], &hmac) < 0) {
fido_log_debug("%s: credman_prepare_hmac", __func__);
goto fail;
}
fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
- if ((r = cbor_add_pin_params(dev, &hmac, pk, ecdh, pin,
- &argv[3], &argv[2])) != FIDO_OK) {
- fido_log_debug("%s: cbor_add_pin_params", __func__);
+ if ((r = cbor_add_uv_params(dev, cmd, &hmac, pk, ecdh, pin,
+ rp_id, &argv[3], &argv[2])) != FIDO_OK) {
+ fido_log_debug("%s: cbor_add_uv_params", __func__);
goto fail;
}
}
/* framing and transmission */
- if (cbor_build_frame(CTAP_CBOR_CRED_MGMT_PRE, argv, nitems(argv),
- &f) < 0 || fido_tx(dev, CTAP_CMD_CBOR, f.ptr, f.len) < 0) {
+ if (cbor_build_frame(cmd, 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;
{
int r;
- if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin)) != FIDO_OK ||
+ if ((r = credman_tx(dev, CMD_CRED_METADATA, NULL, pin, NULL,
+ FIDO_OPT_TRUE)) != 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)
{
- if (fido_dev_is_fido2(dev) == false)
- return (FIDO_ERR_INVALID_COMMAND);
- if (pin == NULL)
- return (FIDO_ERR_INVALID_ARGUMENT);
-
return (credman_get_metadata_wait(dev, metadata, pin, -1));
}
}
switch (cbor_get_uint8(key)) {
- case 6: /* user entity */
+ case 6:
return (cbor_decode_user(val, &cred->user));
case 7:
return (cbor_decode_cred_id(val, &cred->attcred.id));
fido_cred_set_prot(cred, (int)prot) != FIDO_OK)
return (-1);
return (0);
+ case 11:
+ return (fido_blob_decode(val, &cred->largeblob_key));
default:
fido_log_debug("%s: cbor type", __func__);
return (0); /* ignore */
rp_dgst.ptr = dgst;
rp_dgst.len = sizeof(dgst);
- if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin)) != FIDO_OK ||
+ if ((r = credman_tx(dev, CMD_RK_BEGIN, &rp_dgst, pin, rp_id,
+ FIDO_OPT_TRUE)) != FIDO_OK ||
(r = credman_rx_rk(dev, rk, ms)) != FIDO_OK)
return (r);
while (rk->n_rx < rk->n_alloc) {
- if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL)) != FIDO_OK ||
+ if ((r = credman_tx(dev, CMD_RK_NEXT, NULL, NULL, NULL,
+ FIDO_OPT_FALSE)) != 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)
{
- if (fido_dev_is_fido2(dev) == false)
- return (FIDO_ERR_INVALID_COMMAND);
- if (pin == NULL)
- return (FIDO_ERR_INVALID_ARGUMENT);
-
return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
}
if (fido_blob_set(&cred, cred_id, cred_id_len) < 0)
return (FIDO_ERR_INVALID_ARGUMENT);
- if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin)) != FIDO_OK ||
+ if ((r = credman_tx(dev, CMD_DELETE_CRED, &cred, pin, NULL,
+ FIDO_OPT_TRUE)) != 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)
{
- if (fido_dev_is_fido2(dev) == false)
- return (FIDO_ERR_INVALID_COMMAND);
- if (pin == NULL)
- return (FIDO_ERR_INVALID_ARGUMENT);
-
return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
}
free(rp->ptr[i].rp_entity.name);
rp->ptr[i].rp_entity.id = NULL;
rp->ptr[i].rp_entity.name = NULL;
- free(rp->ptr[i].rp_id_hash.ptr);
- memset(&rp->ptr[i].rp_id_hash, 0,
- sizeof(rp->ptr[i].rp_id_hash));
+ fido_blob_reset(&rp->ptr[i].rp_id_hash);
}
free(rp->ptr);
{
int r;
- if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin)) != FIDO_OK ||
+ if ((r = credman_tx(dev, CMD_RP_BEGIN, NULL, pin, NULL,
+ FIDO_OPT_TRUE)) != FIDO_OK ||
(r = credman_rx_rp(dev, rp, ms)) != FIDO_OK)
return (r);
while (rp->n_rx < rp->n_alloc) {
- if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL)) != FIDO_OK ||
+ if ((r = credman_tx(dev, CMD_RP_NEXT, NULL, NULL, NULL,
+ FIDO_OPT_FALSE)) != 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)
{
- if (fido_dev_is_fido2(dev) == false)
- return (FIDO_ERR_INVALID_COMMAND);
- if (pin == NULL)
- return (FIDO_ERR_INVALID_ARGUMENT);
-
return (credman_get_rp_wait(dev, rp, pin, -1));
}
+static int
+credman_set_dev_rk_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin,
+ int ms)
+{
+ int r;
+
+ if ((r = credman_tx(dev, CMD_UPDATE_CRED, cred, pin, NULL,
+ FIDO_OPT_TRUE)) != FIDO_OK ||
+ (r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+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));
+}
+
fido_credman_rk_t *
fido_credman_rk_new(void)
{
* license that can be found in the LICENSE file.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#ifdef HAVE_SYS_RANDOM_H
-#include <sys/random.h>
-#endif
-
#include <openssl/sha.h>
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
#include "fido.h"
-#if defined(_WIN32)
-#include <windows.h>
-
-#include <winternl.h>
-#include <winerror.h>
-#include <stdio.h>
-#include <bcrypt.h>
-#include <sal.h>
-
-static int
-obtain_nonce(uint64_t *nonce)
-{
- NTSTATUS status;
-
- status = BCryptGenRandom(NULL, (unsigned char *)nonce, sizeof(*nonce),
- BCRYPT_USE_SYSTEM_PREFERRED_RNG);
-
- if (!NT_SUCCESS(status))
- return (-1);
-
- return (0);
-}
-#elif defined(HAVE_ARC4RANDOM_BUF)
-static int
-obtain_nonce(uint64_t *nonce)
-{
- arc4random_buf(nonce, sizeof(*nonce));
- return (0);
-}
-#elif defined(HAVE_GETRANDOM)
-static int
-obtain_nonce(uint64_t *nonce)
-{
- if (getrandom(nonce, sizeof(*nonce), 0) < 0)
- return (-1);
- return (0);
-}
-#elif defined(HAVE_DEV_URANDOM)
-static int
-obtain_nonce(uint64_t *nonce)
-{
- int fd = -1;
- int ok = -1;
- ssize_t r;
-
- if ((fd = open(FIDO_RANDOM_DEV, O_RDONLY)) < 0)
- goto fail;
- if ((r = read(fd, nonce, sizeof(*nonce))) < 0 ||
- (size_t)r != sizeof(*nonce))
- goto fail;
-
- ok = 0;
-fail:
- if (fd != -1)
- close(fd);
-
- return (ok);
-}
-#else
-#error "please provide an implementation of obtain_nonce() for your platform"
-#endif /* _WIN32 */
-
#ifndef TLS
#define TLS
#endif
} 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,
#endif
static void
-fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
+fido_dev_set_extension_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
{
- char * const *ptr;
- const bool *val;
- size_t len;
-
- ptr = fido_cbor_info_extensions_ptr(info);
- len = fido_cbor_info_extensions_len(info);
+ char * const *ptr = fido_cbor_info_extensions_ptr(info);
+ size_t len = fido_cbor_info_extensions_len(info);
for (size_t i = 0; i < len; i++)
if (strcmp(ptr[i], "credProtect") == 0)
dev->flags |= FIDO_DEV_CRED_PROT;
+}
- ptr = fido_cbor_info_options_name_ptr(info);
- val = fido_cbor_info_options_value_ptr(info);
- len = fido_cbor_info_options_len(info);
+static void
+fido_dev_set_option_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
+{
+ char * const *ptr = fido_cbor_info_options_name_ptr(info);
+ const bool *val = fido_cbor_info_options_value_ptr(info);
+ size_t len = fido_cbor_info_options_len(info);
for (size_t i = 0; i < len; i++)
if (strcmp(ptr[i], "clientPin") == 0) {
- if (val[i] == true)
- dev->flags |= FIDO_DEV_PIN_SET;
- else
- dev->flags |= 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;
+ } else if (strcmp(ptr[i], "pinUvAuthToken") == 0) {
+ if (val[i])
+ dev->flags |= FIDO_DEV_TOKEN_PERMS;
}
}
+static void
+fido_dev_set_protocol_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
+{
+ const uint8_t *ptr = fido_cbor_info_protocols_ptr(info);
+ size_t len = fido_cbor_info_protocols_len(info);
+
+ for (size_t i = 0; i < len; i++)
+ switch (ptr[i]) {
+ case CTAP_PIN_PROTOCOL1:
+ dev->flags |= FIDO_DEV_PIN_PROTOCOL1;
+ break;
+ case CTAP_PIN_PROTOCOL2:
+ dev->flags |= FIDO_DEV_PIN_PROTOCOL2;
+ break;
+ default:
+ fido_log_debug("%s: unknown protocol %u", __func__,
+ ptr[i]);
+ break;
+ }
+}
+
+static void
+fido_dev_set_flags(fido_dev_t *dev, const fido_cbor_info_t *info)
+{
+ fido_dev_set_extension_flags(dev, info);
+ fido_dev_set_option_flags(dev, info);
+ fido_dev_set_protocol_flags(dev, info);
+}
+
static int
fido_dev_open_tx(fido_dev_t *dev, const char *path)
{
- const uint8_t cmd = CTAP_CMD_INIT;
- int r;
+ int r;
if (dev->io_handle != NULL) {
fido_log_debug("%s: handle=%p", __func__, dev->io_handle);
return (FIDO_ERR_INVALID_ARGUMENT);
}
- if (obtain_nonce(&dev->nonce) < 0) {
- fido_log_debug("%s: obtain_nonce", __func__);
+ if (dev->cid != CTAP_CID_BROADCAST) {
+ fido_log_debug("%s: cid=0x%x", __func__, dev->cid);
+ return (FIDO_ERR_INVALID_ARGUMENT);
+ }
+
+ if (fido_get_random(&dev->nonce, sizeof(dev->nonce)) < 0) {
+ fido_log_debug("%s: fido_get_random", __func__);
return (FIDO_ERR_INTERNAL);
}
goto fail;
}
- if (fido_tx(dev, cmd, &dev->nonce, sizeof(dev->nonce)) < 0) {
+ if (fido_tx(dev, CTAP_CMD_INIT, &dev->nonce, sizeof(dev->nonce)) < 0) {
fido_log_debug("%s: fido_tx", __func__);
r = FIDO_ERR_TX;
goto fail;
r = FIDO_ERR_INTERNAL;
goto fail;
}
- if (fido_dev_get_cbor_info_wait(dev, info, ms) != FIDO_OK) {
+ if ((r = fido_dev_get_cbor_info_wait(dev, info,
+ ms)) != FIDO_OK) {
+ fido_log_debug("%s: fido_dev_cbor_info_wait: %d",
+ __func__, r);
+ if (disable_u2f_fallback)
+ goto fail;
fido_log_debug("%s: falling back to u2f", __func__);
fido_dev_force_u2f(dev);
} else {
}
if (fido_dev_is_fido2(dev) && info != NULL) {
+ dev->maxmsgsize = fido_cbor_info_maxmsgsiz(info);
fido_log_debug("%s: FIDO_MAXMSG=%d, maxmsgsiz=%lu", __func__,
- FIDO_MAXMSG, (unsigned long)fido_cbor_info_maxmsgsiz(info));
+ FIDO_MAXMSG, (unsigned long)dev->maxmsgsize);
}
r = FIDO_OK;
{
int r;
+#ifdef USE_WINHELLO
+ if (strcmp(path, FIDO_WINHELLO_PATH) == 0)
+ return (fido_winhello_open(dev));
+#endif
if ((r = fido_dev_open_tx(dev, path)) != FIDO_OK ||
(r = fido_dev_open_rx(dev, ms)) != FIDO_OK)
return (r);
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);
+#endif
+#ifdef USE_WINHELLO
+ if (fido_dev_register_manifest_func(fido_winhello_manifest) != FIDO_OK)
+ return (FIDO_ERR_INTERNAL);
+#endif
for (curr = manifest_funcs; curr != NULL; curr = curr->next) {
curr_olen = 0;
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,
+ };
+ }
+#endif
+
return (fido_dev_open_wait(dev, path, -1));
}
int
fido_dev_close(fido_dev_t *dev)
{
+#ifdef USE_WINHELLO
+ if (dev->flags & FIDO_DEV_WINHELLO)
+ return (fido_winhello_close(dev));
+#endif
if (dev->io_handle == NULL || dev->io.close == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);
dev->io.close(dev->io_handle);
dev->io_handle = NULL;
+ dev->cid = CTAP_CID_BROADCAST;
return (FIDO_OK);
}
+int
+fido_dev_set_sigmask(fido_dev_t *dev, const fido_sigset_t *sigmask)
+{
+ if (dev->io_own || dev->io_handle == NULL || sigmask == NULL)
+ return (FIDO_ERR_INVALID_ARGUMENT);
+
+#ifdef NFC_LINUX
+ if (dev->transport.rx == fido_nfc_rx)
+ return (fido_nfc_set_sigmask(dev->io_handle, sigmask));
+#endif
+ return (fido_hid_set_sigmask(dev->io_handle, sigmask));
+}
+
int
fido_dev_cancel(fido_dev_t *dev)
{
+#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)
return (FIDO_ERR_TX);
if (fido_dev_supports_pin(dev)) {
if ((argv[7] = cbor_new_definite_bytestring()) == NULL ||
- (argv[8] = cbor_encode_pin_opt()) == NULL) {
+ (argv[8] = cbor_encode_pin_opt(dev)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
goto fail;
}
{
if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
fido_log_init();
+
+ disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK);
}
fido_dev_t *
if ((dev = calloc(1, sizeof(*dev))) == NULL)
return (NULL);
- dev->cid = CTAP_CID_BROADCAST;
-
+#if 0
if (di->io.open == NULL || di->io.close == NULL ||
di->io.read == NULL || di->io.write == NULL) {
fido_log_debug("%s: NULL function", __func__);
fido_dev_free(&dev);
return (NULL);
}
+#endif
dev->io = di->io;
+ dev->io_own = di->transport.tx != NULL || di->transport.rx != NULL;
dev->transport = di->transport;
+ dev->cid = CTAP_CID_BROADCAST;
if ((dev->path = strdup(di->path)) == NULL) {
fido_log_debug("%s: strdup", __func__);
return (dev->attr.flags & FIDO_CAP_CBOR);
}
+bool
+fido_dev_is_winhello(const fido_dev_t *dev)
+{
+ return (dev->flags & FIDO_DEV_WINHELLO);
+}
+
bool
fido_dev_supports_pin(const fido_dev_t *dev)
{
return (dev->flags & FIDO_DEV_CRED_PROT);
}
+bool
+fido_dev_supports_credman(const fido_dev_t *dev)
+{
+ return (dev->flags & FIDO_DEV_CREDMAN);
+}
+
+bool
+fido_dev_supports_uv(const fido_dev_t *dev)
+{
+ return (dev->flags & (FIDO_DEV_UV_SET|FIDO_DEV_UV_UNSET));
+}
+
+bool
+fido_dev_has_uv(const fido_dev_t *dev)
+{
+ return (dev->flags & FIDO_DEV_UV_SET);
+}
+
+bool
+fido_dev_supports_permissions(const fido_dev_t *dev)
+{
+ return (dev->flags & FIDO_DEV_TOKEN_PERMS);
+}
+
void
fido_dev_force_u2f(fido_dev_t *dev)
{
{
dev->attr.flags |= FIDO_CAP_CBOR;
}
+
+uint8_t
+fido_dev_get_pin_protocol(const fido_dev_t *dev)
+{
+ if (dev->flags & FIDO_DEV_PIN_PROTOCOL2)
+ return (CTAP_PIN_PROTOCOL2);
+ else if (dev->flags & FIDO_DEV_PIN_PROTOCOL1)
+ return (CTAP_PIN_PROTOCOL1);
+
+ return (0);
+}
+
+uint64_t
+fido_dev_maxmsgsize(const fido_dev_t *dev)
+{
+ return (dev->maxmsgsize);
+}
/*
- * 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 <openssl/evp.h>
#include <openssl/sha.h>
+#if defined(LIBRESSL_VERSION_NUMBER)
+#include <openssl/hkdf.h>
+#elif OPENSSL_VERSION_NUMBER >= 0x10100000L
+#include <openssl/kdf.h>
+#endif
#include "fido.h"
#include "fido/es256.h"
+#if defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L
static int
-do_ecdh(const es256_sk_t *sk, const es256_pk_t *pk, fido_blob_t **ecdh)
+hkdf_sha256(uint8_t *key, const char *info, const fido_blob_t *secret)
{
- EVP_PKEY *pk_evp = NULL;
- EVP_PKEY *sk_evp = NULL;
- EVP_PKEY_CTX *ctx = NULL;
- fido_blob_t *secret = NULL;
- int ok = -1;
+ const EVP_MD *md;
+ uint8_t salt[32];
- *ecdh = NULL;
+ memset(salt, 0, sizeof(salt));
+ if ((md = EVP_sha256()) == NULL ||
+ HKDF(key, SHA256_DIGEST_LENGTH, md, secret->ptr, secret->len, salt,
+ sizeof(salt), (const uint8_t *)info, strlen(info)) != 1)
+ return -1;
+
+ return 0;
+}
+#else
+static int
+hkdf_sha256(uint8_t *key, char *info, fido_blob_t *secret)
+{
+ const EVP_MD *const_md;
+ EVP_MD *md = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t keylen = SHA256_DIGEST_LENGTH;
+ uint8_t salt[32];
+ int ok = -1;
+
+ memset(salt, 0, sizeof(salt));
+ if (secret->len > INT_MAX || strlen(info) > INT_MAX) {
+ fido_log_debug("%s: invalid param", __func__);
+ goto fail;
+ }
+ if ((const_md = EVP_sha256()) == NULL ||
+ (md = EVP_MD_meth_dup(const_md)) == NULL ||
+ (ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, NULL)) == NULL) {
+ fido_log_debug("%s: init", __func__);
+ goto fail;
+ }
+ if (EVP_PKEY_derive_init(ctx) < 1 ||
+ 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) {
+ fido_log_debug("%s: EVP_PKEY_CTX", __func__);
+ goto fail;
+ }
+ if (EVP_PKEY_derive(ctx, key, &keylen) < 1) {
+ fido_log_debug("%s: EVP_PKEY_derive", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ if (md != NULL)
+ EVP_MD_meth_free(md);
+ if (ctx != NULL)
+ EVP_PKEY_CTX_free(ctx);
+
+ return ok;
+}
+#endif /* defined(LIBRESSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+static int
+kdf(uint8_t prot, fido_blob_t *key, /* const */ fido_blob_t *secret)
+{
+ char hmac_info[] = "CTAP2 HMAC key"; /* const */
+ char aes_info[] = "CTAP2 AES key"; /* const */
+
+ switch (prot) {
+ case CTAP_PIN_PROTOCOL1:
+ /* use sha256 on the resulting secret */
+ key->len = SHA256_DIGEST_LENGTH;
+ if ((key->ptr = calloc(1, key->len)) == NULL ||
+ SHA256(secret->ptr, secret->len, key->ptr) != key->ptr) {
+ fido_log_debug("%s: SHA256", __func__);
+ return -1;
+ }
+ break;
+ case CTAP_PIN_PROTOCOL2:
+ /* use two instances of hkdf-sha256 on the resulting secret */
+ key->len = 2 * SHA256_DIGEST_LENGTH;
+ if ((key->ptr = calloc(1, key->len)) == NULL ||
+ hkdf_sha256(key->ptr, hmac_info, secret) < 0 ||
+ hkdf_sha256(key->ptr + SHA256_DIGEST_LENGTH, aes_info,
+ secret) < 0) {
+ fido_log_debug("%s: hkdf", __func__);
+ return -1;
+ }
+ break;
+ default:
+ fido_log_debug("%s: unknown pin protocol %u", __func__, prot);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+do_ecdh(const fido_dev_t *dev, const es256_sk_t *sk, const es256_pk_t *pk,
+ fido_blob_t **ecdh)
+{
+ EVP_PKEY *pk_evp = NULL;
+ EVP_PKEY *sk_evp = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ fido_blob_t *secret = NULL;
+ int ok = -1;
- /* allocate blobs for secret & ecdh */
+ *ecdh = NULL;
if ((secret = fido_blob_new()) == NULL ||
(*ecdh = fido_blob_new()) == NULL)
goto fail;
-
- /* wrap the keys as openssl objects */
if ((pk_evp = es256_pk_to_EVP_PKEY(pk)) == NULL ||
(sk_evp = es256_sk_to_EVP_PKEY(sk)) == NULL) {
fido_log_debug("%s: es256_to_EVP_PKEY", __func__);
goto fail;
}
-
- /* set ecdh parameters */
if ((ctx = EVP_PKEY_CTX_new(sk_evp, NULL)) == NULL ||
EVP_PKEY_derive_init(ctx) <= 0 ||
EVP_PKEY_derive_set_peer(ctx, pk_evp) <= 0) {
fido_log_debug("%s: EVP_PKEY_derive_init", __func__);
goto fail;
}
-
- /* perform ecdh */
if (EVP_PKEY_derive(ctx, NULL, &secret->len) <= 0 ||
(secret->ptr = calloc(1, secret->len)) == NULL ||
EVP_PKEY_derive(ctx, secret->ptr, &secret->len) <= 0) {
fido_log_debug("%s: EVP_PKEY_derive", __func__);
goto fail;
}
-
- /* use sha256 as a kdf on the resulting secret */
- (*ecdh)->len = SHA256_DIGEST_LENGTH;
- if (((*ecdh)->ptr = calloc(1, (*ecdh)->len)) == NULL ||
- SHA256(secret->ptr, secret->len, (*ecdh)->ptr) != (*ecdh)->ptr) {
- fido_log_debug("%s: sha256", __func__);
+ if (kdf(fido_dev_get_pin_protocol(dev), *ecdh, secret) < 0) {
+ fido_log_debug("%s: kdf", __func__);
goto fail;
}
fido_blob_free(&secret);
- return (ok);
+ return ok;
}
int
fido_do_ecdh(fido_dev_t *dev, es256_pk_t **pk, fido_blob_t **ecdh)
{
- es256_sk_t *sk = NULL; /* our private key */
- es256_pk_t *ak = NULL; /* authenticator's public key */
- int r;
-
- *pk = NULL; /* our public key; returned */
- *ecdh = NULL; /* shared ecdh secret; returned */
+ es256_sk_t *sk = NULL; /* our private key */
+ es256_pk_t *ak = NULL; /* authenticator's public key */
+ int r;
+ *pk = NULL;
+ *ecdh = NULL;
if ((sk = es256_sk_new()) == NULL || (*pk = es256_pk_new()) == NULL) {
r = FIDO_ERR_INTERNAL;
goto fail;
}
-
if (es256_sk_create(sk) < 0 || es256_derive_pk(sk, *pk) < 0) {
fido_log_debug("%s: es256_derive_pk", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
-
if ((ak = es256_pk_new()) == NULL ||
fido_dev_authkey(dev, ak) != FIDO_OK) {
fido_log_debug("%s: fido_dev_authkey", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
}
-
- if (do_ecdh(sk, ak, ecdh) < 0) {
+ if (do_ecdh(dev, sk, ak, ecdh) < 0) {
fido_log_debug("%s: do_ecdh", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
fido_blob_free(ecdh);
}
- return (r);
+ return r;
}
*/
#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/evp.h>
#include <openssl/obj_mac.h>
-#include <string.h>
#include "fido.h"
#include "fido/eddsa.h"
if (pkp == NULL || (pk = *pkp) == NULL)
return;
- explicit_bzero(pk, sizeof(*pk));
- free(pk);
-
+ freezero(pk, sizeof(*pk));
*pkp = NULL;
}
return "FIDO_ERR_UNSUPPORTED_EXTENSION";
case FIDO_ERR_FP_DATABASE_FULL:
return "FIDO_ERR_FP_DATABASE_FULL";
+ case FIDO_ERR_LARGEBLOB_STORAGE_FULL:
+ return "FIDO_ERR_LARGEBLOB_STORAGE_FULL";
case FIDO_ERR_CREDENTIAL_EXCLUDED:
return "FIDO_ERR_CREDENTIAL_EXCLUDED";
case FIDO_ERR_PROCESSING:
return "FIDO_ERR_UP_REQUIRED";
case FIDO_ERR_UV_BLOCKED:
return "FIDO_ERR_UV_BLOCKED";
+ case FIDO_ERR_UV_INVALID:
+ return "FIDO_ERR_UV_INVALID";
+ case FIDO_ERR_UNAUTHORIZED_PERM:
+ return "FIDO_ERR_UNAUTHORIZED_PERM";
case FIDO_ERR_ERR_OTHER:
return "FIDO_ERR_ERR_OTHER";
case FIDO_ERR_SPEC_LAST:
return "FIDO_ERR_INVALID_ARGUMENT";
case FIDO_ERR_USER_PRESENCE_REQUIRED:
return "FIDO_ERR_USER_PRESENCE_REQUIRED";
+ case FIDO_ERR_NOTFOUND:
+ return "FIDO_ERR_NOTFOUND";
+ case FIDO_ERR_COMPRESS:
+ return "FIDO_ERR_COMPRESS";
case FIDO_ERR_INTERNAL:
return "FIDO_ERR_INTERNAL";
default:
*/
#include <openssl/bn.h>
-#include <openssl/ec.h>
-#include <openssl/evp.h>
#include <openssl/obj_mac.h>
-#include <string.h>
#include "fido.h"
#include "fido/es256.h"
if (skp == NULL || (sk = *skp) == NULL)
return;
- explicit_bzero(sk, sizeof(*sk));
- free(sk);
-
+ freezero(sk, sizeof(*sk));
*skp = NULL;
}
if (pkp == NULL || (pk = *pkp) == NULL)
return;
- explicit_bzero(pk, sizeof(*pk));
- free(pk);
-
+ freezero(pk, sizeof(*pk));
*pkp = NULL;
}
_fido_assert_allow_cred
_fido_assert_authdata_len
_fido_assert_authdata_ptr
+_fido_assert_blob_len
+_fido_assert_blob_ptr
_fido_assert_clientdata_hash_len
_fido_assert_clientdata_hash_ptr
_fido_assert_count
_fido_assert_hmac_secret_ptr
_fido_assert_id_len
_fido_assert_id_ptr
+_fido_assert_largeblob_key_len
+_fido_assert_largeblob_key_ptr
_fido_assert_new
_fido_assert_rp_id
_fido_assert_set_authdata
_fido_assert_set_authdata_raw
+_fido_assert_set_clientdata
_fido_assert_set_clientdata_hash
_fido_assert_set_count
_fido_assert_set_extensions
_fido_assert_set_hmac_salt
+_fido_assert_set_hmac_secret
_fido_assert_set_options
_fido_assert_set_rp
_fido_assert_set_sig
_fido_bio_template_set_name
_fido_cbor_info_aaguid_len
_fido_cbor_info_aaguid_ptr
+_fido_cbor_info_algorithm_cose
+_fido_cbor_info_algorithm_count
+_fido_cbor_info_algorithm_type
_fido_cbor_info_extensions_len
_fido_cbor_info_extensions_ptr
_fido_cbor_info_free
_fido_cbor_info_maxmsgsiz
+_fido_cbor_info_maxcredbloblen
_fido_cbor_info_maxcredcntlst
_fido_cbor_info_maxcredidlen
_fido_cbor_info_fwversion
_fido_cbor_info_options_value_ptr
_fido_cbor_info_protocols_len
_fido_cbor_info_protocols_ptr
+_fido_cbor_info_transports_len
+_fido_cbor_info_transports_ptr
_fido_cbor_info_versions_len
_fido_cbor_info_versions_ptr
_fido_cred_authdata_len
_fido_cred_authdata_ptr
+_fido_cred_authdata_raw_len
+_fido_cred_authdata_raw_ptr
_fido_cred_clientdata_hash_len
_fido_cred_clientdata_hash_ptr
_fido_cred_display_name
_fido_cred_exclude
_fido_cred_flags
+_fido_cred_largeblob_key_len
+_fido_cred_largeblob_key_ptr
+_fido_cred_sigcount
_fido_cred_fmt
_fido_cred_free
_fido_cred_id_len
_fido_credman_rp_id_hash_ptr
_fido_credman_rp_name
_fido_credman_rp_new
+_fido_credman_set_dev_rk
_fido_cred_new
_fido_cred_prot
_fido_cred_pubkey_len
_fido_cred_rp_name
_fido_cred_set_authdata
_fido_cred_set_authdata_raw
+_fido_cred_set_blob
+_fido_cred_set_clientdata
_fido_cred_set_clientdata_hash
_fido_cred_set_extensions
_fido_cred_set_fmt
+_fido_cred_set_id
_fido_cred_set_options
_fido_cred_set_prot
_fido_cred_set_rk
_fido_dev_build
_fido_dev_cancel
_fido_dev_close
+_fido_dev_enable_entattest
_fido_dev_flags
_fido_dev_force_fido2
+_fido_dev_force_pin_change
_fido_dev_force_u2f
_fido_dev_free
_fido_dev_get_assert
_fido_dev_get_cbor_info
_fido_dev_get_retry_count
+_fido_dev_get_uv_retry_count
_fido_dev_get_touch_begin
_fido_dev_get_touch_status
_fido_dev_has_pin
+_fido_dev_has_uv
_fido_dev_info_free
_fido_dev_info_manifest
_fido_dev_info_manufacturer_string
_fido_dev_info_ptr
_fido_dev_info_vendor
_fido_dev_is_fido2
+_fido_dev_is_winhello
_fido_dev_major
_fido_dev_make_cred
_fido_dev_minor
_fido_dev_reset
_fido_dev_set_io_functions
_fido_dev_set_pin
+_fido_dev_set_pin_minlen
+_fido_dev_set_sigmask
_fido_dev_set_transport_functions
_fido_dev_supports_cred_prot
+_fido_dev_supports_credman
+_fido_dev_supports_permissions
_fido_dev_supports_pin
+_fido_dev_supports_uv
+_fido_dev_toggle_always_uv
+_fido_dev_largeblob_get
+_fido_dev_largeblob_get_array
+_fido_dev_largeblob_remove
+_fido_dev_largeblob_set
+_fido_dev_largeblob_set_array
_fido_init
_fido_set_log_handler
_fido_strerr
#ifndef _EXTERN_H
#define _EXTERN_H
+#ifdef __MINGW32__
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
#include <stdint.h>
#include "fido/types.h"
#endif /* __cplusplus */
/* aes256 */
-int aes256_cbc_dec(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
-int aes256_cbc_enc(const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
+int aes256_cbc_dec(const fido_dev_t *dev, const fido_blob_t *,
+ const fido_blob_t *, fido_blob_t *);
+int aes256_cbc_enc(const fido_dev_t *dev, const fido_blob_t *,
+ const fido_blob_t *, fido_blob_t *);
+int aes256_gcm_dec(const fido_blob_t *, const fido_blob_t *,
+ const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
+int aes256_gcm_enc(const fido_blob_t *, const fido_blob_t *,
+ const fido_blob_t *, const fido_blob_t *, fido_blob_t *);
/* cbor encoding functions */
+cbor_item_t *cbor_build_uint(const uint64_t);
cbor_item_t *cbor_flatten_vector(cbor_item_t **, size_t);
-cbor_item_t *cbor_encode_assert_options(fido_opt_t, fido_opt_t);
-cbor_item_t *cbor_encode_change_pin_auth(const fido_blob_t *,
- const fido_blob_t *, const fido_blob_t *);
-cbor_item_t *cbor_encode_extensions(const fido_cred_ext_t *);
-cbor_item_t *cbor_encode_hmac_secret_param(const fido_blob_t *,
- const es256_pk_t *, const fido_blob_t *);
-cbor_item_t *cbor_encode_options(fido_opt_t, fido_opt_t);
-cbor_item_t *cbor_encode_pin_auth(const fido_blob_t *, const fido_blob_t *);
-cbor_item_t *cbor_encode_pin_enc(const fido_blob_t *, const fido_blob_t *);
-cbor_item_t *cbor_encode_pin_hash_enc(const fido_blob_t *, const fido_blob_t *);
-cbor_item_t *cbor_encode_pin_opt(void);
+cbor_item_t *cbor_encode_assert_opt(fido_opt_t, fido_opt_t);
+cbor_item_t *cbor_encode_change_pin_auth(const fido_dev_t *,
+ const fido_blob_t *, const fido_blob_t *, const fido_blob_t *);
+cbor_item_t *cbor_encode_cred_ext(const fido_cred_ext_t *, const fido_blob_t *);
+cbor_item_t *cbor_encode_assert_ext(fido_dev_t *,
+ const fido_assert_ext_t *, const fido_blob_t *, const es256_pk_t *);
+cbor_item_t *cbor_encode_cred_opt(fido_opt_t, fido_opt_t);
+cbor_item_t *cbor_encode_pin_auth(const fido_dev_t *, const fido_blob_t *,
+ const fido_blob_t *);
+cbor_item_t *cbor_encode_pin_opt(const fido_dev_t *);
cbor_item_t *cbor_encode_pubkey(const fido_blob_t *);
cbor_item_t *cbor_encode_pubkey_list(const fido_blob_array_t *);
cbor_item_t *cbor_encode_pubkey_param(int);
cbor_item_t *cbor_encode_rp_entity(const fido_rp_t *);
-cbor_item_t *cbor_encode_set_pin_auth(const fido_blob_t *, const fido_blob_t *);
cbor_item_t *cbor_encode_user_entity(const fido_user_t *);
cbor_item_t *es256_pk_encode(const es256_pk_t *, int);
int cbor_decode_cred_authdata(const cbor_item_t *, int, fido_blob_t *,
fido_authdata_t *, fido_attcred_t *, fido_cred_ext_t *);
int cbor_decode_assert_authdata(const cbor_item_t *, fido_blob_t *,
- fido_authdata_t *, int *, fido_blob_t *);
+ fido_authdata_t *, fido_assert_extattr_t *);
int cbor_decode_cred_id(const cbor_item_t *, fido_blob_t *);
int cbor_decode_fmt(const cbor_item_t *, char **);
int cbor_decode_pubkey(const cbor_item_t *, int *, void *);
int cbor_string_copy(const cbor_item_t *, char **);
int cbor_parse_reply(const unsigned char *, size_t, void *,
int(*)(const cbor_item_t *, const cbor_item_t *, void *));
-int cbor_add_pin_params(fido_dev_t *, const fido_blob_t *, const es256_pk_t *,
- const fido_blob_t *,const char *, cbor_item_t **, cbor_item_t **);
+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 **);
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);
+
+/* deflate */
+int fido_compress(fido_blob_t *, const fido_blob_t *);
+int fido_uncompress(fido_blob_t *, const fido_blob_t *, size_t);
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
void fido_hid_close(void *);
int fido_hid_read(void *, unsigned char *, size_t, int);
int fido_hid_write(void *, const unsigned char *, size_t);
+int fido_hid_get_usage(const uint8_t *, size_t, uint32_t *);
+int fido_hid_get_report_len(const uint8_t *, size_t, size_t *, size_t *);
+int fido_hid_unix_open(const char *);
+int fido_hid_unix_wait(int, int, const fido_sigset_t *);
+int fido_hid_set_sigmask(void *, const fido_sigset_t *);
size_t fido_hid_report_in_len(void *);
size_t fido_hid_report_out_len(void *);
+/* nfc i/o */
+void *fido_nfc_open(const char *);
+void fido_nfc_close(void *);
+int fido_nfc_read(void *, unsigned char *, size_t, int);
+int fido_nfc_write(void *, const unsigned char *, size_t);
+int fido_nfc_rx(fido_dev_t *, uint8_t, unsigned char *, size_t, int);
+int fido_nfc_tx(fido_dev_t *, uint8_t, const unsigned char *, size_t);
+int fido_nfc_set_sigmask(void *, const fido_sigset_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_cbor_info(fido_dev_t *, fido_cbor_info_t *);
+int fido_winhello_make_cred(fido_dev_t *, fido_cred_t *, const char *);
+
/* generic i/o */
int fido_rx_cbor_status(fido_dev_t *, int);
int fido_rx(fido_dev_t *, uint8_t, void *, size_t, int);
#define fido_log_init(...) do { /* nothing */ } while (0)
#define fido_log_debug(...) do { /* nothing */ } while (0)
#define fido_log_xxd(...) do { /* nothing */ } while (0)
+#define fido_log_error(...) do { /* nothing */ } while (0)
#else
#ifdef __GNUC__
void fido_log_init(void);
void fido_log_debug(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
-void fido_log_xxd(const void *, size_t);
+void fido_log_xxd(const void *, size_t, const char *, ...)
+ __attribute__((__format__ (printf, 3, 4)));
+void fido_log_error(int, const char *, ...)
+ __attribute__((__format__ (printf, 2, 3)));
#else
void fido_log_init(void);
void fido_log_debug(const char *, ...);
-void fido_log_xxd(const void *, size_t);
+void fido_log_xxd(const void *, size_t, const char *, ...);
+void fido_log_error(int, const char *, ...);
#endif /* __GNUC__ */
#endif /* FIDO_NO_DIAGNOSTIC */
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_get_pin_token(fido_dev_t *, const char *, const fido_blob_t *,
- const es256_pk_t *, fido_blob_t *);
+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 *);
+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 *);
/* misc */
void fido_assert_reset_rx(fido_assert_t *);
void fido_assert_reset_tx(fido_assert_t *);
void fido_cred_reset_rx(fido_cred_t *);
void fido_cred_reset_tx(fido_cred_t *);
-int fido_check_rp_id(const char *, const unsigned char *);
+void fido_cbor_info_reset(fido_cbor_info_t *);
+int fido_blob_serialise(fido_blob_t *, const cbor_item_t *);
int fido_check_flags(uint8_t, fido_opt_t, fido_opt_t);
+int fido_check_rp_id(const char *, const unsigned char *);
+int fido_get_random(void *, size_t);
+int fido_sha256(fido_blob_t *, const u_char *, size_t);
/* crypto */
int fido_verify_sig_es256(const fido_blob_t *, const es256_pk_t *,
int fido_get_signed_hash(int, fido_blob_t *, const fido_blob_t *,
const fido_blob_t *);
-/* hid device manifest */
+/* device manifest functions */
int fido_hid_manifest(fido_dev_info_t *, size_t, size_t *);
+int fido_nfc_manifest(fido_dev_info_t *, size_t, size_t *);
/* device manifest registration */
typedef int (*dev_manifest_func_t)(fido_dev_info_t *, size_t, size_t *);
#endif
/* internal device capability flags */
-#define FIDO_DEV_PIN_SET 0x01
-#define FIDO_DEV_PIN_UNSET 0x02
-#define FIDO_DEV_CRED_PROT 0x04
+#define FIDO_DEV_PIN_SET 0x001
+#define FIDO_DEV_PIN_UNSET 0x002
+#define FIDO_DEV_CRED_PROT 0x004
+#define FIDO_DEV_CREDMAN 0x008
+#define FIDO_DEV_PIN_PROTOCOL1 0x010
+#define FIDO_DEV_PIN_PROTOCOL2 0x020
+#define FIDO_DEV_UV_SET 0x040
+#define FIDO_DEV_UV_UNSET 0x080
+#define FIDO_DEV_TOKEN_PERMS 0x100
+#define FIDO_DEV_WINHELLO 0x200
/* miscellanea */
#define FIDO_DUMMY_CLIENTDATA ""
#define FIDO_DUMMY_RP_ID "localhost"
#define FIDO_DUMMY_USER_NAME "dummy"
#define FIDO_DUMMY_USER_ID 1
+#define FIDO_WINHELLO_PATH "windows://hello"
#ifdef __cplusplus
} /* extern "C" */
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#ifdef _FIDO_INTERNAL
+#include <sys/types.h>
+
#include <cbor.h>
#include <limits.h>
extern "C" {
#endif /* __cplusplus */
-#if defined(_MSC_VER)
-#define FIDO_DEPRECATED(reason) __declspec(deprecated(reason))
-#elif defined(__OpenBSD__)
-#define FIDO_DEPRECATED(reason)
-#else
-#define FIDO_DEPRECATED(reason) __attribute__((__deprecated__(reason)))
-#endif
-
fido_assert_t *fido_assert_new(void);
fido_cred_t *fido_cred_new(void);
fido_dev_t *fido_dev_new(void);
/* fido_init() flags. */
#define FIDO_DEBUG 0x01
+#define FIDO_DISABLE_U2F_FALLBACK 0x02
void fido_init(int);
void fido_set_log_handler(fido_log_handler_t *);
const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_largeblob_key_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_sig_ptr(const fido_assert_t *, size_t);
const unsigned char *fido_assert_user_id_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_blob_ptr(const fido_assert_t *, size_t);
char **fido_cbor_info_extensions_ptr(const fido_cbor_info_t *);
char **fido_cbor_info_options_name_ptr(const fido_cbor_info_t *);
+char **fido_cbor_info_transports_ptr(const fido_cbor_info_t *);
char **fido_cbor_info_versions_ptr(const fido_cbor_info_t *);
const bool *fido_cbor_info_options_value_ptr(const fido_cbor_info_t *);
const char *fido_assert_rp_id(const fido_assert_t *);
const char *fido_assert_user_display_name(const fido_assert_t *, size_t);
const char *fido_assert_user_icon(const fido_assert_t *, size_t);
const char *fido_assert_user_name(const fido_assert_t *, size_t);
+const char *fido_cbor_info_algorithm_type(const fido_cbor_info_t *, size_t);
const char *fido_cred_display_name(const fido_cred_t *);
const char *fido_cred_fmt(const fido_cred_t *);
const char *fido_cred_rp_id(const fido_cred_t *);
const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_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_pubkey_ptr(const fido_cred_t *);
const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
+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 *,
size_t);
int fido_assert_set_authdata_raw(fido_assert_t *, size_t, const unsigned char *,
size_t);
+int fido_assert_set_clientdata(fido_assert_t *, const unsigned char *, size_t);
int fido_assert_set_clientdata_hash(fido_assert_t *, const unsigned char *,
size_t);
int fido_assert_set_count(fido_assert_t *, size_t);
int fido_assert_set_extensions(fido_assert_t *, int);
int fido_assert_set_hmac_salt(fido_assert_t *, const unsigned char *, size_t);
-FIDO_DEPRECATED("use fido_assert_set_up/fido_assert_set_uv")
+int fido_assert_set_hmac_secret(fido_assert_t *, size_t, const unsigned char *,
+ size_t);
int fido_assert_set_options(fido_assert_t *, bool, bool);
int fido_assert_set_rp(fido_assert_t *, const char *);
int fido_assert_set_up(fido_assert_t *, fido_opt_t);
int fido_assert_set_uv(fido_assert_t *, fido_opt_t);
int fido_assert_set_sig(fido_assert_t *, size_t, const unsigned char *, size_t);
int fido_assert_verify(const fido_assert_t *, size_t, int, const void *);
+int fido_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_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_clientdata(fido_cred_t *, const unsigned char *, size_t);
int fido_cred_set_clientdata_hash(fido_cred_t *, const unsigned char *, size_t);
int fido_cred_set_extensions(fido_cred_t *, int);
int fido_cred_set_fmt(fido_cred_t *, const char *);
-FIDO_DEPRECATED("use fido_cred_set_rk/fido_cred_set_uv")
+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_prot(fido_cred_t *, int);
int fido_cred_set_rk(fido_cred_t *, fido_opt_t);
int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
int fido_cred_verify(const fido_cred_t *);
int fido_cred_verify_self(const fido_cred_t *);
+int fido_dev_set_sigmask(fido_dev_t *, const fido_sigset_t *);
int fido_dev_cancel(fido_dev_t *);
int fido_dev_close(fido_dev_t *);
int fido_dev_get_assert(fido_dev_t *, fido_assert_t *, const char *);
int fido_dev_get_cbor_info(fido_dev_t *, fido_cbor_info_t *);
int fido_dev_get_retry_count(fido_dev_t *, int *);
+int fido_dev_get_uv_retry_count(fido_dev_t *, int *);
int fido_dev_get_touch_begin(fido_dev_t *);
int fido_dev_get_touch_status(fido_dev_t *, int *, int);
int fido_dev_info_manifest(fido_dev_info_t *, size_t, size_t *);
size_t fido_assert_count(const fido_assert_t *);
size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t);
size_t fido_assert_id_len(const fido_assert_t *, size_t);
+size_t fido_assert_largeblob_key_len(const fido_assert_t *, size_t);
size_t fido_assert_sig_len(const fido_assert_t *, size_t);
size_t fido_assert_user_id_len(const fido_assert_t *, size_t);
+size_t fido_assert_blob_len(const fido_assert_t *, size_t);
size_t fido_cbor_info_aaguid_len(const fido_cbor_info_t *);
+size_t fido_cbor_info_algorithm_count(const fido_cbor_info_t *);
size_t fido_cbor_info_extensions_len(const fido_cbor_info_t *);
size_t fido_cbor_info_options_len(const fido_cbor_info_t *);
size_t fido_cbor_info_protocols_len(const fido_cbor_info_t *);
+size_t fido_cbor_info_transports_len(const fido_cbor_info_t *);
size_t fido_cbor_info_versions_len(const fido_cbor_info_t *);
size_t fido_cred_authdata_len(const fido_cred_t *);
+size_t fido_cred_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_pubkey_len(const fido_cred_t *);
size_t fido_cred_sig_len(const fido_cred_t *);
size_t fido_cred_x5c_len(const fido_cred_t *);
+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_cred_flags(const fido_cred_t *);
+uint32_t fido_cred_sigcount(const fido_cred_t *);
uint8_t fido_dev_protocol(const fido_dev_t *);
uint8_t fido_dev_major(const fido_dev_t *);
uint8_t fido_dev_minor(const fido_dev_t *);
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_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_uv(const fido_dev_t *);
+
+int fido_dev_largeblob_get(fido_dev_t *, const unsigned char *, size_t,
+ unsigned char **, size_t *);
+int fido_dev_largeblob_set(fido_dev_t *, const unsigned char *, size_t,
+ const unsigned char *, size_t, const char *);
+int fido_dev_largeblob_remove(fido_dev_t *, const unsigned char *, size_t,
+ const char *);
+int fido_dev_largeblob_get_array(fido_dev_t *, unsigned char **, size_t *);
+int fido_dev_largeblob_set_array(fido_dev_t *, const unsigned char *, size_t,
+ const char *);
#ifdef __cplusplus
} /* extern "C" */
--- /dev/null
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#ifndef _FIDO_CONFIG_H
+#define _FIDO_CONFIG_H
+
+#ifdef _FIDO_INTERNAL
+#include "blob.h"
+#include "fido/err.h"
+#include "fido/param.h"
+#include "fido/types.h"
+#else
+#include <fido.h>
+#include <fido/err.h>
+#include <fido/param.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+int fido_dev_enable_entattest(fido_dev_t *, const char *);
+int fido_dev_force_pin_change(fido_dev_t *, const char *);
+int fido_dev_toggle_always_uv(fido_dev_t *, const char *);
+int fido_dev_set_pin_minlen(fido_dev_t *, size_t, const char *);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* !_FIDO_CONFIG_H */
/*
- * 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.
*/
int fido_credman_get_dev_rk(fido_dev_t *, const char *, fido_credman_rk_t *,
const char *);
int fido_credman_get_dev_rp(fido_dev_t *, fido_credman_rp_t *, const char *);
+int fido_credman_set_dev_rk(fido_dev_t *, fido_cred_t *, const char *);
size_t fido_credman_rk_count(const fido_credman_rk_t *);
size_t fido_credman_rp_count(const fido_credman_rp_t *);
#ifndef _FIDO_ERR_H
#define _FIDO_ERR_H
-#define FIDO_ERR_SUCCESS 0x00
+#define FIDO_ERR_SUCCESS 0x00
#define FIDO_ERR_INVALID_COMMAND 0x01
#define FIDO_ERR_INVALID_PARAMETER 0x02
#define FIDO_ERR_INVALID_LENGTH 0x03
#define FIDO_ERR_LIMIT_EXCEEDED 0x15
#define FIDO_ERR_UNSUPPORTED_EXTENSION 0x16
#define FIDO_ERR_FP_DATABASE_FULL 0x17
+#define FIDO_ERR_LARGEBLOB_STORAGE_FULL 0x18
#define FIDO_ERR_CREDENTIAL_EXCLUDED 0x19
#define FIDO_ERR_PROCESSING 0x21
#define FIDO_ERR_INVALID_CREDENTIAL 0x22
#define FIDO_ERR_ACTION_TIMEOUT 0x3a
#define FIDO_ERR_UP_REQUIRED 0x3b
#define FIDO_ERR_UV_BLOCKED 0x3c
+#define FIDO_ERR_UV_INVALID 0x3f
+#define FIDO_ERR_UNAUTHORIZED_PERM 0x40
#define FIDO_ERR_ERR_OTHER 0x7f
#define FIDO_ERR_SPEC_LAST 0xdf
#define FIDO_ERR_INVALID_ARGUMENT -7
#define FIDO_ERR_USER_PRESENCE_REQUIRED -8
#define FIDO_ERR_INTERNAL -9
+#define FIDO_ERR_NOTFOUND -10
+#define FIDO_ERR_COMPRESS -11
#ifdef __cplusplus
extern "C" {
#define CTAP_CBOR_CLIENT_PIN 0x06
#define CTAP_CBOR_RESET 0x07
#define CTAP_CBOR_NEXT_ASSERT 0x08
+#define CTAP_CBOR_LARGEBLOB 0x0c
+#define CTAP_CBOR_CONFIG 0x0d
#define CTAP_CBOR_BIO_ENROLL_PRE 0x40
#define CTAP_CBOR_CRED_MGMT_PRE 0x41
+/* Supported CTAP PIN/UV Auth Protocols. */
+#define CTAP_PIN_PROTOCOL1 1
+#define CTAP_PIN_PROTOCOL2 2
+
/* U2F command opcodes. */
#define U2F_CMD_REGISTER 0x01
#define U2F_CMD_AUTH 0x02
#define U2F_AUTH_CHECK 0x07
/* ISO7816-4 status words. */
+#define SW1_MORE_DATA 0x61
#define SW_CONDITIONS_NOT_SATISFIED 0x6985
#define SW_WRONG_DATA 0x6a80
#define SW_NO_ERROR 0x9000
/* Supported extensions. */
#define FIDO_EXT_HMAC_SECRET 0x01
#define FIDO_EXT_CRED_PROTECT 0x02
+#define FIDO_EXT_LARGEBLOB_KEY 0x04
+#define FIDO_EXT_CRED_BLOB 0x08
/* Supported credential protection policies. */
#define FIDO_CRED_PROT_UV_OPTIONAL 0x01
#define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0x02
#define FIDO_CRED_PROT_UV_REQUIRED 0x03
+#ifdef _FIDO_INTERNAL
+#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)
+#endif /* _FIDO_INTERNAL */
+
#endif /* !_FIDO_PARAM_H */
#ifndef _FIDO_TYPES_H
#define _FIDO_TYPES_H
+#ifdef __MINGW32__
+#include <sys/types.h>
+#endif
+
+#include <signal.h>
#include <stddef.h>
#include <stdint.h>
typedef void fido_log_handler_t(const char *);
+#ifdef _WIN32
+typedef int fido_sigset_t;
+#else
+typedef sigset_t fido_sigset_t;
+#endif
+
#ifdef _FIDO_INTERNAL
#include "packed.h"
#include "blob.h"
} fido_cred_ext_t;
typedef struct fido_cred {
+ fido_blob_t cd; /* client data */
fido_blob_t cdh; /* client data hash */
fido_rp_t rp; /* relying party */
fido_user_t user; /* user entity */
int type; /* cose algorithm */
char *fmt; /* credential format */
fido_cred_ext_t authdata_ext; /* decoded extensions */
- fido_blob_t authdata_cbor; /* raw cbor payload */
+ fido_blob_t authdata_cbor; /* cbor-encoded payload */
+ fido_blob_t authdata_raw; /* cbor-decoded 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_blob_t largeblob_key; /* decoded large blob key */
+ fido_blob_t blob; /* FIDO 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_assert_extattr_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_blob_t id; /* credential id */
+ fido_user_t user; /* user attributes */
+ fido_blob_t hmac_secret; /* hmac secret */
+ fido_assert_extattr_t 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_blob_t largeblob_key; /* decoded large blob key */
} fido_assert_stmt;
+typedef struct fido_assert_ext {
+ int mask; /* enabled extensions */
+ fido_blob_t hmac_salt; /* optional hmac-secret salt */
+} fido_assert_ext_t;
+
typedef struct fido_assert {
char *rp_id; /* relying party id */
+ fido_blob_t cd; /* client data */
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_ext_t 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 */
size_t len;
} fido_byte_array_t;
+typedef struct fido_algo {
+ char *type;
+ int cose;
+} fido_algo_t;
+
+typedef struct fido_algo_array {
+ fido_algo_t *ptr;
+ size_t len;
+} 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 */
+ uint64_t maxcredbloblen; /* max credBlob length */
} fido_cbor_info_t;
typedef struct fido_dev_info {
})
typedef struct fido_dev {
- uint64_t nonce; /* issued nonce */
- fido_ctap_info_t attr; /* device attributes */
- uint32_t cid; /* assigned channel id */
- char *path; /* device path */
- void *io_handle; /* abstract i/o handle */
- fido_dev_io_t io; /* i/o functions */
- bool io_own; /* device has own io/transport */
- size_t rx_len; /* length of HID input reports */
- size_t tx_len; /* length of HID output reports */
- int flags; /* internal flags; see FIDO_DEV_* */
- fido_dev_transport_t transport; /* transport functions */
+ uint64_t nonce; /* issued nonce */
+ fido_ctap_info_t attr; /* device attributes */
+ uint32_t cid; /* assigned channel id */
+ char *path; /* device path */
+ void *io_handle; /* abstract i/o handle */
+ fido_dev_io_t io; /* i/o functions */
+ bool io_own; /* device has own io/transport */
+ size_t rx_len; /* length of HID input reports */
+ size_t tx_len; /* length of HID output reports */
+ int flags; /* internal flags; see FIDO_DEV_* */
+ fido_dev_transport_t transport; /* transport functions */
+ uint64_t maxmsgsize; /* max message size */
} fido_dev_t;
#else
* license that can be found in the LICENSE file.
*/
-#include <string.h>
#include "fido.h"
+static int
+get_key_len(uint8_t tag, uint8_t *key, size_t *key_len)
+{
+ *key = tag & 0xfc;
+ if ((*key & 0xf0) == 0xf0) {
+ fido_log_debug("%s: *key=0x%02x", __func__, *key);
+ return (-1);
+ }
+
+ *key_len = tag & 0x3;
+ if (*key_len == 3) {
+ *key_len = 4;
+ }
+
+ return (0);
+}
+
+static int
+get_key_val(const void *body, size_t key_len, uint32_t *val)
+{
+ const uint8_t *ptr = body;
+
+ switch (key_len) {
+ case 0:
+ *val = 0;
+ break;
+ case 1:
+ *val = ptr[0];
+ break;
+ case 2:
+ *val = (uint32_t)((ptr[1] << 8) | ptr[0]);
+ break;
+ default:
+ fido_log_debug("%s: key_len=%zu", __func__, key_len);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+fido_hid_get_usage(const uint8_t *report_ptr, size_t report_len,
+ uint32_t *usage_page)
+{
+ const uint8_t *ptr = report_ptr;
+ size_t len = report_len;
+
+ while (len > 0) {
+ const uint8_t tag = ptr[0];
+ ptr++;
+ len--;
+
+ uint8_t key;
+ size_t key_len;
+ uint32_t key_val;
+
+ if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
+ get_key_val(ptr, key_len, &key_val) < 0) {
+ return (-1);
+ }
+
+ if (key == 0x4) {
+ *usage_page = key_val;
+ }
+
+ ptr += key_len;
+ len -= key_len;
+ }
+
+ return (0);
+}
+
+int
+fido_hid_get_report_len(const uint8_t *report_ptr, size_t report_len,
+ size_t *report_in_len, size_t *report_out_len)
+{
+ const uint8_t *ptr = report_ptr;
+ size_t len = report_len;
+ uint32_t report_size = 0;
+
+ while (len > 0) {
+ const uint8_t tag = ptr[0];
+ ptr++;
+ len--;
+
+ uint8_t key;
+ size_t key_len;
+ uint32_t key_val;
+
+ if (get_key_len(tag, &key, &key_len) < 0 || key_len > len ||
+ get_key_val(ptr, key_len, &key_val) < 0) {
+ return (-1);
+ }
+
+ if (key == 0x94) {
+ report_size = key_val;
+ } else if (key == 0x80) {
+ *report_in_len = (size_t)report_size;
+ } else if (key == 0x90) {
+ *report_out_len = (size_t)report_size;
+ }
+
+ ptr += key_len;
+ len -= key_len;
+ }
+
+ return (0);
+}
+
fido_dev_info_t *
fido_dev_info_new(size_t n)
{
return;
for (size_t i = 0; i < n; i++) {
- const fido_dev_info_t *di = &devlist[i];
+ const fido_dev_info_t *di;
+ di = &devlist[i];
free(di->path);
free(di->manufacturer);
free(di->product);
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <usbhid.h>
int fd;
size_t report_in_len;
size_t report_out_len;
+ sigset_t sigmask;
+ const sigset_t *sigmaskp;
};
int
for (i = *olen = 0; i < MAX_UHID && *olen < ilen; i++) {
snprintf(path, sizeof(path), "/dev/fido/%zu", i);
- if ((fd = open(path, O_RDWR)) == -1) {
- if (errno != ENOENT && errno != ENXIO) {
- fido_log_debug("%s: open %s: %s", __func__,
- path, strerror(errno));
- }
+ if ((fd = fido_hid_unix_open(path)) == -1)
continue;
- }
- close(fd);
+ if (close(fd) == -1)
+ fido_log_error(errno, "%s: close", __func__);
memset(&udi, 0, sizeof(udi));
strlcpy(udi.udi_vendor, "OpenBSD", sizeof(udi.udi_vendor));
di = &devlist[*olen];
memset(di, 0, sizeof(*di));
+ di->io = (fido_dev_io_t) {
+ fido_hid_open,
+ fido_hid_close,
+ fido_hid_read,
+ fido_hid_write,
+ };
if ((di->path = strdup(path)) == NULL ||
(di->manufacturer = strdup(udi.udi_vendor)) == NULL ||
(di->product = strdup(udi.udi_product)) == NULL) {
explicit_bzero(di, sizeof(*di));
return FIDO_ERR_INTERNAL;
}
- di->vendor_id = udi.udi_vendorNo;
- di->product_id = udi.udi_productNo;
- di->io = (fido_dev_io_t) {
- fido_hid_open,
- fido_hid_close,
- fido_hid_read,
- fido_hid_write,
- };
+ di->vendor_id = (int16_t)udi.udi_vendorNo;
+ di->product_id = (int16_t)udi.udi_productNo;
(*olen)++;
}
* synched now.
*/
fido_log_debug("%s: got reply", __func__);
- fido_log_xxd(data, ctx->report_out_len);
+ fido_log_xxd(data, ctx->report_out_len, "%s", __func__);
return 0;
}
fido_log_debug("%s: no response", __func__);
struct hid_openbsd *ret = NULL;
if ((ret = calloc(1, sizeof(*ret))) == NULL ||
- (ret->fd = open(path, O_RDWR)) < 0) {
+ (ret->fd = fido_hid_unix_open(path)) == -1) {
free(ret);
return (NULL);
}
- ret->report_in_len = ret->report_out_len = MAX_U2FHID_LEN;
+ ret->report_in_len = ret->report_out_len = CTAP_MAX_REPORT_LEN;
fido_log_debug("%s: inlen = %zu outlen = %zu", __func__,
ret->report_in_len, ret->report_out_len);
{
struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
- close(ctx->fd);
+ if (close(ctx->fd) == -1)
+ fido_log_error(errno, "%s: close", __func__);
+
free(ctx);
}
int
-waitfd(int fd, int ms)
+fido_hid_set_sigmask(void *handle, const fido_sigset_t *sigmask)
{
- struct timespec ts_start, ts_now, ts_delta;
- struct pollfd pfd;
- int ms_remain, r;
+ struct hid_openbsd *ctx = handle;
- if (ms < 0)
- return 0;
- memset(&pfd, 0, sizeof(pfd));
- pfd.fd = fd;
- pfd.events = POLLIN;
- if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) {
- fido_log_debug("%s: clock_gettime: %s",
- __func__, strerror(errno));
- return -1;
- }
- for (ms_remain = ms; ms_remain > 0;) {
- if ((r = poll(&pfd, 1, ms_remain)) > 0)
- return 0;
- else if (r == 0)
- break;
- else if (errno != EINTR) {
- fido_log_debug("%s: poll: %s",
- __func__, strerror(errno));
- return -1;
- }
- /* poll interrupted - subtract time already waited */
- if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) {
- fido_log_debug("%s: clock_gettime: %s",
- __func__, strerror(errno));
- return -1;
- }
- timespecsub(&ts_now, &ts_start, &ts_delta);
- ms_remain = ms - ((ts_delta.tv_sec * 1000) +
- (ts_delta.tv_nsec / 1000000));
- }
- return -1;
+ ctx->sigmask = *sigmask;
+ ctx->sigmaskp = &ctx->sigmask;
+
+ return (FIDO_OK);
}
int
struct hid_openbsd *ctx = (struct hid_openbsd *)handle;
ssize_t r;
- fido_log_debug("%s: %zu timeout %d", __func__, len, ms);
if (len != ctx->report_in_len) {
fido_log_debug("%s: invalid len: got %zu, want %zu", __func__,
len, ctx->report_in_len);
return (-1);
}
- if (waitfd(ctx->fd, ms) != 0) {
+
+ if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
fido_log_debug("%s: fd not ready", __func__);
return (-1);
}
- if ((r = read(ctx->fd, buf, len)) == -1 || (size_t)r != len) {
- fido_log_debug("%s: read: %s", __func__, strerror(errno));
+
+ if ((r = read(ctx->fd, buf, len)) == -1) {
+ fido_log_error(errno, "%s: read", __func__);
+ return (-1);
+ }
+
+ if (r < 0 || (size_t)r != len) {
+ fido_log_debug("%s: %zd != %zu", __func__, r, len);
return (-1);
}
+
return ((int)len);
}
len, ctx->report_out_len);
return (-1);
}
- if ((r = write(ctx->fd, buf + 1, len - 1)) == -1 ||
- (size_t)r != len - 1) {
- fido_log_debug("%s: write: %s", __func__, strerror(errno));
+
+ if ((r = write(ctx->fd, buf + 1, len - 1)) == -1) {
+ fido_log_error(errno, "%s: write", __func__);
return (-1);
}
+
+ if (r < 0 || (size_t)r != len - 1) {
+ fido_log_debug("%s: %zd != %zu", __func__, r, len - 1);
+ return (-1);
+ }
+
return ((int)len);
}
--- /dev/null
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "fido.h"
+
+#ifdef __NetBSD__
+#define ppoll pollts
+#endif
+
+int
+fido_hid_unix_open(const char *path)
+{
+ int fd;
+ struct stat st;
+
+ if ((fd = open(path, O_RDWR)) == -1) {
+ if (errno != ENOENT && errno != ENXIO)
+ fido_log_error(errno, "%s: open %s", __func__, path);
+ return (-1);
+ }
+
+ if (fstat(fd, &st) == -1) {
+ fido_log_error(errno, "%s: fstat %s", __func__, path);
+ if (close(fd) == -1)
+ fido_log_error(errno, "%s: close", __func__);
+ return (-1);
+ }
+
+ if (S_ISCHR(st.st_mode) == 0) {
+ fido_log_debug("%s: S_ISCHR %s", __func__, path);
+ if (close(fd) == -1)
+ fido_log_error(errno, "%s: close", __func__);
+ return (-1);
+ }
+
+ return (fd);
+}
+
+int
+fido_hid_unix_wait(int fd, int ms, const fido_sigset_t *sigmask)
+{
+ struct timespec ts;
+ struct pollfd pfd;
+ int r;
+
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.events = POLLIN;
+ pfd.fd = fd;
+
+#ifdef FIDO_FUZZ
+ if (ms < 0)
+ return (0);
+#endif
+ if (ms > -1) {
+ ts.tv_sec = ms / 1000;
+ ts.tv_nsec = (ms % 1000) * 1000000;
+ }
+
+ if ((r = ppoll(&pfd, 1, ms > -1 ? &ts : NULL, sigmask)) < 1) {
+ if (r == -1)
+ fido_log_error(errno, "%s: ppoll", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
/*
- * 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 <string.h>
#include "fido.h"
static int
-decode_version(const cbor_item_t *item, void *arg)
+decode_string(const cbor_item_t *item, void *arg)
{
- fido_str_array_t *v = arg;
- const size_t i = v->len;
+ fido_str_array_t *a = arg;
+ const size_t i = a->len;
/* keep ptr[x] and len consistent */
- if (cbor_string_copy(item, &v->ptr[i]) < 0) {
+ if (cbor_string_copy(item, &a->ptr[i]) < 0) {
fido_log_debug("%s: cbor_string_copy", __func__);
return (-1);
}
- v->len++;
+ a->len++;
return (0);
}
static int
-decode_versions(const cbor_item_t *item, fido_str_array_t *v)
+decode_string_array(const cbor_item_t *item, fido_str_array_t *v)
{
v->ptr = NULL;
v->len = 0;
if (v->ptr == NULL)
return (-1);
- if (cbor_array_iter(item, v, decode_version) < 0) {
- fido_log_debug("%s: decode_version", __func__);
- return (-1);
- }
-
- return (0);
-}
-
-static int
-decode_extension(const cbor_item_t *item, void *arg)
-{
- fido_str_array_t *e = arg;
- const size_t i = e->len;
-
- /* keep ptr[x] and len consistent */
- if (cbor_string_copy(item, &e->ptr[i]) < 0) {
- fido_log_debug("%s: cbor_string_copy", __func__);
- return (-1);
- }
-
- e->len++;
-
- return (0);
-}
-
-static int
-decode_extensions(const cbor_item_t *item, fido_str_array_t *e)
-{
- e->ptr = NULL;
- e->len = 0;
-
- if (cbor_isa_array(item) == false ||
- cbor_array_is_definite(item) == false) {
- fido_log_debug("%s: cbor type", __func__);
- return (-1);
- }
-
- e->ptr = calloc(cbor_array_size(item), sizeof(char *));
- if (e->ptr == NULL)
- return (-1);
-
- if (cbor_array_iter(item, e, decode_extension) < 0) {
- fido_log_debug("%s: decode_extension", __func__);
+ if (cbor_array_iter(item, v, decode_string) < 0) {
+ fido_log_debug("%s: decode_string", __func__);
return (-1);
}
return (0);
}
+static int
+decode_algorithm_entry(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ fido_algo_t *alg = arg;
+ char *name = NULL;
+ int ok = -1;
+
+ if (cbor_string_copy(key, &name) < 0) {
+ fido_log_debug("%s: cbor type", __func__);
+ ok = 0; /* ignore */
+ goto out;
+ }
+
+ if (!strcmp(name, "alg")) {
+ if (cbor_isa_negint(val) == false ||
+ cbor_get_int(val) > INT_MAX || alg->cose != 0) {
+ fido_log_debug("%s: alg", __func__);
+ goto out;
+ }
+ alg->cose = -(int)cbor_get_int(val) - 1;
+ } else if (!strcmp(name, "type")) {
+ if (cbor_string_copy(val, &alg->type) < 0) {
+ fido_log_debug("%s: type", __func__);
+ goto out;
+ }
+ }
+
+ ok = 0;
+out:
+ free(name);
+
+ 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)
+{
+ fido_algo_array_t *aa = arg;
+ const size_t i = aa->len;
+
+ if (cbor_isa_map(item) == false ||
+ cbor_map_is_definite(item) == false) {
+ fido_log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ memset(&aa->ptr[i], 0, sizeof(aa->ptr[i]));
+
+ 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]);
+ return (-1);
+ }
+
+ /* keep ptr[x] and len consistent */
+ aa->len++;
+
+ return (0);
+}
+
+static int
+decode_algorithms(const cbor_item_t *item, fido_algo_array_t *aa)
+{
+ aa->ptr = NULL;
+ aa->len = 0;
+
+ if (cbor_isa_array(item) == false ||
+ cbor_array_is_definite(item) == false) {
+ fido_log_debug("%s: cbor type", __func__);
+ return (-1);
+ }
+
+ aa->ptr = calloc(cbor_array_size(item), sizeof(fido_algo_t));
+ if (aa->ptr == NULL)
+ return (-1);
+
+ if (cbor_array_iter(item, aa, decode_algorithm) < 0) {
+ fido_log_debug("%s: decode_algorithm", __func__);
+ return (-1);
+ }
+
+ return (0);
+}
+
static int
parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
switch (cbor_get_uint8(key)) {
case 1: /* versions */
- return (decode_versions(val, &ci->versions));
+ return (decode_string_array(val, &ci->versions));
case 2: /* extensions */
- return (decode_extensions(val, &ci->extensions));
+ return (decode_string_array(val, &ci->extensions));
case 3: /* aaguid */
return (decode_aaguid(val, ci->aaguid, sizeof(ci->aaguid)));
case 4: /* options */
return (cbor_decode_uint64(val, &ci->maxcredcntlst));
case 8: /* maxCredentialIdLength */
return (cbor_decode_uint64(val, &ci->maxcredidlen));
+ case 9: /* transports */
+ return (decode_string_array(val, &ci->transports));
+ case 10: /* algorithms */
+ return (decode_algorithms(val, &ci->algorithms));
case 14: /* fwVersion */
return (cbor_decode_uint64(val, &ci->fwversion));
+ case 15: /* maxCredBlobLen */
+ return (cbor_decode_uint64(val, &ci->maxcredbloblen));
default: /* ignore */
fido_log_debug("%s: cbor type", __func__);
return (0);
fido_log_debug("%s: dev=%p, ci=%p, ms=%d", __func__, (void *)dev,
(void *)ci, ms);
- memset(ci, 0, sizeof(*ci));
+ fido_cbor_info_reset(ci);
if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
ms)) < 0) {
{
int r;
+#ifdef USE_WINHELLO
+ 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 ||
(r = fido_dev_get_cbor_info_rx(dev, ci, ms)) != FIDO_OK)
return (r);
ba->len = 0;
}
-void
-fido_cbor_info_free(fido_cbor_info_t **ci_p)
+static void
+free_algo_array(fido_algo_array_t *aa)
{
- fido_cbor_info_t *ci;
+ for (size_t i = 0; i < aa->len; i++)
+ free_algo(&aa->ptr[i]);
- if (ci_p == NULL || (ci = *ci_p) == NULL)
- return;
+ 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(ci);
+ free_algo_array(&ci->algorithms);
+}
+
+void
+fido_cbor_info_free(fido_cbor_info_t **ci_p)
+{
+ fido_cbor_info_t *ci;
+ if (ci_p == NULL || (ci = *ci_p) == NULL)
+ return;
+ fido_cbor_info_reset(ci);
+ free(ci);
*ci_p = NULL;
}
return (ci->extensions.len);
}
+char **
+fido_cbor_info_transports_ptr(const fido_cbor_info_t *ci)
+{
+ return (ci->transports.ptr);
+}
+
+size_t
+fido_cbor_info_transports_len(const fido_cbor_info_t *ci)
+{
+ return (ci->transports.len);
+}
+
const unsigned char *
fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *ci)
{
return (ci->options.len);
}
+uint64_t
+fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *ci)
+{
+ return (ci->maxcredbloblen);
+}
+
uint64_t
fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *ci)
{
{
return (ci->protocols.len);
}
+
+size_t
+fido_cbor_info_algorithm_count(const fido_cbor_info_t *ci)
+{
+ return (ci->algorithms.len);
+}
+
+const char *
+fido_cbor_info_algorithm_type(const fido_cbor_info_t *ci, size_t idx)
+{
+ if (idx >= ci->algorithms.len)
+ return (NULL);
+
+ return (ci->algorithms.ptr[idx].type);
+}
+
+int
+fido_cbor_info_algorithm_cose(const fido_cbor_info_t *ci, size_t idx)
+{
+ if (idx >= ci->algorithms.len)
+ return (0);
+
+ return (ci->algorithms.ptr[idx].cose);
+}
* license that can be found in the LICENSE file.
*/
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
#include "fido.h"
#include "packed.h"
int
fido_tx(fido_dev_t *d, uint8_t cmd, const void *buf, size_t count)
{
- fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu", __func__,
- (void *)d, cmd, (const void *)buf, count);
- fido_log_xxd(buf, count);
+ 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));
#ifdef FIDO_FUZZ
fp->cid = d->cid;
#endif
- } while (fp->cid == d->cid &&
- fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE));
+ } while (fp->cid != d->cid || (fp->cid == d->cid &&
+ fp->body.init.cmd == (CTAP_FRAME_INIT | CTAP_KEEPALIVE)));
if (d->rx_len > sizeof(*fp))
return (-1);
- fido_log_debug("%s: initiation frame at %p", __func__, (void *)fp);
- fido_log_xxd(fp, d->rx_len);
-
+ fido_log_xxd(fp, d->rx_len, "%s", __func__);
#ifdef FIDO_FUZZ
fp->body.init.cmd = (CTAP_FRAME_INIT | cmd);
#endif
return (-1);
}
- fido_log_debug("%s: continuation frame at %p", __func__,
- (void *)&f);
- fido_log_xxd(&f, d->rx_len);
-
+ fido_log_xxd(&f, d->rx_len, "%s", __func__);
#ifdef FIDO_FUZZ
f.cid = d->cid;
f.body.cont.seq = (uint8_t)seq;
{
int n;
- fido_log_debug("%s: d=%p, cmd=0x%02x, buf=%p, count=%zu, ms=%d",
- __func__, (void *)d, cmd, (const void *)buf, count, ms);
+ fido_log_debug("%s: dev=%p, cmd=0x%02x, ms=%d", __func__, (void *)d,
+ cmd, ms);
if (d->transport.rx != NULL)
return (d->transport.rx(d, cmd, buf, count, ms));
fido_log_debug("%s: invalid argument", __func__);
return (-1);
}
- if ((n = rx(d, cmd, buf, count, ms)) >= 0) {
- fido_log_debug("%s: buf=%p, len=%d", __func__, (void *)buf, n);
- fido_log_xxd(buf, (size_t)n);
- }
+ if ((n = rx(d, cmd, buf, count, ms)) >= 0)
+ fido_log_xxd(buf, (size_t)n, "%s", __func__);
return (n);
}
* license that can be found in the LICENSE file.
*/
-#include <string.h>
#include "fido.h"
iso7816_apdu_t *
-iso7816_new(uint8_t ins, uint8_t p1, uint16_t payload_len)
+iso7816_new(uint8_t cla, uint8_t ins, uint8_t p1, uint16_t payload_len)
{
- iso7816_apdu_t *apdu;
- size_t alloc_len;
+ iso7816_apdu_t *apdu;
+ size_t alloc_len;
alloc_len = sizeof(iso7816_apdu_t) + payload_len + 2; /* le1 le2 */
-
if ((apdu = calloc(1, alloc_len)) == NULL)
- return (NULL);
-
+ return NULL;
apdu->alloc_len = alloc_len;
apdu->payload_len = payload_len;
apdu->payload_ptr = apdu->payload;
+ apdu->header.cla = cla;
apdu->header.ins = ins;
apdu->header.p1 = p1;
apdu->header.lc2 = (uint8_t)((payload_len >> 8) & 0xff);
apdu->header.lc3 = (uint8_t)(payload_len & 0xff);
- return (apdu);
+ return apdu;
}
void
if (apdu_p == NULL || (apdu = *apdu_p) == NULL)
return;
-
- explicit_bzero(apdu, apdu->alloc_len);
- free(apdu);
-
+ freezero(apdu, apdu->alloc_len);
*apdu_p = NULL;
}
iso7816_add(iso7816_apdu_t *apdu, const void *buf, size_t cnt)
{
if (cnt > apdu->payload_len || cnt > UINT16_MAX)
- return (-1);
-
+ return -1;
memcpy(apdu->payload_ptr, buf, cnt);
apdu->payload_ptr += cnt;
apdu->payload_len = (uint16_t)(apdu->payload_len - cnt);
- return (0);
+ return 0;
}
const unsigned char *
iso7816_ptr(const iso7816_apdu_t *apdu)
{
- return ((const unsigned char *)&apdu->header);
+ return (const unsigned char *)&apdu->header;
}
size_t
iso7816_len(const iso7816_apdu_t *apdu)
{
- return (apdu->alloc_len - sizeof(apdu->alloc_len) -
- sizeof(apdu->payload_len) - sizeof(apdu->payload_ptr));
+ return apdu->alloc_len - sizeof(apdu->alloc_len) -
+ sizeof(apdu->payload_len) - sizeof(apdu->payload_ptr);
}
const unsigned char *iso7816_ptr(const iso7816_apdu_t *);
int iso7816_add(iso7816_apdu_t *, const void *, size_t);
-iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint16_t);
+iso7816_apdu_t *iso7816_new(uint8_t, uint8_t, uint8_t, uint16_t);
size_t iso7816_len(const iso7816_apdu_t *);
void iso7816_free(iso7816_apdu_t **);
--- /dev/null
+/*
+ * Copyright (c) 2020 Yubico AB. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+#include <openssl/sha.h>
+
+#include "fido.h"
+#include "fido/es256.h"
+
+#define LARGEBLOB_DIGEST_LENGTH 16
+#define LARGEBLOB_NONCE_LENGTH 12
+#define LARGEBLOB_TAG_LENGTH 16
+
+typedef struct largeblob {
+ size_t origsiz;
+ fido_blob_t ciphertext;
+ fido_blob_t nonce;
+} largeblob_t;
+
+static largeblob_t *
+largeblob_new(void)
+{
+ return calloc(1, sizeof(largeblob_t));
+}
+
+static void
+largeblob_reset(largeblob_t *blob)
+{
+ fido_blob_reset(&blob->ciphertext);
+ fido_blob_reset(&blob->nonce);
+ blob->origsiz = 0;
+}
+
+static void
+largeblob_free(largeblob_t **blob_ptr)
+{
+ largeblob_t *blob;
+
+ if (blob_ptr == NULL || (blob = *blob_ptr) == NULL)
+ return;
+ largeblob_reset(blob);
+ free(blob);
+ *blob_ptr = NULL;
+}
+
+static int
+largeblob_aad(fido_blob_t *aad, uint64_t size)
+{
+ uint8_t buf[4 + sizeof(uint64_t)];
+
+ buf[0] = 0x62; /* b */
+ buf[1] = 0x6c; /* l */
+ buf[2] = 0x6f; /* o */
+ buf[3] = 0x62; /* b */
+ size = htole64(size);
+ memcpy(&buf[4], &size, sizeof(uint64_t));
+
+ return fido_blob_set(aad, buf, sizeof(buf));
+}
+
+static fido_blob_t *
+largeblob_decrypt(const largeblob_t *blob, const fido_blob_t *key)
+{
+ fido_blob_t *plaintext = NULL, *aad = NULL;
+ int ok = -1;
+
+ if ((plaintext = fido_blob_new()) == NULL ||
+ (aad = fido_blob_new()) == NULL) {
+ fido_log_debug("%s: fido_blob_new", __func__);
+ goto fail;
+ }
+ if (largeblob_aad(aad, blob->origsiz) < 0) {
+ fido_log_debug("%s: largeblob_aad", __func__);
+ goto fail;
+ }
+ if (aes256_gcm_dec(key, &blob->nonce, aad, &blob->ciphertext,
+ plaintext) < 0) {
+ fido_log_debug("%s: aes256_gcm_dec", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ fido_blob_free(&aad);
+
+ if (ok < 0)
+ fido_blob_free(&plaintext);
+
+ return plaintext;
+}
+
+static int
+largeblob_get_nonce(largeblob_t *blob)
+{
+ uint8_t buf[LARGEBLOB_NONCE_LENGTH];
+ int ok = -1;
+
+ if (fido_get_random(buf, sizeof(buf)) < 0) {
+ fido_log_debug("%s: fido_get_random", __func__);
+ goto fail;
+ }
+ if (fido_blob_set(&blob->nonce, buf, sizeof(buf)) < 0) {
+ fido_log_debug("%s: fido_blob_set", __func__);
+ goto fail;
+ }
+
+ ok = 0;
+fail:
+ explicit_bzero(buf, sizeof(buf));
+
+ return ok;
+}
+
+static int
+largeblob_seal(largeblob_t *blob, const fido_blob_t *body,
+ const fido_blob_t *key)
+{
+ fido_blob_t *plaintext = NULL, *aad = NULL;
+ int ok = -1;
+
+ if ((plaintext = fido_blob_new()) == NULL ||
+ (aad = fido_blob_new()) == NULL) {
+ fido_log_debug("%s: fido_blob_new", __func__);
+ goto fail;
+ }
+ if (fido_compress(plaintext, body) != FIDO_OK) {
+ fido_log_debug("%s: fido_compress", __func__);
+ goto fail;
+ }
+ if (largeblob_aad(aad, body->len) < 0) {
+ fido_log_debug("%s: largeblob_aad", __func__);
+ goto fail;
+ }
+ if (largeblob_get_nonce(blob) < 0) {
+ fido_log_debug("%s: largeblob_get_nonce", __func__);
+ goto fail;
+ }
+ if (aes256_gcm_enc(key, &blob->nonce, aad, plaintext,
+ &blob->ciphertext) < 0) {
+ fido_log_debug("%s: aes256_gcm_enc", __func__);
+ goto fail;
+ }
+ blob->origsiz = body->len;
+
+ ok = 0;
+fail:
+ fido_blob_free(&plaintext);
+ fido_blob_free(&aad);
+
+ return ok;
+}
+
+static int
+largeblob_get_tx(fido_dev_t *dev, size_t offset, size_t count)
+{
+ fido_blob_t f;
+ cbor_item_t *argv[3];
+ int r;
+
+ memset(argv, 0, sizeof(argv));
+ memset(&f, 0, sizeof(f));
+
+ if ((argv[0] = cbor_build_uint(count)) == NULL ||
+ (argv[2] = cbor_build_uint(offset)) == NULL) {
+ fido_log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ 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_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);
+
+ return r;
+}
+
+static int
+parse_largeblob_reply(const cbor_item_t *key, const cbor_item_t *val,
+ void *arg)
+{
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 1) {
+ fido_log_debug("%s: cbor type", __func__);
+ return 0; /* ignore */
+ }
+
+ return fido_blob_decode(val, arg);
+}
+
+static int
+largeblob_get_rx(fido_dev_t *dev, fido_blob_t **chunk, int ms)
+{
+ unsigned char reply[FIDO_MAXMSG];
+ int reply_len, r;
+
+ *chunk = NULL;
+ if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
+ ms)) < 0) {
+ fido_log_debug("%s: fido_rx", __func__);
+ return FIDO_ERR_RX;
+ }
+ if ((*chunk = fido_blob_new()) == NULL) {
+ fido_log_debug("%s: fido_blob_new", __func__);
+ return FIDO_ERR_INTERNAL;
+ }
+ if ((r = cbor_parse_reply(reply, (size_t)reply_len, *chunk,
+ parse_largeblob_reply)) != FIDO_OK) {
+ fido_log_debug("%s: parse_largeblob_reply", __func__);
+ fido_blob_free(chunk);
+ return r;
+ }
+
+ return FIDO_OK;
+}
+
+static cbor_item_t *
+largeblob_array_load(const uint8_t *ptr, size_t len)
+{
+ struct cbor_load_result cbor;
+ cbor_item_t *item;
+
+ if (len < LARGEBLOB_DIGEST_LENGTH) {
+ fido_log_debug("%s: len", __func__);
+ return NULL;
+ }
+ len -= LARGEBLOB_DIGEST_LENGTH;
+ if ((item = cbor_load(ptr, len, &cbor)) == NULL) {
+ fido_log_debug("%s: cbor_load", __func__);
+ return NULL;
+ }
+ if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
+ fido_log_debug("%s: cbor type", __func__);
+ cbor_decref(&item);
+ return NULL;
+ }
+
+ return item;
+}
+
+static size_t
+get_chunklen(fido_dev_t *dev)
+{
+ uint64_t maxchunklen;
+
+ if ((maxchunklen = fido_dev_maxmsgsize(dev)) > SIZE_MAX)
+ maxchunklen = SIZE_MAX;
+ if (maxchunklen > FIDO_MAXMSG)
+ maxchunklen = FIDO_MAXMSG;
+ maxchunklen = maxchunklen > 64 ? maxchunklen - 64 : 0;
+
+ return (size_t)maxchunklen;
+}
+
+static int
+largeblob_do_decode(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ largeblob_t *blob = arg;
+ uint64_t origsiz;
+
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8) {
+ fido_log_debug("%s: cbor type", __func__);
+ return 0; /* ignore */
+ }
+
+ switch (cbor_get_uint8(key)) {
+ case 1: /* ciphertext */
+ if (fido_blob_decode(val, &blob->ciphertext) < 0 ||
+ blob->ciphertext.len < LARGEBLOB_TAG_LENGTH)
+ return -1;
+ return 0;
+ case 2: /* nonce */
+ if (fido_blob_decode(val, &blob->nonce) < 0 ||
+ blob->nonce.len != LARGEBLOB_NONCE_LENGTH)
+ return -1;
+ return 0;
+ case 3: /* origSize */
+ if (!cbor_isa_uint(val) ||
+ (origsiz = cbor_get_int(val)) > SIZE_MAX)
+ return -1;
+ blob->origsiz = (size_t)origsiz;
+ return 0;
+ default: /* ignore */
+ fido_log_debug("%s: cbor type", __func__);
+ return 0;
+ }
+}
+
+static int
+largeblob_decode(largeblob_t *blob, const cbor_item_t *item)
+{
+ if (!cbor_isa_map(item) || !cbor_map_is_definite(item)) {
+ fido_log_debug("%s: cbor type", __func__);
+ return -1;
+ }
+ if (cbor_map_iter(item, blob, largeblob_do_decode) < 0) {
+ fido_log_debug("%s: cbor_map_iter", __func__);
+ return -1;
+ }
+ if (fido_blob_is_empty(&blob->ciphertext) ||
+ fido_blob_is_empty(&blob->nonce) || blob->origsiz == 0) {
+ fido_log_debug("%s: incomplete blob", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static cbor_item_t *
+largeblob_encode(const fido_blob_t *body, const fido_blob_t *key)
+{
+ largeblob_t *blob;
+ cbor_item_t *argv[3], *item = NULL;
+
+ memset(argv, 0, sizeof(argv));
+ if ((blob = largeblob_new()) == NULL ||
+ largeblob_seal(blob, body, key) < 0) {
+ fido_log_debug("%s: largeblob_seal", __func__);
+ goto fail;
+ }
+ if ((argv[0] = fido_blob_encode(&blob->ciphertext)) == NULL ||
+ (argv[1] = fido_blob_encode(&blob->nonce)) == NULL ||
+ (argv[2] = cbor_build_uint(blob->origsiz)) == NULL) {
+ fido_log_debug("%s: cbor encode", __func__);
+ goto fail;
+ }
+ item = cbor_flatten_vector(argv, nitems(argv));
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ largeblob_free(&blob);
+
+ return item;
+}
+
+static int
+largeblob_array_lookup(fido_blob_t *out, size_t *idx, const cbor_item_t *item,
+ const fido_blob_t *key)
+{
+ cbor_item_t **v;
+ fido_blob_t *plaintext = NULL;
+ largeblob_t blob;
+ int r;
+
+ memset(&blob, 0, sizeof(blob));
+ if (idx != NULL)
+ *idx = 0;
+ if ((v = cbor_array_handle(item)) == NULL)
+ return FIDO_ERR_INVALID_ARGUMENT;
+ for (size_t i = 0; i < cbor_array_size(item); i++) {
+ if (largeblob_decode(&blob, v[i]) < 0 ||
+ (plaintext = largeblob_decrypt(&blob, key)) == NULL) {
+ fido_log_debug("%s: largeblob_decode", __func__);
+ largeblob_reset(&blob);
+ continue;
+ }
+ if (idx != NULL)
+ *idx = i;
+ break;
+ }
+ if (plaintext == NULL) {
+ fido_log_debug("%s: not found", __func__);
+ return FIDO_ERR_NOTFOUND;
+ }
+ if (out != NULL)
+ r = fido_uncompress(out, plaintext, blob.origsiz);
+ else
+ r = FIDO_OK;
+
+ fido_blob_free(&plaintext);
+ largeblob_reset(&blob);
+
+ return r;
+}
+
+static int
+largeblob_array_digest(u_char out[LARGEBLOB_DIGEST_LENGTH], const u_char *data,
+ size_t len)
+{
+ u_char dgst[SHA256_DIGEST_LENGTH];
+
+ if (data == NULL || len == 0)
+ return -1;
+ if (SHA256(data, len, dgst) != dgst)
+ return -1;
+ memcpy(out, dgst, LARGEBLOB_DIGEST_LENGTH);
+
+ return 0;
+}
+
+static int
+largeblob_array_check(const fido_blob_t *array)
+{
+ u_char expected_hash[LARGEBLOB_DIGEST_LENGTH];
+ size_t body_len;
+
+ fido_log_xxd(array->ptr, array->len, __func__);
+ if (array->len < sizeof(expected_hash)) {
+ fido_log_debug("%s: len %zu", __func__, array->len);
+ return -1;
+ }
+ body_len = array->len - sizeof(expected_hash);
+ if (largeblob_array_digest(expected_hash, array->ptr, body_len) < 0) {
+ fido_log_debug("%s: largeblob_array_digest", __func__);
+ return -1;
+ }
+
+ return timingsafe_bcmp(expected_hash, array->ptr + body_len,
+ sizeof(expected_hash));
+}
+
+static int
+largeblob_get_array(fido_dev_t *dev, cbor_item_t **item)
+{
+ fido_blob_t *array, *chunk = NULL;
+ size_t n;
+ int r;
+
+ *item = NULL;
+ if ((n = get_chunklen(dev)) == 0)
+ return FIDO_ERR_INVALID_ARGUMENT;
+ if ((array = fido_blob_new()) == NULL)
+ 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) {
+ fido_log_debug("%s: largeblob_get_wait %zu/%zu",
+ __func__, array->len, n);
+ goto fail;
+ }
+ if (fido_blob_append(array, chunk->ptr, chunk->len) < 0) {
+ fido_log_debug("%s: fido_blob_append", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ } while (chunk->len == n);
+
+ if (largeblob_array_check(array) != 0)
+ *item = cbor_new_definite_array(0); /* per spec */
+ else
+ *item = largeblob_array_load(array->ptr, array->len);
+ if (*item == NULL)
+ r = FIDO_ERR_INTERNAL;
+ else
+ r = FIDO_OK;
+fail:
+ fido_blob_free(&array);
+ fido_blob_free(&chunk);
+
+ return r;
+}
+
+static int
+prepare_hmac(size_t offset, const u_char *data, size_t len, fido_blob_t *hmac)
+{
+ uint8_t buf[32 + 2 + sizeof(uint32_t) + SHA256_DIGEST_LENGTH];
+ uint32_t u32_offset;
+
+ if (data == NULL || len == 0) {
+ fido_log_debug("%s: invalid data=%p, len=%zu", __func__,
+ (const void *)data, len);
+ return -1;
+ }
+ if (offset > UINT32_MAX) {
+ fido_log_debug("%s: invalid offset=%zu", __func__, offset);
+ return -1;
+ }
+
+ memset(buf, 0xff, 32);
+ buf[32] = CTAP_CBOR_LARGEBLOB;
+ buf[33] = 0x00;
+ u32_offset = htole32((uint32_t)offset);
+ memcpy(&buf[34], &u32_offset, sizeof(uint32_t));
+ if (SHA256(data, len, &buf[38]) != &buf[38]) {
+ fido_log_debug("%s: SHA256", __func__);
+ return -1;
+ }
+
+ return fido_blob_set(hmac, buf, sizeof(buf));
+}
+
+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)
+{
+ fido_blob_t *hmac = NULL, f;
+ cbor_item_t *argv[6];
+ int r;
+
+ memset(argv, 0, sizeof(argv));
+ memset(&f, 0, sizeof(f));
+
+ if ((argv[1] = cbor_build_bytestring(chunk, chunk_len)) == NULL ||
+ (argv[2] = cbor_build_uint(offset)) == NULL ||
+ (offset == 0 && (argv[3] = cbor_build_uint(totalsiz)) == NULL)) {
+ fido_log_debug("%s: cbor encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ if (token != NULL) {
+ if ((hmac = fido_blob_new()) == NULL ||
+ prepare_hmac(offset, chunk, chunk_len, hmac) < 0 ||
+ (argv[4] = cbor_encode_pin_auth(dev, token, hmac)) == NULL ||
+ (argv[5] = cbor_encode_pin_opt(dev)) == NULL) {
+ fido_log_debug("%s: cbor_encode_pin_auth", __func__);
+ r = FIDO_ERR_INTERNAL;
+ 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_log_debug("%s: fido_tx", __func__);
+ r = FIDO_ERR_TX;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ cbor_vector_free(argv, nitems(argv));
+ fido_blob_free(&hmac);
+ free(f.ptr);
+
+ return r;
+}
+
+static int
+largeblob_get_uv_token(fido_dev_t *dev, const char *pin, fido_blob_t **token)
+{
+ es256_pk_t *pk = NULL;
+ fido_blob_t *ecdh = NULL;
+ int r;
+
+ if ((*token = fido_blob_new()) == NULL)
+ return FIDO_ERR_INTERNAL;
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != 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) {
+ fido_log_debug("%s: fido_dev_get_uv_token", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (r != FIDO_OK)
+ fido_blob_free(token);
+
+ fido_blob_free(&ecdh);
+ es256_pk_free(&pk);
+
+ return r;
+}
+
+static int
+largeblob_set_array(fido_dev_t *dev, const cbor_item_t *item, const char *pin)
+{
+ unsigned char dgst[SHA256_DIGEST_LENGTH];
+ fido_blob_t cbor, *token = NULL;
+ size_t chunklen, maxchunklen, totalsize;
+ int r;
+
+ memset(&cbor, 0, sizeof(cbor));
+
+ if ((maxchunklen = get_chunklen(dev)) == 0) {
+ fido_log_debug("%s: maxchunklen=%zu", __func__, maxchunklen);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ if (!cbor_isa_array(item) || !cbor_array_is_definite(item)) {
+ fido_log_debug("%s: cbor type", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ if ((fido_blob_serialise(&cbor, item)) < 0) {
+ fido_log_debug("%s: fido_blob_serialise", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ if (cbor.len > SIZE_MAX - sizeof(dgst)) {
+ fido_log_debug("%s: cbor.len=%zu", __func__, cbor.len);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ if (SHA256(cbor.ptr, cbor.len, dgst) != dgst) {
+ fido_log_debug("%s: SHA256", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ 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) {
+ fido_log_debug("%s: largeblob_get_uv_token", __func__);
+ goto fail;
+ }
+ }
+ for (size_t offset = 0; offset < cbor.len; offset += chunklen) {
+ 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) {
+ 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) {
+ fido_log_debug("%s: dgst", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ fido_blob_free(&token);
+ fido_blob_reset(&cbor);
+
+ return r;
+}
+
+static int
+largeblob_add(fido_dev_t *dev, const fido_blob_t *key, cbor_item_t *item,
+ const char *pin)
+{
+ cbor_item_t *array = NULL;
+ size_t idx;
+ int r;
+
+ if ((r = largeblob_get_array(dev, &array)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_get_array", __func__);
+ goto fail;
+ }
+
+ switch (r = largeblob_array_lookup(NULL, &idx, array, key)) {
+ case FIDO_OK:
+ if (!cbor_array_replace(array, idx, item)) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ break;
+ case FIDO_ERR_NOTFOUND:
+ if (cbor_array_append(&array, item) < 0) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ break;
+ default:
+ fido_log_debug("%s: largeblob_array_lookup", __func__);
+ goto fail;
+ }
+
+ if ((r = largeblob_set_array(dev, array, pin)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_set_array", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (array != NULL)
+ cbor_decref(&array);
+
+ return r;
+}
+
+static int
+largeblob_drop(fido_dev_t *dev, const fido_blob_t *key, const char *pin)
+{
+ cbor_item_t *array = NULL;
+ size_t idx;
+ int r;
+
+ if ((r = largeblob_get_array(dev, &array)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_get_array", __func__);
+ goto fail;
+ }
+ if ((r = largeblob_array_lookup(NULL, &idx, array, key)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_array_lookup", __func__);
+ goto fail;
+ }
+ if (cbor_array_drop(&array, idx) < 0) {
+ fido_log_debug("%s: cbor_array_drop", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ if ((r = largeblob_set_array(dev, array, pin)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_set_array", __func__);
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ if (array != NULL)
+ cbor_decref(&array);
+
+ return r;
+}
+
+int
+fido_dev_largeblob_get(fido_dev_t *dev, const unsigned char *key_ptr,
+ size_t key_len, unsigned char **blob_ptr, size_t *blob_len)
+{
+ cbor_item_t *item = NULL;
+ fido_blob_t key, body;
+ int r;
+
+ memset(&key, 0, sizeof(key));
+ memset(&body, 0, sizeof(body));
+
+ if (key_len != 32) {
+ fido_log_debug("%s: invalid key len %zu", __func__, key_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if (blob_ptr == NULL || blob_len == NULL) {
+ fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%p", __func__,
+ (const void *)blob_ptr, (const void *)blob_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ *blob_ptr = NULL;
+ *blob_len = 0;
+ if (fido_blob_set(&key, key_ptr, key_len) < 0) {
+ fido_log_debug("%s: fido_blob_set", __func__);
+ return FIDO_ERR_INTERNAL;
+ }
+ if ((r = largeblob_get_array(dev, &item)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_get_array", __func__);
+ goto fail;
+ }
+ if ((r = largeblob_array_lookup(&body, NULL, item, &key)) != FIDO_OK)
+ fido_log_debug("%s: largeblob_array_lookup", __func__);
+ else {
+ *blob_ptr = body.ptr;
+ *blob_len = body.len;
+ }
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ fido_blob_reset(&key);
+
+ return r;
+}
+
+int
+fido_dev_largeblob_set(fido_dev_t *dev, const unsigned char *key_ptr,
+ size_t key_len, const unsigned char *blob_ptr, size_t blob_len,
+ const char *pin)
+{
+ cbor_item_t *item = NULL;
+ fido_blob_t key, body;
+ int r;
+
+ memset(&key, 0, sizeof(key));
+ memset(&body, 0, sizeof(body));
+
+ if (key_len != 32) {
+ fido_log_debug("%s: invalid key len %zu", __func__, key_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if (blob_ptr == NULL || blob_len == 0) {
+ fido_log_debug("%s: invalid blob_ptr=%p, blob_len=%zu", __func__,
+ (const void *)blob_ptr, blob_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if (fido_blob_set(&key, key_ptr, key_len) < 0 ||
+ fido_blob_set(&body, blob_ptr, blob_len) < 0) {
+ fido_log_debug("%s: fido_blob_set", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ if ((item = largeblob_encode(&body, &key)) == NULL) {
+ fido_log_debug("%s: largeblob_encode", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+ if ((r = largeblob_add(dev, &key, item, pin)) != FIDO_OK)
+ fido_log_debug("%s: largeblob_add", __func__);
+fail:
+ if (item != NULL)
+ cbor_decref(&item);
+
+ fido_blob_reset(&key);
+ fido_blob_reset(&body);
+
+ return r;
+}
+
+int
+fido_dev_largeblob_remove(fido_dev_t *dev, const unsigned char *key_ptr,
+ size_t key_len, const char *pin)
+{
+ fido_blob_t key;
+ int r;
+
+ memset(&key, 0, sizeof(key));
+
+ if (key_len != 32) {
+ fido_log_debug("%s: invalid key len %zu", __func__, key_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if (fido_blob_set(&key, key_ptr, key_len) < 0) {
+ fido_log_debug("%s: fido_blob_set", __func__);
+ return FIDO_ERR_INTERNAL;
+ }
+ if ((r = largeblob_drop(dev, &key, pin)) != FIDO_OK)
+ fido_log_debug("%s: largeblob_drop", __func__);
+
+ fido_blob_reset(&key);
+
+ return r;
+}
+
+int
+fido_dev_largeblob_get_array(fido_dev_t *dev, unsigned char **cbor_ptr,
+ size_t *cbor_len)
+{
+ cbor_item_t *item = NULL;
+ fido_blob_t cbor;
+ int r;
+
+ memset(&cbor, 0, sizeof(cbor));
+
+ if (cbor_ptr == NULL || cbor_len == NULL) {
+ fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%p", __func__,
+ (const void *)cbor_ptr, (const void *)cbor_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ *cbor_ptr = NULL;
+ *cbor_len = 0;
+ if ((r = largeblob_get_array(dev, &item)) != FIDO_OK) {
+ fido_log_debug("%s: largeblob_get_array", __func__);
+ return r;
+ }
+ if (fido_blob_serialise(&cbor, item) < 0) {
+ fido_log_debug("%s: fido_blob_serialise", __func__);
+ r = FIDO_ERR_INTERNAL;
+ } else {
+ *cbor_ptr = cbor.ptr;
+ *cbor_len = cbor.len;
+ }
+
+ cbor_decref(&item);
+
+ return r;
+}
+
+int
+fido_dev_largeblob_set_array(fido_dev_t *dev, const unsigned char *cbor_ptr,
+ size_t cbor_len, const char *pin)
+{
+ cbor_item_t *item = NULL;
+ struct cbor_load_result cbor_result;
+ int r;
+
+ if (cbor_ptr == NULL || cbor_len == 0) {
+ fido_log_debug("%s: invalid cbor_ptr=%p, cbor_len=%zu", __func__,
+ (const void *)cbor_ptr, cbor_len);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if ((item = cbor_load(cbor_ptr, cbor_len, &cbor_result)) == NULL) {
+ fido_log_debug("%s: cbor_load", __func__);
+ return FIDO_ERR_INVALID_ARGUMENT;
+ }
+ if ((r = largeblob_set_array(dev, item, pin)) != FIDO_OK)
+ fido_log_debug("%s: largeblob_set_array", __func__);
+
+ cbor_decref(&item);
+
+ 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.
*/
+#undef _GNU_SOURCE /* XSI strerror_r() */
+
#include <stdarg.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include "fido.h"
fprintf(stderr, "%s", str);
}
+static void
+do_log(const char *suffix, const char *fmt, va_list args)
+{
+ char line[LINELEN], body[LINELEN];
+
+ vsnprintf(body, sizeof(body), fmt, args);
+
+ if (suffix != NULL)
+ snprintf(line, sizeof(line), "%.180s: %.70s\n", body, suffix);
+ else
+ snprintf(line, sizeof(line), "%.180s\n", body);
+
+ log_handler(line);
+}
+
void
fido_log_init(void)
{
void
fido_log_debug(const char *fmt, ...)
{
- char line[LINELEN];
- va_list ap;
- int r;
+ va_list args;
if (!logging || log_handler == NULL)
return;
- va_start(ap, fmt);
- r = vsnprintf(line, sizeof(line) - 1, fmt, ap);
- va_end(ap);
- if (r < 0 || (size_t)r >= sizeof(line) - 1)
- return;
- strlcat(line, "\n", sizeof(line));
- log_handler(line);
+ va_start(args, fmt);
+ do_log(NULL, fmt, args);
+ va_end(args);
}
void
-fido_log_xxd(const void *buf, size_t count)
+fido_log_xxd(const void *buf, size_t count, const char *fmt, ...)
{
const uint8_t *ptr = buf;
- char row[XXDROW];
- char xxd[XXDLEN];
+ char row[XXDROW], xxd[XXDLEN];
+ va_list args;
- if (!logging || log_handler == NULL || count == 0)
+ if (!logging || log_handler == NULL)
return;
+ snprintf(row, sizeof(row), "buf=%p, len=%zu", buf, count);
+ va_start(args, fmt);
+ do_log(row, fmt, args);
+ va_end(args);
*row = '\0';
for (size_t i = 0; i < count; i++) {
}
}
+void
+fido_log_error(int errnum, const char *fmt, ...)
+{
+ char errstr[LINELEN];
+ va_list args;
+
+ if (!logging || log_handler == NULL)
+ return;
+ if (strerror_r(errnum, errstr, sizeof(errstr)) != 0)
+ snprintf(errstr, sizeof(errstr), "error %d", errnum);
+
+ va_start(args, fmt);
+ do_log(errstr, fmt, args);
+ va_end(args);
+}
+
void
fido_set_log_handler(fido_log_handler_t *handler)
{
* license that can be found in the LICENSE file.
*/
-#include <string.h>
-
+#include <openssl/sha.h>
#include "fido.h"
#include "fido/es256.h"
+#define CTAP21_UV_TOKEN_PERM_MAKECRED 0x01
+#define CTAP21_UV_TOKEN_PERM_ASSERT 0x02
+#define CTAP21_UV_TOKEN_PERM_CRED_MGMT 0x04
+#define CTAP21_UV_TOKEN_PERM_BIO 0x08
+#define CTAP21_UV_TOKEN_PERM_LARGEBLOB 0x10
+#define CTAP21_UV_TOKEN_PERM_CONFIG 0x20
+
+int
+fido_sha256(fido_blob_t *digest, const u_char *data, size_t data_len)
+{
+ if ((digest->ptr = calloc(1, SHA256_DIGEST_LENGTH)) == NULL)
+ return (-1);
+
+ digest->len = SHA256_DIGEST_LENGTH;
+
+ if (SHA256(data, data_len, digest->ptr) != digest->ptr) {
+ fido_blob_reset(digest);
+ return (-1);
+ }
+
+ return (0);
+}
+
static int
-parse_pintoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+pin_sha256_enc(const fido_dev_t *dev, const fido_blob_t *shared,
+ const fido_blob_t *pin, fido_blob_t **out)
{
- fido_blob_t *token = arg;
+ fido_blob_t *ph = NULL;
+ int r;
- if (cbor_isa_uint(key) == false ||
- cbor_int_get_width(key) != CBOR_INT_8 ||
- cbor_get_uint8(key) != 2) {
- fido_log_debug("%s: cbor type", __func__);
- return (0); /* ignore */
+ if ((*out = fido_blob_new()) == NULL ||
+ (ph = fido_blob_new()) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
}
- return (fido_blob_decode(val, token));
+ if (fido_sha256(ph, pin->ptr, pin->len) < 0 || ph->len < 16) {
+ fido_log_debug("%s: SHA256", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ ph->len = 16; /* first 16 bytes */
+
+ if (aes256_cbc_enc(dev, shared, ph, *out) < 0) {
+ fido_log_debug("%s: aes256_cbc_enc", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ fido_blob_free(&ph);
+
+ return (r);
+}
+
+static int
+pad64(const char *pin, fido_blob_t **ppin)
+{
+ size_t pin_len;
+ size_t ppin_len;
+
+ pin_len = strlen(pin);
+ if (pin_len < 4 || pin_len > 255) {
+ fido_log_debug("%s: invalid pin length", __func__);
+ return (FIDO_ERR_PIN_POLICY_VIOLATION);
+ }
+
+ if ((*ppin = fido_blob_new()) == NULL)
+ return (FIDO_ERR_INTERNAL);
+
+ ppin_len = (pin_len + 63U) & ~63U;
+ if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
+ fido_blob_free(ppin);
+ return (FIDO_ERR_INTERNAL);
+ }
+
+ memcpy((*ppin)->ptr, pin, pin_len);
+ (*ppin)->len = ppin_len;
+
+ return (FIDO_OK);
}
-#ifdef FIDO_UVTOKEN
static int
-parse_uvtoken(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+pin_pad64_enc(const fido_dev_t *dev, const fido_blob_t *shared,
+ const char *pin, fido_blob_t **out)
+{
+ fido_blob_t *ppin = NULL;
+ int r;
+
+ if ((r = pad64(pin, &ppin)) != FIDO_OK) {
+ fido_log_debug("%s: pad64", __func__);
+ goto fail;
+ }
+
+ if ((*out = fido_blob_new()) == NULL) {
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ if (aes256_cbc_enc(dev, shared, ppin, *out) < 0) {
+ fido_log_debug("%s: aes256_cbc_enc", __func__);
+ r = FIDO_ERR_INTERNAL;
+ goto fail;
+ }
+
+ r = FIDO_OK;
+fail:
+ fido_blob_free(&ppin);
+
+ return (r);
+}
+
+static cbor_item_t *
+encode_uv_permission(uint8_t cmd)
{
- return (parse_pintoken(key, val, arg));
+ switch (cmd) {
+ case CTAP_CBOR_ASSERT:
+ return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_ASSERT));
+ case CTAP_CBOR_BIO_ENROLL_PRE:
+ return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_BIO));
+ case CTAP_CBOR_CONFIG:
+ return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CONFIG));
+ case CTAP_CBOR_MAKECRED:
+ return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_MAKECRED));
+ case CTAP_CBOR_CRED_MGMT_PRE:
+ return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_CRED_MGMT));
+ case CTAP_CBOR_LARGEBLOB:
+ return (cbor_build_uint8(CTAP21_UV_TOKEN_PERM_LARGEBLOB));
+ default:
+ fido_log_debug("%s: cmd 0x%02x", __func__, cmd);
+ return (NULL);
+ }
}
-#endif /* FIDO_UVTOKEN */
static int
-fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
- const fido_blob_t *ecdh, const es256_pk_t *pk)
+ctap20_uv_token_tx(fido_dev_t *dev, const char *pin, const fido_blob_t *ecdh,
+ const es256_pk_t *pk)
{
fido_blob_t f;
fido_blob_t *p = NULL;
+ fido_blob_t *phe = NULL;
cbor_item_t *argv[6];
int r;
memset(&f, 0, sizeof(f));
memset(argv, 0, sizeof(argv));
+ if (pin == NULL) {
+ fido_log_debug("%s: NULL pin", __func__);
+ r = FIDO_ERR_PIN_REQUIRED;
+ goto fail;
+ }
+
if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
(const unsigned char *)pin, strlen(pin)) < 0) {
fido_log_debug("%s: fido_blob_set", __func__);
goto fail;
}
- if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
+ fido_log_debug("%s: pin_sha256_enc", __func__);
+ goto fail;
+ }
+
+ if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
(argv[1] = cbor_build_uint8(5)) == NULL ||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
- (argv[5] = cbor_encode_pin_hash_enc(ecdh, p)) == NULL) {
+ (argv[5] = fido_blob_encode(phe)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
fail:
cbor_vector_free(argv, nitems(argv));
fido_blob_free(&p);
+ fido_blob_free(&phe);
free(f.ptr);
return (r);
}
-#ifdef FIDO_UVTOKEN
static int
-fido_dev_get_uv_token_tx(fido_dev_t *dev, const es256_pk_t *pk)
+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)
{
fido_blob_t f;
- cbor_item_t *argv[3];
+ fido_blob_t *p = NULL;
+ fido_blob_t *phe = NULL;
+ cbor_item_t *argv[10];
+ uint8_t subcmd;
int r;
memset(&f, 0, sizeof(f));
memset(argv, 0, sizeof(argv));
- if ((argv[0] = cbor_build_uint8(1)) == NULL ||
- (argv[1] = cbor_build_uint8(6)) == NULL ||
- (argv[2] = es256_pk_encode(pk, 1)) == NULL) {
+ if (pin != NULL) {
+ if ((p = fido_blob_new()) == NULL || fido_blob_set(p,
+ (const unsigned char *)pin, strlen(pin)) < 0) {
+ fido_log_debug("%s: fido_blob_set", __func__);
+ r = FIDO_ERR_INVALID_ARGUMENT;
+ goto fail;
+ }
+ if ((r = pin_sha256_enc(dev, ecdh, p, &phe)) != FIDO_OK) {
+ fido_log_debug("%s: pin_sha256_enc", __func__);
+ goto fail;
+ }
+ subcmd = 9; /* getPinUvAuthTokenUsingPinWithPermissions */
+ } else {
+ if (fido_dev_has_uv(dev) == false) {
+ fido_log_debug("%s: fido_dev_has_uv", __func__);
+ r = FIDO_ERR_PIN_REQUIRED;
+ goto fail;
+ }
+ subcmd = 6; /* getPinUvAuthTokenUsingUvWithPermissions */
+ }
+
+ if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
+ (argv[1] = cbor_build_uint8(subcmd)) == NULL ||
+ (argv[2] = es256_pk_encode(pk, 1)) == NULL ||
+ (phe != NULL && (argv[5] = fido_blob_encode(phe)) == NULL) ||
+ (argv[8] = encode_uv_permission(cmd)) == NULL ||
+ (rpid != NULL && (argv[9] = cbor_build_string(rpid)) == NULL)) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
r = FIDO_OK;
fail:
cbor_vector_free(argv, nitems(argv));
+ fido_blob_free(&p);
+ fido_blob_free(&phe);
free(f.ptr);
return (r);
}
-#endif /* FIDO_UVTOKEN */
static int
-fido_dev_get_pin_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
- fido_blob_t *token, int ms)
+parse_uv_token(const cbor_item_t *key, const cbor_item_t *val, void *arg)
{
- fido_blob_t *aes_token = NULL;
- unsigned char reply[FIDO_MAXMSG];
- int reply_len;
- int r;
-
- if ((aes_token = fido_blob_new()) == NULL) {
- r = FIDO_ERR_INTERNAL;
- goto fail;
- }
-
- if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
- ms)) < 0) {
- fido_log_debug("%s: fido_rx", __func__);
- r = FIDO_ERR_RX;
- goto fail;
- }
-
- if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
- parse_pintoken)) != FIDO_OK) {
- fido_log_debug("%s: parse_pintoken", __func__);
- goto fail;
- }
+ fido_blob_t *token = arg;
- if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
- fido_log_debug("%s: aes256_cbc_dec", __func__);
- r = FIDO_ERR_RX;
- goto fail;
+ if (cbor_isa_uint(key) == false ||
+ cbor_int_get_width(key) != CBOR_INT_8 ||
+ cbor_get_uint8(key) != 2) {
+ fido_log_debug("%s: cbor type", __func__);
+ return (0); /* ignore */
}
- r = FIDO_OK;
-fail:
- fido_blob_free(&aes_token);
-
- return (r);
+ return (fido_blob_decode(val, token));
}
-#ifdef FIDO_UVTOKEN
static int
-fido_dev_get_uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh,
- fido_blob_t *token, int ms)
+uv_token_rx(fido_dev_t *dev, const fido_blob_t *ecdh, fido_blob_t *token,
+ int ms)
{
fido_blob_t *aes_token = NULL;
unsigned char reply[FIDO_MAXMSG];
}
if ((r = cbor_parse_reply(reply, (size_t)reply_len, aes_token,
- parse_uvtoken)) != FIDO_OK) {
- fido_log_debug("%s: parse_uvtoken", __func__);
+ parse_uv_token)) != FIDO_OK) {
+ fido_log_debug("%s: parse_uv_token", __func__);
goto fail;
}
- if (aes256_cbc_dec(ecdh, aes_token, token) < 0) {
+ if (aes256_cbc_dec(dev, ecdh, aes_token, token) < 0) {
fido_log_debug("%s: aes256_cbc_dec", __func__);
r = FIDO_ERR_RX;
goto fail;
return (r);
}
-#endif /* FIDO_UVTOKEN */
static int
-fido_dev_get_pin_token_wait(fido_dev_t *dev, const char *pin,
- const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token, int ms)
+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)
{
int r;
-#ifdef FIDO_UVTOKEN
- if (getenv("FIDO_UVTOKEN") != NULL) {
- if ((r = fido_dev_get_uv_token_tx(dev, pk)) != FIDO_OK ||
- (r = fido_dev_get_uv_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
- return (r);
- } else {
- if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
- (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
- return (r);
- }
-#else
- if ((r = fido_dev_get_pin_token_tx(dev, pin, ecdh, pk)) != FIDO_OK ||
- (r = fido_dev_get_pin_token_rx(dev, ecdh, token, ms)) != FIDO_OK)
+ 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);
+ else
+ r = ctap20_uv_token_tx(dev, pin, ecdh, pk);
+ if (r != FIDO_OK)
return (r);
-#endif
- return (FIDO_OK);
+ return (uv_token_rx(dev, ecdh, token, ms));
}
int
-fido_dev_get_pin_token(fido_dev_t *dev, const char *pin,
- const fido_blob_t *ecdh, const es256_pk_t *pk, fido_blob_t *token)
+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)
{
- return (fido_dev_get_pin_token_wait(dev, pin, ecdh, pk, token, -1));
-}
-
-static int
-pad64(const char *pin, fido_blob_t **ppin)
-{
- size_t pin_len;
- size_t ppin_len;
-
- pin_len = strlen(pin);
- if (pin_len < 4 || pin_len > 255) {
- fido_log_debug("%s: invalid pin length", __func__);
- return (FIDO_ERR_PIN_POLICY_VIOLATION);
- }
-
- if ((*ppin = fido_blob_new()) == NULL)
- return (FIDO_ERR_INTERNAL);
-
- ppin_len = (pin_len + 63U) & ~63U;
- if (ppin_len < pin_len || ((*ppin)->ptr = calloc(1, ppin_len)) == NULL) {
- fido_blob_free(ppin);
- return (FIDO_ERR_INTERNAL);
- }
-
- memcpy((*ppin)->ptr, pin, pin_len);
- (*ppin)->len = ppin_len;
-
- return (FIDO_OK);
+ return (uv_token_wait(dev, cmd, pin, ecdh, pk, rpid, token, -1));
}
static int
fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
{
fido_blob_t f;
- fido_blob_t *ppin = NULL;
+ fido_blob_t *ppine = NULL;
fido_blob_t *ecdh = NULL;
fido_blob_t *opin = NULL;
+ fido_blob_t *opinhe = NULL;
cbor_item_t *argv[6];
es256_pk_t *pk = NULL;
int r;
goto fail;
}
- if ((r = pad64(pin, &ppin)) != FIDO_OK) {
- fido_log_debug("%s: pad64", __func__);
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
- if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
- fido_log_debug("%s: fido_do_ecdh", __func__);
+ /* pad and encrypt new pin */
+ if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
+ fido_log_debug("%s: pin_pad64_enc", __func__);
goto fail;
}
- if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ /* hash and encrypt old pin */
+ if ((r = pin_sha256_enc(dev, ecdh, opin, &opinhe)) != FIDO_OK) {
+ fido_log_debug("%s: pin_sha256_enc", __func__);
+ goto fail;
+ }
+
+ if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
(argv[1] = cbor_build_uint8(4)) == NULL ||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
- (argv[3] = cbor_encode_change_pin_auth(ecdh, ppin, opin)) == NULL ||
- (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL ||
- (argv[5] = cbor_encode_pin_hash_enc(ecdh, opin)) == NULL) {
+ (argv[3] = cbor_encode_change_pin_auth(dev, ecdh, ppine, opinhe)) == NULL ||
+ (argv[4] = fido_blob_encode(ppine)) == NULL ||
+ (argv[5] = fido_blob_encode(opinhe)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
fail:
cbor_vector_free(argv, nitems(argv));
es256_pk_free(&pk);
- fido_blob_free(&ppin);
+ fido_blob_free(&ppine);
fido_blob_free(&ecdh);
fido_blob_free(&opin);
+ fido_blob_free(&opinhe);
free(f.ptr);
return (r);
fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
{
fido_blob_t f;
- fido_blob_t *ppin = NULL;
+ fido_blob_t *ppine = NULL;
fido_blob_t *ecdh = NULL;
cbor_item_t *argv[5];
es256_pk_t *pk = NULL;
memset(&f, 0, sizeof(f));
memset(argv, 0, sizeof(argv));
- if ((r = pad64(pin, &ppin)) != FIDO_OK) {
- fido_log_debug("%s: pad64", __func__);
+ if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
+ fido_log_debug("%s: fido_do_ecdh", __func__);
goto fail;
}
- if ((r = fido_do_ecdh(dev, &pk, &ecdh)) != FIDO_OK) {
- fido_log_debug("%s: fido_do_ecdh", __func__);
+ if ((r = pin_pad64_enc(dev, ecdh, pin, &ppine)) != FIDO_OK) {
+ fido_log_debug("%s: pin_pad64_enc", __func__);
goto fail;
}
- if ((argv[0] = cbor_build_uint8(1)) == NULL ||
+ if ((argv[0] = cbor_encode_pin_opt(dev)) == NULL ||
(argv[1] = cbor_build_uint8(3)) == NULL ||
(argv[2] = es256_pk_encode(pk, 1)) == NULL ||
- (argv[3] = cbor_encode_set_pin_auth(ecdh, ppin)) == NULL ||
- (argv[4] = cbor_encode_pin_enc(ecdh, ppin)) == NULL) {
+ (argv[3] = cbor_encode_pin_auth(dev, ecdh, ppine)) == NULL ||
+ (argv[4] = fido_blob_encode(ppine)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
fail:
cbor_vector_free(argv, nitems(argv));
es256_pk_free(&pk);
- fido_blob_free(&ppin);
+ fido_blob_free(&ppine);
fido_blob_free(&ecdh);
free(f.ptr);
return (r);
}
+ if (dev->flags & FIDO_DEV_PIN_UNSET) {
+ dev->flags &= ~FIDO_DEV_PIN_UNSET;
+ dev->flags |= FIDO_DEV_PIN_SET;
+ }
+
return (FIDO_OK);
}
}
static int
-parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+parse_retry_count(const uint8_t keyval, const cbor_item_t *key,
+ const cbor_item_t *val, void *arg)
{
int *retries = arg;
uint64_t n;
if (cbor_isa_uint(key) == false ||
cbor_int_get_width(key) != CBOR_INT_8 ||
- cbor_get_uint8(key) != 3) {
+ cbor_get_uint8(key) != keyval) {
fido_log_debug("%s: cbor type", __func__);
return (0); /* ignore */
}
}
static int
-fido_dev_get_retry_count_tx(fido_dev_t *dev)
+parse_pin_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ return (parse_retry_count(3, key, val, arg));
+}
+
+static int
+parse_uv_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
+{
+ return (parse_retry_count(5, key, val, arg));
+}
+
+static int
+fido_dev_get_retry_count_tx(fido_dev_t *dev, uint8_t subcmd)
{
fido_blob_t f;
cbor_item_t *argv[2];
memset(argv, 0, sizeof(argv));
if ((argv[0] = cbor_build_uint8(1)) == NULL ||
- (argv[1] = cbor_build_uint8(1)) == NULL) {
+ (argv[1] = cbor_build_uint8(subcmd)) == NULL) {
r = FIDO_ERR_INTERNAL;
goto fail;
}
}
static int
-fido_dev_get_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;
}
if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
- parse_retry_count)) != FIDO_OK) {
- fido_log_debug("%s: parse_retry_count", __func__);
+ parse_pin_retry_count)) != FIDO_OK) {
+ fido_log_debug("%s: parse_pin_retry_count", __func__);
return (r);
}
}
static int
-fido_dev_get_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)) != FIDO_OK ||
- (r = fido_dev_get_retry_count_rx(dev, retries, ms)) != FIDO_OK)
+ if ((r = fido_dev_get_retry_count_tx(dev, 1)) != FIDO_OK ||
+ (r = fido_dev_get_pin_retry_count_rx(dev, retries, ms)) != FIDO_OK)
return (r);
return (FIDO_OK);
int
fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
{
- return (fido_dev_get_retry_count_wait(dev, retries, -1));
+ return (fido_dev_get_pin_retry_count_wait(dev, retries, -1));
+}
+
+static int
+fido_dev_get_uv_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
+{
+ unsigned char reply[FIDO_MAXMSG];
+ int reply_len;
+ int r;
+
+ *retries = 0;
+
+ if ((reply_len = fido_rx(dev, CTAP_CMD_CBOR, &reply, sizeof(reply),
+ ms)) < 0) {
+ fido_log_debug("%s: fido_rx", __func__);
+ return (FIDO_ERR_RX);
+ }
+
+ if ((r = cbor_parse_reply(reply, (size_t)reply_len, retries,
+ parse_uv_retry_count)) != FIDO_OK) {
+ fido_log_debug("%s: parse_uv_retry_count", __func__);
+ return (r);
+ }
+
+ return (FIDO_OK);
+}
+
+static int
+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 ||
+ (r = fido_dev_get_uv_retry_count_rx(dev, retries, ms)) != FIDO_OK)
+ return (r);
+
+ return (FIDO_OK);
+}
+
+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
-cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
+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,
- cbor_item_t **auth, cbor_item_t **opt)
+ const char *rpid, cbor_item_t **auth, cbor_item_t **opt)
{
fido_blob_t *token = NULL;
int r;
goto fail;
}
- if ((r = fido_dev_get_pin_token(dev, pin, ecdh, pk, token)) != FIDO_OK) {
- fido_log_debug("%s: fido_dev_get_pin_token", __func__);
+ if ((r = fido_dev_get_uv_token(dev, cmd, pin, ecdh, pk, rpid,
+ token)) != FIDO_OK) {
+ fido_log_debug("%s: fido_dev_get_uv_token", __func__);
goto fail;
}
- if ((*auth = cbor_encode_pin_auth(token, hmac_data)) == NULL ||
- (*opt = cbor_encode_pin_opt()) == NULL) {
+ if ((*auth = cbor_encode_pin_auth(dev, token, hmac_data)) == NULL ||
+ (*opt = cbor_encode_pin_opt(dev)) == NULL) {
fido_log_debug("%s: cbor encode", __func__);
r = FIDO_ERR_INTERNAL;
goto fail;
--- /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.
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+fido_get_random(void *buf, size_t len)
+{
+ arc4random_buf(buf, len);
+ return (0);
+}
* license that can be found in the LICENSE file.
*/
-#include <stdlib.h>
#include "fido.h"
static int
(r = fido_rx_cbor_status(dev, ms)) != FIDO_OK)
return (r);
+ if (dev->flags & FIDO_DEV_PIN_SET) {
+ dev->flags &= ~FIDO_DEV_PIN_SET;
+ dev->flags |= FIDO_DEV_PIN_UNSET;
+ }
+
return (FIDO_OK);
}
#include <openssl/bn.h>
#include <openssl/rsa.h>
-#include <openssl/evp.h>
#include <openssl/obj_mac.h>
-#include <string.h>
#include "fido.h"
#include "fido/rs256.h"
if (pkp == NULL || (pk = *pkp) == NULL)
return;
- explicit_bzero(pk, sizeof(*pk));
- free(pk);
-
+ freezero(pk, sizeof(*pk));
*pkp = NULL;
}
#include <openssl/sha.h>
#include <openssl/x509.h>
-#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
if ((sig->ptr = calloc(1, sig->len)) == NULL ||
fido_buf_read(buf, len, sig->ptr, sig->len) < 0) {
fido_log_debug("%s: fido_buf_read", __func__);
- if (sig->ptr != NULL) {
- explicit_bzero(sig->ptr, sig->len);
- free(sig->ptr);
- sig->ptr = NULL;
- sig->len = 0;
- return (-1);
- }
+ fido_blob_reset(sig);
+ return (-1);
}
return (0);
if (cert != NULL)
X509_free(cert);
- if (ok < 0) {
- free(x5c->ptr);
- x5c->ptr = NULL;
- x5c->len = 0;
- }
+ if (ok < 0)
+ fido_blob_reset(x5c);
return (ok);
}
memset(&challenge, 0xff, sizeof(challenge));
memset(&application, 0xff, sizeof(application));
- if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
+ if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
SHA256_DIGEST_LENGTH)) == NULL ||
iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
iso7816_add(apdu, &application, sizeof(application)) < 0) {
key_id_len = (uint8_t)key_id->len;
- if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
+ if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_CHECK, (uint16_t)(2 *
SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
iso7816_add(apdu, &challenge, sizeof(challenge)) < 0 ||
iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
key_id_len = (uint8_t)key_id->len;
- if ((apdu = iso7816_new(U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
+ if ((apdu = iso7816_new(0, U2F_CMD_AUTH, U2F_AUTH_SIGN, (uint16_t)(2 *
SHA256_DIGEST_LENGTH + sizeof(key_id_len) + key_id_len))) == NULL ||
iso7816_add(apdu, cdh->ptr, cdh->len) < 0 ||
iso7816_add(apdu, &rp_id_hash, sizeof(rp_id_hash)) < 0 ||
if (authdata_cbor)
cbor_decref(&authdata_cbor);
- if (pk_blob.ptr) {
- explicit_bzero(pk_blob.ptr, pk_blob.len);
- free(pk_blob.ptr);
- }
- if (authdata_blob.ptr) {
- explicit_bzero(authdata_blob.ptr, authdata_blob.len);
- free(authdata_blob.ptr);
- }
+ fido_blob_reset(&pk_blob);
+ fido_blob_reset(&authdata_blob);
return (ok);
}
r = FIDO_OK;
fail:
- if (kh) {
- explicit_bzero(kh, kh_len);
- free(kh);
- }
- if (x5c.ptr) {
- explicit_bzero(x5c.ptr, x5c.len);
- free(x5c.ptr);
- }
- if (sig.ptr) {
- explicit_bzero(sig.ptr, sig.len);
- free(sig.ptr);
- }
- if (ad.ptr) {
- explicit_bzero(ad.ptr, ad.len);
- free(ad.ptr);
- }
+ freezero(kh, kh_len);
+ fido_blob_reset(&x5c);
+ fido_blob_reset(&sig);
+ fido_blob_reset(&ad);
return (r);
}
return (FIDO_ERR_INTERNAL);
}
- if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
+ if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
SHA256_DIGEST_LENGTH)) == NULL ||
iso7816_add(apdu, cred->cdh.ptr, cred->cdh.len) < 0 ||
iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {
r = FIDO_OK;
fail:
- if (sig.ptr) {
- explicit_bzero(sig.ptr, sig.len);
- free(sig.ptr);
- }
- if (ad.ptr) {
- explicit_bzero(ad.ptr, ad.len);
- free(ad.ptr);
- }
+ fido_blob_reset(&sig);
+ fido_blob_reset(&ad);
return (r);
}
return (FIDO_ERR_INTERNAL);
}
- if ((apdu = iso7816_new(U2F_CMD_REGISTER, 0, 2 *
+ if ((apdu = iso7816_new(0, U2F_CMD_REGISTER, 0, 2 *
SHA256_DIGEST_LENGTH)) == NULL ||
iso7816_add(apdu, clientdata_hash, sizeof(clientdata_hash)) < 0 ||
iso7816_add(apdu, rp_id_hash, sizeof(rp_id_hash)) < 0) {