From: djm Date: Tue, 26 Oct 2021 21:36:23 +0000 (+0000) Subject: update to libfido2 1.8.0; ok sthen@ "timing is fine" deraadt@ X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=c4a807ed13283181a6a84fdf571f4571a1b25eac;p=openbsd update to libfido2 1.8.0; ok sthen@ "timing is fine" deraadt@ --- diff --git a/lib/libfido2/LICENSE b/lib/libfido2/LICENSE index 340cc352a0f..4224f20992c 100644 --- a/lib/libfido2/LICENSE +++ b/lib/libfido2/LICENSE @@ -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 diff --git a/lib/libfido2/Makefile b/lib/libfido2/Makefile index 9a8153d007f..fc699ff62d4 100644 --- a/lib/libfido2/Makefile +++ b/lib/libfido2/Makefile @@ -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 \ diff --git a/lib/libfido2/README.openbsd b/lib/libfido2/README.openbsd index 643512b1d4f..90f48bcae32 100644 --- a/lib/libfido2/README.openbsd +++ b/lib/libfido2/README.openbsd @@ -1,4 +1,4 @@ -This is an import of https://github.com/Yubico/libfido2 1.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 diff --git a/lib/libfido2/man/fido_assert_new.3 b/lib/libfido2/man/fido_assert_new.3 index f04bdbcba07..d14fd01d68e 100644 --- a/lib/libfido2/man/fido_assert_new.3 +++ b/lib/libfido2/man/fido_assert_new.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: August 11 2020 $ +.Dd $Mdocdate: October 26 2021 $ .Dt FIDO_ASSERT_NEW 3 .Os .Sh NAME @@ -14,14 +14,18 @@ .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 , @@ -49,8 +53,12 @@ .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" @@ -61,8 +69,12 @@ .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 diff --git a/lib/libfido2/man/fido_assert_set_authdata.3 b/lib/libfido2/man/fido_assert_set_authdata.3 index 9565954f264..b5d070f5f1f 100644 --- a/lib/libfido2/man/fido_assert_set_authdata.3 +++ b/lib/libfido2/man/fido_assert_set_authdata.3 @@ -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 diff --git a/lib/libfido2/man/fido_bio_dev_get_info.3 b/lib/libfido2/man/fido_bio_dev_get_info.3 index 7338d7f04d4..df0fb692514 100644 --- a/lib/libfido2/man/fido_bio_dev_get_info.3 +++ b/lib/libfido2/man/fido_bio_dev_get_info.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: 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. diff --git a/lib/libfido2/man/fido_bio_template.3 b/lib/libfido2/man/fido_bio_template.3 index f13bf231c5b..da012b4fb2c 100644 --- a/lib/libfido2/man/fido_bio_template.3 +++ b/lib/libfido2/man/fido_bio_template.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: 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 diff --git a/lib/libfido2/man/fido_cbor_info_new.3 b/lib/libfido2/man/fido_cbor_info_new.3 index 755182a5d75..93a4affd426 100644 --- a/lib/libfido2/man/fido_cbor_info_new.3 +++ b/lib/libfido2/man/fido_cbor_info_new.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: August 11 2020 $ +.Dd $Mdocdate: October 26 2021 $ .Dt FIDO_CBOR_INFO_NEW 3 .Os .Sh NAME @@ -12,12 +12,17 @@ .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 , @@ -40,11 +45,19 @@ .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 @@ -52,12 +65,16 @@ .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 diff --git a/lib/libfido2/man/fido_cred_new.3 b/lib/libfido2/man/fido_cred_new.3 index d5d8f2f0922..f78f6a6f04c 100644 --- a/lib/libfido2/man/fido_cred_new.3 +++ b/lib/libfido2/man/fido_cred_new.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: August 11 2020 $ +.Dd $Mdocdate: October 26 2021 $ .Dt FIDO_CRED_NEW 3 .Os .Sh NAME @@ -15,23 +15,28 @@ .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 @@ -54,12 +59,16 @@ .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" @@ -70,12 +79,16 @@ .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" @@ -87,6 +100,8 @@ .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 diff --git a/lib/libfido2/man/fido_cred_set_authdata.3 b/lib/libfido2/man/fido_cred_set_authdata.3 index 6370df9f5a2..dea4b7dec0a 100644 --- a/lib/libfido2/man/fido_cred_set_authdata.3 +++ b/lib/libfido2/man/fido_cred_set_authdata.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: February 7 2020 $ +.Dd $Mdocdate: October 26 2021 $ .Dt FIDO_CRED_SET_AUTHDATA 3 .Os .Sh NAME @@ -10,10 +10,13 @@ .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. diff --git a/lib/libfido2/man/fido_cred_verify.3 b/lib/libfido2/man/fido_cred_verify.3 index f8f794108ae..47c14948e94 100644 --- a/lib/libfido2/man/fido_cred_verify.3 +++ b/lib/libfido2/man/fido_cred_verify.3 @@ -2,12 +2,12 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: February 7 2020 $ +.Dd $Mdocdate: 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. diff --git a/lib/libfido2/man/fido_credman_metadata_new.3 b/lib/libfido2/man/fido_credman_metadata_new.3 index 4e62c807e03..7fb310909fd 100644 --- a/lib/libfido2/man/fido_credman_metadata_new.3 +++ b/lib/libfido2/man/fido_credman_metadata_new.3 @@ -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 @@ -64,14 +65,16 @@ .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 index 00000000000..366debf4e12 --- /dev/null +++ b/lib/libfido2/man/fido_dev_enable_entattest.3 @@ -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 index 00000000000..cd9d0cce2bf --- /dev/null +++ b/lib/libfido2/man/fido_dev_largeblob_get.3 @@ -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. diff --git a/lib/libfido2/man/fido_dev_make_cred.3 b/lib/libfido2/man/fido_dev_make_cred.3 index 7e5697108e7..158f5ea5a8e 100644 --- a/lib/libfido2/man/fido_dev_make_cred.3 +++ b/lib/libfido2/man/fido_dev_make_cred.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: February 7 2020 $ +.Dd $Mdocdate: 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 diff --git a/lib/libfido2/man/fido_dev_open.3 b/lib/libfido2/man/fido_dev_open.3 index 1f78fdf3c7b..def6a997941 100644 --- a/lib/libfido2/man/fido_dev_open.3 +++ b/lib/libfido2/man/fido_dev_open.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: August 23 2020 $ +.Dd $Mdocdate: October 26 2021 $ .Dt FIDO_DEV_OPEN 3 .Os .Sh NAME @@ -14,9 +14,13 @@ .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 , @@ -42,11 +46,19 @@ .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 diff --git a/lib/libfido2/man/fido_dev_set_io_functions.3 b/lib/libfido2/man/fido_dev_set_io_functions.3 index ad02286ca09..6775b73c1da 100644 --- a/lib/libfido2/man/fido_dev_set_io_functions.3 +++ b/lib/libfido2/man/fido_dev_set_io_functions.3 @@ -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 diff --git a/lib/libfido2/man/fido_dev_set_pin.3 b/lib/libfido2/man/fido_dev_set_pin.3 index a6deddf20bd..a269541b2fb 100644 --- a/lib/libfido2/man/fido_dev_set_pin.3 +++ b/lib/libfido2/man/fido_dev_set_pin.3 @@ -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 diff --git a/lib/libfido2/man/fido_init.3 b/lib/libfido2/man/fido_init.3 index 32629d320c7..cd95c15d158 100644 --- a/lib/libfido2/man/fido_init.3 +++ b/lib/libfido2/man/fido_init.3 @@ -2,7 +2,7 @@ .\" Use of this source code is governed by a BSD-style .\" license that can be found in the LICENSE file. .\" -.Dd $Mdocdate: February 7 2020 $ +.Dd $Mdocdate: 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 , diff --git a/lib/libfido2/shlib_version b/lib/libfido2/shlib_version index d9961ea9fef..3066b9771e7 100644 --- a/lib/libfido2/shlib_version +++ b/lib/libfido2/shlib_version @@ -1,2 +1,2 @@ -major=4 +major=5 minor=0 diff --git a/lib/libfido2/src/aes256.c b/lib/libfido2/src/aes256.c index 767cdb2cba6..f093b7ce0bd 100644 --- a/lib/libfido2/src/aes256.c +++ b/lib/libfido2/src/aes256.c @@ -1,98 +1,215 @@ /* - * 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 -#include - #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); } diff --git a/lib/libfido2/src/assert.c b/lib/libfido2/src/assert.c index 17463878d28..b36f8e32466 100644 --- a/lib/libfido2/src/assert.c +++ b/lib/libfido2/src/assert.c @@ -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 #include -#include #include -#include #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); } diff --git a/lib/libfido2/src/authkey.c b/lib/libfido2/src/authkey.c index 83c25643a37..c3474ccafc0 100644 --- a/lib/libfido2/src/authkey.c +++ b/lib/libfido2/src/authkey.c @@ -4,7 +4,6 @@ * license that can be found in the LICENSE file. */ -#include #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; diff --git a/lib/libfido2/src/bio.c b/lib/libfido2/src/bio.c index c1032d8db5e..06bc32eea7e 100644 --- a/lib/libfido2/src/bio.c +++ b/lib/libfido2/src/bio.c @@ -4,8 +4,6 @@ * license that can be found in the LICENSE file. */ -#include - #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); diff --git a/lib/libfido2/src/blob.c b/lib/libfido2/src/blob.c index d4eae700581..31e4cab0edc 100644 --- a/lib/libfido2/src/blob.c +++ b/lib/libfido2/src/blob.c @@ -4,41 +4,66 @@ * license that can be found in the LICENSE file. */ -#include #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; } diff --git a/lib/libfido2/src/blob.h b/lib/libfido2/src/blob.h index 9e98d03b786..76a8dd994f2 100644 --- a/lib/libfido2/src/blob.h +++ b/lib/libfido2/src/blob.h @@ -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 diff --git a/lib/libfido2/src/buf.c b/lib/libfido2/src/buf.c index 4646476df84..f7161e64a9c 100644 --- a/lib/libfido2/src/buf.c +++ b/lib/libfido2/src/buf.c @@ -4,7 +4,6 @@ * license that can be found in the LICENSE file. */ -#include #include "fido.h" int diff --git a/lib/libfido2/src/cbor.c b/lib/libfido2/src/cbor.c index b30da508b7c..5c1b11583e7 100644 --- a/lib/libfido2/src/cbor.c +++ b/lib/libfido2/src/cbor.c @@ -4,11 +4,8 @@ * license that can be found in the LICENSE file. */ -#include #include #include - -#include #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(¶m); if (pair.key != NULL) cbor_decref(&pair.key); + fido_blob_free(&enc); + + return (r); +} + +cbor_item_t * +cbor_encode_assert_ext(fido_dev_t *dev, const fido_assert_ext_t *ext, + const fido_blob_t *ecdh, const es256_pk_t *pk) +{ + cbor_item_t *item = NULL; + size_t size = 0; + + if (ext->mask & FIDO_EXT_CRED_BLOB) + size++; + if (ext->mask & FIDO_EXT_HMAC_SECRET) + size++; + if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) + size++; + if (size == 0 || (item = cbor_new_definite_map(size)) == NULL) + return (NULL); + + if (ext->mask & FIDO_EXT_CRED_BLOB) { + if (cbor_add_bool(item, "credBlob", FIDO_OPT_TRUE) < 0) { + cbor_decref(&item); + return (NULL); + } + } + if (ext->mask & FIDO_EXT_HMAC_SECRET) { + if (cbor_encode_hmac_secret_param(dev, item, ecdh, pk, + &ext->hmac_salt) < 0) { + cbor_decref(&item); + return (NULL); + } + } + if (ext->mask & FIDO_EXT_LARGEBLOB_KEY) { + if (cbor_encode_largeblob_key_ext(item) < 0) { + cbor_decref(&item); + return (NULL); + } + } + return (item); } @@ -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 index 00000000000..ee5501b4a4a --- /dev/null +++ b/lib/libfido2/src/compress.c @@ -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 +#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 index 00000000000..0dda16163bc --- /dev/null +++ b/lib/libfido2/src/config.c @@ -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); +} diff --git a/lib/libfido2/src/cred.c b/lib/libfido2/src/cred.c index 9f902faf0ae..5e65b08293b 100644 --- a/lib/libfido2/src/cred.c +++ b/lib/libfido2/src/cred.c @@ -4,12 +4,9 @@ * license that can be found in the LICENSE file. */ -#include -#include #include #include -#include #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); +} diff --git a/lib/libfido2/src/credman.c b/lib/libfido2/src/credman.c index 4219807ce48..e48ca4543b1 100644 --- a/lib/libfido2/src/credman.c +++ b/lib/libfido2/src/credman.c @@ -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 -#include - #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) { diff --git a/lib/libfido2/src/dev.c b/lib/libfido2/src/dev.c index 3463ae41d01..a003854f89d 100644 --- a/lib/libfido2/src/dev.c +++ b/lib/libfido2/src/dev.c @@ -4,86 +4,9 @@ * license that can be found in the LICENSE file. */ -#include -#include -#ifdef HAVE_SYS_RANDOM_H -#include -#endif - #include - -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif - #include "fido.h" -#if defined(_WIN32) -#include - -#include -#include -#include -#include -#include - -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); +} diff --git a/lib/libfido2/src/ecdh.c b/lib/libfido2/src/ecdh.c index 7f25c7b9494..3ea47ae6457 100644 --- a/lib/libfido2/src/ecdh.c +++ b/lib/libfido2/src/ecdh.c @@ -1,59 +1,149 @@ /* - * 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 #include +#if defined(LIBRESSL_VERSION_NUMBER) +#include +#elif OPENSSL_VERSION_NUMBER >= 0x10100000L +#include +#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; } diff --git a/lib/libfido2/src/eddsa.c b/lib/libfido2/src/eddsa.c index 44a556319bd..89b84c5a6bd 100644 --- a/lib/libfido2/src/eddsa.c +++ b/lib/libfido2/src/eddsa.c @@ -5,11 +5,8 @@ */ #include -#include -#include #include -#include #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; } diff --git a/lib/libfido2/src/err.c b/lib/libfido2/src/err.c index 19cda2111d2..8c2ae5ff417 100644 --- a/lib/libfido2/src/err.c +++ b/lib/libfido2/src/err.c @@ -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: diff --git a/lib/libfido2/src/es256.c b/lib/libfido2/src/es256.c index 5b4e6d663d7..9cdb48e4832 100644 --- a/lib/libfido2/src/es256.c +++ b/lib/libfido2/src/es256.c @@ -5,11 +5,8 @@ */ #include -#include -#include #include -#include #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; } diff --git a/lib/libfido2/src/export.llvm b/lib/libfido2/src/export.llvm index ffbc1571014..8d3810f92ce 100644 --- a/lib/libfido2/src/export.llvm +++ b/lib/libfido2/src/export.llvm @@ -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 diff --git a/lib/libfido2/src/extern.h b/lib/libfido2/src/extern.h index 4c036cbe074..3be33236f2b 100644 --- a/lib/libfido2/src/extern.h +++ b/lib/libfido2/src/extern.h @@ -7,6 +7,14 @@ #ifndef _EXTERN_H #define _EXTERN_H +#ifdef __MINGW32__ +#include +#endif + +#ifdef HAVE_SIGNAL_H +#include +#endif + #include #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" */ diff --git a/lib/libfido2/src/fido.h b/lib/libfido2/src/fido.h index d6e8974fee2..ef98130b09e 100644 --- a/lib/libfido2/src/fido.h +++ b/lib/libfido2/src/fido.h @@ -13,8 +13,11 @@ #include #include #include +#include #ifdef _FIDO_INTERNAL +#include + #include #include @@ -32,14 +35,6 @@ 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 index 00000000000..869927df914 --- /dev/null +++ b/lib/libfido2/src/fido/config.h @@ -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 +#include +#include +#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 */ diff --git a/lib/libfido2/src/fido/credman.h b/lib/libfido2/src/fido/credman.h index eaffd65ac0c..66a96697050 100644 --- a/lib/libfido2/src/fido/credman.h +++ b/lib/libfido2/src/fido/credman.h @@ -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 *); diff --git a/lib/libfido2/src/fido/err.h b/lib/libfido2/src/fido/err.h index 253914f7e50..74fdf9d2bfe 100644 --- a/lib/libfido2/src/fido/err.h +++ b/lib/libfido2/src/fido/err.h @@ -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" { diff --git a/lib/libfido2/src/fido/param.h b/lib/libfido2/src/fido/param.h index 14ee74e4d55..025bb57dd81 100644 --- a/lib/libfido2/src/fido/param.h +++ b/lib/libfido2/src/fido/param.h @@ -31,9 +31,15 @@ #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 @@ -92,10 +99,19 @@ /* 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 */ diff --git a/lib/libfido2/src/fido/types.h b/lib/libfido2/src/fido/types.h index cce1a44ddc8..00b6058c7e1 100644 --- a/lib/libfido2/src/fido/types.h +++ b/lib/libfido2/src/fido/types.h @@ -7,6 +7,11 @@ #ifndef _FIDO_TYPES_H #define _FIDO_TYPES_H +#ifdef __MINGW32__ +#include +#endif + +#include #include #include @@ -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 diff --git a/lib/libfido2/src/hid.c b/lib/libfido2/src/hid.c index 783d22c9a37..a3768ad3cae 100644 --- a/lib/libfido2/src/hid.c +++ b/lib/libfido2/src/hid.c @@ -4,9 +4,117 @@ * license that can be found in the LICENSE file. */ -#include #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); diff --git a/lib/libfido2/src/hid_openbsd.c b/lib/libfido2/src/hid_openbsd.c index 58b8c3e9475..dd9d7653197 100644 --- a/lib/libfido2/src/hid_openbsd.c +++ b/lib/libfido2/src/hid_openbsd.c @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -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 index 00000000000..4b2aff9d67f --- /dev/null +++ b/lib/libfido2/src/hid_unix.c @@ -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 + +#include +#include +#include +#include + +#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); +} diff --git a/lib/libfido2/src/info.c b/lib/libfido2/src/info.c index 3199ac2dbef..57bc8de4406 100644 --- a/lib/libfido2/src/info.c +++ b/lib/libfido2/src/info.c @@ -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 #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); +} diff --git a/lib/libfido2/src/io.c b/lib/libfido2/src/io.c index 9d2de884bf1..e2594203efb 100644 --- a/lib/libfido2/src/io.c +++ b/lib/libfido2/src/io.c @@ -4,10 +4,6 @@ * license that can be found in the LICENSE file. */ -#include -#include -#include - #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); } diff --git a/lib/libfido2/src/iso7816.c b/lib/libfido2/src/iso7816.c index 4fe6329a9d5..a11aae3e99d 100644 --- a/lib/libfido2/src/iso7816.c +++ b/lib/libfido2/src/iso7816.c @@ -4,29 +4,27 @@ * license that can be found in the LICENSE file. */ -#include #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); } diff --git a/lib/libfido2/src/iso7816.h b/lib/libfido2/src/iso7816.h index 563243fa01f..5f5363a63a5 100644 --- a/lib/libfido2/src/iso7816.h +++ b/lib/libfido2/src/iso7816.h @@ -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 index 00000000000..fa453f5de33 --- /dev/null +++ b/lib/libfido2/src/largeblob.c @@ -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 + +#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; +} diff --git a/lib/libfido2/src/log.c b/lib/libfido2/src/log.c index d6f09346af3..ab18ae12b4f 100644 --- a/lib/libfido2/src/log.c +++ b/lib/libfido2/src/log.c @@ -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 #include -#include -#include #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) { diff --git a/lib/libfido2/src/pin.c b/lib/libfido2/src/pin.c index 8b23ae33415..d3104e0ca6e 100644 --- a/lib/libfido2/src/pin.c +++ b/lib/libfido2/src/pin.c @@ -4,46 +4,165 @@ * license that can be found in the LICENSE file. */ -#include - +#include #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 index 00000000000..289050cb5c2 --- /dev/null +++ b/lib/libfido2/src/random.c @@ -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 +#include +#include + +int +fido_get_random(void *buf, size_t len) +{ + arc4random_buf(buf, len); + return (0); +} diff --git a/lib/libfido2/src/reset.c b/lib/libfido2/src/reset.c index ebda1cdada1..11380cea090 100644 --- a/lib/libfido2/src/reset.c +++ b/lib/libfido2/src/reset.c @@ -4,7 +4,6 @@ * license that can be found in the LICENSE file. */ -#include #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); } diff --git a/lib/libfido2/src/rs256.c b/lib/libfido2/src/rs256.c index 9f30163c67b..c6d87a3ea22 100644 --- a/lib/libfido2/src/rs256.c +++ b/lib/libfido2/src/rs256.c @@ -6,10 +6,8 @@ #include #include -#include #include -#include #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; } diff --git a/lib/libfido2/src/u2f.c b/lib/libfido2/src/u2f.c index 3c6ea82731d..c5fbe0cfbb6 100644 --- a/lib/libfido2/src/u2f.c +++ b/lib/libfido2/src/u2f.c @@ -7,7 +7,6 @@ #include #include -#include #ifdef HAVE_UNISTD_H #include #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) {