10afa8e06SEd Maste /* 20afa8e06SEd Maste * Copyright (c) 2021 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. 50afa8e06SEd Maste */ 60afa8e06SEd Maste 70afa8e06SEd Maste #include <sys/types.h> 80afa8e06SEd Maste 90afa8e06SEd Maste #include <stdlib.h> 100afa8e06SEd Maste #include <windows.h> 110afa8e06SEd Maste 120afa8e06SEd Maste #include "fido.h" 13f540a430SEd Maste #include "webauthn.h" 140afa8e06SEd Maste 15*3e696dfbSEd Maste #ifndef NTE_INVALID_PARAMETER 16*3e696dfbSEd Maste #define NTE_INVALID_PARAMETER _HRESULT_TYPEDEF_(0x80090027) 17*3e696dfbSEd Maste #endif 18*3e696dfbSEd Maste #ifndef NTE_NOT_SUPPORTED 19*3e696dfbSEd Maste #define NTE_NOT_SUPPORTED _HRESULT_TYPEDEF_(0x80090029) 20*3e696dfbSEd Maste #endif 21*3e696dfbSEd Maste #ifndef NTE_DEVICE_NOT_FOUND 22*3e696dfbSEd Maste #define NTE_DEVICE_NOT_FOUND _HRESULT_TYPEDEF_(0x80090035) 23*3e696dfbSEd Maste #endif 24*3e696dfbSEd Maste 250afa8e06SEd Maste #define MAXCHARS 128 260afa8e06SEd Maste #define MAXCREDS 128 270afa8e06SEd Maste #define MAXMSEC 6000 * 1000 280afa8e06SEd Maste #define VENDORID 0x045e 290afa8e06SEd Maste #define PRODID 0x0001 300afa8e06SEd Maste 310afa8e06SEd Maste struct winhello_assert { 320afa8e06SEd Maste WEBAUTHN_CLIENT_DATA cd; 330afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS opt; 340afa8e06SEd Maste WEBAUTHN_ASSERTION *assert; 350afa8e06SEd Maste wchar_t *rp_id; 360afa8e06SEd Maste }; 370afa8e06SEd Maste 380afa8e06SEd Maste struct winhello_cred { 390afa8e06SEd Maste WEBAUTHN_RP_ENTITY_INFORMATION rp; 400afa8e06SEd Maste WEBAUTHN_USER_ENTITY_INFORMATION user; 410afa8e06SEd Maste WEBAUTHN_COSE_CREDENTIAL_PARAMETER alg; 420afa8e06SEd Maste WEBAUTHN_COSE_CREDENTIAL_PARAMETERS cose; 430afa8e06SEd Maste WEBAUTHN_CLIENT_DATA cd; 440afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS opt; 450afa8e06SEd Maste WEBAUTHN_CREDENTIAL_ATTESTATION *att; 460afa8e06SEd Maste wchar_t *rp_id; 470afa8e06SEd Maste wchar_t *rp_name; 480afa8e06SEd Maste wchar_t *user_name; 490afa8e06SEd Maste wchar_t *user_icon; 500afa8e06SEd Maste wchar_t *display_name; 510afa8e06SEd Maste }; 520afa8e06SEd Maste 53*3e696dfbSEd Maste typedef DWORD WINAPI webauthn_get_api_version_t(void); 54*3e696dfbSEd Maste typedef PCWSTR WINAPI webauthn_strerr_t(HRESULT); 55*3e696dfbSEd Maste typedef HRESULT WINAPI webauthn_get_assert_t(HWND, LPCWSTR, 56f540a430SEd Maste PCWEBAUTHN_CLIENT_DATA, 57f540a430SEd Maste PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, 58f540a430SEd Maste PWEBAUTHN_ASSERTION *); 59*3e696dfbSEd Maste typedef HRESULT WINAPI webauthn_make_cred_t(HWND, 60f540a430SEd Maste PCWEBAUTHN_RP_ENTITY_INFORMATION, 61f540a430SEd Maste PCWEBAUTHN_USER_ENTITY_INFORMATION, 62f540a430SEd Maste PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS, 63f540a430SEd Maste PCWEBAUTHN_CLIENT_DATA, 64f540a430SEd Maste PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS, 65f540a430SEd Maste PWEBAUTHN_CREDENTIAL_ATTESTATION *); 66*3e696dfbSEd Maste typedef void WINAPI webauthn_free_assert_t(PWEBAUTHN_ASSERTION); 67*3e696dfbSEd Maste typedef void WINAPI webauthn_free_attest_t(PWEBAUTHN_CREDENTIAL_ATTESTATION); 68*3e696dfbSEd Maste 69*3e696dfbSEd Maste static TLS BOOL webauthn_loaded; 70*3e696dfbSEd Maste static TLS HMODULE webauthn_handle; 71*3e696dfbSEd Maste static TLS webauthn_get_api_version_t *webauthn_get_api_version; 72*3e696dfbSEd Maste static TLS webauthn_strerr_t *webauthn_strerr; 73*3e696dfbSEd Maste static TLS webauthn_get_assert_t *webauthn_get_assert; 74*3e696dfbSEd Maste static TLS webauthn_make_cred_t *webauthn_make_cred; 75*3e696dfbSEd Maste static TLS webauthn_free_assert_t *webauthn_free_assert; 76*3e696dfbSEd Maste static TLS webauthn_free_attest_t *webauthn_free_attest; 77f540a430SEd Maste 78f540a430SEd Maste static int 79f540a430SEd Maste webauthn_load(void) 80f540a430SEd Maste { 81*3e696dfbSEd Maste DWORD n = 1; 82*3e696dfbSEd Maste 83f540a430SEd Maste if (webauthn_loaded || webauthn_handle != NULL) { 84f540a430SEd Maste fido_log_debug("%s: already loaded", __func__); 85f540a430SEd Maste return -1; 86f540a430SEd Maste } 87f540a430SEd Maste if ((webauthn_handle = LoadLibrary("webauthn.dll")) == NULL) { 88f540a430SEd Maste fido_log_debug("%s: LoadLibrary", __func__); 89f540a430SEd Maste return -1; 90f540a430SEd Maste } 91f540a430SEd Maste 92*3e696dfbSEd Maste if ((webauthn_get_api_version = 93*3e696dfbSEd Maste (webauthn_get_api_version_t *)GetProcAddress(webauthn_handle, 94f540a430SEd Maste "WebAuthNGetApiVersionNumber")) == NULL) { 95f540a430SEd Maste fido_log_debug("%s: WebAuthNGetApiVersionNumber", __func__); 96*3e696dfbSEd Maste /* WebAuthNGetApiVersionNumber might not exist */ 97*3e696dfbSEd Maste } 98*3e696dfbSEd Maste if (webauthn_get_api_version != NULL && 99*3e696dfbSEd Maste (n = webauthn_get_api_version()) < 1) { 100*3e696dfbSEd Maste fido_log_debug("%s: unsupported api %lu", __func__, (u_long)n); 101f540a430SEd Maste goto fail; 102f540a430SEd Maste } 103*3e696dfbSEd Maste fido_log_debug("%s: api version %lu", __func__, (u_long)n); 104*3e696dfbSEd Maste if ((webauthn_strerr = 105*3e696dfbSEd Maste (webauthn_strerr_t *)GetProcAddress(webauthn_handle, 106f540a430SEd Maste "WebAuthNGetErrorName")) == NULL) { 107f540a430SEd Maste fido_log_debug("%s: WebAuthNGetErrorName", __func__); 108f540a430SEd Maste goto fail; 109f540a430SEd Maste } 110*3e696dfbSEd Maste if ((webauthn_get_assert = 111*3e696dfbSEd Maste (webauthn_get_assert_t *)GetProcAddress(webauthn_handle, 112f540a430SEd Maste "WebAuthNAuthenticatorGetAssertion")) == NULL) { 113f540a430SEd Maste fido_log_debug("%s: WebAuthNAuthenticatorGetAssertion", 114f540a430SEd Maste __func__); 115f540a430SEd Maste goto fail; 116f540a430SEd Maste } 117*3e696dfbSEd Maste if ((webauthn_make_cred = 118*3e696dfbSEd Maste (webauthn_make_cred_t *)GetProcAddress(webauthn_handle, 119f540a430SEd Maste "WebAuthNAuthenticatorMakeCredential")) == NULL) { 120f540a430SEd Maste fido_log_debug("%s: WebAuthNAuthenticatorMakeCredential", 121f540a430SEd Maste __func__); 122f540a430SEd Maste goto fail; 123f540a430SEd Maste } 124*3e696dfbSEd Maste if ((webauthn_free_assert = 125*3e696dfbSEd Maste (webauthn_free_assert_t *)GetProcAddress(webauthn_handle, 126f540a430SEd Maste "WebAuthNFreeAssertion")) == NULL) { 127f540a430SEd Maste fido_log_debug("%s: WebAuthNFreeAssertion", __func__); 128f540a430SEd Maste goto fail; 129f540a430SEd Maste } 130*3e696dfbSEd Maste if ((webauthn_free_attest = 131*3e696dfbSEd Maste (webauthn_free_attest_t *)GetProcAddress(webauthn_handle, 132f540a430SEd Maste "WebAuthNFreeCredentialAttestation")) == NULL) { 133f540a430SEd Maste fido_log_debug("%s: WebAuthNFreeCredentialAttestation", 134f540a430SEd Maste __func__); 135f540a430SEd Maste goto fail; 136f540a430SEd Maste } 137f540a430SEd Maste 138f540a430SEd Maste webauthn_loaded = true; 139f540a430SEd Maste 140f540a430SEd Maste return 0; 141f540a430SEd Maste fail: 142f540a430SEd Maste fido_log_debug("%s: GetProcAddress", __func__); 143f540a430SEd Maste webauthn_get_api_version = NULL; 144f540a430SEd Maste webauthn_strerr = NULL; 145f540a430SEd Maste webauthn_get_assert = NULL; 146f540a430SEd Maste webauthn_make_cred = NULL; 147f540a430SEd Maste webauthn_free_assert = NULL; 148f540a430SEd Maste webauthn_free_attest = NULL; 149f540a430SEd Maste FreeLibrary(webauthn_handle); 150f540a430SEd Maste webauthn_handle = NULL; 151f540a430SEd Maste 152f540a430SEd Maste return -1; 153f540a430SEd Maste } 154f540a430SEd Maste 1550afa8e06SEd Maste static wchar_t * 1560afa8e06SEd Maste to_utf16(const char *utf8) 1570afa8e06SEd Maste { 1580afa8e06SEd Maste int nch; 1590afa8e06SEd Maste wchar_t *utf16; 1600afa8e06SEd Maste 1610afa8e06SEd Maste if (utf8 == NULL) { 1620afa8e06SEd Maste fido_log_debug("%s: NULL", __func__); 1630afa8e06SEd Maste return NULL; 1640afa8e06SEd Maste } 1650afa8e06SEd Maste if ((nch = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) < 1 || 1660afa8e06SEd Maste (size_t)nch > MAXCHARS) { 1670afa8e06SEd Maste fido_log_debug("%s: MultiByteToWideChar %d", __func__, nch); 1680afa8e06SEd Maste return NULL; 1690afa8e06SEd Maste } 1700afa8e06SEd Maste if ((utf16 = calloc((size_t)nch, sizeof(*utf16))) == NULL) { 1710afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 1720afa8e06SEd Maste return NULL; 1730afa8e06SEd Maste } 1740afa8e06SEd Maste if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, nch) != nch) { 1750afa8e06SEd Maste fido_log_debug("%s: MultiByteToWideChar", __func__); 1760afa8e06SEd Maste free(utf16); 1770afa8e06SEd Maste return NULL; 1780afa8e06SEd Maste } 1790afa8e06SEd Maste 1800afa8e06SEd Maste return utf16; 1810afa8e06SEd Maste } 1820afa8e06SEd Maste 1830afa8e06SEd Maste static int 1840afa8e06SEd Maste to_fido(HRESULT hr) 1850afa8e06SEd Maste { 1860afa8e06SEd Maste switch (hr) { 1870afa8e06SEd Maste case NTE_NOT_SUPPORTED: 1880afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_OPTION; 1890afa8e06SEd Maste case NTE_INVALID_PARAMETER: 1900afa8e06SEd Maste return FIDO_ERR_INVALID_PARAMETER; 1910afa8e06SEd Maste case NTE_TOKEN_KEYSET_STORAGE_FULL: 1920afa8e06SEd Maste return FIDO_ERR_KEY_STORE_FULL; 1930afa8e06SEd Maste case NTE_DEVICE_NOT_FOUND: 1940afa8e06SEd Maste case NTE_NOT_FOUND: 1950afa8e06SEd Maste return FIDO_ERR_NOT_ALLOWED; 1960afa8e06SEd Maste default: 197*3e696dfbSEd Maste fido_log_debug("%s: hr=0x%lx", __func__, (u_long)hr); 1980afa8e06SEd Maste return FIDO_ERR_INTERNAL; 1990afa8e06SEd Maste } 2000afa8e06SEd Maste } 2010afa8e06SEd Maste 2020afa8e06SEd Maste static int 2030afa8e06SEd Maste pack_cd(WEBAUTHN_CLIENT_DATA *out, const fido_blob_t *in) 2040afa8e06SEd Maste { 2050afa8e06SEd Maste if (in->ptr == NULL) { 2060afa8e06SEd Maste fido_log_debug("%s: NULL", __func__); 2070afa8e06SEd Maste return -1; 2080afa8e06SEd Maste } 2090afa8e06SEd Maste if (in->len > ULONG_MAX) { 2100afa8e06SEd Maste fido_log_debug("%s: in->len=%zu", __func__, in->len); 2110afa8e06SEd Maste return -1; 2120afa8e06SEd Maste } 2130afa8e06SEd Maste out->dwVersion = WEBAUTHN_CLIENT_DATA_CURRENT_VERSION; 2140afa8e06SEd Maste out->cbClientDataJSON = (DWORD)in->len; 2150afa8e06SEd Maste out->pbClientDataJSON = in->ptr; 2160afa8e06SEd Maste out->pwszHashAlgId = WEBAUTHN_HASH_ALGORITHM_SHA_256; 2170afa8e06SEd Maste 2180afa8e06SEd Maste return 0; 2190afa8e06SEd Maste } 2200afa8e06SEd Maste 2210afa8e06SEd Maste static int 2220afa8e06SEd Maste pack_credlist(WEBAUTHN_CREDENTIALS *out, const fido_blob_array_t *in) 2230afa8e06SEd Maste { 2240afa8e06SEd Maste WEBAUTHN_CREDENTIAL *c; 2250afa8e06SEd Maste 2260afa8e06SEd Maste if (in->len == 0) { 2270afa8e06SEd Maste return 0; /* nothing to do */ 2280afa8e06SEd Maste } 2290afa8e06SEd Maste if (in->len > MAXCREDS) { 2300afa8e06SEd Maste fido_log_debug("%s: in->len=%zu", __func__, in->len); 2310afa8e06SEd Maste return -1; 2320afa8e06SEd Maste } 2330afa8e06SEd Maste if ((out->pCredentials = calloc(in->len, sizeof(*c))) == NULL) { 2340afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 2350afa8e06SEd Maste return -1; 2360afa8e06SEd Maste } 2370afa8e06SEd Maste out->cCredentials = (DWORD)in->len; 2380afa8e06SEd Maste for (size_t i = 0; i < in->len; i++) { 2390afa8e06SEd Maste if (in->ptr[i].len > ULONG_MAX) { 2400afa8e06SEd Maste fido_log_debug("%s: %zu", __func__, in->ptr[i].len); 2410afa8e06SEd Maste return -1; 2420afa8e06SEd Maste } 2430afa8e06SEd Maste c = &out->pCredentials[i]; 2440afa8e06SEd Maste c->dwVersion = WEBAUTHN_CREDENTIAL_CURRENT_VERSION; 2450afa8e06SEd Maste c->cbId = (DWORD)in->ptr[i].len; 2460afa8e06SEd Maste c->pbId = in->ptr[i].ptr; 2470afa8e06SEd Maste c->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; 2480afa8e06SEd Maste } 2490afa8e06SEd Maste 2500afa8e06SEd Maste return 0; 2510afa8e06SEd Maste } 2520afa8e06SEd Maste 2530afa8e06SEd Maste static int 254*3e696dfbSEd Maste set_cred_uv(DWORD *out, fido_opt_t uv, const char *pin) 2550afa8e06SEd Maste { 2560afa8e06SEd Maste if (pin) { 2570afa8e06SEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 2580afa8e06SEd Maste return 0; 2590afa8e06SEd Maste } 2600afa8e06SEd Maste 2610afa8e06SEd Maste switch (uv) { 2620afa8e06SEd Maste case FIDO_OPT_OMIT: 263*3e696dfbSEd Maste case FIDO_OPT_FALSE: 264*3e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 265*3e696dfbSEd Maste break; 266*3e696dfbSEd Maste case FIDO_OPT_TRUE: 267*3e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 268*3e696dfbSEd Maste break; 269*3e696dfbSEd Maste } 270*3e696dfbSEd Maste 271*3e696dfbSEd Maste return 0; 272*3e696dfbSEd Maste } 273*3e696dfbSEd Maste 274*3e696dfbSEd Maste static int 275*3e696dfbSEd Maste set_assert_uv(DWORD *out, fido_opt_t uv, const char *pin) 276*3e696dfbSEd Maste { 277*3e696dfbSEd Maste if (pin) { 278*3e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 279*3e696dfbSEd Maste return 0; 280*3e696dfbSEd Maste } 281*3e696dfbSEd Maste 282*3e696dfbSEd Maste switch (uv) { 283*3e696dfbSEd Maste case FIDO_OPT_OMIT: 284*3e696dfbSEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; 2850afa8e06SEd Maste break; 2860afa8e06SEd Maste case FIDO_OPT_FALSE: 2870afa8e06SEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; 2880afa8e06SEd Maste break; 2890afa8e06SEd Maste case FIDO_OPT_TRUE: 2900afa8e06SEd Maste *out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; 2910afa8e06SEd Maste break; 2920afa8e06SEd Maste } 2930afa8e06SEd Maste 2940afa8e06SEd Maste return 0; 2950afa8e06SEd Maste } 2960afa8e06SEd Maste 2970afa8e06SEd Maste static int 2980afa8e06SEd Maste pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out, 299f540a430SEd Maste const fido_rp_t *in) 3000afa8e06SEd Maste { 3010afa8e06SEd Maste /* keep non-const copies of pwsz* for free() */ 3020afa8e06SEd Maste out->dwVersion = WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION; 3030afa8e06SEd Maste if ((out->pwszId = *id = to_utf16(in->id)) == NULL) { 3040afa8e06SEd Maste fido_log_debug("%s: id", __func__); 3050afa8e06SEd Maste return -1; 3060afa8e06SEd Maste } 3070afa8e06SEd Maste if (in->name && (out->pwszName = *name = to_utf16(in->name)) == NULL) { 3080afa8e06SEd Maste fido_log_debug("%s: name", __func__); 3090afa8e06SEd Maste return -1; 3100afa8e06SEd Maste } 3110afa8e06SEd Maste return 0; 3120afa8e06SEd Maste } 3130afa8e06SEd Maste 3140afa8e06SEd Maste static int 3150afa8e06SEd Maste pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name, 316f540a430SEd Maste WEBAUTHN_USER_ENTITY_INFORMATION *out, const fido_user_t *in) 3170afa8e06SEd Maste { 3180afa8e06SEd Maste if (in->id.ptr == NULL || in->id.len > ULONG_MAX) { 3190afa8e06SEd Maste fido_log_debug("%s: id", __func__); 3200afa8e06SEd Maste return -1; 3210afa8e06SEd Maste } 3220afa8e06SEd Maste out->dwVersion = WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION; 3230afa8e06SEd Maste out->cbId = (DWORD)in->id.len; 3240afa8e06SEd Maste out->pbId = in->id.ptr; 3250afa8e06SEd Maste /* keep non-const copies of pwsz* for free() */ 3260afa8e06SEd Maste if (in->name != NULL) { 3270afa8e06SEd Maste if ((out->pwszName = *name = to_utf16(in->name)) == NULL) { 3280afa8e06SEd Maste fido_log_debug("%s: name", __func__); 3290afa8e06SEd Maste return -1; 3300afa8e06SEd Maste } 3310afa8e06SEd Maste } 3320afa8e06SEd Maste if (in->icon != NULL) { 3330afa8e06SEd Maste if ((out->pwszIcon = *icon = to_utf16(in->icon)) == NULL) { 3340afa8e06SEd Maste fido_log_debug("%s: icon", __func__); 3350afa8e06SEd Maste return -1; 3360afa8e06SEd Maste } 3370afa8e06SEd Maste } 3380afa8e06SEd Maste if (in->display_name != NULL) { 3390afa8e06SEd Maste if ((out->pwszDisplayName = *display_name = 3400afa8e06SEd Maste to_utf16(in->display_name)) == NULL) { 3410afa8e06SEd Maste fido_log_debug("%s: display_name", __func__); 3420afa8e06SEd Maste return -1; 3430afa8e06SEd Maste } 3440afa8e06SEd Maste } 3450afa8e06SEd Maste 3460afa8e06SEd Maste return 0; 3470afa8e06SEd Maste } 3480afa8e06SEd Maste 3490afa8e06SEd Maste static int 3500afa8e06SEd Maste pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg, 3510afa8e06SEd Maste WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *cose, int type) 3520afa8e06SEd Maste { 3530afa8e06SEd Maste switch (type) { 3540afa8e06SEd Maste case COSE_ES256: 3550afa8e06SEd Maste alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256; 3560afa8e06SEd Maste break; 3570afa8e06SEd Maste case COSE_EDDSA: 3580afa8e06SEd Maste alg->lAlg = -8; /* XXX */; 3590afa8e06SEd Maste break; 3600afa8e06SEd Maste case COSE_RS256: 3610afa8e06SEd Maste alg->lAlg = WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256; 3620afa8e06SEd Maste break; 3630afa8e06SEd Maste default: 3640afa8e06SEd Maste fido_log_debug("%s: type %d", __func__, type); 3650afa8e06SEd Maste return -1; 3660afa8e06SEd Maste } 3670afa8e06SEd Maste alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION; 3680afa8e06SEd Maste alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY; 3690afa8e06SEd Maste cose->cCredentialParameters = 1; 3700afa8e06SEd Maste cose->pCredentialParameters = alg; 3710afa8e06SEd Maste 3720afa8e06SEd Maste return 0; 3730afa8e06SEd Maste } 3740afa8e06SEd Maste 3750afa8e06SEd Maste static int 376f540a430SEd Maste pack_cred_ext(WEBAUTHN_EXTENSIONS *out, const fido_cred_ext_t *in) 3770afa8e06SEd Maste { 3780afa8e06SEd Maste WEBAUTHN_EXTENSION *e; 3790afa8e06SEd Maste WEBAUTHN_CRED_PROTECT_EXTENSION_IN *p; 3800afa8e06SEd Maste BOOL *b; 3810afa8e06SEd Maste size_t n = 0, i = 0; 3820afa8e06SEd Maste 3830afa8e06SEd Maste if (in->mask == 0) { 3840afa8e06SEd Maste return 0; /* nothing to do */ 3850afa8e06SEd Maste } 3860afa8e06SEd Maste if (in->mask & ~(FIDO_EXT_HMAC_SECRET | FIDO_EXT_CRED_PROTECT)) { 387*3e696dfbSEd Maste fido_log_debug("%s: mask 0x%x", __func__, in->mask); 3880afa8e06SEd Maste return -1; 3890afa8e06SEd Maste } 3900afa8e06SEd Maste if (in->mask & FIDO_EXT_HMAC_SECRET) 3910afa8e06SEd Maste n++; 3920afa8e06SEd Maste if (in->mask & FIDO_EXT_CRED_PROTECT) 3930afa8e06SEd Maste n++; 3940afa8e06SEd Maste if ((out->pExtensions = calloc(n, sizeof(*e))) == NULL) { 3950afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 3960afa8e06SEd Maste return -1; 3970afa8e06SEd Maste } 3980afa8e06SEd Maste out->cExtensions = (DWORD)n; 3990afa8e06SEd Maste if (in->mask & FIDO_EXT_HMAC_SECRET) { 4000afa8e06SEd Maste if ((b = calloc(1, sizeof(*b))) == NULL) { 4010afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 4020afa8e06SEd Maste return -1; 4030afa8e06SEd Maste } 4040afa8e06SEd Maste *b = true; 4050afa8e06SEd Maste e = &out->pExtensions[i]; 4060afa8e06SEd Maste e->pwszExtensionIdentifier = 4070afa8e06SEd Maste WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET; 4080afa8e06SEd Maste e->pvExtension = b; 4090afa8e06SEd Maste e->cbExtension = sizeof(*b); 4100afa8e06SEd Maste i++; 4110afa8e06SEd Maste } 4120afa8e06SEd Maste if (in->mask & FIDO_EXT_CRED_PROTECT) { 4130afa8e06SEd Maste if ((p = calloc(1, sizeof(*p))) == NULL) { 4140afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 4150afa8e06SEd Maste return -1; 4160afa8e06SEd Maste } 4170afa8e06SEd Maste p->dwCredProtect = (DWORD)in->prot; 4180afa8e06SEd Maste p->bRequireCredProtect = true; 4190afa8e06SEd Maste e = &out->pExtensions[i]; 4200afa8e06SEd Maste e->pwszExtensionIdentifier = 4210afa8e06SEd Maste WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT; 4220afa8e06SEd Maste e->pvExtension = p; 4230afa8e06SEd Maste e->cbExtension = sizeof(*p); 4240afa8e06SEd Maste i++; 4250afa8e06SEd Maste } 4260afa8e06SEd Maste 4270afa8e06SEd Maste return 0; 4280afa8e06SEd Maste } 4290afa8e06SEd Maste 4300afa8e06SEd Maste static int 431f540a430SEd Maste unpack_assert_authdata(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 4320afa8e06SEd Maste { 4330afa8e06SEd Maste int r; 4340afa8e06SEd Maste 4350afa8e06SEd Maste if (wa->cbAuthenticatorData > SIZE_MAX) { 4360afa8e06SEd Maste fido_log_debug("%s: cbAuthenticatorData", __func__); 4370afa8e06SEd Maste return -1; 4380afa8e06SEd Maste } 4390afa8e06SEd Maste if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData, 4400afa8e06SEd Maste (size_t)wa->cbAuthenticatorData)) != FIDO_OK) { 4410afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_authdata_raw: %s", __func__, 4420afa8e06SEd Maste fido_strerr(r)); 4430afa8e06SEd Maste return -1; 4440afa8e06SEd Maste } 4450afa8e06SEd Maste 4460afa8e06SEd Maste return 0; 4470afa8e06SEd Maste } 4480afa8e06SEd Maste 4490afa8e06SEd Maste static int 450f540a430SEd Maste unpack_assert_sig(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 4510afa8e06SEd Maste { 4520afa8e06SEd Maste int r; 4530afa8e06SEd Maste 4540afa8e06SEd Maste if (wa->cbSignature > SIZE_MAX) { 4550afa8e06SEd Maste fido_log_debug("%s: cbSignature", __func__); 4560afa8e06SEd Maste return -1; 4570afa8e06SEd Maste } 4580afa8e06SEd Maste if ((r = fido_assert_set_sig(assert, 0, wa->pbSignature, 4590afa8e06SEd Maste (size_t)wa->cbSignature)) != FIDO_OK) { 4600afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_sig: %s", __func__, 4610afa8e06SEd Maste fido_strerr(r)); 4620afa8e06SEd Maste return -1; 4630afa8e06SEd Maste } 4640afa8e06SEd Maste 4650afa8e06SEd Maste return 0; 4660afa8e06SEd Maste } 4670afa8e06SEd Maste 4680afa8e06SEd Maste static int 469f540a430SEd Maste unpack_cred_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 4700afa8e06SEd Maste { 4710afa8e06SEd Maste if (wa->Credential.cbId > SIZE_MAX) { 4720afa8e06SEd Maste fido_log_debug("%s: Credential.cbId", __func__); 4730afa8e06SEd Maste return -1; 4740afa8e06SEd Maste } 4750afa8e06SEd Maste if (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId, 4760afa8e06SEd Maste (size_t)wa->Credential.cbId) < 0) { 4770afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__); 4780afa8e06SEd Maste return -1; 4790afa8e06SEd Maste } 4800afa8e06SEd Maste 4810afa8e06SEd Maste return 0; 4820afa8e06SEd Maste } 4830afa8e06SEd Maste 4840afa8e06SEd Maste static int 485f540a430SEd Maste unpack_user_id(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 4860afa8e06SEd Maste { 4870afa8e06SEd Maste if (wa->cbUserId == 0) 4880afa8e06SEd Maste return 0; /* user id absent */ 4890afa8e06SEd Maste if (wa->cbUserId > SIZE_MAX) { 4900afa8e06SEd Maste fido_log_debug("%s: cbUserId", __func__); 4910afa8e06SEd Maste return -1; 4920afa8e06SEd Maste } 4930afa8e06SEd Maste if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId, 4940afa8e06SEd Maste (size_t)wa->cbUserId) < 0) { 4950afa8e06SEd Maste fido_log_debug("%s: fido_blob_set", __func__); 4960afa8e06SEd Maste return -1; 4970afa8e06SEd Maste } 4980afa8e06SEd Maste 4990afa8e06SEd Maste return 0; 5000afa8e06SEd Maste } 5010afa8e06SEd Maste 5020afa8e06SEd Maste static int 503f540a430SEd Maste translate_fido_assert(struct winhello_assert *ctx, const fido_assert_t *assert, 504f540a430SEd Maste const char *pin, int ms) 5050afa8e06SEd Maste { 5060afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt; 5070afa8e06SEd Maste 5080afa8e06SEd Maste /* not supported by webauthn.h */ 5090afa8e06SEd Maste if (assert->up == FIDO_OPT_FALSE) { 5100afa8e06SEd Maste fido_log_debug("%s: up %d", __func__, assert->up); 5110afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_OPTION; 5120afa8e06SEd Maste } 5130afa8e06SEd Maste /* not implemented */ 5140afa8e06SEd Maste if (assert->ext.mask) { 5150afa8e06SEd Maste fido_log_debug("%s: ext 0x%x", __func__, assert->ext.mask); 5160afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_EXTENSION; 5170afa8e06SEd Maste } 5180afa8e06SEd Maste if ((ctx->rp_id = to_utf16(assert->rp_id)) == NULL) { 5190afa8e06SEd Maste fido_log_debug("%s: rp_id", __func__); 5200afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5210afa8e06SEd Maste } 5220afa8e06SEd Maste if (pack_cd(&ctx->cd, &assert->cd) < 0) { 5230afa8e06SEd Maste fido_log_debug("%s: pack_cd", __func__); 5240afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5250afa8e06SEd Maste } 5260afa8e06SEd Maste /* options */ 5270afa8e06SEd Maste opt = &ctx->opt; 5280afa8e06SEd Maste opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1; 529f540a430SEd Maste opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; 5300afa8e06SEd Maste if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) { 5310afa8e06SEd Maste fido_log_debug("%s: pack_credlist", __func__); 5320afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5330afa8e06SEd Maste } 534*3e696dfbSEd Maste if (set_assert_uv(&opt->dwUserVerificationRequirement, assert->uv, 535*3e696dfbSEd Maste pin) < 0) { 536*3e696dfbSEd Maste fido_log_debug("%s: set_assert_uv", __func__); 5370afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5380afa8e06SEd Maste } 5390afa8e06SEd Maste 5400afa8e06SEd Maste return FIDO_OK; 5410afa8e06SEd Maste } 5420afa8e06SEd Maste 5430afa8e06SEd Maste static int 544f540a430SEd Maste translate_winhello_assert(fido_assert_t *assert, const WEBAUTHN_ASSERTION *wa) 5450afa8e06SEd Maste { 5460afa8e06SEd Maste int r; 5470afa8e06SEd Maste 5480afa8e06SEd Maste if (assert->stmt_len > 0) { 5490afa8e06SEd Maste fido_log_debug("%s: stmt_len=%zu", __func__, assert->stmt_len); 5500afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5510afa8e06SEd Maste } 5520afa8e06SEd Maste if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) { 5530afa8e06SEd Maste fido_log_debug("%s: fido_assert_set_count: %s", __func__, 5540afa8e06SEd Maste fido_strerr(r)); 5550afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5560afa8e06SEd Maste } 5570afa8e06SEd Maste if (unpack_assert_authdata(assert, wa) < 0) { 5580afa8e06SEd Maste fido_log_debug("%s: unpack_assert_authdata", __func__); 5590afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5600afa8e06SEd Maste } 5610afa8e06SEd Maste if (unpack_assert_sig(assert, wa) < 0) { 5620afa8e06SEd Maste fido_log_debug("%s: unpack_assert_sig", __func__); 5630afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5640afa8e06SEd Maste } 5650afa8e06SEd Maste if (unpack_cred_id(assert, wa) < 0) { 5660afa8e06SEd Maste fido_log_debug("%s: unpack_cred_id", __func__); 5670afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5680afa8e06SEd Maste } 5690afa8e06SEd Maste if (unpack_user_id(assert, wa) < 0) { 5700afa8e06SEd Maste fido_log_debug("%s: unpack_user_id", __func__); 5710afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5720afa8e06SEd Maste } 5730afa8e06SEd Maste 5740afa8e06SEd Maste return FIDO_OK; 5750afa8e06SEd Maste } 5760afa8e06SEd Maste 5770afa8e06SEd Maste static int 578f540a430SEd Maste translate_fido_cred(struct winhello_cred *ctx, const fido_cred_t *cred, 579f540a430SEd Maste const char *pin, int ms) 5800afa8e06SEd Maste { 5810afa8e06SEd Maste WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *opt; 5820afa8e06SEd Maste 5830afa8e06SEd Maste if (pack_rp(&ctx->rp_id, &ctx->rp_name, &ctx->rp, &cred->rp) < 0) { 5840afa8e06SEd Maste fido_log_debug("%s: pack_rp", __func__); 5850afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5860afa8e06SEd Maste } 5870afa8e06SEd Maste if (pack_user(&ctx->user_name, &ctx->user_icon, &ctx->display_name, 5880afa8e06SEd Maste &ctx->user, &cred->user) < 0) { 5890afa8e06SEd Maste fido_log_debug("%s: pack_user", __func__); 5900afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5910afa8e06SEd Maste } 5920afa8e06SEd Maste if (pack_cose(&ctx->alg, &ctx->cose, cred->type) < 0) { 5930afa8e06SEd Maste fido_log_debug("%s: pack_cose", __func__); 5940afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5950afa8e06SEd Maste } 5960afa8e06SEd Maste if (pack_cd(&ctx->cd, &cred->cd) < 0) { 5970afa8e06SEd Maste fido_log_debug("%s: pack_cd", __func__); 5980afa8e06SEd Maste return FIDO_ERR_INTERNAL; 5990afa8e06SEd Maste } 6000afa8e06SEd Maste /* options */ 6010afa8e06SEd Maste opt = &ctx->opt; 6020afa8e06SEd Maste opt->dwVersion = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1; 603f540a430SEd Maste opt->dwTimeoutMilliseconds = ms < 0 ? MAXMSEC : (DWORD)ms; 604f540a430SEd Maste opt->dwAttestationConveyancePreference = 605f540a430SEd Maste WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; 6060afa8e06SEd Maste if (pack_credlist(&opt->CredentialList, &cred->excl) < 0) { 6070afa8e06SEd Maste fido_log_debug("%s: pack_credlist", __func__); 6080afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6090afa8e06SEd Maste } 6100afa8e06SEd Maste if (pack_cred_ext(&opt->Extensions, &cred->ext) < 0) { 6110afa8e06SEd Maste fido_log_debug("%s: pack_cred_ext", __func__); 6120afa8e06SEd Maste return FIDO_ERR_UNSUPPORTED_EXTENSION; 6130afa8e06SEd Maste } 614*3e696dfbSEd Maste if (set_cred_uv(&opt->dwUserVerificationRequirement, (cred->ext.mask & 615f540a430SEd Maste FIDO_EXT_CRED_PROTECT) ? FIDO_OPT_TRUE : cred->uv, pin) < 0) { 616*3e696dfbSEd Maste fido_log_debug("%s: set_cred_uv", __func__); 6170afa8e06SEd Maste return FIDO_ERR_INTERNAL; 6180afa8e06SEd Maste } 6190afa8e06SEd Maste if (cred->rk == FIDO_OPT_TRUE) { 6200afa8e06SEd Maste opt->bRequireResidentKey = true; 6210afa8e06SEd Maste } 6220afa8e06SEd Maste 6230afa8e06SEd Maste return FIDO_OK; 6240afa8e06SEd Maste } 6250afa8e06SEd Maste 6260afa8e06SEd Maste static int 627f540a430SEd Maste decode_attobj(const cbor_item_t *key, const cbor_item_t *val, void *arg) 6280afa8e06SEd Maste { 629f540a430SEd Maste fido_cred_t *cred = arg; 630f540a430SEd Maste char *name = NULL; 631f540a430SEd Maste int ok = -1; 632f540a430SEd Maste 633f540a430SEd Maste if (cbor_string_copy(key, &name) < 0) { 634f540a430SEd Maste fido_log_debug("%s: cbor type", __func__); 635f540a430SEd Maste ok = 0; /* ignore */ 636f540a430SEd Maste goto fail; 6370afa8e06SEd Maste } 6380afa8e06SEd Maste 639f540a430SEd Maste if (!strcmp(name, "fmt")) { 640f540a430SEd Maste if (cbor_decode_fmt(val, &cred->fmt) < 0) { 641f540a430SEd Maste fido_log_debug("%s: cbor_decode_fmt", __func__); 642f540a430SEd Maste goto fail; 6430afa8e06SEd Maste } 644f540a430SEd Maste } else if (!strcmp(name, "attStmt")) { 645f540a430SEd Maste if (cbor_decode_attstmt(val, &cred->attstmt) < 0) { 646f540a430SEd Maste fido_log_debug("%s: cbor_decode_attstmt", __func__); 647f540a430SEd Maste goto fail; 6480afa8e06SEd Maste } 649f540a430SEd Maste } else if (!strcmp(name, "authData")) { 650*3e696dfbSEd Maste if (fido_blob_decode(val, &cred->authdata_raw) < 0) { 651*3e696dfbSEd Maste fido_log_debug("%s: fido_blob_decode", __func__); 652*3e696dfbSEd Maste goto fail; 653*3e696dfbSEd Maste } 654f540a430SEd Maste if (cbor_decode_cred_authdata(val, cred->type, 655f540a430SEd Maste &cred->authdata_cbor, &cred->authdata, &cred->attcred, 656f540a430SEd Maste &cred->authdata_ext) < 0) { 657f540a430SEd Maste fido_log_debug("%s: cbor_decode_cred_authdata", 658f540a430SEd Maste __func__); 659f540a430SEd Maste goto fail; 6600afa8e06SEd Maste } 6610afa8e06SEd Maste } 6620afa8e06SEd Maste 663f540a430SEd Maste ok = 0; 664f540a430SEd Maste fail: 665f540a430SEd Maste free(name); 666f540a430SEd Maste 667f540a430SEd Maste return (ok); 6680afa8e06SEd Maste } 6690afa8e06SEd Maste 6700afa8e06SEd Maste static int 671*3e696dfbSEd Maste translate_winhello_cred(fido_cred_t *cred, 672*3e696dfbSEd Maste const WEBAUTHN_CREDENTIAL_ATTESTATION *att) 673f540a430SEd Maste { 674f540a430SEd Maste cbor_item_t *item = NULL; 675f540a430SEd Maste struct cbor_load_result cbor; 676f540a430SEd Maste int r = FIDO_ERR_INTERNAL; 677f540a430SEd Maste 678f540a430SEd Maste if (att->pbAttestationObject == NULL || 679f540a430SEd Maste att->cbAttestationObject > SIZE_MAX) { 680f540a430SEd Maste fido_log_debug("%s: pbAttestationObject", __func__); 681f540a430SEd Maste goto fail; 682f540a430SEd Maste } 683f540a430SEd Maste if ((item = cbor_load(att->pbAttestationObject, 684f540a430SEd Maste (size_t)att->cbAttestationObject, &cbor)) == NULL) { 685f540a430SEd Maste fido_log_debug("%s: cbor_load", __func__); 686f540a430SEd Maste goto fail; 687f540a430SEd Maste } 688f540a430SEd Maste if (cbor_isa_map(item) == false || 689f540a430SEd Maste cbor_map_is_definite(item) == false || 690f540a430SEd Maste cbor_map_iter(item, cred, decode_attobj) < 0) { 691f540a430SEd Maste fido_log_debug("%s: cbor type", __func__); 692f540a430SEd Maste goto fail; 693f540a430SEd Maste } 694f540a430SEd Maste 695f540a430SEd Maste r = FIDO_OK; 696f540a430SEd Maste fail: 697f540a430SEd Maste if (item != NULL) 698f540a430SEd Maste cbor_decref(&item); 699f540a430SEd Maste 700f540a430SEd Maste return r; 701f540a430SEd Maste } 702f540a430SEd Maste 703f540a430SEd Maste static int 7040afa8e06SEd Maste winhello_get_assert(HWND w, struct winhello_assert *ctx) 7050afa8e06SEd Maste { 7060afa8e06SEd Maste HRESULT hr; 7070afa8e06SEd Maste int r = FIDO_OK; 7080afa8e06SEd Maste 709f540a430SEd Maste if ((hr = webauthn_get_assert(w, ctx->rp_id, &ctx->cd, &ctx->opt, 710f540a430SEd Maste &ctx->assert)) != S_OK) { 7110afa8e06SEd Maste r = to_fido(hr); 712f540a430SEd Maste fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), 713f540a430SEd Maste fido_strerr(r)); 7140afa8e06SEd Maste } 7150afa8e06SEd Maste 7160afa8e06SEd Maste return r; 7170afa8e06SEd Maste } 7180afa8e06SEd Maste 7190afa8e06SEd Maste static int 7200afa8e06SEd Maste winhello_make_cred(HWND w, struct winhello_cred *ctx) 7210afa8e06SEd Maste { 7220afa8e06SEd Maste HRESULT hr; 7230afa8e06SEd Maste int r = FIDO_OK; 7240afa8e06SEd Maste 725f540a430SEd Maste if ((hr = webauthn_make_cred(w, &ctx->rp, &ctx->user, &ctx->cose, 726f540a430SEd Maste &ctx->cd, &ctx->opt, &ctx->att)) != S_OK) { 7270afa8e06SEd Maste r = to_fido(hr); 728f540a430SEd Maste fido_log_debug("%s: %ls -> %s", __func__, webauthn_strerr(hr), 729f540a430SEd Maste fido_strerr(r)); 7300afa8e06SEd Maste } 7310afa8e06SEd Maste 7320afa8e06SEd Maste return r; 7330afa8e06SEd Maste } 7340afa8e06SEd Maste 7350afa8e06SEd Maste static void 7360afa8e06SEd Maste winhello_assert_free(struct winhello_assert *ctx) 7370afa8e06SEd Maste { 7380afa8e06SEd Maste if (ctx == NULL) 7390afa8e06SEd Maste return; 7400afa8e06SEd Maste if (ctx->assert != NULL) 741f540a430SEd Maste webauthn_free_assert(ctx->assert); 7420afa8e06SEd Maste 7430afa8e06SEd Maste free(ctx->rp_id); 7440afa8e06SEd Maste free(ctx->opt.CredentialList.pCredentials); 7450afa8e06SEd Maste free(ctx); 7460afa8e06SEd Maste } 7470afa8e06SEd Maste 7480afa8e06SEd Maste static void 7490afa8e06SEd Maste winhello_cred_free(struct winhello_cred *ctx) 7500afa8e06SEd Maste { 7510afa8e06SEd Maste if (ctx == NULL) 7520afa8e06SEd Maste return; 7530afa8e06SEd Maste if (ctx->att != NULL) 754f540a430SEd Maste webauthn_free_attest(ctx->att); 7550afa8e06SEd Maste 7560afa8e06SEd Maste free(ctx->rp_id); 7570afa8e06SEd Maste free(ctx->rp_name); 7580afa8e06SEd Maste free(ctx->user_name); 7590afa8e06SEd Maste free(ctx->user_icon); 7600afa8e06SEd Maste free(ctx->display_name); 7610afa8e06SEd Maste free(ctx->opt.CredentialList.pCredentials); 7620afa8e06SEd Maste for (size_t i = 0; i < ctx->opt.Extensions.cExtensions; i++) { 7630afa8e06SEd Maste WEBAUTHN_EXTENSION *e; 7640afa8e06SEd Maste e = &ctx->opt.Extensions.pExtensions[i]; 7650afa8e06SEd Maste free(e->pvExtension); 7660afa8e06SEd Maste } 7670afa8e06SEd Maste free(ctx->opt.Extensions.pExtensions); 7680afa8e06SEd Maste free(ctx); 7690afa8e06SEd Maste } 7700afa8e06SEd Maste 7710afa8e06SEd Maste int 7720afa8e06SEd Maste fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen) 7730afa8e06SEd Maste { 7740afa8e06SEd Maste fido_dev_info_t *di; 7750afa8e06SEd Maste 7760afa8e06SEd Maste if (ilen == 0) { 7770afa8e06SEd Maste return FIDO_OK; 7780afa8e06SEd Maste } 7790afa8e06SEd Maste if (devlist == NULL) { 7800afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT; 7810afa8e06SEd Maste } 782*3e696dfbSEd Maste if (!webauthn_loaded && webauthn_load() < 0) { 783*3e696dfbSEd Maste fido_log_debug("%s: webauthn_load", __func__); 784*3e696dfbSEd Maste return FIDO_OK; /* not an error */ 7850afa8e06SEd Maste } 7860afa8e06SEd Maste 7870afa8e06SEd Maste di = &devlist[*olen]; 7880afa8e06SEd Maste memset(di, 0, sizeof(*di)); 7890afa8e06SEd Maste di->path = strdup(FIDO_WINHELLO_PATH); 7900afa8e06SEd Maste di->manufacturer = strdup("Microsoft Corporation"); 7910afa8e06SEd Maste di->product = strdup("Windows Hello"); 7920afa8e06SEd Maste di->vendor_id = VENDORID; 7930afa8e06SEd Maste di->product_id = PRODID; 7940afa8e06SEd Maste if (di->path == NULL || di->manufacturer == NULL || 7950afa8e06SEd Maste di->product == NULL) { 7960afa8e06SEd Maste free(di->path); 7970afa8e06SEd Maste free(di->manufacturer); 7980afa8e06SEd Maste free(di->product); 7990afa8e06SEd Maste explicit_bzero(di, sizeof(*di)); 8000afa8e06SEd Maste return FIDO_ERR_INTERNAL; 8010afa8e06SEd Maste } 8020afa8e06SEd Maste ++(*olen); 8030afa8e06SEd Maste 8040afa8e06SEd Maste return FIDO_OK; 8050afa8e06SEd Maste } 8060afa8e06SEd Maste 8070afa8e06SEd Maste int 8080afa8e06SEd Maste fido_winhello_open(fido_dev_t *dev) 8090afa8e06SEd Maste { 810f540a430SEd Maste if (!webauthn_loaded && webauthn_load() < 0) { 811f540a430SEd Maste fido_log_debug("%s: webauthn_load", __func__); 812f540a430SEd Maste return FIDO_ERR_INTERNAL; 813f540a430SEd Maste } 8140afa8e06SEd Maste if (dev->flags != 0) 8150afa8e06SEd Maste return FIDO_ERR_INVALID_ARGUMENT; 8160afa8e06SEd Maste dev->attr.flags = FIDO_CAP_CBOR | FIDO_CAP_WINK; 8170afa8e06SEd Maste dev->flags = FIDO_DEV_WINHELLO | FIDO_DEV_CRED_PROT | FIDO_DEV_PIN_SET; 8180afa8e06SEd Maste 8190afa8e06SEd Maste return FIDO_OK; 8200afa8e06SEd Maste } 8210afa8e06SEd Maste 8220afa8e06SEd Maste int 8230afa8e06SEd Maste fido_winhello_close(fido_dev_t *dev) 8240afa8e06SEd Maste { 8250afa8e06SEd Maste memset(dev, 0, sizeof(*dev)); 8260afa8e06SEd Maste 8270afa8e06SEd Maste return FIDO_OK; 8280afa8e06SEd Maste } 8290afa8e06SEd Maste 8300afa8e06SEd Maste int 8310afa8e06SEd Maste fido_winhello_cancel(fido_dev_t *dev) 8320afa8e06SEd Maste { 8330afa8e06SEd Maste (void)dev; 8340afa8e06SEd Maste 8350afa8e06SEd Maste return FIDO_ERR_INTERNAL; 8360afa8e06SEd Maste } 8370afa8e06SEd Maste 8380afa8e06SEd Maste int 8390afa8e06SEd Maste fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert, 840f540a430SEd Maste const char *pin, int ms) 8410afa8e06SEd Maste { 8420afa8e06SEd Maste HWND w; 8430afa8e06SEd Maste struct winhello_assert *ctx; 8440afa8e06SEd Maste int r = FIDO_ERR_INTERNAL; 8450afa8e06SEd Maste 8460afa8e06SEd Maste (void)dev; 8470afa8e06SEd Maste 848f540a430SEd Maste fido_assert_reset_rx(assert); 849f540a430SEd Maste 8500afa8e06SEd Maste if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 8510afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 8520afa8e06SEd Maste goto fail; 8530afa8e06SEd Maste } 8540afa8e06SEd Maste if ((w = GetForegroundWindow()) == NULL) { 8550afa8e06SEd Maste fido_log_debug("%s: GetForegroundWindow", __func__); 856*3e696dfbSEd Maste if ((w = GetTopWindow(NULL)) == NULL) { 857*3e696dfbSEd Maste fido_log_debug("%s: GetTopWindow", __func__); 8580afa8e06SEd Maste goto fail; 8590afa8e06SEd Maste } 860*3e696dfbSEd Maste } 861f540a430SEd Maste if ((r = translate_fido_assert(ctx, assert, pin, ms)) != FIDO_OK) { 8620afa8e06SEd Maste fido_log_debug("%s: translate_fido_assert", __func__); 8630afa8e06SEd Maste goto fail; 8640afa8e06SEd Maste } 865*3e696dfbSEd Maste if ((r = winhello_get_assert(w, ctx)) != FIDO_OK) { 8660afa8e06SEd Maste fido_log_debug("%s: winhello_get_assert", __func__); 8670afa8e06SEd Maste goto fail; 8680afa8e06SEd Maste } 8690afa8e06SEd Maste if ((r = translate_winhello_assert(assert, ctx->assert)) != FIDO_OK) { 8700afa8e06SEd Maste fido_log_debug("%s: translate_winhello_assert", __func__); 8710afa8e06SEd Maste goto fail; 8720afa8e06SEd Maste } 8730afa8e06SEd Maste 8740afa8e06SEd Maste fail: 8750afa8e06SEd Maste winhello_assert_free(ctx); 8760afa8e06SEd Maste 8770afa8e06SEd Maste return r; 8780afa8e06SEd Maste } 8790afa8e06SEd Maste 8800afa8e06SEd Maste int 8810afa8e06SEd Maste fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci) 8820afa8e06SEd Maste { 8830afa8e06SEd Maste const char *v[3] = { "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE" }; 8840afa8e06SEd Maste const char *e[2] = { "credProtect", "hmac-secret" }; 8850afa8e06SEd Maste const char *t[2] = { "nfc", "usb" }; 8860afa8e06SEd Maste const char *o[4] = { "rk", "up", "plat", "clientPin" }; 8870afa8e06SEd Maste 8880afa8e06SEd Maste (void)dev; 8890afa8e06SEd Maste 8900afa8e06SEd Maste fido_cbor_info_reset(ci); 8910afa8e06SEd Maste 892f540a430SEd Maste if (fido_str_array_pack(&ci->versions, v, nitems(v)) < 0 || 893f540a430SEd Maste fido_str_array_pack(&ci->extensions, e, nitems(e)) < 0 || 894f540a430SEd Maste fido_str_array_pack(&ci->transports, t, nitems(t)) < 0) { 895f540a430SEd Maste fido_log_debug("%s: fido_str_array_pack", __func__); 8960afa8e06SEd Maste return FIDO_ERR_INTERNAL; 8970afa8e06SEd Maste } 8980afa8e06SEd Maste if ((ci->options.name = calloc(nitems(o), sizeof(char *))) == NULL || 8990afa8e06SEd Maste (ci->options.value = calloc(nitems(o), sizeof(bool))) == NULL) { 9000afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 9010afa8e06SEd Maste return FIDO_ERR_INTERNAL; 9020afa8e06SEd Maste } 9030afa8e06SEd Maste for (size_t i = 0; i < nitems(o); i++) { 9040afa8e06SEd Maste if ((ci->options.name[i] = strdup(o[i])) == NULL) { 9050afa8e06SEd Maste fido_log_debug("%s: strdup", __func__); 9060afa8e06SEd Maste return FIDO_ERR_INTERNAL; 9070afa8e06SEd Maste } 9080afa8e06SEd Maste ci->options.value[i] = true; 9090afa8e06SEd Maste ci->options.len++; 9100afa8e06SEd Maste } 9110afa8e06SEd Maste 9120afa8e06SEd Maste return FIDO_OK; 9130afa8e06SEd Maste } 9140afa8e06SEd Maste 9150afa8e06SEd Maste int 916f540a430SEd Maste fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin, 917f540a430SEd Maste int ms) 9180afa8e06SEd Maste { 9190afa8e06SEd Maste HWND w; 9200afa8e06SEd Maste struct winhello_cred *ctx; 9210afa8e06SEd Maste int r = FIDO_ERR_INTERNAL; 9220afa8e06SEd Maste 9230afa8e06SEd Maste (void)dev; 9240afa8e06SEd Maste 925f540a430SEd Maste fido_cred_reset_rx(cred); 926f540a430SEd Maste 9270afa8e06SEd Maste if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { 9280afa8e06SEd Maste fido_log_debug("%s: calloc", __func__); 9290afa8e06SEd Maste goto fail; 9300afa8e06SEd Maste } 9310afa8e06SEd Maste if ((w = GetForegroundWindow()) == NULL) { 9320afa8e06SEd Maste fido_log_debug("%s: GetForegroundWindow", __func__); 933*3e696dfbSEd Maste if ((w = GetTopWindow(NULL)) == NULL) { 934*3e696dfbSEd Maste fido_log_debug("%s: GetTopWindow", __func__); 9350afa8e06SEd Maste goto fail; 9360afa8e06SEd Maste } 937*3e696dfbSEd Maste } 938f540a430SEd Maste if ((r = translate_fido_cred(ctx, cred, pin, ms)) != FIDO_OK) { 9390afa8e06SEd Maste fido_log_debug("%s: translate_fido_cred", __func__); 9400afa8e06SEd Maste goto fail; 9410afa8e06SEd Maste } 9420afa8e06SEd Maste if ((r = winhello_make_cred(w, ctx)) != FIDO_OK) { 9430afa8e06SEd Maste fido_log_debug("%s: winhello_make_cred", __func__); 9440afa8e06SEd Maste goto fail; 9450afa8e06SEd Maste } 9460afa8e06SEd Maste if ((r = translate_winhello_cred(cred, ctx->att)) != FIDO_OK) { 9470afa8e06SEd Maste fido_log_debug("%s: translate_winhello_cred", __func__); 9480afa8e06SEd Maste goto fail; 9490afa8e06SEd Maste } 9500afa8e06SEd Maste 9510afa8e06SEd Maste r = FIDO_OK; 9520afa8e06SEd Maste fail: 9530afa8e06SEd Maste winhello_cred_free(ctx); 9540afa8e06SEd Maste 9550afa8e06SEd Maste return r; 9560afa8e06SEd Maste } 957