update to libfido2 1.8.0; ok sthen@ "timing is fine" deraadt@
authordjm <djm@openbsd.org>
Tue, 26 Oct 2021 21:36:23 +0000 (21:36 +0000)
committerdjm <djm@openbsd.org>
Tue, 26 Oct 2021 21:36:23 +0000 (21:36 +0000)
59 files changed:
lib/libfido2/LICENSE
lib/libfido2/Makefile
lib/libfido2/README.openbsd
lib/libfido2/man/fido_assert_new.3
lib/libfido2/man/fido_assert_set_authdata.3
lib/libfido2/man/fido_bio_dev_get_info.3
lib/libfido2/man/fido_bio_template.3
lib/libfido2/man/fido_cbor_info_new.3
lib/libfido2/man/fido_cred_new.3
lib/libfido2/man/fido_cred_set_authdata.3
lib/libfido2/man/fido_cred_verify.3
lib/libfido2/man/fido_credman_metadata_new.3
lib/libfido2/man/fido_dev_enable_entattest.3 [new file with mode: 0644]
lib/libfido2/man/fido_dev_largeblob_get.3 [new file with mode: 0644]
lib/libfido2/man/fido_dev_make_cred.3
lib/libfido2/man/fido_dev_open.3
lib/libfido2/man/fido_dev_set_io_functions.3
lib/libfido2/man/fido_dev_set_pin.3
lib/libfido2/man/fido_init.3
lib/libfido2/shlib_version
lib/libfido2/src/aes256.c
lib/libfido2/src/assert.c
lib/libfido2/src/authkey.c
lib/libfido2/src/bio.c
lib/libfido2/src/blob.c
lib/libfido2/src/blob.h
lib/libfido2/src/buf.c
lib/libfido2/src/cbor.c
lib/libfido2/src/compress.c [new file with mode: 0644]
lib/libfido2/src/config.c [new file with mode: 0644]
lib/libfido2/src/cred.c
lib/libfido2/src/credman.c
lib/libfido2/src/dev.c
lib/libfido2/src/ecdh.c
lib/libfido2/src/eddsa.c
lib/libfido2/src/err.c
lib/libfido2/src/es256.c
lib/libfido2/src/export.llvm
lib/libfido2/src/extern.h
lib/libfido2/src/fido.h
lib/libfido2/src/fido/config.h [new file with mode: 0644]
lib/libfido2/src/fido/credman.h
lib/libfido2/src/fido/err.h
lib/libfido2/src/fido/param.h
lib/libfido2/src/fido/types.h
lib/libfido2/src/hid.c
lib/libfido2/src/hid_openbsd.c
lib/libfido2/src/hid_unix.c [new file with mode: 0644]
lib/libfido2/src/info.c
lib/libfido2/src/io.c
lib/libfido2/src/iso7816.c
lib/libfido2/src/iso7816.h
lib/libfido2/src/largeblob.c [new file with mode: 0644]
lib/libfido2/src/log.c
lib/libfido2/src/pin.c
lib/libfido2/src/random.c [new file with mode: 0644]
lib/libfido2/src/reset.c
lib/libfido2/src/rs256.c
lib/libfido2/src/u2f.c

index 340cc35..4224f20 100644 (file)
@@ -1,4 +1,4 @@
-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
index 9a8153d..fc699ff 100644 (file)
@@ -1,26 +1,28 @@
-# $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
@@ -31,6 +33,7 @@ MAN+= fido_cred_set_authdata.3 fido_cred_verify.3 fido_credman_metadata_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 \
index 643512b..90f48bc 100644 (file)
@@ -1,4 +1,4 @@
-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:
 
@@ -7,3 +7,4 @@ Only a subset of the upstream files have been imported: src/, man/ and LICENSE.
 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
index f04bdbc..d14fd01 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: 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"
@@ -143,19 +155,26 @@ NUL-terminated UTF-8 strings.
 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
@@ -192,12 +211,18 @@ function returns a pointer to the client data hash of
 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
@@ -214,4 +239,5 @@ qualifier is invoked.
 .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
index 9565954..b5d070f 100644 (file)
@@ -2,16 +2,18 @@
 .\" 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 ,
