diff options
Diffstat (limited to 'contrib/libfido2/src/winhello.c')
-rw-r--r-- | contrib/libfido2/src/winhello.c | 574 |
1 files changed, 329 insertions, 245 deletions
diff --git a/contrib/libfido2/src/winhello.c b/contrib/libfido2/src/winhello.c index 0fe5b4cfe4c7..efc7dc22f851 100644 --- a/contrib/libfido2/src/winhello.c +++ b/contrib/libfido2/src/winhello.c @@ -1,16 +1,27 @@ /* - * Copyright (c) 2021 Yubico AB. All rights reserved. + * Copyright (c) 2021-2022 Yubico AB. All rights reserved. * Use of this source code is governed by a BSD-style * license that can be found in the LICENSE file. + * SPDX-License-Identifier: BSD-2-Clause */ #include <sys/types.h> #include <stdlib.h> #include <windows.h> -#include <webauthn.h> #include "fido.h" +#include "webauthn.h" + +#ifndef NTE_INVALID_PARAMETER +#define NTE_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80090027) +#endif +#ifndef NTE_NOT_SUPPORTED +#define NTE_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80090029) +#endif +#ifndef NTE_DEVICE_NOT_FOUND +#define NTE_DEVICE_NOT_FOUND _HRESULT_TYPEDEF_(0x80090035) +#endif #define MAXCHARS 128 #define MAXCREDS 128 @@ -40,6 +51,108 @@ struct winhello_cred { wchar_t *display_name; }; +typedef DWORD WINAPI webauthn_get_api_version_t(void); +typedef PCWSTR WINAPI webauthn_strerr_t(HRESULT); +typedef HRESULT WINAPI webauthn_get_assert_t(HWND, LPCWSTR, + PCWEBAUTHN_CLIENT_DATA, + PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, + PWEBAUTHN_ASSERTION *); +typedef HRESULT WINAPI webauthn_make_cred_t(HWND, + PCWEBAUTHN_RP_ENTITY_INFORMATION, + PCWEBAUTHN_USER_ENTITY_INFORMATION, + PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS, + PCWEBAUTHN_CLIENT_DATA, + PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS, + PWEBAUTHN_CREDENTIAL_ATTESTATION *); +typedef void WINAPI webauthn_free_assert_t(PWEBAUTHN_ASSERTION); +typedef void WINAPI webauthn_free_attest_t(PWEBAUTHN_CREDENTIAL_ATTESTATION); + +static TLS BOOL webauthn_loaded; +static TLS HMODULE webauthn_handle; +static TLS webauthn_get_api_version_t *webauthn_get_api_version; +static TLS webauthn_strerr_t *webauthn_strerr; +static TLS webauthn_get_assert_t *webauthn_get_assert; +static TLS webauthn_make_cred_t *webauthn_make_cred; +static TLS webauthn_free_assert_t *webauthn_free_assert; +static TLS webauthn_free_attest_t *webauthn_free_attest; + +static int +webauthn_load(void) +{ + DWORD n = 1; + + if (webauthn_loaded || webauthn_handle != NULL) { + fido_log_debug("%s: already loaded", __func__); + return -1; + } + if ((webauthn_handle = LoadLibrary(TEXT("webauthn.dll"))) == NULL) { + fido_log_debug("%s: LoadLibrary", __func__); + return -1; + } + + if ((webauthn_get_api_version = + (webauthn_get_api_version_t *)GetProcAddress(webauthn_handle, + "WebAuthNGetApiVersionNumber")) == NULL) { + fido_log_debug("%s: WebAuthNGetApiVersionNumber", __func__); + /* WebAuthNGetApiVersionNumber might not exist */ + } + if (webauthn_get_api_version != NULL && + (n = webauthn_get_api_version()) < 1) { + fido_log_debug("%s: unsupported api %lu", __func__, (u_long)n); + goto fail; + } + fido_log_debug("%s: api version %lu", __func__, (u_long)n); + if ((webauthn_strerr = + (webauthn_strerr_t *)GetProcAddress(webauthn_handle, + "WebAuthNGetErrorName")) == NULL) { + fido_log_debug("%s: WebAuthNGetErrorName", __func__); + goto fail; + } + if ((webauthn_get_assert = + (webauthn_get_assert_t *)GetProcAddress(webauthn_handle, + "WebAuthNAuthenticatorGetAssertion")) == NULL) { + fido_log_debug("%s: WebAuthNAuthenticatorGetAssertion", + __func__); + goto fail; + } + if ((webauthn_make_cred = + (webauthn_make_cred_t *)GetProcAddress(webauthn_handle, + "WebAuthNAuthenticatorMakeCredential")) == NULL) { + fido_log_debug("%s: WebAuthNAuthenticatorMakeCredential", + __func__); + goto fail; + } + if ((webauthn_free_assert = + (webauthn_free_assert_t *)GetProcAddress(webauthn_handle, + "WebAuthNFreeAssertion")) == NULL) { + fido_log_debug("%s: WebAuthNFreeAssertion", __func__); + goto fail; + } + if ((webauthn_free_attest = + (webauthn_free_attest_t *)GetProcAddress(webauthn_handle, + "WebAuthNFreeCredentialAttestation")) == NULL) { + fido_log_debug("%s: WebAuthNFreeCredentialAttestation", + __func__); + goto fail; + } + + webauthn_loaded = true; + + return 0; +fail: + fido_log_debug("%s: GetProcAddress", __func__); + webauthn_get_api_version = NULL; + webauthn_strerr = NULL; + webauthn_get_assert = NULL; + webauthn_make_cred = NULL; + webauthn_free_assert = NULL; + webauthn_free_attest = NULL; + FreeLibrary(webauthn_handle); + webauthn_handle = NULL; + + return -1; +} + static wchar_t * to_utf16(const char *utf8) { @@ -68,53 +181,6 @@ to_utf16(const char *utf8) return utf16; } -static char * -to_utf8(const wchar_t *utf16) -{ - int nch; - char *utf8; - - if (utf16 == NULL) { - fido_log_debug("%s: NULL", __func__); - return NULL; - } - if ((nch = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16, - -1, NULL, 0, NULL, NULL)) < 1 || (size_t)nch > MAXCHARS) { - fido_log_debug("%s: WideCharToMultiByte %d", __func__); - return NULL; - } - if ((utf8 = calloc((size_t)nch, sizeof(*utf8))) == NULL) { - fido_log_debug("%s: calloc", __func__); - return NULL; - } - if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16, -1, - utf8, nch, NULL, NULL) != nch) { - fido_log_debug("%s: WideCharToMultiByte", __func__); - free(utf8); - return NULL; - } - - return utf8; -} - -static int -to_fido_str_array(fido_str_array_t *sa, const char **v, size_t n) -{ - if ((sa->ptr = calloc(n, sizeof(char *))) == NULL) { - fido_log_debug("%s: calloc", __func__); - return -1; - } - for (size_t i = 0; i < n; i++) { - if ((sa->ptr[i] = strdup(v[i])) == NULL) { - fido_log_debug("%s: strdup", __func__); - return -1; - } - sa->len++; - } - - return 0; -} - static int to_fido(HRESULT hr) { @@ -129,7 +195,7 @@ to_fido(HRESULT hr) case NTE_NOT_FOUND: return FIDO_ERR_NOT_ALLOWED; default: - fido_log_debug("%s: hr=0x%x", __func__, hr); + fido_log_debug("%s: hr=0x%lx", __func__, (u_long)hr); return FIDO_ERR_INTERNAL; } } @@ -186,7 +252,28 @@ pack_credlist(WEBAUTHN_CREDENTIALS *out, const fido_blob_array_t *in) } static int -set_uv(DWORD *out, fido_opt_t uv, const char *pin) +set_cred_uv(DWORD *out, fido_opt_t uv, const char *pin) +{ + if (pin) { + *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; + return 0; + } + + switch (uv) { + case FIDO_OPT_OMIT: + case FIDO_OPT_FALSE: + *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + break; + case FIDO_OPT_TRUE: + *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; + break; + } + + return 0; +} + +static int +set_assert_uv(DWORD *out, fido_opt_t uv, const char *pin) { if (pin) { *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; @@ -195,7 +282,7 @@ set_uv(DWORD *out, fido_opt_t uv, const char *pin) switch (uv) { case FIDO_OPT_OMIT: - *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY; + *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; break; case FIDO_OPT_FALSE: *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; @@ -210,7 +297,7 @@ set_uv(DWORD *out, fido_opt_t uv, const char *pin) static int pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out, - fido_rp_t *in) + const fido_rp_t *in) { /* keep non-const copies of pwsz* for free() */ out->dwVersion = WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION; @@ -227,7 +314,7 @@ pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out, static int pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name, - WEBAUTHN_USER_ENTITY_INFORMATION *out, fido_user_t *in) + WEBAUTHN_USER_ENTITY_INFORMATION *out, const fido_user_t *in) { if (in->id.ptr == NULL || in->id.len > ULONG_MAX) { fido_log_debug("%s: id", __func__); @@ -268,6 +355,9 @@ pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg, case COSE_ES256: alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256; break; + case COSE_ES384: + alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384; + break; case COSE_EDDSA: alg->lAlg = -8; /* XXX */; break; @@ -287,7 +377,7 @@ pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg, } static int -pack_cred_ext(WEBAUTHN_EXTENSIONS *out, fido_cred_ext_t *in) +pack_cred_ext(WEBAUTHN_EXTENSIONS *out, const fido_cred_ext_t *in) { WEBAUTHN_EXTENSION *e; WEBAUTHN_CRED_PROTECT_EXTENSION_IN *p; @@ -298,7 +388,7 @@ pack_cred_ext(WEBAUTHN_EXTENSIONS *out, fido_cred_ext_t *in) return 0; /* nothing to do */ } if (in->mask & ~(FIDO_EXT_HMAC_SECRET | FIDO_EXT_CRED_PROTECT)) { - fido_log_debug("%s: mask 0x%x", in->mask); + fido_log_debug("%s: mask 0x%x", __func__, in->mask); return -1; } if (in->mask & FIDO_EXT_HMAC_SECRET) @@ -342,103 +432,48 @@ pack_cred_ext(WEBAUTHN_EXTENSIONS *out, fido_cred_ext_t *in) } static int -unpack_fmt(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att) -{ - char *fmt; - int r; - - if ((fmt = to_utf8(att->pwszFormatType)) == NULL) { - fido_log_debug("%s: fmt", __func__); - return -1; - } - r = fido_cred_set_fmt(cred, fmt); - free(fmt); - fmt = NULL; - if (r != FIDO_OK) { - fido_log_debug("%s: fido_cred_set_fmt: %s", __func__, - fido_strerr(r)); - return -1; - } - - return 0; -} - -static int -unpack_cred_authdata(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att) -{ - int r; - - if (att->cbAuthenticatorData > SIZE_MAX) { - fido_log_debug("%s: cbAuthenticatorData", __func__); - return -1; - } - if ((r = fido_cred_set_authdata_raw(cred, att->pbAuthenticatorData, - (size_t)att->cbAuthenticatorData)) != FIDO_OK) { - fido_log_debug("%s: fido_cred_set_authdata_raw: %s", __func__, - fido_strerr(r)); - return -1; - } - - return 0; -} - -static int -unpack_cred_sig(fido_cred_t *cred, WEBAUTHN_COMMON_ATTESTATION *attr) +pack_assert_ext(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *out, + const fido_assert_ext_t *in) { - int r; + WEBAUTHN_HMAC_SECRET_SALT_VALUES *v; + WEBAUTHN_HMAC_SECRET_SALT *s; - if (attr->cbSignature > SIZE_MAX) { - fido_log_debug("%s: cbSignature", __func__); - return -1; + if (in->mask == 0) { + return 0; /* nothing to do */ } - if ((r = fido_cred_set_sig(cred, attr->pbSignature, - (size_t)attr->cbSignature)) != FIDO_OK) { - fido_log_debug("%s: fido_cred_set_sig: %s", __func__, - fido_strerr(r)); + if (in->mask != FIDO_EXT_HMAC_SECRET) { + fido_log_debug("%s: mask 0x%x", __func__, in->mask); return -1; } - - return 0; -} - -static int -unpack_x5c(fido_cred_t *cred, WEBAUTHN_COMMON_ATTESTATION *attr) -{ - int r; - - fido_log_debug("%s: %u cert(s)", __func__, attr->cX5c); - - if (attr->cX5c == 0) - return 0; /* self-attestation */ - if (attr->lAlg != WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256) { - fido_log_debug("%s: lAlg %d", __func__, attr->lAlg); - return -1; - } - if (attr->pX5c[0].cbData > SIZE_MAX) { - fido_log_debug("%s: cbData", __func__); + if (in->hmac_salt.ptr == NULL || + in->hmac_salt.len != WEBAUTHN_CTAP_ONE_HMAC_SECRET_LENGTH) { + fido_log_debug("%s: salt %p/%zu", __func__, + (const void *)in->hmac_salt.ptr, in->hmac_salt.len); return -1; } - if ((r = fido_cred_set_x509(cred, attr->pX5c[0].pbData, - (size_t)attr->pX5c[0].cbData)) != FIDO_OK) { - fido_log_debug("%s: fido_cred_set_x509: %s", __func__, - fido_strerr(r)); + if ((v = calloc(1, sizeof(*v))) == NULL || + (s = calloc(1, sizeof(*s))) == NULL) { + free(v); + fido_log_debug("%s: calloc", __func__); return -1; } + s->cbFirst = (DWORD)in->hmac_salt.len; + s->pbFirst = in->hmac_salt.ptr; + v->pGlobalHmacSalt = s; + out->pHmacSecretSaltValues = v; + out->dwFlags |= WEBAUTHN_AUTHENTICATOR_HMAC_SECRET_VALUES_FLAG; + out->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6; return 0; } static int -unpack_assert_authdata(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) +unpack_assert_authdata(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; - if (wa->cbAuthenticatorData > SIZE_MAX) { - fido_log_debug("%s: cbAuthenticatorData", __func__); - return -1; - } if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData, - (size_t)wa->cbAuthenticatorData)) != FIDO_OK) { + wa->cbAuthenticatorData)) != FIDO_OK) { fido_log_debug("%s: fido_assert_set_authdata_raw: %s", __func__, fido_strerr(r)); return -1; @@ -448,16 +483,12 @@ unpack_assert_authdata(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) } static int -unpack_assert_sig(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) +unpack_assert_sig(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; - if (wa->cbSignature > SIZE_MAX) { - fido_log_debug("%s: cbSignature", __func__); - return -1; - } if ((r = fido_assert_set_sig(assert, 0, wa->pbSignature, - (size_t)wa->cbSignature)) != FIDO_OK) { + wa->cbSignature)) != FIDO_OK) { fido_log_debug("%s: fido_assert_set_sig: %s", __func__, fido_strerr(r)); return -1; @@ -467,14 +498,10 @@ unpack_assert_sig(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) } static int -unpack_cred_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) +unpack_cred_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { - if (wa->Credential.cbId > SIZE_MAX) { - fido_log_debug("%s: Credential.cbId", __func__); - return -1; - } if (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId, - (size_t)wa->Credential.cbId) < 0) { + wa->Credential.cbId) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return -1; } @@ -483,16 +510,44 @@ unpack_cred_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) } static int -unpack_user_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) +unpack_user_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { if (wa->cbUserId == 0) return 0; /* user id absent */ - if (wa->cbUserId > SIZE_MAX) { - fido_log_debug("%s: cbUserId", __func__); + if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, + wa->cbUserId) < 0) { + fido_log_debug("%s: fido_blob_set", __func__); return -1; } - if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, - (size_t)wa->cbUserId) < 0) { + + return 0; +} + +static int +unpack_hmac_secret(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) +{ + if (wa->dwVersion != WEBAUTHN_ASSERTION_VERSION_3) { + fido_log_debug("%s: dwVersion %u", __func__, + (unsigned)wa->dwVersion); + return 0; /* proceed without hmac-secret */ + } + if (wa->pHmacSecret == NULL || + wa->pHmacSecret->cbFirst == 0 || + wa->pHmacSecret->pbFirst == NULL) { + fido_log_debug("%s: hmac-secret absent", __func__); + return 0; /* proceed without hmac-secret */ + } + if (wa->pHmacSecret->cbSecond != 0 || + wa->pHmacSecret->pbSecond != NULL) { + fido_log_debug("%s: 64-byte hmac-secret", __func__); + return 0; /* proceed without hmac-secret */ + } + if (!fido_blob_is_empty(&assert->stmt[0].hmac_secret)) { + fido_log_debug("%s: fido_blob_is_empty", __func__); + return -1; + } + if (fido_blob_set(&assert->stmt[0].hmac_secret, + wa->pHmacSecret->pbFirst, wa->pHmacSecret->cbFirst) < 0) { fido_log_debug("%s: fido_blob_set", __func__); return -1; } @@ -501,8 +556,8 @@ unpack_user_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) } static int -translate_fido_assert(struct winhello_assert *ctx, fido_assert_t *assert, - const char *pin) +translate_fido_assert(struct winhello_assert *ctx, const fido_assert_t *assert, + const char *pin, int ms) { WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt; @@ -511,11 +566,6 @@ translate_fido_assert(struct winhello_assert *ctx, fido_assert_t *assert, fido_log_debug("%s: up %d", __func__, assert->up); return FIDO_ERR_UNSUPPORTED_OPTION; } - /* not implemented */ - if (assert->ext.mask) { - fido_log_debug("%s: ext 0x%x", __func__, assert->ext.mask); - return FIDO_ERR_UNSUPPORTED_EXTENSION; - } if ((ctx->rp_id = to_utf16(assert->rp_id)) == NULL) { fido_log_debug("%s: rp_id", __func__); return FIDO_ERR_INTERNAL; @@ -527,13 +577,18 @@ translate_fido_assert(struct winhello_assert *ctx, fido_assert_t *assert, /* options */ opt = &ctx->opt; opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1; - opt->dwTimeoutMilliseconds = MAXMSEC; + opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) { fido_log_debug("%s: pack_credlist", __func__); return FIDO_ERR_INTERNAL; } - if (set_uv(&opt->dwUserVerificationRequirement, assert->uv, pin) < 0) { - fido_log_debug("%s: set_uv", __func__); + if (pack_assert_ext(opt, &assert->ext) < 0) { + fido_log_debug("%s: pack_assert_ext", __func__); + return FIDO_ERR_UNSUPPORTED_EXTENSION; + } + if (set_assert_uv(&opt->dwUserVerificationRequirement, assert->uv, + pin) < 0) { + fido_log_debug("%s: set_assert_uv", __func__); return FIDO_ERR_INTERNAL; } @@ -541,7 +596,7 @@ translate_fido_assert(struct winhello_assert *ctx, fido_assert_t *assert, } static int -translate_winhello_assert(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) +translate_winhello_assert(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) { int r; @@ -570,13 +625,18 @@ translate_winhello_assert(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa) fido_log_debug("%s: unpack_user_id", __func__); return FIDO_ERR_INTERNAL; } + if (assert->ext.mask & FIDO_EXT_HMAC_SECRET && + unpack_hmac_secret(assert, wa) < 0) { + fido_log_debug("%s: unpack_hmac_secret", __func__); + return FIDO_ERR_INTERNAL; + } return FIDO_OK; } static int -translate_fido_cred(struct winhello_cred *ctx, fido_cred_t *cred, - const char *pin) +translate_fido_cred(struct winhello_cred *ctx, const fido_cred_t *cred, + const char *pin, int ms) { WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *opt; @@ -600,7 +660,9 @@ translate_fido_cred(struct winhello_cred *ctx, fido_cred_t *cred, /* options */ opt = &ctx->opt; opt->dwVersion = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1; - opt->dwTimeoutMilliseconds = MAXMSEC; + opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; + opt->dwAttestationConveyancePreference = + WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; if (pack_credlist(&opt->CredentialList, &cred->excl) < 0) { fido_log_debug("%s: pack_credlist", __func__); return FIDO_ERR_INTERNAL; @@ -609,8 +671,9 @@ translate_fido_cred(struct winhello_cred *ctx, fido_cred_t *cred, fido_log_debug("%s: pack_cred_ext", __func__); return FIDO_ERR_UNSUPPORTED_EXTENSION; } - if (set_uv(&opt->dwUserVerificationRequirement, cred->uv, pin) < 0) { - fido_log_debug("%s: set_uv", __func__); + if (set_cred_uv(&opt->dwUserVerificationRequirement, (cred->ext.mask & + FIDO_EXT_CRED_PROTECT) ? FIDO_OPT_TRUE : cred->uv, pin) < 0) { + fido_log_debug("%s: set_cred_uv", __func__); return FIDO_ERR_INTERNAL; } if (cred->rk == FIDO_OPT_TRUE) { @@ -621,66 +684,78 @@ translate_fido_cred(struct winhello_cred *ctx, fido_cred_t *cred, } static int -translate_winhello_cred(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att) +decode_attobj(const cbor_item_t *key, const cbor_item_t *val, void *arg) { - if (unpack_fmt(cred, att) < 0) { - fido_log_debug("%s: unpack_fmt", __func__); - return FIDO_ERR_INTERNAL; - } - if (unpack_cred_authdata(cred, att) < 0) { - fido_log_debug("%s: unpack_cred_authdata", __func__); - return FIDO_ERR_INTERNAL; + fido_cred_t *cred = 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 fail; } - switch (att->dwAttestationDecodeType) { - case WEBAUTHN_ATTESTATION_DECODE_NONE: - if (att->pvAttestationDecode != NULL) { - fido_log_debug("%s: pvAttestationDecode", __func__); - return FIDO_ERR_INTERNAL; + if (!strcmp(name, "fmt")) { + if (cbor_decode_fmt(val, &cred->fmt) < 0) { + fido_log_debug("%s: cbor_decode_fmt", __func__); + goto fail; } - break; - case WEBAUTHN_ATTESTATION_DECODE_COMMON: - if (att->pvAttestationDecode == NULL) { - fido_log_debug("%s: pvAttestationDecode", __func__); - return FIDO_ERR_INTERNAL; + } else if (!strcmp(name, "attStmt")) { + if (cbor_decode_attstmt(val, &cred->attstmt) < 0) { + fido_log_debug("%s: cbor_decode_attstmt", __func__); + goto fail; } - if (unpack_cred_sig(cred, att->pvAttestationDecode) < 0) { - fido_log_debug("%s: unpack_cred_sig", __func__); - return FIDO_ERR_INTERNAL; + } else if (!strcmp(name, "authData")) { + if (fido_blob_decode(val, &cred->authdata_raw) < 0) { + fido_log_debug("%s: fido_blob_decode", __func__); + goto fail; } - if (unpack_x5c(cred, att->pvAttestationDecode) < 0) { - fido_log_debug("%s: unpack_x5c", __func__); - return FIDO_ERR_INTERNAL; + if (cbor_decode_cred_authdata(val, cred->type, + &cred->authdata_cbor, &cred->authdata, &cred->attcred, + &cred->authdata_ext) < 0) { + fido_log_debug("%s: cbor_decode_cred_authdata", + __func__); + goto fail; } - break; - default: - fido_log_debug("%s: dwAttestationDecodeType: %u", __func__, - att->dwAttestationDecodeType); - return FIDO_ERR_INTERNAL; } - return FIDO_OK; + ok = 0; +fail: + free(name); + + return (ok); } static int -winhello_manifest(BOOL *present) +translate_winhello_cred(fido_cred_t *cred, + const WEBAUTHN_CREDENTIAL_ATTESTATION *att) { - DWORD n; - HRESULT hr; - int r = FIDO_OK; + cbor_item_t *item = NULL; + struct cbor_load_result cbor; + int r = FIDO_ERR_INTERNAL; - if ((n = WebAuthNGetApiVersionNumber()) < 1) { - fido_log_debug("%s: unsupported api %u", __func__, n); - return FIDO_ERR_INTERNAL; + if (att->pbAttestationObject == NULL) { + fido_log_debug("%s: pbAttestationObject", __func__); + goto fail; } - fido_log_debug("%s: api version %u", __func__, n); - hr = WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable(present); - if (hr != S_OK) { - r = to_fido(hr); - fido_log_debug("%s: %ls -> %s", __func__, - WebAuthNGetErrorName(hr), fido_strerr(r)); + if ((item = cbor_load(att->pbAttestationObject, + att->cbAttestationObject, &cbor)) == NULL) { + fido_log_debug("%s: cbor_load", __func__); + goto fail; + } + if (cbor_isa_map(item) == false || + cbor_map_is_definite(item) == false || + cbor_map_iter(item, cred, decode_attobj) < 0) { + fido_log_debug("%s: cbor type", __func__); + goto fail; } + r = FIDO_OK; +fail: + if (item != NULL) + cbor_decref(&item); + return r; } @@ -690,12 +765,11 @@ winhello_get_assert(HWND w, struct winhello_assert *ctx) HRESULT hr; int r = FIDO_OK; - hr = WebAuthNAuthenticatorGetAssertion(w, ctx->rp_id, &ctx->cd, - &ctx->opt, &ctx->assert); - if (hr != S_OK) { + if ((hr = webauthn_get_assert(w, ctx->rp_id, &ctx->cd, &ctx->opt, + &ctx->assert)) != S_OK) { r = to_fido(hr); - fido_log_debug("%s: %ls -> %s", __func__, - WebAuthNGetErrorName(hr), fido_strerr(r)); + fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), + fido_strerr(r)); } return r; @@ -707,12 +781,11 @@ winhello_make_cred(HWND w, struct winhello_cred *ctx) HRESULT hr; int r = FIDO_OK; - hr = WebAuthNAuthenticatorMakeCredential(w, &ctx->rp, &ctx->user, - &ctx->cose, &ctx->cd, &ctx->opt, &ctx->att); - if (hr != S_OK) { + if ((hr = webauthn_make_cred(w, &ctx->rp, &ctx->user, &ctx->cose, + &ctx->cd, &ctx->opt, &ctx->att)) != S_OK) { r = to_fido(hr); - fido_log_debug("%s: %ls -> %s", __func__, - WebAuthNGetErrorName(hr), fido_strerr(r)); + fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), + fido_strerr(r)); } return r; @@ -724,10 +797,13 @@ winhello_assert_free(struct winhello_assert *ctx) if (ctx == NULL) return; if (ctx->assert != NULL) - WebAuthNFreeAssertion(ctx->assert); + webauthn_free_assert(ctx->assert); free(ctx->rp_id); free(ctx->opt.CredentialList.pCredentials); + if (ctx->opt.pHmacSecretSaltValues != NULL) + free(ctx->opt.pHmacSecretSaltValues->pGlobalHmacSalt); + free(ctx->opt.pHmacSecretSaltValues); free(ctx); } @@ -737,7 +813,7 @@ winhello_cred_free(struct winhello_cred *ctx) if (ctx == NULL) return; if (ctx->att != NULL) - WebAuthNFreeCredentialAttestation(ctx->att); + webauthn_free_attest(ctx->att); free(ctx->rp_id); free(ctx->rp_name); @@ -757,8 +833,6 @@ winhello_cred_free(struct winhello_cred *ctx) int fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) { - int r; - BOOL present; fido_dev_info_t *di; if (ilen == 0) { @@ -767,13 +841,9 @@ fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) if (devlist == NULL) { return FIDO_ERR_INVALID_ARGUMENT; } - if ((r = winhello_manifest(&present)) != FIDO_OK) { - fido_log_debug("%s: winhello_manifest", __func__); - return r; - } - if (present == false) { - fido_log_debug("%s: not present", __func__); - return FIDO_OK; + if (!webauthn_loaded && webauthn_load() < 0) { + fido_log_debug("%s: webauthn_load", __func__); + return FIDO_OK; /* not an error */ } di = &devlist[*olen]; @@ -799,9 +869,12 @@ fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) int fido_winhello_open(fido_dev_t *dev) { + if (!webauthn_loaded && webauthn_load() < 0) { + fido_log_debug("%s: webauthn_load", __func__); + return FIDO_ERR_INTERNAL; + } if (dev->flags != 0) return FIDO_ERR_INVALID_ARGUMENT; - dev->attr.flags = FIDO_CAP_CBOR | FIDO_CAP_WINK; dev->flags = FIDO_DEV_WINHELLO | FIDO_DEV_CRED_PROT | FIDO_DEV_PIN_SET; @@ -826,7 +899,7 @@ fido_winhello_cancel(fido_dev_t *dev) int fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert, - const char *pin) + const char *pin, int ms) { HWND w; struct winhello_assert *ctx; @@ -834,19 +907,24 @@ fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert, (void)dev; + fido_assert_reset_rx(assert); + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } if ((w = GetForegroundWindow()) == NULL) { fido_log_debug("%s: GetForegroundWindow", __func__); - goto fail; + if ((w = GetTopWindow(NULL)) == NULL) { + fido_log_debug("%s: GetTopWindow", __func__); + goto fail; + } } - if ((r = translate_fido_assert(ctx, assert, pin)) != FIDO_OK) { + if ((r = translate_fido_assert(ctx, assert, pin, ms)) != FIDO_OK) { fido_log_debug("%s: translate_fido_assert", __func__); goto fail; } - if ((r = winhello_get_assert(w, ctx)) != S_OK) { + if ((r = winhello_get_assert(w, ctx)) != FIDO_OK) { fido_log_debug("%s: winhello_get_assert", __func__); goto fail; } @@ -867,16 +945,16 @@ fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) const char *v[3] = { "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE" }; const char *e[2] = { "credProtect", "hmac-secret" }; const char *t[2] = { "nfc", "usb" }; - const char *o[4] = { "rk", "up", "plat", "clientPin" }; + const char *o[4] = { "rk", "up", "uv", "plat" }; (void)dev; fido_cbor_info_reset(ci); - if (to_fido_str_array(&ci->versions, v, nitems(v)) < 0 || - to_fido_str_array(&ci->extensions, e, nitems(e)) < 0 || - to_fido_str_array(&ci->transports, t, nitems(t)) < 0) { - fido_log_debug("%s: to_fido_str_array", __func__); + if (fido_str_array_pack(&ci->versions, v, nitems(v)) < 0 || + fido_str_array_pack(&ci->extensions, e, nitems(e)) < 0 || + fido_str_array_pack(&ci->transports, t, nitems(t)) < 0) { + fido_log_debug("%s: fido_str_array_pack", __func__); return FIDO_ERR_INTERNAL; } if ((ci->options.name = calloc(nitems(o), sizeof(char *))) == NULL || @@ -897,7 +975,8 @@ fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) } int -fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) +fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin, + int ms) { HWND w; struct winhello_cred *ctx; @@ -905,15 +984,20 @@ fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin) (void)dev; + fido_cred_reset_rx(cred); + if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { fido_log_debug("%s: calloc", __func__); goto fail; } if ((w = GetForegroundWindow()) == NULL) { fido_log_debug("%s: GetForegroundWindow", __func__); - goto fail; + if ((w = GetTopWindow(NULL)) == NULL) { + fido_log_debug("%s: GetTopWindow", __func__); + goto fail; + } } - if ((r = translate_fido_cred(ctx, cred, pin)) != FIDO_OK) { + if ((r = translate_fido_cred(ctx, cred, pin, ms)) != FIDO_OK) { fido_log_debug("%s: translate_fido_cred", __func__); goto fail; } |