xref: /freebsd/contrib/libfido2/src/winhello.c (revision 60a517b66a69b8c011b04063ef63a938738719bd)
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