@@ -31,6 +33,8 @@ typedef enum {
 .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"
@@ -39,6 +43,8 @@ typedef enum {
 .Ft int
 .Fn fido_assert_set_hmac_salt "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
 .Ft int
+.Fn fido_assert_set_hmac_secret "fido_assert_t *assert" "const unsigned char *ptr" "size_t len"
+.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"
@@ -100,9 +106,10 @@ Alternatively, a raw binary blob may be passed to
 .Fn fido_assert_set_authdata_raw .
 .Pp
 The
-.Fn fido_assert_set_clientdata_hash
+.Fn fido_assert_set_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
@@ -117,6 +124,18 @@ A copy of
 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
@@ -136,8 +155,11 @@ function sets the extensions of
 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
@@ -178,6 +200,11 @@ set of functions can be found in the
 .Pa examples/assert.c
 file shipped with
 .Em libfido2 .
+.Pp
+.Fn fido_assert_set_hmac_secret
+is not normally useful in a FIDO client or server \(em it is provided
+to enable testing other functionality that relies on retrieving the
+HMAC secret from an assertion obtained from an authenticator.
 .Sh RETURN VALUES
 The
 .Nm
index 7338d7f..df0fb69 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
 .Dt FIDO_BIO_DEV_GET_INFO 3
 .Os
 .Sh NAME
@@ -101,7 +101,7 @@ function sets the friendly name of
 .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 ,
@@ -120,11 +120,3 @@ is returned.
 .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.
index f13bf23..da012b4 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: August 11 2020 $
+.Dd $Mdocdate: October 26 2021 $
 .Dt FIDO_BIO_TEMPLATE 3
 .Os
 .Sh NAME
@@ -164,6 +164,16 @@ Please note that the first template in
 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
index 755182a..93a4aff 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: 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"
@@ -107,16 +124,19 @@ The
 .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
@@ -132,11 +152,42 @@ The length of the options array is returned by
 .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
@@ -162,6 +213,7 @@ The
 .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
index d5d8f2f..f78f6a6 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: 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
@@ -163,25 +178,30 @@ or NULL if the respective entry is not set.
 .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 ,
@@ -200,10 +220,17 @@ The
 .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 ,
@@ -211,6 +238,7 @@ If not NULL, pointers returned by
 .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
@@ -225,4 +253,5 @@ qualifier is invoked.
 .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
index 6370df9..dea4b7d 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: 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 ,
@@ -38,6 +41,10 @@ typedef enum {
 .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"
@@ -46,6 +53,8 @@ typedef enum {
 .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"
@@ -73,10 +82,11 @@ The
 .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 ,
@@ -95,6 +105,25 @@ must be a CBOR-encoded byte string, as obtained from
 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
@@ -151,9 +180,11 @@ function sets the extensions of
 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
@@ -162,6 +193,18 @@ is zero, the extensions of
 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
@@ -185,10 +228,10 @@ and
 .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
@@ -197,18 +240,19 @@ by default, allowing the authenticator to use its default settings.
 .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.
index f8f7941..47c1494 100644 (file)
@@ -2,12 +2,12 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: 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
@@ -15,7 +15,7 @@
 .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
@@ -29,7 +29,7 @@ A brief description follows:
 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
@@ -56,6 +56,11 @@ are defined in
 .In fido/err.h .
 If
 .Fa cred
+does not contain attestation data, then
+.Dv FIDO_ERR_INVALID_ARGUMENT
+is returned.
+If
+.Fa cred
 passes verification, then
 .Dv FIDO_OK
 is returned.
index 4e62c80..7fb3109 100644 (file)
@@ -1,8 +1,8 @@
-.\" 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
@@ -23,6 +23,7 @@
 .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
@@ -191,6 +194,23 @@ has an
 (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
@@ -284,6 +304,7 @@ has an
 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
@@ -297,12 +318,9 @@ Functions returning pointers are not guaranteed to succeed, and
 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.
diff --git a/lib/libfido2/man/fido_dev_enable_entattest.3 b/lib/libfido2/man/fido_dev_enable_entattest.3
new file mode 100644 (file)
index 0000000..366debf
--- /dev/null
@@ -0,0 +1,98 @@
+.\" 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
diff --git a/lib/libfido2/man/fido_dev_largeblob_get.3 b/lib/libfido2/man/fido_dev_largeblob_get.3
new file mode 100644 (file)
index 0000000..cd9d0cc
--- /dev/null
@@ -0,0 +1,194 @@
+.\" 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.
index 7e56971..158f5ea 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: October 26 2021 $
 .Dt FIDO_DEV_MAKE_CRED 3
 .Os
 .Sh NAME
@@ -33,7 +33,7 @@ defined in
 .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
index 1f78fdf..def6a99 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: 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
@@ -66,6 +78,18 @@ where
 .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
@@ -126,6 +150,22 @@ if
 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
@@ -150,6 +190,23 @@ if
 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 .
@@ -189,4 +246,5 @@ On error, a different error code defined in
 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
index ad02286..6775b73 100644 (file)
@@ -2,11 +2,12 @@
 .\" 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
@@ -15,137 +16,118 @@ typedef void *fido_dev_io_open_t(const char *);
 typedef void  fido_dev_io_close_t(void *);
 typedef int   fido_dev_io_read_t(void *, unsigned char *, size_t, int);
 typedef int   fido_dev_io_write_t(void *, const unsigned char *, size_t);
-typedef 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
index a6deddf..a269541 100644 (file)
@@ -2,12 +2,13 @@
 .\" 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
@@ -17,6 +18,8 @@
 .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
@@ -51,6 +54,16 @@ before lock-out, where
 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 ,
@@ -60,6 +73,7 @@ device.
 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.
@@ -67,6 +81,7 @@ 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
index 32629d3..cd95c15 100644 (file)
@@ -2,7 +2,7 @@
 .\" Use of this source code is governed by a BSD-style
 .\" license that can be found in the LICENSE file.
 .\"
-.Dd $Mdocdate: February 7 2020 $
+.Dd $Mdocdate: October 26 2021 $
 .Dt FIDO_INIT 3
 .Os
 .Sh NAME
@@ -20,7 +20,8 @@ function initialises the
 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
@@ -33,6 +34,17 @@ on
 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 ,
index d9961ea..3066b97 100644 (file)
@@ -1,2 +1,2 @@
-major=4
+major=5
 minor=0
index 767cdb2..f093b7c 100644 (file)
 /*
- * 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);
 }
index 1746387..b36f8e3 100644 (file)
@@ -1,15 +1,12 @@
 /*
- * 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"
@@ -67,12 +64,13 @@ parse_assert_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                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);
@@ -84,7 +82,9 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
     const es256_pk_t *pk, const fido_blob_t *ecdh, const char *pin)
 {
        fido_blob_t      f;
+       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));
@@ -115,44 +115,35 @@ fido_dev_get_assert_tx(fido_dev_t *dev, fido_assert_t *assert,
                }
        }
 
-       /* 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;
@@ -271,12 +262,14 @@ fido_dev_get_assert_wait(fido_dev_t *dev, fido_assert_t *assert,
 }
 
 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);
@@ -295,6 +288,11 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
        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);
@@ -302,12 +300,14 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
        }
 
        if (fido_dev_is_fido2(dev) == false) {
-               if (pin != NULL || assert->ext != 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;
@@ -315,8 +315,8 @@ fido_dev_get_assert(fido_dev_t *dev, fido_assert_t *assert, const char *pin)
        }
 
        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;
@@ -353,6 +353,8 @@ fido_check_flags(uint8_t flags, fido_opt_t up, fido_opt_t uv)
 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);
@@ -363,8 +365,8 @@ check_extensions(int authdata_ext, int 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;
@@ -566,7 +568,7 @@ fido_assert_verify(const fido_assert_t *assert, size_t idx, int cose_alg,
                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;
@@ -612,11 +614,28 @@ 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);
@@ -627,7 +646,19 @@ fido_assert_set_hmac_salt(fido_assert_t *assert, const unsigned char *salt,
     size_t salt_len)
 {
        if ((salt_len != 32 && salt_len != 64) ||
-           fido_blob_set(&assert->hmac_salt, salt, salt_len) < 0)
+           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);
@@ -686,10 +717,13 @@ fail:
 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);
 }
@@ -741,42 +775,41 @@ void
 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;
@@ -789,12 +822,9 @@ fido_assert_free(fido_assert_t **assert_p)
 
        if (assert_p == NULL || (assert = *assert_p) == NULL)
                return;
-
        fido_assert_reset_tx(assert);
        fido_assert_reset_rx(assert);
-
        free(assert);
-
        *assert_p = NULL;
 }
 
@@ -945,16 +975,48 @@ fido_assert_hmac_secret_len(const fido_assert_t *assert, size_t idx)
        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
@@ -979,7 +1041,7 @@ fido_assert_set_authdata(fido_assert_t *assert, size_t idx,
        }
 
        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;
@@ -1017,7 +1079,7 @@ fido_assert_set_authdata_raw(fido_assert_t *assert, size_t idx,
        }
 
        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;
@@ -1034,32 +1096,15 @@ 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);
 }
 
index 83c2564..c3474cc 100644 (file)
@@ -4,7 +4,6 @@
  * license that can be found in the LICENSE file.
  */
 
-#include <string.h>
 #include "fido.h"
 
 static int
@@ -35,7 +34,7 @@ fido_dev_authkey_tx(fido_dev_t *dev)
        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;
index c1032d8..06bc32e 100644 (file)
@@ -4,8 +4,6 @@
  * license that can be found in the LICENSE file.
  */
 
-#include <string.h>
-
 #include "fido.h"
 #include "fido/bio.h"
 #include "fido/es256.h"
@@ -59,7 +57,7 @@ fail:
 }
 
 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];
@@ -67,6 +65,7 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
        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));
@@ -75,14 +74,14 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
 
        /* 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;
@@ -95,22 +94,22 @@ bio_tx(fido_dev_t *dev, uint8_t cmd, cbor_item_t **sub_argv, size_t sub_argc,
                        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;
@@ -131,9 +130,8 @@ static void
 bio_reset_template(fido_bio_template_t *t)
 {
        free(t->name);
-       free(t->id.ptr);
        t->name = NULL;
-       memset(&t->id, 0, sizeof(t->id));
+       fido_blob_reset(&t->id);
 }
 
 static void
@@ -461,8 +459,9 @@ fido_bio_dev_enroll_begin(fido_dev_t *dev, fido_bio_template_t *t,
                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;
        }
 
@@ -762,9 +761,7 @@ int
 fido_bio_template_set_id(fido_bio_template_t *t, const unsigned char *ptr,
     size_t len)
 {
-       free(t->id.ptr);
-       t->id.ptr = NULL;
-       t->id.len = 0;
+       fido_blob_reset(&t->id);
 
        if (ptr && fido_blob_set(&t->id, ptr, len) < 0)
                return (FIDO_ERR_INTERNAL);
index d4eae70..31e4cab 100644 (file)
@@ -4,41 +4,66 @@
  * 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
@@ -49,14 +74,8 @@ fido_blob_free(fido_blob_t **bp)
        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;
 }
 
@@ -68,11 +87,8 @@ fido_free_blob_array(fido_blob_array_t *array)
 
        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);
@@ -84,19 +100,34 @@ cbor_item_t *
 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;
 }
index 9e98d03..76a8dd9 100644 (file)
@@ -28,8 +28,10 @@ cbor_item_t *fido_blob_encode(const fido_blob_t *);
 fido_blob_t *fido_blob_new(void);
 int fido_blob_decode(const cbor_item_t *, fido_blob_t *);
 int fido_blob_is_empty(const fido_blob_t *);
-int fido_blob_set(fido_blob_t *, const unsigned char *, size_t);
+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
index 4646476..f7161e6 100644 (file)
@@ -4,7 +4,6 @@
  * license that can be found in the LICENSE file.
  */
 
-#include <string.h>
 #include "fido.h"
 
 int
index b30da50..5c1b115 100644 (file)
@@ -4,11 +4,8 @@
  * 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
@@ -563,21 +560,37 @@ fail:
        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);
                }
