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