Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

winhello: support u2f appid #689

Merged
merged 1 commit into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions man/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ list(APPEND MAN_ALIAS
fido_assert_set_authdata fido_assert_set_sig
fido_assert_set_authdata fido_assert_set_up
fido_assert_set_authdata fido_assert_set_uv
fido_assert_set_authdata fido_assert_set_winhello_appid
fido_bio_dev_get_info fido_bio_dev_enroll_begin
fido_bio_dev_get_info fido_bio_dev_enroll_cancel
fido_bio_dev_get_info fido_bio_dev_enroll_continue
Expand Down
59 changes: 56 additions & 3 deletions man/fido_assert_set_authdata.3
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
.Dd $Mdocdate: April 27 2022 $
.Dd $Mdocdate: April 8 2023 $
.Dt FIDO_ASSERT_SET_AUTHDATA 3
.Os
.Sh NAME
Expand All @@ -40,7 +40,8 @@
.Nm fido_assert_set_up ,
.Nm fido_assert_set_uv ,
.Nm fido_assert_set_rp ,
.Nm fido_assert_set_sig
.Nm fido_assert_set_sig ,
.Nm fido_assert_set_winhello_appid
.Nd set parameters of a FIDO2 assertion
.Sh SYNOPSIS
.In fido.h
Expand Down Expand Up @@ -75,6 +76,8 @@ typedef enum {
.Fn fido_assert_set_rp "fido_assert_t *assert" "const char *id"
.Ft int
.Fn fido_assert_set_sig "fido_assert_t *assert" "size_t idx" "const unsigned char *ptr" "size_t len"
.Ft int
.Fn fido_assert_set_winhello_appid "fido_assert_t *assert" "const char *id"
.Sh DESCRIPTION
The
.Nm
Expand Down Expand Up @@ -226,6 +229,55 @@ Both are
.Dv FIDO_OPT_OMIT
by default, allowing the authenticator to use its default settings.
.Pp
The
.Fn fido_assert_set_winhello_appid
function sets the U2F application
.Fa id
.Pq Dq U2F AppID
of
.Fa assert ,
where
.Fa id
is a NUL-terminated UTF-8 string.
The content of
.Fa id
is copied, and no references to the passed pointer are kept.
The
.Fn fido_assert_set_winhello_appid
function is a no-op unless
.Fa assert
is passed to
.Xr fido_dev_get_assert 3
with a device
.Fa dev
on which
.Xr fido_dev_is_winhello 3
holds true.
In this case,
.Em libfido2
will instruct Windows Hello to try the assertion twice,
first with the
.Fa id
passed to
.Fn fido_assert_set_rp ,
and a second time with the
.Fa id
passed to
.Fn fido_assert_set_winhello_appid .
If the second assertion succeeds,
.Xr fido_assert_rp_id 3
will point to the U2F AppID once
.Xr fido_dev_get_assert 3
completes.
This mechanism exists in Windows Hello to ensure U2F backwards
compatibility without the application inadvertently prompting the
user twice.
Note that
.Fn fido_assert_set_winhello_appid
is not needed on platforms offering CTAP primitives, since the
authenticator can be silently probed for the existence of U2F
credentials.
.Pp
Use of the
.Nm
set of functions may happen in two distinct situations:
Expand Down Expand Up @@ -258,4 +310,5 @@ set of functions are defined in
.Sh SEE ALSO
.Xr fido_assert_allow_cred 3 ,
.Xr fido_assert_verify 3 ,
.Xr fido_dev_get_assert 3
.Xr fido_dev_get_assert 3 ,
.Xr fido_dev_is_winhello 3
30 changes: 30 additions & 0 deletions src/assert.c
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,34 @@ fido_assert_set_rp(fido_assert_t *assert, const char *id)
return (FIDO_OK);
}

#ifdef USE_WINHELLO
int
fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id)
{
if (assert->appid != NULL) {
free(assert->appid);
assert->appid = NULL;
}

if (id == NULL)
return (FIDO_ERR_INVALID_ARGUMENT);

if ((assert->appid = strdup(id)) == NULL)
return (FIDO_ERR_INTERNAL);

return (FIDO_OK);
}
#else
int
fido_assert_set_winhello_appid(fido_assert_t *assert, const char *id)
{
(void)assert;
(void)id;

return (FIDO_ERR_UNSUPPORTED_EXTENSION);
}
#endif /* USE_WINHELLO */