@@ -590,18 +603,29 @@ cbor_encode_extensions(const fido_cred_ext_t *ext)
                        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);
@@ -612,13 +636,12 @@ cbor_encode_options(fido_opt_t rk, fido_opt_t uv)
 }
 
 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);
@@ -629,62 +652,55 @@ cbor_encode_assert_options(fido_opt_t up, fido_opt_t uv)
 }
 
 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;
@@ -695,65 +711,54 @@ cbor_encode_change_pin_auth(const fido_blob_t *key, const fido_blob_t *new_pin,
 #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);
@@ -762,112 +767,59 @@ fail:
        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;
        }
 
@@ -875,21 +827,61 @@ cbor_encode_hmac_secret_param(const fido_blob_t *ecdh, const es256_pk_t *pk,
 
        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(&param);
        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);
 }
 
@@ -903,7 +895,8 @@ cbor_decode_fmt(const cbor_item_t *item, char **fmt)
                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);
@@ -1058,8 +1051,7 @@ decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
        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) {
@@ -1085,7 +1077,6 @@ decode_attcred(const unsigned char **buf, size_t *len, int cose_alg,
 
        if ((item = cbor_load(*buf, *len, &cbor)) == NULL) {
                fido_log_debug("%s: cbor_load", __func__);
-               fido_log_xxd(*buf, *len);
                goto fail;
        }
 
@@ -1112,7 +1103,7 @@ 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;
@@ -1141,6 +1132,15 @@ decode_extension(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                }
                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;
@@ -1151,28 +1151,25 @@ out:
 }
 
 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;
        }
