10afa8e06SEd Maste /* 22ccfa855SEd Maste * Copyright (c) 2021-2022 Yubico AB. All rights reserved. 30afa8e06SEd Maste * Use of this source code is governed by a BSD-style 40afa8e06SEd Maste * license that can be found in the LICENSE file. 52ccfa855SEd Maste * SPDX-License-Identifier: BSD-2-Clause 60afa8e06SEd Maste */ 70afa8e06SEd Maste 80afa8e06SEd Maste #include <sys/types.h> 90afa8e06SEd Maste 100afa8e06SEd Maste #include <stdlib.h> 110afa8e06SEd Maste #include <windows.h> 120afa8e06SEd Maste 130afa8e06SEd Maste #include "fido.h" 14f540a430SEd Maste #include "webauthn.h" 150afa8e06SEd Maste 163e696dfbSEd Maste #ifndef NTE_INVALID_PARAMETER 173e696dfbSEd Maste #define NTE_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80090027) 183e696dfbSEd Maste #endif 193e696dfbSEd Maste #ifndef NTE_NOT_SUPPORTED 203e696dfbSEd Maste #define NTE_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80090029) 213e696dfbSEd Maste #endif 223e696dfbSEd Maste #ifndef NTE_DEVICE_NOT_FOUND 233e696dfbSEd Maste #define NTE_DEVICE_NOT_FOUND _HRESULT_TYPEDEF_(0x80090035) 243e696dfbSEd Maste #endif 25*60a517b6SEd Maste #ifndef NTE_USER_CANCELLED 26*60a517b6SEd Maste #define NTE_USER_CANCELLED _HRESULT_TYPEDEF_(0x80090036L) 27*60a517b6SEd Maste #endif 283e696dfbSEd Maste 290afa8e06SEd Maste #define MAXCHARS 128 300afa8e06SEd Maste #define MAXCREDS 128 310afa8e06SEd Maste #define MAXMSEC 6000 * 1000 320afa8e06SEd Maste #define VENDORID 0x045e 330afa8e06SEd Maste #define PRODID 0x0001 340afa8e06SEd Maste 350afa8e06SEd Maste struct winhello_assert { 360afa8e06SEd Maste WEBAUTHN_CLIENT_DATA cd; 370afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS opt; 380afa8e06SEd Maste WEBAUTHN_ASSERTION *assert; 390afa8e06SEd Maste wchar_t *rp_id; 40*60a517b6SEd Maste wchar_t *appid; 410afa8e06SEd Maste }; 420afa8e06SEd Maste 430afa8e06SEd Maste struct winhello_cred { 440afa8e06SEd Maste WEBAUTHN_RP_ENTITY_INFORMATION rp; 450afa8e06SEd Maste WEBAUTHN_USER_ENTITY_INFORMATION user; 460afa8e06SEd Maste WEBAUTHN_COSE_CREDENTIAL_PARAMETER alg; 470afa8e06SEd Maste WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose; 480afa8e06SEd Maste WEBAUTHN_CLIENT_DATA cd; 490afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS opt; 500afa8e06SEd Maste WEBAUTHN_CREDENTIAL_ATTESTATION *att; 510afa8e06SEd Maste wchar_t *rp_id; 520afa8e06SEd Maste wchar_t *rp_name; 530afa8e06SEd Maste wchar_t *user_name; 540afa8e06SEd Maste wchar_t *user_icon; 550afa8e06SEd Maste wchar_t *display_name; 560afa8e06SEd Maste }; 570afa8e06SEd Maste 583e696dfbSEd Maste typedef DWORD WINAPI webauthn_get_api_version_t(void); 593e696dfbSEd Maste typedef PCWSTR WINAPI webauthn_strerr_t(HRESULT); 603e696dfbSEd Maste typedef HRESULT WINAPI webauthn_get_assert_t(HWND, LPCWSTR, 61f540a430SEd Maste PCWEBAUTHN_CLIENT_DATA, 62f540a430SEd Maste PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, 63f540a430SEd Maste PWEBAUTHN_ASSERTION *); 643e696dfbSEd Maste typedef HRESULT WINAPI webauthn_make_cred_t(HWND, 65f540a430SEd Maste PCWEBAUTHN_RP_ENTITY_INFORMATION, 66f540a430SEd Maste PCWEBAUTHN_USER_ENTITY_INFORMATION, 67f540a430SEd Maste PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS, 68f540a430SEd Maste PCWEBAUTHN_CLIENT_DATA, 69f540a430SEd Maste PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS, 70f540a430SEd Maste PWEBAUTHN_CREDENTIAL_ATTESTATION *); 713e696dfbSEd Maste typedef void WINAPI webauthn_free_assert_t(PWEBAUTHN_ASSERTION); 723e696dfbSEd Maste typedef void WINAPI webauthn_free_attest_t(PWEBAUTHN_CREDENTIAL_ATTESTATION); 733e696dfbSEd Maste 743e696dfbSEd Maste static TLS BOOL webauthn_loaded; 753e696dfbSEd Maste static TLS HMODULE webauthn_handle; 763e696dfbSEd Maste static TLS webauthn_get_api_version_t *webauthn_get_api_version; 773e696dfbSEd Maste static TLS webauthn_strerr_t *webauthn_strerr; 783e696dfbSEd Maste static TLS webauthn_get_assert_t *webauthn_get_assert; 793e696dfbSEd Maste static TLS webauthn_make_cred_t *webauthn_make_cred; 803e696dfbSEd Maste static TLS webauthn_free_assert_t *webauthn_free_assert; 813e696dfbSEd Maste static TLS webauthn_free_attest_t *webauthn_free_attest; 82f540a430SEd Maste 83f540a430SEd Maste static int 84f540a430SEd Maste webauthn_load(void) 85f540a430SEd Maste { 863e696dfbSEd Maste DWORD n = 1; 873e696dfbSEd Maste 88f540a430SEd Maste if (webauthn_loaded || webauthn_handle != NULL) { 89f540a430SEd Maste fido_log_debug("%s: already loaded", __func__); 90f540a430SEd Maste return -1; 91f540a430SEd Maste } 922ccfa855SEd Maste if ((webauthn_handle = LoadLibrary(TEXT("webauthn.dll"))) == NULL) { 93f540a430SEd Maste fido_log_debug("%s: LoadLibrary", __func__); 94f540a430SEd Maste return -1; 95f540a430SEd Maste } 96f540a430SEd Maste 973e696dfbSEd Maste if ((webauthn_get_api_version = 983e696dfbSEd Maste (webauthn_get_api_version_t *)GetProcAddress(webauthn_handle, 99f540a430SEd Maste "WebAuthNGetApiVersionNumber")) == NULL) { 100f540a430SEd Maste fido_log_debug("%s: WebAuthNGetApiVersionNumber", __func__); 1013e696dfbSEd Maste /* WebAuthNGetApiVersionNumber might not exist */ 1023e696dfbSEd Maste } 1033e696dfbSEd Maste if (webauthn_get_api_version != NULL && 1043e696dfbSEd Maste (n = webauthn_get_api_version()) < 1) { 1053e696dfbSEd Maste fido_log_debug("%s: unsupported api %lu", __func__, (u_long)n); 106f540a430SEd Maste goto fail; 107f540a430SEd Maste } 1083e696dfbSEd Maste fido_log_debug("%s: api version %lu", __func__, (u_long)n); 1093e696dfbSEd Maste if ((webauthn_strerr = 1103e696dfbSEd Maste (webauthn_strerr_t *)GetProcAddress(webauthn_handle, 111f540a430SEd Maste "WebAuthNGetErrorName")) == NULL) { 112f540a430SEd Maste fido_log_debug("%s: WebAuthNGetErrorName", __func__); 113f540a430SEd Maste goto fail; 114f540a430SEd Maste } 1153e696dfbSEd Maste if ((webauthn_get_assert = 1163e696dfbSEd Maste (webauthn_get_assert_t *)GetProcAddress(webauthn_handle, 117f540a430SEd Maste "WebAuthNAuthenticatorGetAssertion")) == NULL) { 118f540a430SEd Maste fido_log_debug("%s: WebAuthNAuthenticatorGetAssertion", 119f540a430SEd Maste __func__); 120f540a430SEd Maste goto fail; 121f540a430SEd Maste } 1223e696dfbSEd Maste if ((webauthn_make_cred = 1233e696dfbSEd Maste (webauthn_make_cred_t *)GetProcAddress(webauthn_handle, 124f540a430SEd Maste "WebAuthNAuthenticatorMakeCredential")) == NULL) { 125f540a430SEd Maste fido_log_debug("%s: WebAuthNAuthenticatorMakeCredential", 126f540a430SEd Maste __func__); 127f540a430SEd Maste goto fail; 128f540a430SEd Maste } 1293e696dfbSEd Maste if ((webauthn_free_assert = 1303e696dfbSEd Maste (webauthn_free_assert_t *)GetProcAddress(webauthn_handle, 131f540a430SEd Maste "WebAuthNFreeAssertion")) == NULL) { 132f540a430SEd Maste fido_log_debug("%s: WebAuthNFreeAssertion", __func__); 133f540a430SEd Maste goto fail; 134f540a430SEd Maste } 1353e696dfbSEd Maste if ((webauthn_free_attest = 1363e696dfbSEd Maste (webauthn_free_attest_t *)GetProcAddress(webauthn_handle, 137f540a430SEd Maste "WebAuthNFreeCredentialAttestation")) == NULL) { 138f540a430SEd Maste fido_log_debug("%s: WebAuthNFreeCredentialAttestation", 139f540a430SEd Maste __func__); 140f540a430SEd Maste goto fail; 141f540a430SEd Maste } 142f540a430SEd Maste 143f540a430SEd Maste webauthn_loaded = true; 144f540a430SEd Maste 145f540a430SEd Maste return 0; 146f540a430SEd Maste fail: 147f540a430SEd Maste fido_log_debug("%s: GetProcAddress", __func__); 148f540a430SEd Maste webauthn_get_api_version = NULL; 149f540a430SEd Maste webauthn_strerr = NULL; 150f540a430SEd Maste webauthn_get_assert = NULL; 151f540a430SEd Maste webauthn_make_cred = NULL; 152f540a430SEd Maste webauthn_free_assert = NULL; 153f540a430SEd Maste webauthn_free_attest = NULL; 154f540a430SEd Maste FreeLibrary(webauthn_handle); 155f540a430SEd Maste webauthn_handle = NULL; 156f540a430SEd Maste 157f540a430SEd Maste return -1; 158f540a430SEd Maste } 159f540a430SEd Maste 1600afa8e06SEd Maste static wchar_t * 1610afa8e06SEd Maste to_utf16(const char *utf8) 1620afa8e06SEd Maste { 1630afa8e06SEd Maste int nch; 1640afa8e06SEd Maste wchar_t *utf16; 1650afa8e06SEd Maste 1660afa8e06SEd Maste if (utf8 == NULL) { 1670afa8e06SEd Maste fido_log_debug("%s: NULL", __func__); 1680afa8e06SEd Maste return NULL; 1690afa8e06SEd Maste } 1700afa8e06SEd Maste if ((nch = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) < 1 || 1710afa8e06SEd Maste (size_t)nch > MAXCHARS) { 1720afa8e06SEd Maste fido_log_debug("%s: MultiByteToWideChar %d", __func__, nch); 1730afa8e06SEd Maste return NULL; 1740afa8e06SEd Maste } 1750afa8e06SEd Maste if ((utf16 = calloc((size_t)nch, sizeof(*utf16))) == NULL) { 1760afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 1770afa8e06SEd Maste return NULL; 1780afa8e06SEd Maste } 1790afa8e06SEd Maste if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, nch) != nch) { 1800afa8e06SEd Maste fido_log_debug("%s: MultiByteToWideChar", __func__); 1810afa8e06SEd Maste free(utf16); 1820afa8e06SEd Maste return NULL; 1830afa8e06SEd Maste } 1840afa8e06SEd Maste 1850afa8e06SEd Maste return utf16; 1860afa8e06SEd Maste } 1870afa8e06SEd Maste 1880afa8e06SEd Maste static int 1890afa8e06SEd Maste to_fido(HRESULT hr) 1900afa8e06SEd Maste { 1910afa8e06SEd Maste switch (hr) { 1920afa8e06SEd Maste case NTE_NOT_SUPPORTED: 1930afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_OPTION; 1940afa8e06SEd Maste case NTE_INVALID_PARAMETER: 1950afa8e06SEd Maste return FIDO_ERR_INVALID_PARAMETER; 1960afa8e06SEd Maste case NTE_TOKEN_KEYSET_STORAGE_FULL: 1970afa8e06SEd Maste return FIDO_ERR_KEY_STORE_FULL; 1980afa8e06SEd Maste case NTE_DEVICE_NOT_FOUND: 1990afa8e06SEd Maste case NTE_NOT_FOUND: 2000afa8e06SEd Maste return FIDO_ERR_NOT_ALLOWED; 201*60a517b6SEd Maste case __HRESULT_FROM_WIN32(ERROR_CANCELLED): 202*60a517b6SEd Maste case NTE_USER_CANCELLED: 203*60a517b6SEd Maste return FIDO_ERR_OPERATION_DENIED; 2040afa8e06SEd Maste default: 2053e696dfbSEd Maste fido_log_debug("%s: hr=0x%lx", __func__, (u_long)hr); 2060afa8e06SEd Maste return FIDO_ERR_INTERNAL; 2070afa8e06SEd Maste } 2080afa8e06SEd Maste } 2090afa8e06SEd Maste 2100afa8e06SEd Maste static int 2110afa8e06SEd Maste pack_cd(WEBAUTHN_CLIENT_DATA *out, const fido_blob_t *in) 2120afa8e06SEd Maste { 2130afa8e06SEd Maste if (in->ptr == NULL) { 2140afa8e06SEd Maste fido_log_debug("%s: NULL", __func__); 2150afa8e06SEd Maste return -1; 2160afa8e06SEd Maste } 2170afa8e06SEd Maste if (in->len > ULONG_MAX) { 2180afa8e06SEd Maste fido_log_debug("%s: in->len=%zu", __func__, in->len); 2190afa8e06SEd Maste return -1; 2200afa8e06SEd Maste } 2210afa8e06SEd Maste out->dwVersion = WEBAUTHN_CLIENT_DATA_CURRENT_VERSION; 2220afa8e06SEd Maste out->cbClientDataJSON = (DWORD)in->len; 2230afa8e06SEd Maste out->pbClientDataJSON = in->ptr; 2240afa8e06SEd Maste out->pwszHashAlgId = WEBAUTHN_HASH_ALGORITHM_SHA_256; 2250afa8e06SEd Maste 2260afa8e06SEd Maste return 0; 2270afa8e06SEd Maste } 2280afa8e06SEd Maste 2290afa8e06SEd Maste static int 2300afa8e06SEd Maste pack_credlist(WEBAUTHN_CREDENTIALS *out, const fido_blob_array_t *in) 2310afa8e06SEd Maste { 2320afa8e06SEd Maste WEBAUTHN_CREDENTIAL *c; 2330afa8e06SEd Maste 2340afa8e06SEd Maste if (in->len == 0) { 2350afa8e06SEd Maste return 0; /* nothing to do */ 2360afa8e06SEd Maste } 2370afa8e06SEd Maste if (in->len > MAXCREDS) { 2380afa8e06SEd Maste fido_log_debug("%s: in->len=%zu", __func__, in->len); 2390afa8e06SEd Maste return -1; 2400afa8e06SEd Maste } 2410afa8e06SEd Maste if ((out->pCredentials = calloc(in->len, sizeof(*c))) == NULL) { 2420afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 2430afa8e06SEd Maste return -1; 2440afa8e06SEd Maste } 2450afa8e06SEd Maste out->cCredentials = (DWORD)in->len; 2460afa8e06SEd Maste for (size_t i = 0; i < in->len; i++) { 2470afa8e06SEd Maste if (in->ptr[i].len > ULONG_MAX) { 2480afa8e06SEd Maste fido_log_debug("%s: %zu", __func__, in->ptr[i].len); 2490afa8e06SEd Maste return -1; 2500afa8e06SEd Maste } 2510afa8e06SEd Maste c = &out->pCredentials[i]; 2520afa8e06SEd Maste c->dwVersion = WEBAUTHN_CREDENTIAL_CURRENT_VERSION; 2530afa8e06SEd Maste c->cbId = (DWORD)in->ptr[i].len; 2540afa8e06SEd Maste c->pbId = in->ptr[i].ptr; 2550afa8e06SEd Maste c->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; 2560afa8e06SEd Maste } 2570afa8e06SEd Maste 2580afa8e06SEd Maste return 0; 2590afa8e06SEd Maste } 2600afa8e06SEd Maste 2610afa8e06SEd Maste static int 2623e696dfbSEd Maste set_cred_uv(DWORD *out, fido_opt_t uv, const char *pin) 2630afa8e06SEd Maste { 2640afa8e06SEd Maste if (pin) { 2650afa8e06SEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 2660afa8e06SEd Maste return 0; 2670afa8e06SEd Maste } 2680afa8e06SEd Maste 2690afa8e06SEd Maste switch (uv) { 2700afa8e06SEd Maste case FIDO_OPT_OMIT: 2713e696dfbSEd Maste case FIDO_OPT_FALSE: 2723e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 2733e696dfbSEd Maste break; 2743e696dfbSEd Maste case FIDO_OPT_TRUE: 2753e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 2763e696dfbSEd Maste break; 2773e696dfbSEd Maste } 2783e696dfbSEd Maste 2793e696dfbSEd Maste return 0; 2803e696dfbSEd Maste } 2813e696dfbSEd Maste 2823e696dfbSEd Maste static int 2833e696dfbSEd Maste set_assert_uv(DWORD *out, fido_opt_t uv, const char *pin) 2843e696dfbSEd Maste { 2853e696dfbSEd Maste if (pin) { 2863e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 2873e696dfbSEd Maste return 0; 2883e696dfbSEd Maste } 2893e696dfbSEd Maste 2903e696dfbSEd Maste switch (uv) { 2913e696dfbSEd Maste case FIDO_OPT_OMIT: 2923e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; 2930afa8e06SEd Maste break; 2940afa8e06SEd Maste case FIDO_OPT_FALSE: 2950afa8e06SEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 2960afa8e06SEd Maste break; 2970afa8e06SEd Maste case FIDO_OPT_TRUE: 2980afa8e06SEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 2990afa8e06SEd Maste break; 3000afa8e06SEd Maste } 3010afa8e06SEd Maste 3020afa8e06SEd Maste return 0; 3030afa8e06SEd Maste } 3040afa8e06SEd Maste 3050afa8e06SEd Maste static int 3060afa8e06SEd Maste pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out, 307f540a430SEd Maste const fido_rp_t *in) 3080afa8e06SEd Maste { 3090afa8e06SEd Maste /* keep non-const copies of pwsz* for free() */ 3100afa8e06SEd Maste out->dwVersion = WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION; 3110afa8e06SEd Maste if ((out->pwszId = *id = to_utf16(in->id)) == NULL) { 3120afa8e06SEd Maste fido_log_debug("%s: id", __func__); 3130afa8e06SEd Maste return -1; 3140afa8e06SEd Maste } 3150afa8e06SEd Maste if (in->name && (out->pwszName = *name = to_utf16(in->name)) == NULL) { 3160afa8e06SEd Maste fido_log_debug("%s: name", __func__); 3170afa8e06SEd Maste return -1; 3180afa8e06SEd Maste } 3190afa8e06SEd Maste return 0; 3200afa8e06SEd Maste } 3210afa8e06SEd Maste 3220afa8e06SEd Maste static int 3230afa8e06SEd Maste pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name, 324f540a430SEd Maste WEBAUTHN_USER_ENTITY_INFORMATION *out, const fido_user_t *in) 3250afa8e06SEd Maste { 3260afa8e06SEd Maste if (in->id.ptr == NULL || in->id.len > ULONG_MAX) { 3270afa8e06SEd Maste fido_log_debug("%s: id", __func__); 3280afa8e06SEd Maste return -1; 3290afa8e06SEd Maste } 3300afa8e06SEd Maste out->dwVersion = WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION; 3310afa8e06SEd Maste out->cbId = (DWORD)in->id.len; 3320afa8e06SEd Maste out->pbId = in->id.ptr; 3330afa8e06SEd Maste /* keep non-const copies of pwsz* for free() */ 3340afa8e06SEd Maste if (in->name != NULL) { 3350afa8e06SEd Maste if ((out->pwszName = *name = to_utf16(in->name)) == NULL) { 3360afa8e06SEd Maste fido_log_debug("%s: name", __func__); 3370afa8e06SEd Maste return -1; 3380afa8e06SEd Maste } 3390afa8e06SEd Maste } 3400afa8e06SEd Maste if (in->icon != NULL) { 3410afa8e06SEd Maste if ((out->pwszIcon = *icon = to_utf16(in->icon)) == NULL) { 3420afa8e06SEd Maste fido_log_debug("%s: icon", __func__); 3430afa8e06SEd Maste return -1; 3440afa8e06SEd Maste } 3450afa8e06SEd Maste } 3460afa8e06SEd Maste if (in->display_name != NULL) { 3470afa8e06SEd Maste if ((out->pwszDisplayName = *display_name = 3480afa8e06SEd Maste to_utf16(in->display_name)) == NULL) { 3490afa8e06SEd Maste fido_log_debug("%s: display_name", __func__); 3500afa8e06SEd Maste return -1; 3510afa8e06SEd Maste } 3520afa8e06SEd Maste } 3530afa8e06SEd Maste 3540afa8e06SEd Maste return 0; 3550afa8e06SEd Maste } 3560afa8e06SEd Maste 3570afa8e06SEd Maste static int 3580afa8e06SEd Maste pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg, 3590afa8e06SEd Maste WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *cose, int type) 3600afa8e06SEd Maste { 3610afa8e06SEd Maste switch (type) { 3620afa8e06SEd Maste case COSE_ES256: 3630afa8e06SEd Maste alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256; 3640afa8e06SEd Maste break; 3652ccfa855SEd Maste case COSE_ES384: 3662ccfa855SEd Maste alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P384_WITH_SHA384; 3672ccfa855SEd Maste break; 3680afa8e06SEd Maste case COSE_EDDSA: 3690afa8e06SEd Maste alg->lAlg = -8; /* XXX */; 3700afa8e06SEd Maste break; 3710afa8e06SEd Maste case COSE_RS256: 3720afa8e06SEd Maste alg->lAlg = WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256; 3730afa8e06SEd Maste break; 3740afa8e06SEd Maste default: 3750afa8e06SEd Maste fido_log_debug("%s: type %d", __func__, type); 3760afa8e06SEd Maste return -1; 3770afa8e06SEd Maste } 3780afa8e06SEd Maste alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION; 3790afa8e06SEd Maste alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; 3800afa8e06SEd Maste cose->cCredentialParameters = 1; 3810afa8e06SEd Maste cose->pCredentialParameters = alg; 3820afa8e06SEd Maste 3830afa8e06SEd Maste return 0; 3840afa8e06SEd Maste } 3850afa8e06SEd Maste 3860afa8e06SEd Maste static int 387f540a430SEd Maste pack_cred_ext(WEBAUTHN_EXTENSIONS *out, const fido_cred_ext_t *in) 3880afa8e06SEd Maste { 3890afa8e06SEd Maste WEBAUTHN_EXTENSION *e; 3900afa8e06SEd Maste WEBAUTHN_CRED_PROTECT_EXTENSION_IN *p; 3910afa8e06SEd Maste BOOL *b; 3920afa8e06SEd Maste size_t n = 0, i = 0; 3930afa8e06SEd Maste 3940afa8e06SEd Maste if (in->mask == 0) { 3950afa8e06SEd Maste return 0; /* nothing to do */ 3960afa8e06SEd Maste } 3970afa8e06SEd Maste if (in->mask & ~(FIDO_EXT_HMAC_SECRET | FIDO_EXT_CRED_PROTECT)) { 3983e696dfbSEd Maste fido_log_debug("%s: mask 0x%x", __func__, in->mask); 3990afa8e06SEd Maste return -1; 4000afa8e06SEd Maste } 4010afa8e06SEd Maste if (in->mask & FIDO_EXT_HMAC_SECRET) 4020afa8e06SEd Maste n++; 4030afa8e06SEd Maste if (in->mask & FIDO_EXT_CRED_PROTECT) 4040afa8e06SEd Maste n++; 4050afa8e06SEd Maste if ((out->pExtensions = calloc(n, sizeof(*e))) == NULL) { 4060afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 4070afa8e06SEd Maste return -1; 4080afa8e06SEd Maste } 4090afa8e06SEd Maste out->cExtensions = (DWORD)n; 4100afa8e06SEd Maste if (in->mask & FIDO_EXT_HMAC_SECRET) { 411*60a517b6SEd Maste /* 412*60a517b6SEd Maste * NOTE: webauthn.dll ignores requests to enable hmac-secret 413*60a517b6SEd Maste * unless a discoverable credential is also requested. 414*60a517b6SEd Maste */ 4150afa8e06SEd Maste if ((b = calloc(1, sizeof(*b))) == NULL) { 4160afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 4170afa8e06SEd Maste return -1; 4180afa8e06SEd Maste } 4190afa8e06SEd Maste *b = true; 4200afa8e06SEd Maste e = &out->pExtensions[i]; 4210afa8e06SEd Maste e->pwszExtensionIdentifier = 4220afa8e06SEd Maste WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET; 4230afa8e06SEd Maste e->pvExtension = b; 4240afa8e06SEd Maste e->cbExtension = sizeof(*b); 4250afa8e06SEd Maste i++; 4260afa8e06SEd Maste } 4270afa8e06SEd Maste if (in->mask & FIDO_EXT_CRED_PROTECT) { 4280afa8e06SEd Maste if ((p = calloc(1, sizeof(*p))) == NULL) { 4290afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 4300afa8e06SEd Maste return -1; 4310afa8e06SEd Maste } 4320afa8e06SEd Maste p->dwCredProtect = (DWORD)in->prot; 4330afa8e06SEd Maste p->bRequireCredProtect = true; 4340afa8e06SEd Maste e = &out->pExtensions[i]; 4350afa8e06SEd Maste e->pwszExtensionIdentifier = 4360afa8e06SEd Maste WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT; 4370afa8e06SEd Maste e->pvExtension = p; 4380afa8e06SEd Maste e->cbExtension = sizeof(*p); 4390afa8e06SEd Maste i++; 4400afa8e06SEd Maste } 4410afa8e06SEd Maste 4420afa8e06SEd Maste return 0; 4430afa8e06SEd Maste } 4440afa8e06SEd Maste 4450afa8e06SEd Maste static int 4462ccfa855SEd Maste pack_assert_ext(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *out, 4472ccfa855SEd Maste const fido_assert_ext_t *in) 4482ccfa855SEd Maste { 4492ccfa855SEd Maste WEBAUTHN_HMAC_SECRET_SALT_VALUES *v; 4502ccfa855SEd Maste WEBAUTHN_HMAC_SECRET_SALT *s; 4512ccfa855SEd Maste 4522ccfa855SEd Maste if (in->mask == 0) { 4532ccfa855SEd Maste return 0; /* nothing to do */ 4542ccfa855SEd Maste } 4552ccfa855SEd Maste if (in->mask != FIDO_EXT_HMAC_SECRET) { 4562ccfa855SEd Maste fido_log_debug("%s: mask 0x%x", __func__, in->mask); 4572ccfa855SEd Maste return -1; 4582ccfa855SEd Maste } 4592ccfa855SEd Maste if (in->hmac_salt.ptr == NULL || 4602ccfa855SEd Maste in->hmac_salt.len != WEBAUTHN_CTAP_ONE_HMAC_SECRET_LENGTH) { 4612ccfa855SEd Maste fido_log_debug("%s: salt %p/%zu", __func__, 4622ccfa855SEd Maste (const void *)in->hmac_salt.ptr, in->hmac_salt.len); 4632ccfa855SEd Maste return -1; 4642ccfa855SEd Maste } 4652ccfa855SEd Maste if ((v = calloc(1, sizeof(*v))) == NULL || 4662ccfa855SEd Maste (s = calloc(1, sizeof(*s))) == NULL) { 4672ccfa855SEd Maste free(v); 4682ccfa855SEd Maste fido_log_debug("%s: calloc", __func__); 4692ccfa855SEd Maste return -1; 4702ccfa855SEd Maste } 4712ccfa855SEd Maste s->cbFirst = (DWORD)in->hmac_salt.len; 4722ccfa855SEd Maste s->pbFirst = in->hmac_salt.ptr; 4732ccfa855SEd Maste v->pGlobalHmacSalt = s; 4742ccfa855SEd Maste out->pHmacSecretSaltValues = v; 4752ccfa855SEd Maste out->dwFlags |= WEBAUTHN_AUTHENTICATOR_HMAC_SECRET_VALUES_FLAG; 4762ccfa855SEd Maste out->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_6; 4772ccfa855SEd Maste 4782ccfa855SEd Maste return 0; 4792ccfa855SEd Maste } 4802ccfa855SEd Maste 4812ccfa855SEd Maste static int 482*60a517b6SEd Maste pack_appid(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt, const char *id, 483*60a517b6SEd Maste wchar_t **appid) 484*60a517b6SEd Maste { 485*60a517b6SEd Maste if (id == NULL) 486*60a517b6SEd Maste return 0; /* nothing to do */ 487*60a517b6SEd Maste if ((opt->pbU2fAppId = calloc(1, sizeof(*opt->pbU2fAppId))) == NULL) { 488*60a517b6SEd Maste fido_log_debug("%s: calloc", __func__); 489*60a517b6SEd Maste return -1; 490*60a517b6SEd Maste } 491*60a517b6SEd Maste if ((*appid = to_utf16(id)) == NULL) { 492*60a517b6SEd Maste fido_log_debug("%s: to_utf16", __func__); 493*60a517b6SEd Maste return -1; 494*60a517b6SEd Maste } 495*60a517b6SEd Maste fido_log_debug("%s: using %s", __func__, id); 496*60a517b6SEd Maste opt->pwszU2fAppId = *appid; 497*60a517b6SEd Maste opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_2; 498*60a517b6SEd Maste 499*60a517b6SEd Maste return 0; 500*60a517b6SEd Maste } 501*60a517b6SEd Maste 502*60a517b6SEd Maste static void 503*60a517b6SEd Maste unpack_appid(fido_assert_t *assert, 504*60a517b6SEd Maste const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt) 505*60a517b6SEd Maste { 506*60a517b6SEd Maste if (assert->appid == NULL || opt->pbU2fAppId == NULL) 507*60a517b6SEd Maste return; /* nothing to do */ 508*60a517b6SEd Maste if (*opt->pbU2fAppId == false) { 509*60a517b6SEd Maste fido_log_debug("%s: not used", __func__); 510*60a517b6SEd Maste return; 511*60a517b6SEd Maste } 512*60a517b6SEd Maste fido_log_debug("%s: %s -> %s", __func__, assert->rp_id, assert->appid); 513*60a517b6SEd Maste free(assert->rp_id); 514*60a517b6SEd Maste assert->rp_id = assert->appid; 515*60a517b6SEd Maste assert->appid = NULL; 516*60a517b6SEd Maste } 517*60a517b6SEd Maste 518*60a517b6SEd Maste static int 519f540a430SEd Maste unpack_assert_authdata(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 5200afa8e06SEd Maste { 5210afa8e06SEd Maste int r; 5220afa8e06SEd Maste 5230afa8e06SEd Maste if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData, 5242ccfa855SEd Maste wa->cbAuthenticatorData)) != FIDO_OK) { 5250afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_authdata_raw: %s", __func__, 5260afa8e06SEd Maste fido_strerr(r)); 5270afa8e06SEd Maste return -1; 5280afa8e06SEd Maste } 5290afa8e06SEd Maste 5300afa8e06SEd Maste return 0; 5310afa8e06SEd Maste } 5320afa8e06SEd Maste 5330afa8e06SEd Maste static int 534f540a430SEd Maste unpack_assert_sig(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 5350afa8e06SEd Maste { 5360afa8e06SEd Maste int r; 5370afa8e06SEd Maste 5380afa8e06SEd Maste if ((r = fido_assert_set_sig(assert, 0, wa->pbSignature, 5392ccfa855SEd Maste wa->cbSignature)) != FIDO_OK) { 5400afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_sig: %s", __func__, 5410afa8e06SEd Maste fido_strerr(r)); 5420afa8e06SEd Maste return -1; 5430afa8e06SEd Maste } 5440afa8e06SEd Maste 5450afa8e06SEd Maste return 0; 5460afa8e06SEd Maste } 5470afa8e06SEd Maste 5480afa8e06SEd Maste static int 549f540a430SEd Maste unpack_cred_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 5500afa8e06SEd Maste { 5510afa8e06SEd Maste if (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId, 5522ccfa855SEd Maste wa->Credential.cbId) < 0) { 5530afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__); 5540afa8e06SEd Maste return -1; 5550afa8e06SEd Maste } 5560afa8e06SEd Maste 5570afa8e06SEd Maste return 0; 5580afa8e06SEd Maste } 5590afa8e06SEd Maste 5600afa8e06SEd Maste static int 561f540a430SEd Maste unpack_user_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 5620afa8e06SEd Maste { 5630afa8e06SEd Maste if (wa->cbUserId == 0) 5640afa8e06SEd Maste return 0; /* user id absent */ 5652ccfa855SEd Maste if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, 5662ccfa855SEd Maste wa->cbUserId) < 0) { 5672ccfa855SEd Maste fido_log_debug("%s: fido_blob_set", __func__); 5680afa8e06SEd Maste return -1; 5690afa8e06SEd Maste } 5702ccfa855SEd Maste 5712ccfa855SEd Maste return 0; 5722ccfa855SEd Maste } 5732ccfa855SEd Maste 5742ccfa855SEd Maste static int 5752ccfa855SEd Maste unpack_hmac_secret(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 5762ccfa855SEd Maste { 577*60a517b6SEd Maste if (wa->dwVersion < WEBAUTHN_ASSERTION_VERSION_3) { 5782ccfa855SEd Maste fido_log_debug("%s: dwVersion %u", __func__, 5792ccfa855SEd Maste (unsigned)wa->dwVersion); 5802ccfa855SEd Maste return 0; /* proceed without hmac-secret */ 5812ccfa855SEd Maste } 5822ccfa855SEd Maste if (wa->pHmacSecret == NULL || 5832ccfa855SEd Maste wa->pHmacSecret->cbFirst == 0 || 5842ccfa855SEd Maste wa->pHmacSecret->pbFirst == NULL) { 5852ccfa855SEd Maste fido_log_debug("%s: hmac-secret absent", __func__); 5862ccfa855SEd Maste return 0; /* proceed without hmac-secret */ 5872ccfa855SEd Maste } 5882ccfa855SEd Maste if (wa->pHmacSecret->cbSecond != 0 || 5892ccfa855SEd Maste wa->pHmacSecret->pbSecond != NULL) { 5902ccfa855SEd Maste fido_log_debug("%s: 64-byte hmac-secret", __func__); 5912ccfa855SEd Maste return 0; /* proceed without hmac-secret */ 5922ccfa855SEd Maste } 5932ccfa855SEd Maste if (!fido_blob_is_empty(&assert->stmt[0].hmac_secret)) { 5942ccfa855SEd Maste fido_log_debug("%s: fido_blob_is_empty", __func__); 5952ccfa855SEd Maste return -1; 5962ccfa855SEd Maste } 5972ccfa855SEd Maste if (fido_blob_set(&assert->stmt[0].hmac_secret, 5982ccfa855SEd Maste wa->pHmacSecret->pbFirst, wa->pHmacSecret->cbFirst) < 0) { 5990afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__); 6000afa8e06SEd Maste return -1; 6010afa8e06SEd Maste } 6020afa8e06SEd Maste 6030afa8e06SEd Maste return 0; 6040afa8e06SEd Maste } 6050afa8e06SEd Maste 6060afa8e06SEd Maste static int 607f540a430SEd Maste translate_fido_assert(struct winhello_assert *ctx, const fido_assert_t *assert, 608f540a430SEd Maste const char *pin, int ms) 6090afa8e06SEd Maste { 6100afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt; 6110afa8e06SEd Maste 6120afa8e06SEd Maste /* not supported by webauthn.h */ 6130afa8e06SEd Maste if (assert->up == FIDO_OPT_FALSE) { 6140afa8e06SEd Maste fido_log_debug("%s: up %d", __func__, assert->up); 6150afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_OPTION; 6160afa8e06SEd Maste } 6170afa8e06SEd Maste if ((ctx->rp_id = to_utf16(assert->rp_id)) == NULL) { 6180afa8e06SEd Maste fido_log_debug("%s: rp_id", __func__); 6190afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6200afa8e06SEd Maste } 6210afa8e06SEd Maste if (pack_cd(&ctx->cd, &assert->cd) < 0) { 6220afa8e06SEd Maste fido_log_debug("%s: pack_cd", __func__); 6230afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6240afa8e06SEd Maste } 6250afa8e06SEd Maste /* options */ 6260afa8e06SEd Maste opt = &ctx->opt; 6270afa8e06SEd Maste opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1; 628f540a430SEd Maste opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; 629*60a517b6SEd Maste if (pack_appid(opt, assert->appid, &ctx->appid) < 0) { 630*60a517b6SEd Maste fido_log_debug("%s: pack_appid" , __func__); 631*60a517b6SEd Maste return FIDO_ERR_INTERNAL; 632*60a517b6SEd Maste } 6330afa8e06SEd Maste if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) { 6340afa8e06SEd Maste fido_log_debug("%s: pack_credlist", __func__); 6350afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6360afa8e06SEd Maste } 6372ccfa855SEd Maste if (pack_assert_ext(opt, &assert->ext) < 0) { 6382ccfa855SEd Maste fido_log_debug("%s: pack_assert_ext", __func__); 6392ccfa855SEd Maste return FIDO_ERR_UNSUPPORTED_EXTENSION; 6402ccfa855SEd Maste } 6413e696dfbSEd Maste if (set_assert_uv(&opt->dwUserVerificationRequirement, assert->uv, 6423e696dfbSEd Maste pin) < 0) { 6433e696dfbSEd Maste fido_log_debug("%s: set_assert_uv", __func__); 6440afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6450afa8e06SEd Maste } 6460afa8e06SEd Maste 6470afa8e06SEd Maste return FIDO_OK; 6480afa8e06SEd Maste } 6490afa8e06SEd Maste 6500afa8e06SEd Maste static int 651*60a517b6SEd Maste translate_winhello_assert(fido_assert_t *assert, 652*60a517b6SEd Maste const struct winhello_assert *ctx) 6530afa8e06SEd Maste { 654*60a517b6SEd Maste const WEBAUTHN_ASSERTION *wa = ctx->assert; 655*60a517b6SEd Maste const WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt = &ctx->opt; 6560afa8e06SEd Maste int r; 6570afa8e06SEd Maste 6580afa8e06SEd Maste if (assert->stmt_len > 0) { 6590afa8e06SEd Maste fido_log_debug("%s: stmt_len=%zu", __func__, assert->stmt_len); 6600afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6610afa8e06SEd Maste } 6620afa8e06SEd Maste if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) { 6630afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_count: %s", __func__, 6640afa8e06SEd Maste fido_strerr(r)); 6650afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6660afa8e06SEd Maste } 667*60a517b6SEd Maste unpack_appid(assert, opt); 6680afa8e06SEd Maste if (unpack_assert_authdata(assert, wa) < 0) { 6690afa8e06SEd Maste fido_log_debug("%s: unpack_assert_authdata", __func__); 6700afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6710afa8e06SEd Maste } 6720afa8e06SEd Maste if (unpack_assert_sig(assert, wa) < 0) { 6730afa8e06SEd Maste fido_log_debug("%s: unpack_assert_sig", __func__); 6740afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6750afa8e06SEd Maste } 6760afa8e06SEd Maste if (unpack_cred_id(assert, wa) < 0) { 6770afa8e06SEd Maste fido_log_debug("%s: unpack_cred_id", __func__); 6780afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6790afa8e06SEd Maste } 6800afa8e06SEd Maste if (unpack_user_id(assert, wa) < 0) { 6810afa8e06SEd Maste fido_log_debug("%s: unpack_user_id", __func__); 6820afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6830afa8e06SEd Maste } 6842ccfa855SEd Maste if (assert->ext.mask & FIDO_EXT_HMAC_SECRET && 6852ccfa855SEd Maste unpack_hmac_secret(assert, wa) < 0) { 6862ccfa855SEd Maste fido_log_debug("%s: unpack_hmac_secret", __func__); 6872ccfa855SEd Maste return FIDO_ERR_INTERNAL; 6882ccfa855SEd Maste } 6890afa8e06SEd Maste 6900afa8e06SEd Maste return FIDO_OK; 6910afa8e06SEd Maste } 6920afa8e06SEd Maste 6930afa8e06SEd Maste static int 694f540a430SEd Maste translate_fido_cred(struct winhello_cred *ctx, const fido_cred_t *cred, 695f540a430SEd Maste const char *pin, int ms) 6960afa8e06SEd Maste { 6970afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *opt; 6980afa8e06SEd Maste 6990afa8e06SEd Maste if (pack_rp(&ctx->rp_id, &ctx->rp_name, &ctx->rp, &cred->rp) < 0) { 7000afa8e06SEd Maste fido_log_debug("%s: pack_rp", __func__); 7010afa8e06SEd Maste return FIDO_ERR_INTERNAL; 7020afa8e06SEd Maste } 7030afa8e06SEd Maste if (pack_user(&ctx->user_name, &ctx->user_icon, &ctx->display_name, 7040afa8e06SEd Maste &ctx->user, &cred->user) < 0) { 7050afa8e06SEd Maste fido_log_debug("%s: pack_user", __func__); 7060afa8e06SEd Maste return FIDO_ERR_INTERNAL; 7070afa8e06SEd Maste } 7080afa8e06SEd Maste if (pack_cose(&ctx->alg, &ctx->cose, cred->type) < 0) { 7090afa8e06SEd Maste fido_log_debug("%s: pack_cose", __func__); 7100afa8e06SEd Maste return FIDO_ERR_INTERNAL; 7110afa8e06SEd Maste } 7120afa8e06SEd Maste if (pack_cd(&ctx->cd, &cred->cd) < 0) { 7130afa8e06SEd Maste fido_log_debug("%s: pack_cd", __func__); 7140afa8e06SEd Maste return FIDO_ERR_INTERNAL; 7150afa8e06SEd Maste } 7160afa8e06SEd Maste /* options */ 7170afa8e06SEd Maste opt = &ctx->opt; 7180afa8e06SEd Maste opt->dwVersion = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1; 719f540a430SEd Maste opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; 720f540a430SEd Maste opt->dwAttestationConveyancePreference = 721f540a430SEd Maste WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; 7220afa8e06SEd Maste if (pack_credlist(&opt->CredentialList, &cred->excl) < 0) { 7230afa8e06SEd Maste fido_log_debug("%s: pack_credlist", __func__); 7240afa8e06SEd Maste return FIDO_ERR_INTERNAL; 7250afa8e06SEd Maste } 7260afa8e06SEd Maste if (pack_cred_ext(&opt->Extensions, &cred->ext) < 0) { 7270afa8e06SEd Maste fido_log_debug("%s: pack_cred_ext", __func__); 7280afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_EXTENSION; 7290afa8e06SEd Maste } 7303e696dfbSEd Maste if (set_cred_uv(&opt->dwUserVerificationRequirement, (cred->ext.mask & 731f540a430SEd Maste FIDO_EXT_CRED_PROTECT) ? FIDO_OPT_TRUE : cred->uv, pin) < 0) { 7323e696dfbSEd Maste fido_log_debug("%s: set_cred_uv", __func__); 7330afa8e06SEd Maste return FIDO_ERR_INTERNAL; 7340afa8e06SEd Maste } 7350afa8e06SEd Maste if (cred->rk == FIDO_OPT_TRUE) { 7360afa8e06SEd Maste opt->bRequireResidentKey = true; 7370afa8e06SEd Maste } 7380afa8e06SEd Maste 7390afa8e06SEd Maste return FIDO_OK; 7400afa8e06SEd Maste } 7410afa8e06SEd Maste 7420afa8e06SEd Maste static int 743f540a430SEd Maste decode_attobj(const cbor_item_t *key, const cbor_item_t *val, void *arg) 7440afa8e06SEd Maste { 745f540a430SEd Maste fido_cred_t *cred = arg; 746f540a430SEd Maste char *name = NULL; 747f540a430SEd Maste int ok = -1; 748f540a430SEd Maste 749f540a430SEd Maste if (cbor_string_copy(key, &name) < 0) { 750f540a430SEd Maste fido_log_debug("%s: cbor type", __func__); 751f540a430SEd Maste ok = 0; /* ignore */ 752f540a430SEd Maste goto fail; 7530afa8e06SEd Maste } 7540afa8e06SEd Maste 755f540a430SEd Maste if (!strcmp(name, "fmt")) { 756f540a430SEd Maste if (cbor_decode_fmt(val, &cred->fmt) < 0) { 757f540a430SEd Maste fido_log_debug("%s: cbor_decode_fmt", __func__); 758f540a430SEd Maste goto fail; 7590afa8e06SEd Maste } 760f540a430SEd Maste } else if (!strcmp(name, "attStmt")) { 761f540a430SEd Maste if (cbor_decode_attstmt(val, &cred->attstmt) < 0) { 762f540a430SEd Maste fido_log_debug("%s: cbor_decode_attstmt", __func__); 763f540a430SEd Maste goto fail; 7640afa8e06SEd Maste } 765f540a430SEd Maste } else if (!strcmp(name, "authData")) { 7663e696dfbSEd Maste if (fido_blob_decode(val, &cred->authdata_raw) < 0) { 7673e696dfbSEd Maste fido_log_debug("%s: fido_blob_decode", __func__); 7683e696dfbSEd Maste goto fail; 7693e696dfbSEd Maste } 770f540a430SEd Maste if (cbor_decode_cred_authdata(val, cred->type, 771f540a430SEd Maste &cred->authdata_cbor, &cred->authdata, &cred->attcred, 772f540a430SEd Maste &cred->authdata_ext) < 0) { 773f540a430SEd Maste fido_log_debug("%s: cbor_decode_cred_authdata", 774f540a430SEd Maste __func__); 775f540a430SEd Maste goto fail; 7760afa8e06SEd Maste } 7770afa8e06SEd Maste } 7780afa8e06SEd Maste 779f540a430SEd Maste ok = 0; 780f540a430SEd Maste fail: 781f540a430SEd Maste free(name); 782f540a430SEd Maste 783f540a430SEd Maste return (ok); 7840afa8e06SEd Maste } 7850afa8e06SEd Maste 7860afa8e06SEd Maste static int 7873e696dfbSEd Maste translate_winhello_cred(fido_cred_t *cred, 7883e696dfbSEd Maste const WEBAUTHN_CREDENTIAL_ATTESTATION *att) 789f540a430SEd Maste { 790f540a430SEd Maste cbor_item_t *item = NULL; 791f540a430SEd Maste struct cbor_load_result cbor; 792f540a430SEd Maste int r = FIDO_ERR_INTERNAL; 793f540a430SEd Maste 7942ccfa855SEd Maste if (att->pbAttestationObject == NULL) { 795f540a430SEd Maste fido_log_debug("%s: pbAttestationObject", __func__); 796f540a430SEd Maste goto fail; 797f540a430SEd Maste } 798f540a430SEd Maste if ((item = cbor_load(att->pbAttestationObject, 7992ccfa855SEd Maste att->cbAttestationObject, &cbor)) == NULL) { 800f540a430SEd Maste fido_log_debug("%s: cbor_load", __func__); 801f540a430SEd Maste goto fail; 802f540a430SEd Maste } 803f540a430SEd Maste if (cbor_isa_map(item) == false || 804f540a430SEd Maste cbor_map_is_definite(item) == false || 805f540a430SEd Maste cbor_map_iter(item, cred, decode_attobj) < 0) { 806f540a430SEd Maste fido_log_debug("%s: cbor type", __func__); 807f540a430SEd Maste goto fail; 808f540a430SEd Maste } 809f540a430SEd Maste 810f540a430SEd Maste r = FIDO_OK; 811f540a430SEd Maste fail: 812f540a430SEd Maste if (item != NULL) 813f540a430SEd Maste cbor_decref(&item); 814f540a430SEd Maste 815f540a430SEd Maste return r; 816f540a430SEd Maste } 817f540a430SEd Maste 818f540a430SEd Maste static int 8190afa8e06SEd Maste winhello_get_assert(HWND w, struct winhello_assert *ctx) 8200afa8e06SEd Maste { 8210afa8e06SEd Maste HRESULT hr; 8220afa8e06SEd Maste int r = FIDO_OK; 8230afa8e06SEd Maste 824f540a430SEd Maste if ((hr = webauthn_get_assert(w, ctx->rp_id, &ctx->cd, &ctx->opt, 825f540a430SEd Maste &ctx->assert)) != S_OK) { 8260afa8e06SEd Maste r = to_fido(hr); 827f540a430SEd Maste fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), 828f540a430SEd Maste fido_strerr(r)); 8290afa8e06SEd Maste } 8300afa8e06SEd Maste 8310afa8e06SEd Maste return r; 8320afa8e06SEd Maste } 8330afa8e06SEd Maste 8340afa8e06SEd Maste static int 8350afa8e06SEd Maste winhello_make_cred(HWND w, struct winhello_cred *ctx) 8360afa8e06SEd Maste { 8370afa8e06SEd Maste HRESULT hr; 8380afa8e06SEd Maste int r = FIDO_OK; 8390afa8e06SEd Maste 840f540a430SEd Maste if ((hr = webauthn_make_cred(w, &ctx->rp, &ctx->user, &ctx->cose, 841f540a430SEd Maste &ctx->cd, &ctx->opt, &ctx->att)) != S_OK) { 8420afa8e06SEd Maste r = to_fido(hr); 843f540a430SEd Maste fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), 844f540a430SEd Maste fido_strerr(r)); 8450afa8e06SEd Maste } 8460afa8e06SEd Maste 8470afa8e06SEd Maste return r; 8480afa8e06SEd Maste } 8490afa8e06SEd Maste 8500afa8e06SEd Maste static void 8510afa8e06SEd Maste winhello_assert_free(struct winhello_assert *ctx) 8520afa8e06SEd Maste { 8530afa8e06SEd Maste if (ctx == NULL) 8540afa8e06SEd Maste return; 8550afa8e06SEd Maste if (ctx->assert != NULL) 856f540a430SEd Maste webauthn_free_assert(ctx->assert); 8570afa8e06SEd Maste 8580afa8e06SEd Maste free(ctx->rp_id); 859*60a517b6SEd Maste free(ctx->appid); 8600afa8e06SEd Maste free(ctx->opt.CredentialList.pCredentials); 8612ccfa855SEd Maste if (ctx->opt.pHmacSecretSaltValues != NULL) 8622ccfa855SEd Maste free(ctx->opt.pHmacSecretSaltValues->pGlobalHmacSalt); 8632ccfa855SEd Maste free(ctx->opt.pHmacSecretSaltValues); 8640afa8e06SEd Maste free(ctx); 8650afa8e06SEd Maste } 8660afa8e06SEd Maste 8670afa8e06SEd Maste static void 8680afa8e06SEd Maste winhello_cred_free(struct winhello_cred *ctx) 8690afa8e06SEd Maste { 8700afa8e06SEd Maste if (ctx == NULL) 8710afa8e06SEd Maste return; 8720afa8e06SEd Maste if (ctx->att != NULL) 873f540a430SEd Maste webauthn_free_attest(ctx->att); 8740afa8e06SEd Maste 8750afa8e06SEd Maste free(ctx->rp_id); 8760afa8e06SEd Maste free(ctx->rp_name); 8770afa8e06SEd Maste free(ctx->user_name); 8780afa8e06SEd Maste free(ctx->user_icon); 8790afa8e06SEd Maste free(ctx->display_name); 8800afa8e06SEd Maste free(ctx->opt.CredentialList.pCredentials); 8810afa8e06SEd Maste for (size_t i = 0; i < ctx->opt.Extensions.cExtensions; i++) { 8820afa8e06SEd Maste WEBAUTHN_EXTENSION *e; 8830afa8e06SEd Maste e = &ctx->opt.Extensions.pExtensions[i]; 8840afa8e06SEd Maste free(e->pvExtension); 8850afa8e06SEd Maste } 8860afa8e06SEd Maste free(ctx->opt.Extensions.pExtensions); 8870afa8e06SEd Maste free(ctx); 8880afa8e06SEd Maste } 8890afa8e06SEd Maste 8900afa8e06SEd Maste int 8910afa8e06SEd Maste fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 8920afa8e06SEd Maste { 8930afa8e06SEd Maste fido_dev_info_t *di; 8940afa8e06SEd Maste 8950afa8e06SEd Maste if (ilen == 0) { 8960afa8e06SEd Maste return FIDO_OK; 8970afa8e06SEd Maste } 8980afa8e06SEd Maste if (devlist == NULL) { 8990afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT; 9000afa8e06SEd Maste } 9013e696dfbSEd Maste if (!webauthn_loaded && webauthn_load() < 0) { 9023e696dfbSEd Maste fido_log_debug("%s: webauthn_load", __func__); 9033e696dfbSEd Maste return FIDO_OK; /* not an error */ 9040afa8e06SEd Maste } 9050afa8e06SEd Maste 9060afa8e06SEd Maste di = &devlist[*olen]; 9070afa8e06SEd Maste memset(di, 0, sizeof(*di)); 9080afa8e06SEd Maste di->path = strdup(FIDO_WINHELLO_PATH); 9090afa8e06SEd Maste di->manufacturer = strdup("Microsoft Corporation"); 9100afa8e06SEd Maste di->product = strdup("Windows Hello"); 9110afa8e06SEd Maste di->vendor_id = VENDORID; 9120afa8e06SEd Maste di->product_id = PRODID; 9130afa8e06SEd Maste if (di->path == NULL || di->manufacturer == NULL || 9140afa8e06SEd Maste di->product == NULL) { 9150afa8e06SEd Maste free(di->path); 9160afa8e06SEd Maste free(di->manufacturer); 9170afa8e06SEd Maste free(di->product); 9180afa8e06SEd Maste explicit_bzero(di, sizeof(*di)); 9190afa8e06SEd Maste return FIDO_ERR_INTERNAL; 9200afa8e06SEd Maste } 9210afa8e06SEd Maste ++(*olen); 9220afa8e06SEd Maste 9230afa8e06SEd Maste return FIDO_OK; 9240afa8e06SEd Maste } 9250afa8e06SEd Maste 9260afa8e06SEd Maste int 9270afa8e06SEd Maste fido_winhello_open(fido_dev_t *dev) 9280afa8e06SEd Maste { 929f540a430SEd Maste if (!webauthn_loaded && webauthn_load() < 0) { 930f540a430SEd Maste fido_log_debug("%s: webauthn_load", __func__); 931f540a430SEd Maste return FIDO_ERR_INTERNAL; 932f540a430SEd Maste } 9330afa8e06SEd Maste if (dev->flags != 0) 9340afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT; 9350afa8e06SEd Maste dev->attr.flags = FIDO_CAP_CBOR | FIDO_CAP_WINK; 9360afa8e06SEd Maste dev->flags = FIDO_DEV_WINHELLO | FIDO_DEV_CRED_PROT | FIDO_DEV_PIN_SET; 9370afa8e06SEd Maste 9380afa8e06SEd Maste return FIDO_OK; 9390afa8e06SEd Maste } 9400afa8e06SEd Maste 9410afa8e06SEd Maste int 9420afa8e06SEd Maste fido_winhello_close(fido_dev_t *dev) 9430afa8e06SEd Maste { 9440afa8e06SEd Maste memset(dev, 0, sizeof(*dev)); 9450afa8e06SEd Maste 9460afa8e06SEd Maste return FIDO_OK; 9470afa8e06SEd Maste } 9480afa8e06SEd Maste 9490afa8e06SEd Maste int 9500afa8e06SEd Maste fido_winhello_cancel(fido_dev_t *dev) 9510afa8e06SEd Maste { 9520afa8e06SEd Maste (void)dev; 9530afa8e06SEd Maste 9540afa8e06SEd Maste return FIDO_ERR_INTERNAL; 9550afa8e06SEd Maste } 9560afa8e06SEd Maste 9570afa8e06SEd Maste int 9580afa8e06SEd Maste fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert, 959f540a430SEd Maste const char *pin, int ms) 9600afa8e06SEd Maste { 9610afa8e06SEd Maste HWND w; 9620afa8e06SEd Maste struct winhello_assert *ctx; 9630afa8e06SEd Maste int r = FIDO_ERR_INTERNAL; 9640afa8e06SEd Maste 9650afa8e06SEd Maste (void)dev; 9660afa8e06SEd Maste 967f540a430SEd Maste fido_assert_reset_rx(assert); 968f540a430SEd Maste 9690afa8e06SEd Maste if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 9700afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 9710afa8e06SEd Maste goto fail; 9720afa8e06SEd Maste } 9730afa8e06SEd Maste if ((w = GetForegroundWindow()) == NULL) { 9740afa8e06SEd Maste fido_log_debug("%s: GetForegroundWindow", __func__); 9753e696dfbSEd Maste if ((w = GetTopWindow(NULL)) == NULL) { 9763e696dfbSEd Maste fido_log_debug("%s: GetTopWindow", __func__); 9770afa8e06SEd Maste goto fail; 9780afa8e06SEd Maste } 9793e696dfbSEd Maste } 980f540a430SEd Maste if ((r = translate_fido_assert(ctx, assert, pin, ms)) != FIDO_OK) { 9810afa8e06SEd Maste fido_log_debug("%s: translate_fido_assert", __func__); 9820afa8e06SEd Maste goto fail; 9830afa8e06SEd Maste } 9843e696dfbSEd Maste if ((r = winhello_get_assert(w, ctx)) != FIDO_OK) { 9850afa8e06SEd Maste fido_log_debug("%s: winhello_get_assert", __func__); 9860afa8e06SEd Maste goto fail; 9870afa8e06SEd Maste } 988*60a517b6SEd Maste if ((r = translate_winhello_assert(assert, ctx)) != FIDO_OK) { 9890afa8e06SEd Maste fido_log_debug("%s: translate_winhello_assert", __func__); 9900afa8e06SEd Maste goto fail; 9910afa8e06SEd Maste } 9920afa8e06SEd Maste 9930afa8e06SEd Maste fail: 9940afa8e06SEd Maste winhello_assert_free(ctx); 9950afa8e06SEd Maste 9960afa8e06SEd Maste return r; 9970afa8e06SEd Maste } 9980afa8e06SEd Maste 9990afa8e06SEd Maste int 10000afa8e06SEd Maste fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) 10010afa8e06SEd Maste { 10020afa8e06SEd Maste const char *v[3] = { "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE" }; 10030afa8e06SEd Maste const char *e[2] = { "credProtect", "hmac-secret" }; 10040afa8e06SEd Maste const char *t[2] = { "nfc", "usb" }; 10052ccfa855SEd Maste const char *o[4] = { "rk", "up", "uv", "plat" }; 10060afa8e06SEd Maste 10070afa8e06SEd Maste (void)dev; 10080afa8e06SEd Maste 10090afa8e06SEd Maste fido_cbor_info_reset(ci); 10100afa8e06SEd Maste 1011f540a430SEd Maste if (fido_str_array_pack(&ci->versions, v, nitems(v)) < 0 || 1012f540a430SEd Maste fido_str_array_pack(&ci->extensions, e, nitems(e)) < 0 || 1013f540a430SEd Maste fido_str_array_pack(&ci->transports, t, nitems(t)) < 0) { 1014f540a430SEd Maste fido_log_debug("%s: fido_str_array_pack", __func__); 10150afa8e06SEd Maste return FIDO_ERR_INTERNAL; 10160afa8e06SEd Maste } 10170afa8e06SEd Maste if ((ci->options.name = calloc(nitems(o), sizeof(char *))) == NULL || 10180afa8e06SEd Maste (ci->options.value = calloc(nitems(o), sizeof(bool))) == NULL) { 10190afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 10200afa8e06SEd Maste return FIDO_ERR_INTERNAL; 10210afa8e06SEd Maste } 10220afa8e06SEd Maste for (size_t i = 0; i < nitems(o); i++) { 10230afa8e06SEd Maste if ((ci->options.name[i] = strdup(o[i])) == NULL) { 10240afa8e06SEd Maste fido_log_debug("%s: strdup", __func__); 10250afa8e06SEd Maste return FIDO_ERR_INTERNAL; 10260afa8e06SEd Maste } 10270afa8e06SEd Maste ci->options.value[i] = true; 10280afa8e06SEd Maste ci->options.len++; 10290afa8e06SEd Maste } 10300afa8e06SEd Maste 10310afa8e06SEd Maste return FIDO_OK; 10320afa8e06SEd Maste } 10330afa8e06SEd Maste 10340afa8e06SEd Maste int 1035f540a430SEd Maste fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin, 1036f540a430SEd Maste int ms) 10370afa8e06SEd Maste { 10380afa8e06SEd Maste HWND w; 10390afa8e06SEd Maste struct winhello_cred *ctx; 10400afa8e06SEd Maste int r = FIDO_ERR_INTERNAL; 10410afa8e06SEd Maste 10420afa8e06SEd Maste (void)dev; 10430afa8e06SEd Maste 1044f540a430SEd Maste fido_cred_reset_rx(cred); 1045f540a430SEd Maste 10460afa8e06SEd Maste if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 10470afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 10480afa8e06SEd Maste goto fail; 10490afa8e06SEd Maste } 10500afa8e06SEd Maste if ((w = GetForegroundWindow()) == NULL) { 10510afa8e06SEd Maste fido_log_debug("%s: GetForegroundWindow", __func__); 10523e696dfbSEd Maste if ((w = GetTopWindow(NULL)) == NULL) { 10533e696dfbSEd Maste fido_log_debug("%s: GetTopWindow", __func__); 10540afa8e06SEd Maste goto fail; 10550afa8e06SEd Maste } 10563e696dfbSEd Maste } 1057f540a430SEd Maste if ((r = translate_fido_cred(ctx, cred, pin, ms)) != FIDO_OK) { 10580afa8e06SEd Maste fido_log_debug("%s: translate_fido_cred", __func__); 10590afa8e06SEd Maste goto fail; 10600afa8e06SEd Maste } 10610afa8e06SEd Maste if ((r = winhello_make_cred(w, ctx)) != FIDO_OK) { 10620afa8e06SEd Maste fido_log_debug("%s: winhello_make_cred", __func__); 10630afa8e06SEd Maste goto fail; 10640afa8e06SEd Maste } 10650afa8e06SEd Maste if ((r = translate_winhello_cred(cred, ctx->att)) != FIDO_OK) { 10660afa8e06SEd Maste fido_log_debug("%s: translate_winhello_cred", __func__); 10670afa8e06SEd Maste goto fail; 10680afa8e06SEd Maste } 10690afa8e06SEd Maste 10700afa8e06SEd Maste r = FIDO_OK; 10710afa8e06SEd Maste fail: 10720afa8e06SEd Maste winhello_cred_free(ctx); 10730afa8e06SEd Maste 10740afa8e06SEd Maste return r; 10750afa8e06SEd Maste } 1076