int
fido_assert_allow_cred(fido_assert_t *assert, const unsigned char *ptr,
size_t len)
Expand Down Expand Up @@ -745,12 +773,14 @@ void
fido_assert_reset_tx(fido_assert_t *assert)
{
free(assert->rp_id);
free(assert->appid);
fido_blob_reset(&assert->cd);
fido_blob_reset(&assert->cdh);
fido_blob_reset(&assert->ext.hmac_salt);
fido_assert_empty_allow_list(assert);
memset(&assert->ext, 0, sizeof(assert->ext));
assert->rp_id = NULL;
assert->appid = NULL;
assert->up = FIDO_OPT_OMIT;
assert->uv = FIDO_OPT_OMIT;
}
Expand Down
1 change: 1 addition & 0 deletions src/export.gnu
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
fido_assert_set_sig;
fido_assert_set_up;
fido_assert_set_uv;
fido_assert_set_winhello_appid;
fido_assert_sigcount;
fido_assert_sig_len;
fido_assert_sig_ptr;
Expand Down
1 change: 1 addition & 0 deletions src/export.llvm
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ _fido_assert_set_rp
_fido_assert_set_sig
_fido_assert_set_up
_fido_assert_set_uv
_fido_assert_set_winhello_appid
_fido_assert_sigcount
_fido_assert_sig_len
_fido_assert_sig_ptr
Expand Down
1 change: 1 addition & 0 deletions src/export.msvc
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ fido_assert_set_rp
fido_assert_set_sig
fido_assert_set_up
fido_assert_set_uv
fido_assert_set_winhello_appid
fido_assert_sigcount
fido_assert_sig_len
fido_assert_sig_ptr
Expand Down
1 change: 1 addition & 0 deletions src/fido.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ 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_set_winhello_appid(fido_assert_t *, const char *);
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_empty_exclude_list(fido_cred_t *);
Expand Down
1 change: 1 addition & 0 deletions src/fido/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ typedef struct fido_assert_ext {

typedef struct fido_assert {
char *rp_id; /* relying party id */
char *appid; /* winhello u2f appid */
fido_blob_t cd; /* client data */
fido_blob_t cdh; /* client data hash */
fido_blob_array_t allow_list; /* list of allowed credentials */
Expand Down
51 changes: 49 additions & 2 deletions src/winhello.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ struct winhello_assert {
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS opt;
WEBAUTHN_ASSERTION *assert;
wchar_t *rp_id;
wchar_t *appid;
};

struct winhello_cred {
Expand Down Expand Up @@ -473,6 +474,43 @@ pack_assert_ext(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *out,
return 0;
}

static int
pack_appid(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt, const char *id,
wchar_t **appid)
{
if (id == NULL)
return 0; /* nothing to do */
if ((opt->pbU2fAppId = calloc(1, sizeof(*opt->pbU2fAppId))) == NULL) {
fido_log_debug("%s: calloc", __func__);
return -1;
}
if ((*appid = to_utf16(id)) == NULL) {
fido_log_debug("%s: to_utf16", __func__);
return -1;
}
fido_log_debug("%s: using %s", __func__, id);
opt->pwszU2fAppId = *appid;
opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2;

return 0;
}

static void
unpack_appid(fido_assert_t *assert,
const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt)
{
if (assert->appid == NULL || opt->pbU2fAppId == NULL)
return; /* nothing to do */
if (*opt->pbU2fAppId == false) {
fido_log_debug("%s: not used", __func__);
return;
}
fido_log_debug("%s: %s -> %s", __func__, assert->rp_id, assert->appid);
free(assert->rp_id);
assert->rp_id = assert->appid;
assert->appid = NULL;
}

static int
unpack_assert_authdata(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa)
{
Expand Down Expand Up @@ -584,6 +622,10 @@ translate_fido_assert(struct winhello_assert *ctx, const fido_assert_t *assert,
opt = &ctx->opt;
opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1;
opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms;
if (pack_appid(opt, assert->appid, &ctx->appid) < 0) {
fido_log_debug("%s: pack_appid" , __func__);
return FIDO_ERR_INTERNAL;
}
if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) {
fido_log_debug("%s: pack_credlist", __func__);
return FIDO_ERR_INTERNAL;
Expand All @@ -602,8 +644,11 @@ translate_fido_assert(struct winhello_assert *ctx, const fido_assert_t *assert,
}

static int
translate_winhello_assert(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa)
translate_winhello_assert(fido_assert_t *assert,
const struct winhello_assert *ctx)
{
const WEBAUTHN_ASSERTION *wa = ctx->assert;
const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt = &ctx->opt;
int r;

if (assert->stmt_len > 0) {
Expand All @@ -615,6 +660,7 @@ translate_winhello_assert(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa)
fido_strerr(r));
return FIDO_ERR_INTERNAL;
}
unpack_appid(assert, opt);
if (unpack_assert_authdata(assert, wa) < 0) {
fido_log_debug("%s: unpack_assert_authdata", __func__);
return FIDO_ERR_INTERNAL;
Expand Down Expand Up @@ -806,6 +852,7 @@ winhello_assert_free(struct winhello_assert *ctx)
webauthn_free_assert(ctx->assert);

free(ctx->rp_id);
free(ctx->appid);
free(ctx->opt.CredentialList.pCredentials);
if (ctx->opt.pHmacSecretSaltValues != NULL)
free(ctx->opt.pHmacSecretSaltValues->pGlobalHmacSalt);
Expand Down Expand Up @@ -934,7 +981,7 @@ fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert,
fido_log_debug("%s: winhello_get_assert", __func__);
goto fail;
}
if ((r = translate_winhello_assert(assert, ctx->assert)) != FIDO_OK) {
if ((r = translate_winhello_assert(assert, ctx)) != FIDO_OK) {
fido_log_debug("%s: translate_winhello_assert", __func__);
goto fail;
}
Expand Down