@@ -1189,19 +1186,34 @@ 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);
 
@@ -1209,25 +1221,23 @@ out:
 }
 
 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;
        }
@@ -1267,9 +1277,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
 
        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__);
@@ -1286,7 +1294,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
 
        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);
        }
 
@@ -1297,7 +1305,7 @@ cbor_decode_cred_authdata(const cbor_item_t *item, int cose_alg,
 
 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;
@@ -1328,14 +1336,12 @@ cbor_decode_assert_authdata(const cbor_item_t *item, fido_blob_t *authdata_cbor,
 
        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 */
@@ -1351,7 +1357,7 @@ decode_x5c(const cbor_item_t *item, void *arg)
        if (x5c->len)
                return (0); /* ignore */
 
-       return (cbor_bytestring_copy(item, &x5c->ptr, &x5c->len));
+       return (fido_blob_decode(item, x5c));
 }
 
 static int
@@ -1381,8 +1387,7 @@ decode_attstmt_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                        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;
                }
@@ -1442,7 +1447,7 @@ decode_cred_id_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
        }
 
        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;
                }
@@ -1496,7 +1501,7 @@ decode_user_entry(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                        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;
                }
@@ -1567,3 +1572,64 @@ cbor_decode_rp_entity(const cbor_item_t *item, fido_rp_t *rp)
 
        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;
+}
diff --git a/lib/libfido2/src/compress.c b/lib/libfido2/src/compress.c
new file mode 100644 (file)
index 0000000..ee5501b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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);
+}
diff --git a/lib/libfido2/src/config.c b/lib/libfido2/src/config.c
new file mode 100644 (file)
index 0000000..0dda161
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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);
+}
index 9f902fa..5e65b08 100644 (file)
@@ -4,12 +4,9 @@
  * 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"
 
@@ -28,11 +25,17 @@ parse_makecred_reply(const cbor_item_t *key, const cbor_item_t *val, void *arg)
        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);
@@ -44,8 +47,10 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
 {
        fido_blob_t      f;
        fido_blob_t     *ecdh = NULL;
+       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));
@@ -77,36 +82,38 @@ fido_dev_make_cred_tx(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
 
        /* 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;
@@ -145,8 +152,7 @@ fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
        }
 
        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);
        }
@@ -155,7 +161,8 @@ fido_dev_make_cred_rx(fido_dev_t *dev, fido_cred_t *cred, int ms)
 }
 
 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;
 
@@ -169,6 +176,10 @@ fido_dev_make_cred_wait(fido_dev_t *dev, fido_cred_t *cred, const char *pin, int
 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)
@@ -180,9 +191,16 @@ fido_dev_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
 }
 
 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
@@ -322,7 +340,7 @@ fido_cred_verify(const fido_cred_t *cred)
                        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) {
@@ -330,6 +348,10 @@ fido_cred_verify(const fido_cred_t *cred)
                        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) {
@@ -397,7 +419,7 @@ fido_cred_verify_self(const fido_cred_t *cred)
                        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) {
@@ -405,6 +427,10 @@ fido_cred_verify_self(const fido_cred_t *cred)
                        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) {
@@ -447,11 +473,11 @@ fido_cred_new(void)
 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));
 }
@@ -459,16 +485,18 @@ fido_cred_clean_authdata(fido_cred_t *cred)
 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));
@@ -479,31 +507,15 @@ fido_cred_reset_tx(fido_cred_t *cred)
        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
@@ -513,12 +525,9 @@ fido_cred_free(fido_cred_t **cred_p)
 
        if (cred_p == NULL || (cred = *cred_p) == NULL)
                return;
-
        fido_cred_reset_tx(cred);
        fido_cred_reset_rx(cred);
-
        free(cred);
-
        *cred_p = NULL;
 }
 
@@ -527,25 +536,26 @@ fido_cred_set_authdata(fido_cred_t *cred, const unsigned char *ptr, size_t len)
 {
        cbor_item_t             *item = NULL;
        struct cbor_load_result  cbor;
-       int                      r;
+       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;
        }
 
@@ -565,13 +575,17 @@ int
 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;
        }
 
@@ -584,7 +598,6 @@ fido_cred_set_authdata_raw(fido_cred_t *cred, const unsigned char *ptr,
        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;
        }
 
@@ -601,20 +614,19 @@ 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);
 }
@@ -622,18 +634,8 @@ fido_cred_set_x509(fido_cred_t *cred, const unsigned char *ptr, size_t len)
 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);
 }
@@ -666,11 +668,28 @@ fido_cred_exclude(fido_cred_t *cred, const unsigned char *id_ptr, size_t id_len)
        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);
@@ -730,12 +749,8 @@ fido_cred_set_user(fido_cred_t *cred, const unsigned char *user_id,
                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 &&
@@ -766,8 +781,7 @@ fido_cred_set_extensions(fido_cred_t *cred, int ext)
        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;
        }
@@ -819,6 +833,19 @@ fido_cred_set_prot(fido_cred_t *cred, int prot)
        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)
 {
@@ -828,7 +855,8 @@ 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)
@@ -861,6 +889,12 @@ fido_cred_flags(const fido_cred_t *cred)
        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)
 {
@@ -909,6 +943,18 @@ fido_cred_authdata_len(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)
 {
@@ -1026,3 +1072,15 @@ fido_cred_user_id_len(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);
+}
index 4219807..e48ca45 100644 (file)
@@ -1,13 +1,11 @@
 /*
- * 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"
@@ -18,6 +16,7 @@
 #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,
@@ -52,10 +51,11 @@ 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;
 
@@ -67,21 +67,33 @@ credman_prepare_hmac(uint8_t cmd, const fido_blob_t *body, cbor_item_t **param,
        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;
@@ -99,29 +111,36 @@ 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;
                }
@@ -129,16 +148,16 @@ credman_tx(fido_dev_t *dev, uint8_t cmd, const fido_blob_t *param,
                        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;
@@ -208,7 +227,8 @@ credman_get_metadata_wait(fido_dev_t *dev, fido_credman_metadata_t *metadata,
 {
        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);
 
@@ -219,11 +239,6 @@ int
 fido_credman_get_dev_metadata(fido_dev_t *dev, fido_credman_metadata_t *metadata,
     const char *pin)
 {
-       if (fido_dev_is_fido2(dev) == false)
-               return (FIDO_ERR_INVALID_COMMAND);
-       if (pin == NULL)
-               return (FIDO_ERR_INVALID_ARGUMENT);
-
        return (credman_get_metadata_wait(dev, metadata, pin, -1));
 }
 
@@ -240,7 +255,7 @@ credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
        }
 
        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));
@@ -255,6 +270,8 @@ credman_parse_rk(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                    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 */
@@ -387,12 +404,14 @@ credman_get_rk_wait(fido_dev_t *dev, const char *rp_id, fido_credman_rk_t *rk,
        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++;
@@ -405,11 +424,6 @@ int
 fido_credman_get_dev_rk(fido_dev_t *dev, const char *rp_id,
     fido_credman_rk_t *rk, const char *pin)
 {
-       if (fido_dev_is_fido2(dev) == false)
-               return (FIDO_ERR_INVALID_COMMAND);
-       if (pin == NULL)
-               return (FIDO_ERR_INVALID_ARGUMENT);
-
        return (credman_get_rk_wait(dev, rp_id, rk, pin, -1));
 }
 
@@ -425,7 +439,8 @@ credman_del_rk_wait(fido_dev_t *dev, const unsigned char *cred_id,
        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;
 
@@ -440,11 +455,6 @@ int
 fido_credman_del_dev_rk(fido_dev_t *dev, const unsigned char *cred_id,
     size_t cred_id_len, const char *pin)
 {
-       if (fido_dev_is_fido2(dev) == false)
-               return (FIDO_ERR_INVALID_COMMAND);
-       if (pin == NULL)
-               return (FIDO_ERR_INVALID_ARGUMENT);
-
        return (credman_del_rk_wait(dev, cred_id, cred_id_len, pin, -1));
 }
 
@@ -478,9 +488,7 @@ credman_reset_rp(fido_credman_rp_t *rp)
                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);
@@ -591,12 +599,14 @@ credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
 {
        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++;
@@ -608,14 +618,29 @@ credman_get_rp_wait(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin,
 int
 fido_credman_get_dev_rp(fido_dev_t *dev, fido_credman_rp_t *rp, const char *pin)
 {
-       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)
 {
index 3463ae4..a003854 100644 (file)
@@ -4,86 +4,9 @@
  * 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
@@ -94,6 +17,7 @@ typedef struct dev_manifest_func_node {
 } 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,
@@ -120,37 +44,71 @@ set_random_report_len(fido_dev_t *dev)
 #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);
@@ -162,8 +120,13 @@ fido_dev_open_tx(fido_dev_t *dev, const char *path)
                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);
        }
 
@@ -198,7 +161,7 @@ fido_dev_open_tx(fido_dev_t *dev, const char *path)
                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;
@@ -246,7 +209,12 @@ fido_dev_open_rx(fido_dev_t *dev, int ms)
                        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 {
@@ -255,8 +223,9 @@ fido_dev_open_rx(fido_dev_t *dev, int ms)
        }
 
        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;
@@ -276,6 +245,10 @@ fido_dev_open_wait(fido_dev_t *dev, const char *path, int ms)
 {
        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);
@@ -332,6 +305,14 @@ fido_dev_info_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
 
        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;
@@ -359,27 +340,71 @@ fido_dev_open_with_info(fido_dev_t *dev)
 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);
 
@@ -433,7 +458,7 @@ fido_dev_get_touch_begin(fido_dev_t *dev)
 
        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;
                }
@@ -524,6 +549,8 @@ fido_init(int flags)
 {
        if (flags & FIDO_DEBUG || getenv("FIDO_DEBUG") != NULL)
                fido_log_init();
+
+       disable_u2f_fallback = (flags & FIDO_DISABLE_U2F_FALLBACK);
 }
 
 fido_dev_t *
@@ -553,17 +580,19 @@ fido_dev_new_with_info(const fido_dev_info_t *di)
        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__);
@@ -624,6 +653,12 @@ fido_dev_is_fido2(const fido_dev_t *dev)
        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)
 {
@@ -642,6 +677,30 @@ fido_dev_supports_cred_prot(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)
 {
@@ -654,3 +713,20 @@ fido_dev_force_fido2(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);
+}
index 7f25c7b..3ea47ae 100644 (file)
 /*
- * 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;
        }
 
@@ -70,38 +160,34 @@ 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;
@@ -117,5 +203,5 @@ fail:
                fido_blob_free(ecdh);
        }
 
-       return (r);
+       return r;
 }
index 44a5563..89b84c5 100644 (file)
@@ -5,11 +5,8 @@
  */
 
 #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"
 
@@ -132,9 +129,7 @@ eddsa_pk_free(eddsa_pk_t **pkp)
        if (pkp == NULL || (pk = *pkp) == NULL)
                return;
 
-       explicit_bzero(pk, sizeof(*pk));
-       free(pk);
-
+       freezero(pk, sizeof(*pk));
        *pkp = NULL;
 }
 
index 19cda21..8c2ae5f 100644 (file)
@@ -40,6 +40,8 @@ fido_strerr(int n)
                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:
@@ -98,6 +100,10 @@ fido_strerr(int n)
                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:
@@ -118,6 +124,10 @@ fido_strerr(int n)
                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:
index 5b4e6d6..9cdb48e 100644 (file)
@@ -5,11 +5,8 @@
  */
 
 #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"
 
@@ -147,9 +144,7 @@ es256_sk_free(es256_sk_t **skp)
        if (skp == NULL || (sk = *skp) == NULL)
                return;
 
-       explicit_bzero(sk, sizeof(*sk));
-       free(sk);
-
+       freezero(sk, sizeof(*sk));
        *skp = NULL;
 }
 
@@ -167,9 +162,7 @@ es256_pk_free(es256_pk_t **pkp)
        if (pkp == NULL || (pk = *pkp) == NULL)
                return;
 
-       explicit_bzero(pk, sizeof(*pk));
-       free(pk);
-
+       freezero(pk, sizeof(*pk));
        *pkp = NULL;
 }
 
index ffbc157..8d3810f 100644 (file)
@@ -11,6 +11,8 @@ _es256_pk_to_EVP_PKEY
 _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
@@ -20,14 +22,18 @@ _fido_assert_hmac_secret_len
 _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
@@ -70,10 +76,14 @@ _fido_bio_template_set_id
 _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
@@ -83,15 +93,22 @@ _fido_cbor_info_options_name_ptr
 _fido_cbor_info_options_value_ptr
 _fido_cbor_info_protocols_len
 _fido_cbor_info_protocols_ptr
+_fido_cbor_info_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
@@ -117,6 +134,7 @@ _fido_credman_rp_id_hash_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
@@ -125,9 +143,12 @@ _fido_cred_rp_id
 _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
@@ -150,16 +171,20 @@ _fido_cred_x5c_ptr
 _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
@@ -170,6 +195,7 @@ _fido_dev_info_product_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
@@ -179,9 +205,20 @@ _fido_dev_protocol
 _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
index 4c036cb..3be3323 100644 (file)
@@ -7,6 +7,14 @@
 #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"
@@ -17,27 +25,32 @@ extern "C" {
 #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);
 
@@ -46,7 +59,7 @@ int cbor_decode_attstmt(const cbor_item_t *, fido_attstmt_t *);
 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 *);
@@ -71,9 +84,16 @@ int cbor_map_iter(const cbor_item_t *, void *, int(*)(const cbor_item_t *,
 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]))
@@ -88,9 +108,32 @@ void *fido_hid_open(const char *);
 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);
@@ -101,16 +144,21 @@ int fido_tx(fido_dev_t *, uint8_t, const void *, size_t);
 #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 */
 
@@ -121,19 +169,26 @@ int u2f_get_touch_begin(fido_dev_t *);
 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 *,
@@ -145,8 +200,9 @@ int fido_verify_sig_eddsa(const fido_blob_t *, const eddsa_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 *);
@@ -159,15 +215,23 @@ uint32_t uniform_random(uint32_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" */
index d6e8974..ef98130 100644 (file)
 #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);
@@ -57,6 +52,7 @@ void fido_dev_info_free(fido_dev_info_t **, size_t);
 
 /* 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 *);
@@ -65,17 +61,21 @@ const unsigned char *fido_assert_authdata_ptr(const fido_assert_t *, size_t);
 const unsigned char *fido_assert_clientdata_hash_ptr(const fido_assert_t *);
 const unsigned char *fido_assert_hmac_secret_ptr(const fido_assert_t *, size_t);
 const unsigned char *fido_assert_id_ptr(const fido_assert_t *, size_t);
+const unsigned char *fido_assert_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 *);
@@ -88,6 +88,7 @@ const fido_dev_info_t *fido_dev_info_ptr(const fido_dev_info_t *, size_t);
 const uint8_t *fido_cbor_info_protocols_ptr(const fido_cbor_info_t *);
 const unsigned char *fido_cbor_info_aaguid_ptr(const fido_cbor_info_t *);
 const unsigned char *fido_cred_authdata_ptr(const fido_cred_t *);
+const unsigned char *fido_cred_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 *);
@@ -95,32 +96,38 @@ const unsigned char *fido_cred_user_id_ptr(const fido_cred_t *);
 const unsigned char *fido_cred_pubkey_ptr(const fido_cred_t *);
 const unsigned char *fido_cred_sig_ptr(const fido_cred_t *);
 const unsigned char *fido_cred_x5c_ptr(const fido_cred_t *);
+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);
@@ -134,11 +141,13 @@ int fido_cred_set_user(fido_cred_t *, const unsigned char *, size_t,
 int fido_cred_set_x509(fido_cred_t *, const unsigned char *, size_t);
 int fido_cred_verify(const fido_cred_t *);
 int fido_cred_verify_self(const fido_cred_t *);
+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 *);
@@ -155,14 +164,19 @@ size_t fido_assert_clientdata_hash_len(const fido_assert_t *);
 size_t fido_assert_count(const fido_assert_t *);
 size_t fido_assert_hmac_secret_len(const fido_assert_t *, size_t);
 size_t fido_assert_id_len(const fido_assert_t *, size_t);
+size_t fido_assert_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 *);
@@ -170,10 +184,12 @@ size_t fido_cred_user_id_len(const fido_cred_t *);
 size_t fido_cred_pubkey_len(const fido_cred_t *);
 size_t fido_cred_sig_len(const fido_cred_t *);
 size_t fido_cred_x5c_len(const fido_cred_t *);
+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 *);
@@ -182,14 +198,29 @@ uint8_t  fido_dev_flags(const fido_dev_t *);
 int16_t  fido_dev_info_vendor(const fido_dev_info_t *);
 int16_t  fido_dev_info_product(const fido_dev_info_t *);
 uint64_t fido_cbor_info_maxmsgsiz(const fido_cbor_info_t *);
+uint64_t fido_cbor_info_maxcredbloblen(const fido_cbor_info_t *);
 uint64_t fido_cbor_info_maxcredcntlst(const fido_cbor_info_t *);
 uint64_t fido_cbor_info_maxcredidlen(const fido_cbor_info_t *);
 uint64_t fido_cbor_info_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" */
diff --git a/lib/libfido2/src/fido/config.h b/lib/libfido2/src/fido/config.h
new file mode 100644 (file)
index 0000000..869927d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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 */
index eaffd65..66a9669 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019 Yubico AB. All rights reserved.
+ * Copyright (c) 2019-2021 Yubico AB. All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the LICENSE file.
  */
@@ -71,6 +71,7 @@ int fido_credman_get_dev_metadata(fido_dev_t *, fido_credman_metadata_t *,
 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 *);
index 253914f..74fdf9d 100644 (file)
@@ -7,7 +7,7 @@
 #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
@@ -22,6 +22,7 @@
 #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
@@ -51,6 +52,8 @@
 #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
 
@@ -65,6 +68,8 @@
 #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" {
index 14ee74e..025bb57 100644 (file)
 #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
@@ -43,6 +49,7 @@
 #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 */
index cce1a44..00b6058 100644 (file)
@@ -7,6 +7,11 @@
 #ifndef _FIDO_TYPES_H
 #define _FIDO_TYPES_H
 
+#ifdef __MINGW32__
+#include <sys/types.h>
+#endif
+
+#include <signal.h>
 #include <stddef.h>
 #include <stdint.h>
 
@@ -43,6 +48,12 @@ typedef enum {
 
 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"
@@ -118,6 +129,7 @@ typedef struct fido_cred_ext {
 } 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 */
@@ -128,31 +140,45 @@ typedef struct fido_cred {
        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 */
@@ -174,16 +200,29 @@ typedef struct fido_byte_array {
        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 {
@@ -209,17 +248,18 @@ struct fido_ctap_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
index 783d22c..a3768ad 100644 (file)
@@ -4,9 +4,117 @@
  * 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)
 {
@@ -22,7 +130,8 @@ fido_dev_info_free(fido_dev_info_t **devlist_p, 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);
index 58b8c3e..dd9d765 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <signal.h>
 #include <string.h>
 #include <unistd.h>
 #include <usbhid.h>
@@ -27,6 +28,8 @@ struct hid_openbsd {
        int fd;
        size_t report_in_len;
        size_t report_out_len;
+       sigset_t sigmask;
+       const sigset_t *sigmaskp;
 };
 
 int
@@ -46,14 +49,10 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
 
        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));
@@ -65,6 +64,12 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
 
                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) {
@@ -74,14 +79,8 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
                        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)++;
        }
 
@@ -137,7 +136,7 @@ terrible_ping_kludge(struct hid_openbsd *ctx)
                 * 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__);
@@ -150,11 +149,11 @@ fido_hid_open(const char *path)
        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);
 
@@ -176,48 +175,21 @@ fido_hid_close(void *handle)
 {
        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
@@ -226,20 +198,27 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
        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);
 }
 
@@ -254,11 +233,17 @@ fido_hid_write(void *handle, const unsigned char *buf, size_t 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);
 }
 
diff --git a/lib/libfido2/src/hid_unix.c b/lib/libfido2/src/hid_unix.c
new file mode 100644 (file)
index 0000000..4b2aff9
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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);
+}
index 3199ac2..57bc8de 100644 (file)
@@ -1,31 +1,30 @@
 /*
- * 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;
@@ -40,49 +39,8 @@ decode_versions(const cbor_item_t *item, fido_str_array_t *v)
        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);
        }
 
@@ -193,6 +151,99 @@ decode_protocols(const cbor_item_t *item, fido_byte_array_t *p)
        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)
 {
@@ -206,9 +257,9 @@ 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 */
@@ -221,8 +272,14 @@ parse_reply_element(const cbor_item_t *key, const cbor_item_t *val, void *arg)
                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);
@@ -253,7 +310,7 @@ fido_dev_get_cbor_info_rx(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
        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) {
@@ -270,6 +327,10 @@ fido_dev_get_cbor_info_wait(fido_dev_t *dev, fido_cbor_info_t *ci, int ms)
 {
        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);
@@ -325,20 +386,37 @@ free_byte_array(fido_byte_array_t *ba)
        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;
 }
 
@@ -366,6 +444,18 @@ fido_cbor_info_extensions_len(const fido_cbor_info_t *ci)
        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)
 {
@@ -396,6 +486,12 @@ fido_cbor_info_options_len(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)
 {
@@ -431,3 +527,27 @@ fido_cbor_info_protocols_len(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);
+}
index 9d2de88..e259420 100644 (file)
@@ -4,10 +4,6 @@
  * license that can be found in the LICENSE file.
  */
 
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
 #include "fido.h"
 #include "packed.h"
 
@@ -132,9 +128,8 @@ tx(fido_dev_t *d, uint8_t cmd, const unsigned char *buf, size_t count)
 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));
@@ -169,15 +164,13 @@ rx_preamble(fido_dev_t *d, uint8_t cmd, struct frame *fp, int ms)
 #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
@@ -235,10 +228,7 @@ rx(fido_dev_t *d, uint8_t cmd, unsigned char *buf, size_t count, int ms)
                        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;
@@ -267,8 +257,8 @@ fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int ms)
 {
        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));
@@ -276,10 +266,8 @@ fido_rx(fido_dev_t *d, uint8_t cmd, void *buf, size_t count, int 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);
 }
index 4fe6329..a11aae3 100644 (file)
@@ -4,29 +4,27 @@
  * 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
@@ -36,10 +34,7 @@ iso7816_free(iso7816_apdu_t **apdu_p)
 
        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;
 }
 
@@ -47,24 +42,23 @@ int
 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);
 }
index 563243f..5f5363a 100644 (file)
@@ -38,7 +38,7 @@ struct iso7816_apdu {
 
 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 **);
 
diff --git a/lib/libfido2/src/largeblob.c b/lib/libfido2/src/largeblob.c
new file mode 100644 (file)
index 0000000..fa453f5
--- /dev/null
@@ -0,0 +1,881 @@
+/*
+ * 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;
+}
index d6f0934..ab18ae1 100644 (file)
@@ -1,13 +1,13 @@
 /*
- * 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"
 
@@ -30,6 +30,21 @@ log_on_stderr(const char *str)
        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)
 {
@@ -40,32 +55,30 @@ 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++) {
@@ -82,6 +95,22 @@ fido_log_xxd(const void *buf, size_t count)
        }
 }
 
+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)
 {
index 8b23ae3..d3104e0 100644 (file)
  * 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__);
@@ -51,10 +170,15 @@ fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
                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;
@@ -71,25 +195,53 @@ fido_dev_get_pin_token_tx(fido_dev_t *dev, const char *pin,
 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;
@@ -105,56 +257,31 @@ fido_dev_get_uv_token_tx(fido_dev_t *dev, const es256_pk_t *pk)
        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];
@@ -174,12 +301,12 @@ fido_dev_get_uv_token_rx(fido_dev_t *dev, const  fido_blob_t *ecdh,
        }
 
        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;
@@ -191,74 +318,42 @@ 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;
@@ -273,22 +368,29 @@ fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
                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;
@@ -305,9 +407,10 @@ fido_dev_change_pin_tx(fido_dev_t *dev, const char *pin, const char *oldpin)
 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);
@@ -318,7 +421,7 @@ static int
 fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
 {
        fido_blob_t      f;
-       fido_blob_t     *ppin = NULL;
+       fido_blob_t     *ppine = NULL;
        fido_blob_t     *ecdh = NULL;
        cbor_item_t     *argv[5];
        es256_pk_t      *pk = NULL;
@@ -327,21 +430,21 @@ fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
        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;
@@ -358,7 +461,7 @@ fido_dev_set_pin_tx(fido_dev_t *dev, const char *pin)
 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);
 
@@ -388,6 +491,11 @@ fido_dev_set_pin_wait(fido_dev_t *dev, const char *pin, const char *oldpin,
                return (r);
        }
 
+       if (dev->flags & FIDO_DEV_PIN_UNSET) {
+               dev->flags &= ~FIDO_DEV_PIN_UNSET;
+               dev->flags |= FIDO_DEV_PIN_SET;
+       }
+
        return (FIDO_OK);
 }
 
@@ -398,14 +506,15 @@ fido_dev_set_pin(fido_dev_t *dev, const char *pin, const char *oldpin)
 }
 
 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 */
        }
@@ -421,7 +530,19 @@ parse_retry_count(const cbor_item_t *key, const cbor_item_t *val, void *arg)
 }
 
 static int
-fido_dev_get_retry_count_tx(fido_dev_t *dev)
+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];
@@ -431,7 +552,7 @@ fido_dev_get_retry_count_tx(fido_dev_t *dev)
        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;
        }
@@ -452,7 +573,7 @@ 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;
@@ -467,8 +588,8 @@ fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
        }
 
        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);
        }
 
@@ -476,12 +597,12 @@ fido_dev_get_retry_count_rx(fido_dev_t *dev, int *retries, int ms)
 }
 
 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);
@@ -490,13 +611,55 @@ fido_dev_get_retry_count_wait(fido_dev_t *dev, int *retries, int ms)
 int
 fido_dev_get_retry_count(fido_dev_t *dev, int *retries)
 {
-       return (fido_dev_get_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;
@@ -506,13 +669,14 @@ cbor_add_pin_params(fido_dev_t *dev, const fido_blob_t *hmac_data,
                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;
diff --git a/lib/libfido2/src/random.c b/lib/libfido2/src/random.c
new file mode 100644 (file)
index 0000000..289050c
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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);
+}
index ebda1cd..11380ce 100644 (file)
@@ -4,7 +4,6 @@
  * license that can be found in the LICENSE file.
  */
 
-#include <stdlib.h>
 #include "fido.h"
 
 static int
@@ -29,6 +28,11 @@ fido_dev_reset_wait(fido_dev_t *dev, int ms)
            (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);
 }
 
index 9f30163..c6d87a3 100644 (file)
@@ -6,10 +6,8 @@
 
 #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"
 
@@ -100,9 +98,7 @@ rs256_pk_free(rs256_pk_t **pkp)
        if (pkp == NULL || (pk = *pkp) == NULL)
                return;
 
-       explicit_bzero(pk, sizeof(*pk));
-       free(pk);
-
+       freezero(pk, sizeof(*pk));
        *pkp = NULL;
 }
 
index 3c6ea82..c5fbe0c 100644 (file)
@@ -7,7 +7,6 @@
 #include <openssl/sha.h>
 #include <openssl/x509.h>
 
-#include <string.h>
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -32,13 +31,8 @@ sig_get(fido_blob_t *sig, const unsigned char **buf, size_t *len)
        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);
@@ -75,11 +69,8 @@ fail:
        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);
 }
@@ -140,7 +131,7 @@ send_dummy_register(fido_dev_t *dev, int ms)
        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) {
@@ -205,7 +196,7 @@ key_lookup(fido_dev_t *dev, const char *rp_id, const fido_blob_t *key_id,
 
        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 ||
@@ -313,7 +304,7 @@ do_auth(fido_dev_t *dev, const fido_blob_t *cdh, const char *rp_id,
 
        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 ||
@@ -473,14 +464,8 @@ fail:
        if (authdata_cbor)
                cbor_decref(&authdata_cbor);
 
-       if (pk_blob.ptr) {
-               explicit_bzero(pk_blob.ptr, pk_blob.len);
-               free(pk_blob.ptr);
-       }
-       if (authdata_blob.ptr) {
-               explicit_bzero(authdata_blob.ptr, authdata_blob.len);
-               free(authdata_blob.ptr);
-       }
+       fido_blob_reset(&pk_blob);
+       fido_blob_reset(&authdata_blob);
 
        return (ok);
 }
@@ -551,22 +536,10 @@ parse_register_reply(fido_cred_t *cred, const unsigned char *reply, size_t len)
 
        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);
 }
@@ -622,7 +595,7 @@ u2f_register(fido_dev_t *dev, fido_cred_t *cred, int ms)
                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) {
@@ -712,14 +685,8 @@ u2f_authenticate_single(fido_dev_t *dev, const fido_blob_t *key_id,
 
        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);
 }
@@ -792,7 +759,7 @@ u2f_get_touch_begin(fido_dev_t *dev)
                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) {