xref: /freebsd/contrib/libfido2/src/winhello.c (revision 0afa8e065e14bb8fd338d75690e0238c00167d40)
1*0afa8e06SEd Maste /*
2*0afa8e06SEd Maste  * Copyright (c) 2021 Yubico AB. All rights reserved.
3*0afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
4*0afa8e06SEd Maste  * license that can be found in the LICENSE file.
5*0afa8e06SEd Maste  */
6*0afa8e06SEd Maste 
7*0afa8e06SEd Maste #include <sys/types.h>
8*0afa8e06SEd Maste 
9*0afa8e06SEd Maste #include <stdlib.h>
10*0afa8e06SEd Maste #include <windows.h>
11*0afa8e06SEd Maste #include <webauthn.h>
12*0afa8e06SEd Maste 
13*0afa8e06SEd Maste #include "fido.h"
14*0afa8e06SEd Maste 
15*0afa8e06SEd Maste #define MAXCHARS	128
16*0afa8e06SEd Maste #define MAXCREDS	128
17*0afa8e06SEd Maste #define MAXMSEC		6000 * 1000
18*0afa8e06SEd Maste #define VENDORID	0x045e
19*0afa8e06SEd Maste #define PRODID		0x0001
20*0afa8e06SEd Maste 
21*0afa8e06SEd Maste struct winhello_assert {
22*0afa8e06SEd Maste 	WEBAUTHN_CLIENT_DATA				 cd;
23*0afa8e06SEd Maste 	WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS	 opt;
24*0afa8e06SEd Maste 	WEBAUTHN_ASSERTION				*assert;
25*0afa8e06SEd Maste 	wchar_t						*rp_id;
26*0afa8e06SEd Maste };
27*0afa8e06SEd Maste 
28*0afa8e06SEd Maste struct winhello_cred {
29*0afa8e06SEd Maste 	WEBAUTHN_RP_ENTITY_INFORMATION			 rp;
30*0afa8e06SEd Maste 	WEBAUTHN_USER_ENTITY_INFORMATION		 user;
31*0afa8e06SEd Maste 	WEBAUTHN_COSE_CREDENTIAL_PARAMETER		 alg;
32*0afa8e06SEd Maste 	WEBAUTHN_COSE_CREDENTIAL_PARAMETERS		 cose;
33*0afa8e06SEd Maste 	WEBAUTHN_CLIENT_DATA				 cd;
34*0afa8e06SEd Maste 	WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS	 opt;
35*0afa8e06SEd Maste 	WEBAUTHN_CREDENTIAL_ATTESTATION			*att;
36*0afa8e06SEd Maste 	wchar_t						*rp_id;
37*0afa8e06SEd Maste 	wchar_t						*rp_name;
38*0afa8e06SEd Maste 	wchar_t						*user_name;
39*0afa8e06SEd Maste 	wchar_t						*user_icon;
40*0afa8e06SEd Maste 	wchar_t						*display_name;
41*0afa8e06SEd Maste };
42*0afa8e06SEd Maste 
43*0afa8e06SEd Maste static wchar_t *
44*0afa8e06SEd Maste to_utf16(const char *utf8)
45*0afa8e06SEd Maste {
46*0afa8e06SEd Maste 	int nch;
47*0afa8e06SEd Maste 	wchar_t *utf16;
48*0afa8e06SEd Maste 
49*0afa8e06SEd Maste 	if (utf8 == NULL) {
50*0afa8e06SEd Maste 		fido_log_debug("%s: NULL", __func__);
51*0afa8e06SEd Maste 		return NULL;
52*0afa8e06SEd Maste 	}
53*0afa8e06SEd Maste 	if ((nch = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0)) < 1 ||
54*0afa8e06SEd Maste 	    (size_t)nch > MAXCHARS) {
55*0afa8e06SEd Maste 		fido_log_debug("%s: MultiByteToWideChar %d", __func__, nch);
56*0afa8e06SEd Maste 		return NULL;
57*0afa8e06SEd Maste 	}
58*0afa8e06SEd Maste 	if ((utf16 = calloc((size_t)nch, sizeof(*utf16))) == NULL) {
59*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
60*0afa8e06SEd Maste 		return NULL;
61*0afa8e06SEd Maste 	}
62*0afa8e06SEd Maste 	if (MultiByteToWideChar(CP_UTF8, 0, utf8, -1, utf16, nch) != nch) {
63*0afa8e06SEd Maste 		fido_log_debug("%s: MultiByteToWideChar", __func__);
64*0afa8e06SEd Maste 		free(utf16);
65*0afa8e06SEd Maste 		return NULL;
66*0afa8e06SEd Maste 	}
67*0afa8e06SEd Maste 
68*0afa8e06SEd Maste 	return utf16;
69*0afa8e06SEd Maste }
70*0afa8e06SEd Maste 
71*0afa8e06SEd Maste static char *
72*0afa8e06SEd Maste to_utf8(const wchar_t *utf16)
73*0afa8e06SEd Maste {
74*0afa8e06SEd Maste 	int nch;
75*0afa8e06SEd Maste 	char *utf8;
76*0afa8e06SEd Maste 
77*0afa8e06SEd Maste 	if (utf16 == NULL) {
78*0afa8e06SEd Maste 		fido_log_debug("%s: NULL", __func__);
79*0afa8e06SEd Maste 		return NULL;
80*0afa8e06SEd Maste 	}
81*0afa8e06SEd Maste 	if ((nch = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16,
82*0afa8e06SEd Maste 	    -1, NULL, 0, NULL, NULL)) < 1 || (size_t)nch > MAXCHARS) {
83*0afa8e06SEd Maste 		fido_log_debug("%s: WideCharToMultiByte %d", __func__);
84*0afa8e06SEd Maste 		return NULL;
85*0afa8e06SEd Maste 	}
86*0afa8e06SEd Maste 	if ((utf8 = calloc((size_t)nch, sizeof(*utf8))) == NULL) {
87*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
88*0afa8e06SEd Maste 		return NULL;
89*0afa8e06SEd Maste 	}
90*0afa8e06SEd Maste 	if (WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16, -1,
91*0afa8e06SEd Maste 	    utf8, nch, NULL, NULL) != nch) {
92*0afa8e06SEd Maste 		fido_log_debug("%s: WideCharToMultiByte", __func__);
93*0afa8e06SEd Maste 		free(utf8);
94*0afa8e06SEd Maste 		return NULL;
95*0afa8e06SEd Maste 	}
96*0afa8e06SEd Maste 
97*0afa8e06SEd Maste 	return utf8;
98*0afa8e06SEd Maste }
99*0afa8e06SEd Maste 
100*0afa8e06SEd Maste static int
101*0afa8e06SEd Maste to_fido_str_array(fido_str_array_t *sa, const char **v, size_t n)
102*0afa8e06SEd Maste {
103*0afa8e06SEd Maste 	if ((sa->ptr = calloc(n, sizeof(char *))) == NULL) {
104*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
105*0afa8e06SEd Maste 		return -1;
106*0afa8e06SEd Maste 	}
107*0afa8e06SEd Maste 	for (size_t i = 0; i < n; i++) {
108*0afa8e06SEd Maste 		if ((sa->ptr[i] = strdup(v[i])) == NULL) {
109*0afa8e06SEd Maste 			fido_log_debug("%s: strdup", __func__);
110*0afa8e06SEd Maste 			return -1;
111*0afa8e06SEd Maste 		}
112*0afa8e06SEd Maste 		sa->len++;
113*0afa8e06SEd Maste 	}
114*0afa8e06SEd Maste 
115*0afa8e06SEd Maste 	return 0;
116*0afa8e06SEd Maste }
117*0afa8e06SEd Maste 
118*0afa8e06SEd Maste static int
119*0afa8e06SEd Maste to_fido(HRESULT hr)
120*0afa8e06SEd Maste {
121*0afa8e06SEd Maste 	switch (hr) {
122*0afa8e06SEd Maste 	case NTE_NOT_SUPPORTED:
123*0afa8e06SEd Maste 		return FIDO_ERR_UNSUPPORTED_OPTION;
124*0afa8e06SEd Maste 	case NTE_INVALID_PARAMETER:
125*0afa8e06SEd Maste 		return FIDO_ERR_INVALID_PARAMETER;
126*0afa8e06SEd Maste 	case NTE_TOKEN_KEYSET_STORAGE_FULL:
127*0afa8e06SEd Maste 		return FIDO_ERR_KEY_STORE_FULL;
128*0afa8e06SEd Maste 	case NTE_DEVICE_NOT_FOUND:
129*0afa8e06SEd Maste 	case NTE_NOT_FOUND:
130*0afa8e06SEd Maste 		return FIDO_ERR_NOT_ALLOWED;
131*0afa8e06SEd Maste 	default:
132*0afa8e06SEd Maste 		fido_log_debug("%s: hr=0x%x", __func__, hr);
133*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
134*0afa8e06SEd Maste 	}
135*0afa8e06SEd Maste }
136*0afa8e06SEd Maste 
137*0afa8e06SEd Maste static int
138*0afa8e06SEd Maste pack_cd(WEBAUTHN_CLIENT_DATA *out, const fido_blob_t *in)
139*0afa8e06SEd Maste {
140*0afa8e06SEd Maste 	if (in->ptr == NULL) {
141*0afa8e06SEd Maste 		fido_log_debug("%s: NULL", __func__);
142*0afa8e06SEd Maste 		return -1;
143*0afa8e06SEd Maste 	}
144*0afa8e06SEd Maste 	if (in->len > ULONG_MAX) {
145*0afa8e06SEd Maste 		fido_log_debug("%s: in->len=%zu", __func__, in->len);
146*0afa8e06SEd Maste 		return -1;
147*0afa8e06SEd Maste 	}
148*0afa8e06SEd Maste 	out->dwVersion = WEBAUTHN_CLIENT_DATA_CURRENT_VERSION;
149*0afa8e06SEd Maste 	out->cbClientDataJSON = (DWORD)in->len;
150*0afa8e06SEd Maste 	out->pbClientDataJSON = in->ptr;
151*0afa8e06SEd Maste 	out->pwszHashAlgId = WEBAUTHN_HASH_ALGORITHM_SHA_256;
152*0afa8e06SEd Maste 
153*0afa8e06SEd Maste 	return 0;
154*0afa8e06SEd Maste }
155*0afa8e06SEd Maste 
156*0afa8e06SEd Maste static int
157*0afa8e06SEd Maste pack_credlist(WEBAUTHN_CREDENTIALS *out, const fido_blob_array_t *in)
158*0afa8e06SEd Maste {
159*0afa8e06SEd Maste 	WEBAUTHN_CREDENTIAL *c;
160*0afa8e06SEd Maste 
161*0afa8e06SEd Maste 	if (in->len == 0) {
162*0afa8e06SEd Maste 		return 0; /* nothing to do */
163*0afa8e06SEd Maste 	}
164*0afa8e06SEd Maste 	if (in->len > MAXCREDS) {
165*0afa8e06SEd Maste 		fido_log_debug("%s: in->len=%zu", __func__, in->len);
166*0afa8e06SEd Maste 		return -1;
167*0afa8e06SEd Maste 	}
168*0afa8e06SEd Maste 	if ((out->pCredentials = calloc(in->len, sizeof(*c))) == NULL) {
169*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
170*0afa8e06SEd Maste 		return -1;
171*0afa8e06SEd Maste 	}
172*0afa8e06SEd Maste 	out->cCredentials = (DWORD)in->len;
173*0afa8e06SEd Maste 	for (size_t i = 0; i < in->len; i++) {
174*0afa8e06SEd Maste 		if (in->ptr[i].len > ULONG_MAX) {
175*0afa8e06SEd Maste 			fido_log_debug("%s: %zu", __func__, in->ptr[i].len);
176*0afa8e06SEd Maste 			return -1;
177*0afa8e06SEd Maste 		}
178*0afa8e06SEd Maste 		c = &out->pCredentials[i];
179*0afa8e06SEd Maste 		c->dwVersion = WEBAUTHN_CREDENTIAL_CURRENT_VERSION;
180*0afa8e06SEd Maste 		c->cbId = (DWORD)in->ptr[i].len;
181*0afa8e06SEd Maste 		c->pbId = in->ptr[i].ptr;
182*0afa8e06SEd Maste 		c->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY;
183*0afa8e06SEd Maste 	}
184*0afa8e06SEd Maste 
185*0afa8e06SEd Maste 	return 0;
186*0afa8e06SEd Maste }
187*0afa8e06SEd Maste 
188*0afa8e06SEd Maste static int
189*0afa8e06SEd Maste set_uv(DWORD *out, fido_opt_t uv, const char *pin)
190*0afa8e06SEd Maste {
191*0afa8e06SEd Maste 	if (pin) {
192*0afa8e06SEd Maste 		*out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
193*0afa8e06SEd Maste 		return 0;
194*0afa8e06SEd Maste 	}
195*0afa8e06SEd Maste 
196*0afa8e06SEd Maste 	switch (uv) {
197*0afa8e06SEd Maste 	case FIDO_OPT_OMIT:
198*0afa8e06SEd Maste 		*out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_ANY;
199*0afa8e06SEd Maste 		break;
200*0afa8e06SEd Maste 	case FIDO_OPT_FALSE:
201*0afa8e06SEd Maste 		*out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED;
202*0afa8e06SEd Maste 		break;
203*0afa8e06SEd Maste 	case FIDO_OPT_TRUE:
204*0afa8e06SEd Maste 		*out = WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED;
205*0afa8e06SEd Maste 		break;
206*0afa8e06SEd Maste 	}
207*0afa8e06SEd Maste 
208*0afa8e06SEd Maste 	return 0;
209*0afa8e06SEd Maste }
210*0afa8e06SEd Maste 
211*0afa8e06SEd Maste static int
212*0afa8e06SEd Maste pack_rp(wchar_t **id, wchar_t **name, WEBAUTHN_RP_ENTITY_INFORMATION *out,
213*0afa8e06SEd Maste     fido_rp_t *in)
214*0afa8e06SEd Maste {
215*0afa8e06SEd Maste 	/* keep non-const copies of pwsz* for free() */
216*0afa8e06SEd Maste 	out->dwVersion = WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION;
217*0afa8e06SEd Maste 	if ((out->pwszId = *id = to_utf16(in->id)) == NULL) {
218*0afa8e06SEd Maste 		fido_log_debug("%s: id", __func__);
219*0afa8e06SEd Maste 		return -1;
220*0afa8e06SEd Maste 	}
221*0afa8e06SEd Maste 	if (in->name && (out->pwszName = *name = to_utf16(in->name)) == NULL) {
222*0afa8e06SEd Maste 		fido_log_debug("%s: name", __func__);
223*0afa8e06SEd Maste 		return -1;
224*0afa8e06SEd Maste 	}
225*0afa8e06SEd Maste 	return 0;
226*0afa8e06SEd Maste }
227*0afa8e06SEd Maste 
228*0afa8e06SEd Maste static int
229*0afa8e06SEd Maste pack_user(wchar_t **name, wchar_t **icon, wchar_t **display_name,
230*0afa8e06SEd Maste     WEBAUTHN_USER_ENTITY_INFORMATION *out, fido_user_t *in)
231*0afa8e06SEd Maste {
232*0afa8e06SEd Maste 	if (in->id.ptr == NULL || in->id.len > ULONG_MAX) {
233*0afa8e06SEd Maste 		fido_log_debug("%s: id", __func__);
234*0afa8e06SEd Maste 		return -1;
235*0afa8e06SEd Maste 	}
236*0afa8e06SEd Maste 	out->dwVersion = WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION;
237*0afa8e06SEd Maste 	out->cbId = (DWORD)in->id.len;
238*0afa8e06SEd Maste 	out->pbId = in->id.ptr;
239*0afa8e06SEd Maste 	/* keep non-const copies of pwsz* for free() */
240*0afa8e06SEd Maste 	if (in->name != NULL) {
241*0afa8e06SEd Maste 		if ((out->pwszName = *name = to_utf16(in->name)) == NULL) {
242*0afa8e06SEd Maste 			fido_log_debug("%s: name", __func__);
243*0afa8e06SEd Maste 			return -1;
244*0afa8e06SEd Maste 		}
245*0afa8e06SEd Maste 	}
246*0afa8e06SEd Maste 	if (in->icon != NULL) {
247*0afa8e06SEd Maste 		if ((out->pwszIcon = *icon = to_utf16(in->icon)) == NULL) {
248*0afa8e06SEd Maste 			fido_log_debug("%s: icon", __func__);
249*0afa8e06SEd Maste 			return -1;
250*0afa8e06SEd Maste 		}
251*0afa8e06SEd Maste 	}
252*0afa8e06SEd Maste 	if (in->display_name != NULL) {
253*0afa8e06SEd Maste 		if ((out->pwszDisplayName = *display_name =
254*0afa8e06SEd Maste 		    to_utf16(in->display_name)) == NULL) {
255*0afa8e06SEd Maste 			fido_log_debug("%s: display_name", __func__);
256*0afa8e06SEd Maste 			return -1;
257*0afa8e06SEd Maste 		}
258*0afa8e06SEd Maste 	}
259*0afa8e06SEd Maste 
260*0afa8e06SEd Maste 	return 0;
261*0afa8e06SEd Maste }
262*0afa8e06SEd Maste 
263*0afa8e06SEd Maste static int
264*0afa8e06SEd Maste pack_cose(WEBAUTHN_COSE_CREDENTIAL_PARAMETER *alg,
265*0afa8e06SEd Maste     WEBAUTHN_COSE_CREDENTIAL_PARAMETERS *cose, int type)
266*0afa8e06SEd Maste {
267*0afa8e06SEd Maste 	switch (type) {
268*0afa8e06SEd Maste 	case COSE_ES256:
269*0afa8e06SEd Maste 		alg->lAlg = WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256;
270*0afa8e06SEd Maste 		break;
271*0afa8e06SEd Maste 	case COSE_EDDSA:
272*0afa8e06SEd Maste 		alg->lAlg = -8; /* XXX */;
273*0afa8e06SEd Maste 		break;
274*0afa8e06SEd Maste 	case COSE_RS256:
275*0afa8e06SEd Maste 		alg->lAlg = WEBAUTHN_COSE_ALGORITHM_RSASSA_PKCS1_V1_5_WITH_SHA256;
276*0afa8e06SEd Maste 		break;
277*0afa8e06SEd Maste 	default:
278*0afa8e06SEd Maste 		fido_log_debug("%s: type %d", __func__, type);
279*0afa8e06SEd Maste 		return -1;
280*0afa8e06SEd Maste 	}
281*0afa8e06SEd Maste 	alg->dwVersion = WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION;
282*0afa8e06SEd Maste 	alg->pwszCredentialType = WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY;
283*0afa8e06SEd Maste 	cose->cCredentialParameters = 1;
284*0afa8e06SEd Maste 	cose->pCredentialParameters = alg;
285*0afa8e06SEd Maste 
286*0afa8e06SEd Maste 	return 0;
287*0afa8e06SEd Maste }
288*0afa8e06SEd Maste 
289*0afa8e06SEd Maste static int
290*0afa8e06SEd Maste pack_cred_ext(WEBAUTHN_EXTENSIONS *out, fido_cred_ext_t *in)
291*0afa8e06SEd Maste {
292*0afa8e06SEd Maste 	WEBAUTHN_EXTENSION *e;
293*0afa8e06SEd Maste 	WEBAUTHN_CRED_PROTECT_EXTENSION_IN *p;
294*0afa8e06SEd Maste 	BOOL *b;
295*0afa8e06SEd Maste 	size_t n = 0, i = 0;
296*0afa8e06SEd Maste 
297*0afa8e06SEd Maste 	if (in->mask == 0) {
298*0afa8e06SEd Maste 		return 0; /* nothing to do */
299*0afa8e06SEd Maste 	}
300*0afa8e06SEd Maste 	if (in->mask & ~(FIDO_EXT_HMAC_SECRET | FIDO_EXT_CRED_PROTECT)) {
301*0afa8e06SEd Maste 		fido_log_debug("%s: mask 0x%x", in->mask);
302*0afa8e06SEd Maste 		return -1;
303*0afa8e06SEd Maste 	}
304*0afa8e06SEd Maste 	if (in->mask & FIDO_EXT_HMAC_SECRET)
305*0afa8e06SEd Maste 		n++;
306*0afa8e06SEd Maste 	if (in->mask & FIDO_EXT_CRED_PROTECT)
307*0afa8e06SEd Maste 		n++;
308*0afa8e06SEd Maste 	if ((out->pExtensions = calloc(n, sizeof(*e))) == NULL) {
309*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
310*0afa8e06SEd Maste 		return -1;
311*0afa8e06SEd Maste 	}
312*0afa8e06SEd Maste 	out->cExtensions = (DWORD)n;
313*0afa8e06SEd Maste 	if (in->mask & FIDO_EXT_HMAC_SECRET) {
314*0afa8e06SEd Maste 		if ((b = calloc(1, sizeof(*b))) == NULL) {
315*0afa8e06SEd Maste 			fido_log_debug("%s: calloc", __func__);
316*0afa8e06SEd Maste 			return -1;
317*0afa8e06SEd Maste 		}
318*0afa8e06SEd Maste 		*b = true;
319*0afa8e06SEd Maste 		e = &out->pExtensions[i];
320*0afa8e06SEd Maste 		e->pwszExtensionIdentifier =
321*0afa8e06SEd Maste 		    WEBAUTHN_EXTENSIONS_IDENTIFIER_HMAC_SECRET;
322*0afa8e06SEd Maste 		e->pvExtension = b;
323*0afa8e06SEd Maste 		e->cbExtension = sizeof(*b);
324*0afa8e06SEd Maste 		i++;
325*0afa8e06SEd Maste 	}
326*0afa8e06SEd Maste 	if (in->mask & FIDO_EXT_CRED_PROTECT) {
327*0afa8e06SEd Maste 		if ((p = calloc(1, sizeof(*p))) == NULL) {
328*0afa8e06SEd Maste 			fido_log_debug("%s: calloc", __func__);
329*0afa8e06SEd Maste 			return -1;
330*0afa8e06SEd Maste 		}
331*0afa8e06SEd Maste 		p->dwCredProtect = (DWORD)in->prot;
332*0afa8e06SEd Maste 		p->bRequireCredProtect = true;
333*0afa8e06SEd Maste 		e = &out->pExtensions[i];
334*0afa8e06SEd Maste 		e->pwszExtensionIdentifier =
335*0afa8e06SEd Maste 		    WEBAUTHN_EXTENSIONS_IDENTIFIER_CRED_PROTECT;
336*0afa8e06SEd Maste 		e->pvExtension = p;
337*0afa8e06SEd Maste 		e->cbExtension = sizeof(*p);
338*0afa8e06SEd Maste 		i++;
339*0afa8e06SEd Maste 	}
340*0afa8e06SEd Maste 
341*0afa8e06SEd Maste 	return 0;
342*0afa8e06SEd Maste }
343*0afa8e06SEd Maste 
344*0afa8e06SEd Maste static int
345*0afa8e06SEd Maste unpack_fmt(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att)
346*0afa8e06SEd Maste {
347*0afa8e06SEd Maste 	char *fmt;
348*0afa8e06SEd Maste 	int r;
349*0afa8e06SEd Maste 
350*0afa8e06SEd Maste 	if ((fmt = to_utf8(att->pwszFormatType)) == NULL) {
351*0afa8e06SEd Maste 		fido_log_debug("%s: fmt", __func__);
352*0afa8e06SEd Maste 		return -1;
353*0afa8e06SEd Maste 	}
354*0afa8e06SEd Maste 	r = fido_cred_set_fmt(cred, fmt);
355*0afa8e06SEd Maste 	free(fmt);
356*0afa8e06SEd Maste 	fmt = NULL;
357*0afa8e06SEd Maste 	if (r != FIDO_OK) {
358*0afa8e06SEd Maste 		fido_log_debug("%s: fido_cred_set_fmt: %s", __func__,
359*0afa8e06SEd Maste 		    fido_strerr(r));
360*0afa8e06SEd Maste 		return -1;
361*0afa8e06SEd Maste 	}
362*0afa8e06SEd Maste 
363*0afa8e06SEd Maste 	return 0;
364*0afa8e06SEd Maste }
365*0afa8e06SEd Maste 
366*0afa8e06SEd Maste static int
367*0afa8e06SEd Maste unpack_cred_authdata(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att)
368*0afa8e06SEd Maste {
369*0afa8e06SEd Maste 	int r;
370*0afa8e06SEd Maste 
371*0afa8e06SEd Maste 	if (att->cbAuthenticatorData > SIZE_MAX) {
372*0afa8e06SEd Maste 		fido_log_debug("%s: cbAuthenticatorData", __func__);
373*0afa8e06SEd Maste 		return -1;
374*0afa8e06SEd Maste 	}
375*0afa8e06SEd Maste 	if ((r = fido_cred_set_authdata_raw(cred, att->pbAuthenticatorData,
376*0afa8e06SEd Maste 	    (size_t)att->cbAuthenticatorData)) != FIDO_OK) {
377*0afa8e06SEd Maste 		fido_log_debug("%s: fido_cred_set_authdata_raw: %s", __func__,
378*0afa8e06SEd Maste 		    fido_strerr(r));
379*0afa8e06SEd Maste 		return -1;
380*0afa8e06SEd Maste 	}
381*0afa8e06SEd Maste 
382*0afa8e06SEd Maste 	return 0;
383*0afa8e06SEd Maste }
384*0afa8e06SEd Maste 
385*0afa8e06SEd Maste static int
386*0afa8e06SEd Maste unpack_cred_sig(fido_cred_t *cred, WEBAUTHN_COMMON_ATTESTATION *attr)
387*0afa8e06SEd Maste {
388*0afa8e06SEd Maste 	int r;
389*0afa8e06SEd Maste 
390*0afa8e06SEd Maste 	if (attr->cbSignature > SIZE_MAX) {
391*0afa8e06SEd Maste 		fido_log_debug("%s: cbSignature", __func__);
392*0afa8e06SEd Maste 		return -1;
393*0afa8e06SEd Maste 	}
394*0afa8e06SEd Maste 	if ((r = fido_cred_set_sig(cred, attr->pbSignature,
395*0afa8e06SEd Maste 	    (size_t)attr->cbSignature)) != FIDO_OK) {
396*0afa8e06SEd Maste 		fido_log_debug("%s: fido_cred_set_sig: %s", __func__,
397*0afa8e06SEd Maste 		    fido_strerr(r));
398*0afa8e06SEd Maste 		return -1;
399*0afa8e06SEd Maste 	}
400*0afa8e06SEd Maste 
401*0afa8e06SEd Maste 	return 0;
402*0afa8e06SEd Maste }
403*0afa8e06SEd Maste 
404*0afa8e06SEd Maste static int
405*0afa8e06SEd Maste unpack_x5c(fido_cred_t *cred, WEBAUTHN_COMMON_ATTESTATION *attr)
406*0afa8e06SEd Maste {
407*0afa8e06SEd Maste 	int r;
408*0afa8e06SEd Maste 
409*0afa8e06SEd Maste 	fido_log_debug("%s: %u cert(s)", __func__, attr->cX5c);
410*0afa8e06SEd Maste 
411*0afa8e06SEd Maste 	if (attr->cX5c == 0)
412*0afa8e06SEd Maste 		return 0; /* self-attestation */
413*0afa8e06SEd Maste 	if (attr->lAlg != WEBAUTHN_COSE_ALGORITHM_ECDSA_P256_WITH_SHA256) {
414*0afa8e06SEd Maste 		fido_log_debug("%s: lAlg %d", __func__, attr->lAlg);
415*0afa8e06SEd Maste 		return -1;
416*0afa8e06SEd Maste 	}
417*0afa8e06SEd Maste 	if (attr->pX5c[0].cbData > SIZE_MAX) {
418*0afa8e06SEd Maste 		fido_log_debug("%s: cbData", __func__);
419*0afa8e06SEd Maste 		return -1;
420*0afa8e06SEd Maste 	}
421*0afa8e06SEd Maste 	if ((r = fido_cred_set_x509(cred, attr->pX5c[0].pbData,
422*0afa8e06SEd Maste 	    (size_t)attr->pX5c[0].cbData)) != FIDO_OK) {
423*0afa8e06SEd Maste 		fido_log_debug("%s: fido_cred_set_x509: %s", __func__,
424*0afa8e06SEd Maste 		    fido_strerr(r));
425*0afa8e06SEd Maste 		return -1;
426*0afa8e06SEd Maste 	}
427*0afa8e06SEd Maste 
428*0afa8e06SEd Maste 	return 0;
429*0afa8e06SEd Maste }
430*0afa8e06SEd Maste 
431*0afa8e06SEd Maste static int
432*0afa8e06SEd Maste unpack_assert_authdata(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa)
433*0afa8e06SEd Maste {
434*0afa8e06SEd Maste 	int r;
435*0afa8e06SEd Maste 
436*0afa8e06SEd Maste 	if (wa->cbAuthenticatorData > SIZE_MAX) {
437*0afa8e06SEd Maste 		fido_log_debug("%s: cbAuthenticatorData", __func__);
438*0afa8e06SEd Maste 		return -1;
439*0afa8e06SEd Maste 	}
440*0afa8e06SEd Maste 	if ((r = fido_assert_set_authdata_raw(assert, 0, wa->pbAuthenticatorData,
441*0afa8e06SEd Maste 	    (size_t)wa->cbAuthenticatorData)) != FIDO_OK) {
442*0afa8e06SEd Maste 		fido_log_debug("%s: fido_assert_set_authdata_raw: %s", __func__,
443*0afa8e06SEd Maste 		    fido_strerr(r));
444*0afa8e06SEd Maste 		return -1;
445*0afa8e06SEd Maste 	}
446*0afa8e06SEd Maste 
447*0afa8e06SEd Maste 	return 0;
448*0afa8e06SEd Maste }
449*0afa8e06SEd Maste 
450*0afa8e06SEd Maste static int
451*0afa8e06SEd Maste unpack_assert_sig(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa)
452*0afa8e06SEd Maste {
453*0afa8e06SEd Maste 	int r;
454*0afa8e06SEd Maste 
455*0afa8e06SEd Maste 	if (wa->cbSignature > SIZE_MAX) {
456*0afa8e06SEd Maste 		fido_log_debug("%s: cbSignature", __func__);
457*0afa8e06SEd Maste 		return -1;
458*0afa8e06SEd Maste 	}
459*0afa8e06SEd Maste 	if ((r = fido_assert_set_sig(assert, 0, wa->pbSignature,
460*0afa8e06SEd Maste 	    (size_t)wa->cbSignature)) != FIDO_OK) {
461*0afa8e06SEd Maste 		fido_log_debug("%s: fido_assert_set_sig: %s", __func__,
462*0afa8e06SEd Maste 		    fido_strerr(r));
463*0afa8e06SEd Maste 		return -1;
464*0afa8e06SEd Maste 	}
465*0afa8e06SEd Maste 
466*0afa8e06SEd Maste 	return 0;
467*0afa8e06SEd Maste }
468*0afa8e06SEd Maste 
469*0afa8e06SEd Maste static int
470*0afa8e06SEd Maste unpack_cred_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa)
471*0afa8e06SEd Maste {
472*0afa8e06SEd Maste 	if (wa->Credential.cbId > SIZE_MAX) {
473*0afa8e06SEd Maste 		fido_log_debug("%s: Credential.cbId", __func__);
474*0afa8e06SEd Maste 		return -1;
475*0afa8e06SEd Maste 	}
476*0afa8e06SEd Maste 	if (fido_blob_set(&assert->stmt[0].id, wa->Credential.pbId,
477*0afa8e06SEd Maste 	    (size_t)wa->Credential.cbId) < 0) {
478*0afa8e06SEd Maste 		fido_log_debug("%s: fido_blob_set", __func__);
479*0afa8e06SEd Maste 		return -1;
480*0afa8e06SEd Maste 	}
481*0afa8e06SEd Maste 
482*0afa8e06SEd Maste 	return 0;
483*0afa8e06SEd Maste }
484*0afa8e06SEd Maste 
485*0afa8e06SEd Maste static int
486*0afa8e06SEd Maste unpack_user_id(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa)
487*0afa8e06SEd Maste {
488*0afa8e06SEd Maste 	if (wa->cbUserId == 0)
489*0afa8e06SEd Maste 		return 0; /* user id absent */
490*0afa8e06SEd Maste 	if (wa->cbUserId > SIZE_MAX) {
491*0afa8e06SEd Maste 		fido_log_debug("%s: cbUserId", __func__);
492*0afa8e06SEd Maste 		return -1;
493*0afa8e06SEd Maste 	}
494*0afa8e06SEd Maste 	if (fido_blob_set(&assert->stmt[0].user.id, wa->pbUserId,
495*0afa8e06SEd Maste 	    (size_t)wa->cbUserId) < 0) {
496*0afa8e06SEd Maste 		fido_log_debug("%s: fido_blob_set", __func__);
497*0afa8e06SEd Maste 		return -1;
498*0afa8e06SEd Maste 	}
499*0afa8e06SEd Maste 
500*0afa8e06SEd Maste 	return 0;
501*0afa8e06SEd Maste }
502*0afa8e06SEd Maste 
503*0afa8e06SEd Maste static int
504*0afa8e06SEd Maste translate_fido_assert(struct winhello_assert *ctx, fido_assert_t *assert,
505*0afa8e06SEd Maste     const char *pin)
506*0afa8e06SEd Maste {
507*0afa8e06SEd Maste 	WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS *opt;
508*0afa8e06SEd Maste 
509*0afa8e06SEd Maste 	/* not supported by webauthn.h */
510*0afa8e06SEd Maste 	if (assert->up == FIDO_OPT_FALSE) {
511*0afa8e06SEd Maste 		fido_log_debug("%s: up %d", __func__, assert->up);
512*0afa8e06SEd Maste 		return FIDO_ERR_UNSUPPORTED_OPTION;
513*0afa8e06SEd Maste 	}
514*0afa8e06SEd Maste 	/* not implemented */
515*0afa8e06SEd Maste 	if (assert->ext.mask) {
516*0afa8e06SEd Maste 		fido_log_debug("%s: ext 0x%x", __func__, assert->ext.mask);
517*0afa8e06SEd Maste 		return FIDO_ERR_UNSUPPORTED_EXTENSION;
518*0afa8e06SEd Maste 	}
519*0afa8e06SEd Maste 	if ((ctx->rp_id = to_utf16(assert->rp_id)) == NULL) {
520*0afa8e06SEd Maste 		fido_log_debug("%s: rp_id", __func__);
521*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
522*0afa8e06SEd Maste 	}
523*0afa8e06SEd Maste 	if (pack_cd(&ctx->cd, &assert->cd) < 0) {
524*0afa8e06SEd Maste 		fido_log_debug("%s: pack_cd", __func__);
525*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
526*0afa8e06SEd Maste 	}
527*0afa8e06SEd Maste 	/* options */
528*0afa8e06SEd Maste 	opt = &ctx->opt;
529*0afa8e06SEd Maste 	opt->dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_1;
530*0afa8e06SEd Maste 	opt->dwTimeoutMilliseconds = MAXMSEC;
531*0afa8e06SEd Maste 	if (pack_credlist(&opt->CredentialList, &assert->allow_list) < 0) {
532*0afa8e06SEd Maste 		fido_log_debug("%s: pack_credlist", __func__);
533*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
534*0afa8e06SEd Maste 	}
535*0afa8e06SEd Maste 	if (set_uv(&opt->dwUserVerificationRequirement, assert->uv, pin) < 0) {
536*0afa8e06SEd Maste 		fido_log_debug("%s: set_uv", __func__);
537*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
538*0afa8e06SEd Maste 	}
539*0afa8e06SEd Maste 
540*0afa8e06SEd Maste 	return FIDO_OK;
541*0afa8e06SEd Maste }
542*0afa8e06SEd Maste 
543*0afa8e06SEd Maste static int
544*0afa8e06SEd Maste translate_winhello_assert(fido_assert_t *assert, WEBAUTHN_ASSERTION *wa)
545*0afa8e06SEd Maste {
546*0afa8e06SEd Maste 	int r;
547*0afa8e06SEd Maste 
548*0afa8e06SEd Maste 	if (assert->stmt_len > 0) {
549*0afa8e06SEd Maste 		fido_log_debug("%s: stmt_len=%zu", __func__, assert->stmt_len);
550*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
551*0afa8e06SEd Maste 	}
552*0afa8e06SEd Maste 	if ((r = fido_assert_set_count(assert, 1)) != FIDO_OK) {
553*0afa8e06SEd Maste 		fido_log_debug("%s: fido_assert_set_count: %s", __func__,
554*0afa8e06SEd Maste 		    fido_strerr(r));
555*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
556*0afa8e06SEd Maste 	}
557*0afa8e06SEd Maste 	if (unpack_assert_authdata(assert, wa) < 0) {
558*0afa8e06SEd Maste 		fido_log_debug("%s: unpack_assert_authdata", __func__);
559*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
560*0afa8e06SEd Maste 	}
561*0afa8e06SEd Maste 	if (unpack_assert_sig(assert, wa) < 0) {
562*0afa8e06SEd Maste 		fido_log_debug("%s: unpack_assert_sig", __func__);
563*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
564*0afa8e06SEd Maste 	}
565*0afa8e06SEd Maste 	if (unpack_cred_id(assert, wa) < 0) {
566*0afa8e06SEd Maste 		fido_log_debug("%s: unpack_cred_id", __func__);
567*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
568*0afa8e06SEd Maste 	}
569*0afa8e06SEd Maste 	if (unpack_user_id(assert, wa) < 0) {
570*0afa8e06SEd Maste 		fido_log_debug("%s: unpack_user_id", __func__);
571*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
572*0afa8e06SEd Maste 	}
573*0afa8e06SEd Maste 
574*0afa8e06SEd Maste 	return FIDO_OK;
575*0afa8e06SEd Maste }
576*0afa8e06SEd Maste 
577*0afa8e06SEd Maste static int
578*0afa8e06SEd Maste translate_fido_cred(struct winhello_cred *ctx, fido_cred_t *cred,
579*0afa8e06SEd Maste     const char *pin)
580*0afa8e06SEd Maste {
581*0afa8e06SEd Maste 	WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS *opt;
582*0afa8e06SEd Maste 
583*0afa8e06SEd Maste 	if (pack_rp(&ctx->rp_id, &ctx->rp_name, &ctx->rp, &cred->rp) < 0) {
584*0afa8e06SEd Maste 		fido_log_debug("%s: pack_rp", __func__);
585*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
586*0afa8e06SEd Maste 	}
587*0afa8e06SEd Maste 	if (pack_user(&ctx->user_name, &ctx->user_icon, &ctx->display_name,
588*0afa8e06SEd Maste 	    &ctx->user, &cred->user) < 0) {
589*0afa8e06SEd Maste 		fido_log_debug("%s: pack_user", __func__);
590*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
591*0afa8e06SEd Maste 	}
592*0afa8e06SEd Maste 	if (pack_cose(&ctx->alg, &ctx->cose, cred->type) < 0) {
593*0afa8e06SEd Maste 		fido_log_debug("%s: pack_cose", __func__);
594*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
595*0afa8e06SEd Maste 	}
596*0afa8e06SEd Maste 	if (pack_cd(&ctx->cd, &cred->cd) < 0) {
597*0afa8e06SEd Maste 		fido_log_debug("%s: pack_cd", __func__);
598*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
599*0afa8e06SEd Maste 	}
600*0afa8e06SEd Maste 	/* options */
601*0afa8e06SEd Maste 	opt = &ctx->opt;
602*0afa8e06SEd Maste 	opt->dwVersion = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_VERSION_1;
603*0afa8e06SEd Maste 	opt->dwTimeoutMilliseconds = MAXMSEC;
604*0afa8e06SEd Maste 	if (pack_credlist(&opt->CredentialList, &cred->excl) < 0) {
605*0afa8e06SEd Maste 		fido_log_debug("%s: pack_credlist", __func__);
606*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
607*0afa8e06SEd Maste 	}
608*0afa8e06SEd Maste 	if (pack_cred_ext(&opt->Extensions, &cred->ext) < 0) {
609*0afa8e06SEd Maste 		fido_log_debug("%s: pack_cred_ext", __func__);
610*0afa8e06SEd Maste 		return FIDO_ERR_UNSUPPORTED_EXTENSION;
611*0afa8e06SEd Maste 	}
612*0afa8e06SEd Maste 	if (set_uv(&opt->dwUserVerificationRequirement, cred->uv, pin) < 0) {
613*0afa8e06SEd Maste 		fido_log_debug("%s: set_uv", __func__);
614*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
615*0afa8e06SEd Maste 	}
616*0afa8e06SEd Maste 	if (cred->rk == FIDO_OPT_TRUE) {
617*0afa8e06SEd Maste 		opt->bRequireResidentKey = true;
618*0afa8e06SEd Maste 	}
619*0afa8e06SEd Maste 
620*0afa8e06SEd Maste 	return FIDO_OK;
621*0afa8e06SEd Maste }
622*0afa8e06SEd Maste 
623*0afa8e06SEd Maste static int
624*0afa8e06SEd Maste translate_winhello_cred(fido_cred_t *cred, WEBAUTHN_CREDENTIAL_ATTESTATION *att)
625*0afa8e06SEd Maste {
626*0afa8e06SEd Maste 	if (unpack_fmt(cred, att) < 0) {
627*0afa8e06SEd Maste 		fido_log_debug("%s: unpack_fmt", __func__);
628*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
629*0afa8e06SEd Maste 	}
630*0afa8e06SEd Maste 	if (unpack_cred_authdata(cred, att) < 0) {
631*0afa8e06SEd Maste 		fido_log_debug("%s: unpack_cred_authdata", __func__);
632*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
633*0afa8e06SEd Maste 	}
634*0afa8e06SEd Maste 
635*0afa8e06SEd Maste 	switch (att->dwAttestationDecodeType) {
636*0afa8e06SEd Maste 	case WEBAUTHN_ATTESTATION_DECODE_NONE:
637*0afa8e06SEd Maste 		if (att->pvAttestationDecode != NULL) {
638*0afa8e06SEd Maste 			fido_log_debug("%s: pvAttestationDecode", __func__);
639*0afa8e06SEd Maste 			return FIDO_ERR_INTERNAL;
640*0afa8e06SEd Maste 		}
641*0afa8e06SEd Maste 		break;
642*0afa8e06SEd Maste 	case WEBAUTHN_ATTESTATION_DECODE_COMMON:
643*0afa8e06SEd Maste 		if (att->pvAttestationDecode == NULL) {
644*0afa8e06SEd Maste 			fido_log_debug("%s: pvAttestationDecode", __func__);
645*0afa8e06SEd Maste 			return FIDO_ERR_INTERNAL;
646*0afa8e06SEd Maste 		}
647*0afa8e06SEd Maste 		if (unpack_cred_sig(cred, att->pvAttestationDecode) < 0) {
648*0afa8e06SEd Maste 			fido_log_debug("%s: unpack_cred_sig", __func__);
649*0afa8e06SEd Maste 			return FIDO_ERR_INTERNAL;
650*0afa8e06SEd Maste 		}
651*0afa8e06SEd Maste 		if (unpack_x5c(cred, att->pvAttestationDecode) < 0) {
652*0afa8e06SEd Maste 			fido_log_debug("%s: unpack_x5c", __func__);
653*0afa8e06SEd Maste 			return FIDO_ERR_INTERNAL;
654*0afa8e06SEd Maste 		}
655*0afa8e06SEd Maste 		break;
656*0afa8e06SEd Maste 	default:
657*0afa8e06SEd Maste 		fido_log_debug("%s: dwAttestationDecodeType: %u", __func__,
658*0afa8e06SEd Maste 		    att->dwAttestationDecodeType);
659*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
660*0afa8e06SEd Maste 	}
661*0afa8e06SEd Maste 
662*0afa8e06SEd Maste 	return FIDO_OK;
663*0afa8e06SEd Maste }
664*0afa8e06SEd Maste 
665*0afa8e06SEd Maste static int
666*0afa8e06SEd Maste winhello_manifest(BOOL *present)
667*0afa8e06SEd Maste {
668*0afa8e06SEd Maste 	DWORD n;
669*0afa8e06SEd Maste 	HRESULT hr;
670*0afa8e06SEd Maste 	int r = FIDO_OK;
671*0afa8e06SEd Maste 
672*0afa8e06SEd Maste 	if ((n = WebAuthNGetApiVersionNumber()) < 1) {
673*0afa8e06SEd Maste 		fido_log_debug("%s: unsupported api %u", __func__, n);
674*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
675*0afa8e06SEd Maste 	}
676*0afa8e06SEd Maste 	fido_log_debug("%s: api version %u", __func__, n);
677*0afa8e06SEd Maste 	hr = WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable(present);
678*0afa8e06SEd Maste 	if (hr != S_OK)  {
679*0afa8e06SEd Maste 		r = to_fido(hr);
680*0afa8e06SEd Maste 		fido_log_debug("%s: %ls -> %s", __func__,
681*0afa8e06SEd Maste 		    WebAuthNGetErrorName(hr), fido_strerr(r));
682*0afa8e06SEd Maste 	}
683*0afa8e06SEd Maste 
684*0afa8e06SEd Maste 	return r;
685*0afa8e06SEd Maste }
686*0afa8e06SEd Maste 
687*0afa8e06SEd Maste static int
688*0afa8e06SEd Maste winhello_get_assert(HWND w, struct winhello_assert *ctx)
689*0afa8e06SEd Maste {
690*0afa8e06SEd Maste 	HRESULT hr;
691*0afa8e06SEd Maste 	int r = FIDO_OK;
692*0afa8e06SEd Maste 
693*0afa8e06SEd Maste 	hr = WebAuthNAuthenticatorGetAssertion(w, ctx->rp_id, &ctx->cd,
694*0afa8e06SEd Maste 	    &ctx->opt, &ctx->assert);
695*0afa8e06SEd Maste 	if (hr != S_OK) {
696*0afa8e06SEd Maste 		r = to_fido(hr);
697*0afa8e06SEd Maste 		fido_log_debug("%s: %ls -> %s", __func__,
698*0afa8e06SEd Maste 		    WebAuthNGetErrorName(hr), fido_strerr(r));
699*0afa8e06SEd Maste 	}
700*0afa8e06SEd Maste 
701*0afa8e06SEd Maste 	return r;
702*0afa8e06SEd Maste }
703*0afa8e06SEd Maste 
704*0afa8e06SEd Maste static int
705*0afa8e06SEd Maste winhello_make_cred(HWND w, struct winhello_cred *ctx)
706*0afa8e06SEd Maste {
707*0afa8e06SEd Maste 	HRESULT hr;
708*0afa8e06SEd Maste 	int r = FIDO_OK;
709*0afa8e06SEd Maste 
710*0afa8e06SEd Maste 	hr = WebAuthNAuthenticatorMakeCredential(w, &ctx->rp, &ctx->user,
711*0afa8e06SEd Maste 	    &ctx->cose, &ctx->cd, &ctx->opt, &ctx->att);
712*0afa8e06SEd Maste 	if (hr != S_OK) {
713*0afa8e06SEd Maste 		r = to_fido(hr);
714*0afa8e06SEd Maste 		fido_log_debug("%s: %ls -> %s", __func__,
715*0afa8e06SEd Maste 		    WebAuthNGetErrorName(hr), fido_strerr(r));
716*0afa8e06SEd Maste 	}
717*0afa8e06SEd Maste 
718*0afa8e06SEd Maste 	return r;
719*0afa8e06SEd Maste }
720*0afa8e06SEd Maste 
721*0afa8e06SEd Maste static void
722*0afa8e06SEd Maste winhello_assert_free(struct winhello_assert *ctx)
723*0afa8e06SEd Maste {
724*0afa8e06SEd Maste 	if (ctx == NULL)
725*0afa8e06SEd Maste 		return;
726*0afa8e06SEd Maste 	if (ctx->assert != NULL)
727*0afa8e06SEd Maste 		WebAuthNFreeAssertion(ctx->assert);
728*0afa8e06SEd Maste 
729*0afa8e06SEd Maste 	free(ctx->rp_id);
730*0afa8e06SEd Maste 	free(ctx->opt.CredentialList.pCredentials);
731*0afa8e06SEd Maste 	free(ctx);
732*0afa8e06SEd Maste }
733*0afa8e06SEd Maste 
734*0afa8e06SEd Maste static void
735*0afa8e06SEd Maste winhello_cred_free(struct winhello_cred *ctx)
736*0afa8e06SEd Maste {
737*0afa8e06SEd Maste 	if (ctx == NULL)
738*0afa8e06SEd Maste 		return;
739*0afa8e06SEd Maste 	if (ctx->att != NULL)
740*0afa8e06SEd Maste 		WebAuthNFreeCredentialAttestation(ctx->att);
741*0afa8e06SEd Maste 
742*0afa8e06SEd Maste 	free(ctx->rp_id);
743*0afa8e06SEd Maste 	free(ctx->rp_name);
744*0afa8e06SEd Maste 	free(ctx->user_name);
745*0afa8e06SEd Maste 	free(ctx->user_icon);
746*0afa8e06SEd Maste 	free(ctx->display_name);
747*0afa8e06SEd Maste 	free(ctx->opt.CredentialList.pCredentials);
748*0afa8e06SEd Maste 	for (size_t i = 0; i < ctx->opt.Extensions.cExtensions; i++) {
749*0afa8e06SEd Maste 		WEBAUTHN_EXTENSION *e;
750*0afa8e06SEd Maste 		e = &ctx->opt.Extensions.pExtensions[i];
751*0afa8e06SEd Maste 		free(e->pvExtension);
752*0afa8e06SEd Maste 	}
753*0afa8e06SEd Maste 	free(ctx->opt.Extensions.pExtensions);
754*0afa8e06SEd Maste 	free(ctx);
755*0afa8e06SEd Maste }
756*0afa8e06SEd Maste 
757*0afa8e06SEd Maste int
758*0afa8e06SEd Maste fido_winhello_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
759*0afa8e06SEd Maste {
760*0afa8e06SEd Maste 	int r;
761*0afa8e06SEd Maste 	BOOL present;
762*0afa8e06SEd Maste 	fido_dev_info_t *di;
763*0afa8e06SEd Maste 
764*0afa8e06SEd Maste 	if (ilen == 0) {
765*0afa8e06SEd Maste 		return FIDO_OK;
766*0afa8e06SEd Maste 	}
767*0afa8e06SEd Maste 	if (devlist == NULL) {
768*0afa8e06SEd Maste 		return FIDO_ERR_INVALID_ARGUMENT;
769*0afa8e06SEd Maste 	}
770*0afa8e06SEd Maste 	if ((r = winhello_manifest(&present)) != FIDO_OK) {
771*0afa8e06SEd Maste 		fido_log_debug("%s: winhello_manifest", __func__);
772*0afa8e06SEd Maste 		return r;
773*0afa8e06SEd Maste 	}
774*0afa8e06SEd Maste 	if (present == false) {
775*0afa8e06SEd Maste 		fido_log_debug("%s: not present", __func__);
776*0afa8e06SEd Maste 		return FIDO_OK;
777*0afa8e06SEd Maste 	}
778*0afa8e06SEd Maste 
779*0afa8e06SEd Maste 	di = &devlist[*olen];
780*0afa8e06SEd Maste 	memset(di, 0, sizeof(*di));
781*0afa8e06SEd Maste 	di->path = strdup(FIDO_WINHELLO_PATH);
782*0afa8e06SEd Maste 	di->manufacturer = strdup("Microsoft Corporation");
783*0afa8e06SEd Maste 	di->product = strdup("Windows Hello");
784*0afa8e06SEd Maste 	di->vendor_id = VENDORID;
785*0afa8e06SEd Maste 	di->product_id = PRODID;
786*0afa8e06SEd Maste 	if (di->path == NULL || di->manufacturer == NULL ||
787*0afa8e06SEd Maste 	    di->product == NULL) {
788*0afa8e06SEd Maste 		free(di->path);
789*0afa8e06SEd Maste 		free(di->manufacturer);
790*0afa8e06SEd Maste 		free(di->product);
791*0afa8e06SEd Maste 		explicit_bzero(di, sizeof(*di));
792*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
793*0afa8e06SEd Maste 	}
794*0afa8e06SEd Maste 	++(*olen);
795*0afa8e06SEd Maste 
796*0afa8e06SEd Maste 	return FIDO_OK;
797*0afa8e06SEd Maste }
798*0afa8e06SEd Maste 
799*0afa8e06SEd Maste int
800*0afa8e06SEd Maste fido_winhello_open(fido_dev_t *dev)
801*0afa8e06SEd Maste {
802*0afa8e06SEd Maste 	if (dev->flags != 0)
803*0afa8e06SEd Maste 		return FIDO_ERR_INVALID_ARGUMENT;
804*0afa8e06SEd Maste 
805*0afa8e06SEd Maste 	dev->attr.flags = FIDO_CAP_CBOR | FIDO_CAP_WINK;
806*0afa8e06SEd Maste 	dev->flags = FIDO_DEV_WINHELLO | FIDO_DEV_CRED_PROT | FIDO_DEV_PIN_SET;
807*0afa8e06SEd Maste 
808*0afa8e06SEd Maste 	return FIDO_OK;
809*0afa8e06SEd Maste }
810*0afa8e06SEd Maste 
811*0afa8e06SEd Maste int
812*0afa8e06SEd Maste fido_winhello_close(fido_dev_t *dev)
813*0afa8e06SEd Maste {
814*0afa8e06SEd Maste 	memset(dev, 0, sizeof(*dev));
815*0afa8e06SEd Maste 
816*0afa8e06SEd Maste 	return FIDO_OK;
817*0afa8e06SEd Maste }
818*0afa8e06SEd Maste 
819*0afa8e06SEd Maste int
820*0afa8e06SEd Maste fido_winhello_cancel(fido_dev_t *dev)
821*0afa8e06SEd Maste {
822*0afa8e06SEd Maste 	(void)dev;
823*0afa8e06SEd Maste 
824*0afa8e06SEd Maste 	return FIDO_ERR_INTERNAL;
825*0afa8e06SEd Maste }
826*0afa8e06SEd Maste 
827*0afa8e06SEd Maste int
828*0afa8e06SEd Maste fido_winhello_get_assert(fido_dev_t *dev, fido_assert_t *assert,
829*0afa8e06SEd Maste     const char *pin)
830*0afa8e06SEd Maste {
831*0afa8e06SEd Maste 	HWND			 w;
832*0afa8e06SEd Maste 	struct winhello_assert	*ctx;
833*0afa8e06SEd Maste 	int			 r = FIDO_ERR_INTERNAL;
834*0afa8e06SEd Maste 
835*0afa8e06SEd Maste 	(void)dev;
836*0afa8e06SEd Maste 
837*0afa8e06SEd Maste 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
838*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
839*0afa8e06SEd Maste 		goto fail;
840*0afa8e06SEd Maste 	}
841*0afa8e06SEd Maste 	if ((w = GetForegroundWindow()) == NULL) {
842*0afa8e06SEd Maste 		fido_log_debug("%s: GetForegroundWindow", __func__);
843*0afa8e06SEd Maste 		goto fail;
844*0afa8e06SEd Maste 	}
845*0afa8e06SEd Maste 	if ((r = translate_fido_assert(ctx, assert, pin)) != FIDO_OK) {
846*0afa8e06SEd Maste 		fido_log_debug("%s: translate_fido_assert", __func__);
847*0afa8e06SEd Maste 		goto fail;
848*0afa8e06SEd Maste 	}
849*0afa8e06SEd Maste 	if ((r = winhello_get_assert(w, ctx)) != S_OK) {
850*0afa8e06SEd Maste 		fido_log_debug("%s: winhello_get_assert", __func__);
851*0afa8e06SEd Maste 		goto fail;
852*0afa8e06SEd Maste 	}
853*0afa8e06SEd Maste 	if ((r = translate_winhello_assert(assert, ctx->assert)) != FIDO_OK) {
854*0afa8e06SEd Maste 		fido_log_debug("%s: translate_winhello_assert", __func__);
855*0afa8e06SEd Maste 		goto fail;
856*0afa8e06SEd Maste 	}
857*0afa8e06SEd Maste 
858*0afa8e06SEd Maste fail:
859*0afa8e06SEd Maste 	winhello_assert_free(ctx);
860*0afa8e06SEd Maste 
861*0afa8e06SEd Maste 	return r;
862*0afa8e06SEd Maste }
863*0afa8e06SEd Maste 
864*0afa8e06SEd Maste int
865*0afa8e06SEd Maste fido_winhello_get_cbor_info(fido_dev_t *dev, fido_cbor_info_t *ci)
866*0afa8e06SEd Maste {
867*0afa8e06SEd Maste 	const char *v[3] = { "U2F_V2", "FIDO_2_0", "FIDO_2_1_PRE" };
868*0afa8e06SEd Maste 	const char *e[2] = { "credProtect", "hmac-secret" };
869*0afa8e06SEd Maste 	const char *t[2] = { "nfc", "usb" };
870*0afa8e06SEd Maste 	const char *o[4] = { "rk", "up", "plat", "clientPin" };
871*0afa8e06SEd Maste 
872*0afa8e06SEd Maste 	(void)dev;
873*0afa8e06SEd Maste 
874*0afa8e06SEd Maste 	fido_cbor_info_reset(ci);
875*0afa8e06SEd Maste 
876*0afa8e06SEd Maste 	if (to_fido_str_array(&ci->versions, v, nitems(v)) < 0 ||
877*0afa8e06SEd Maste 	    to_fido_str_array(&ci->extensions, e, nitems(e)) < 0 ||
878*0afa8e06SEd Maste 	    to_fido_str_array(&ci->transports, t, nitems(t)) < 0) {
879*0afa8e06SEd Maste 		fido_log_debug("%s: to_fido_str_array", __func__);
880*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
881*0afa8e06SEd Maste 	}
882*0afa8e06SEd Maste 	if ((ci->options.name = calloc(nitems(o), sizeof(char *))) == NULL ||
883*0afa8e06SEd Maste 	    (ci->options.value = calloc(nitems(o), sizeof(bool))) == NULL) {
884*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
885*0afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
886*0afa8e06SEd Maste 	}
887*0afa8e06SEd Maste 	for (size_t i = 0; i < nitems(o); i++) {
888*0afa8e06SEd Maste 		if ((ci->options.name[i] = strdup(o[i])) == NULL) {
889*0afa8e06SEd Maste 			fido_log_debug("%s: strdup", __func__);
890*0afa8e06SEd Maste 			return FIDO_ERR_INTERNAL;
891*0afa8e06SEd Maste 		}
892*0afa8e06SEd Maste 		ci->options.value[i] = true;
893*0afa8e06SEd Maste 		ci->options.len++;
894*0afa8e06SEd Maste 	}
895*0afa8e06SEd Maste 
896*0afa8e06SEd Maste 	return FIDO_OK;
897*0afa8e06SEd Maste }
898*0afa8e06SEd Maste 
899*0afa8e06SEd Maste int
900*0afa8e06SEd Maste fido_winhello_make_cred(fido_dev_t *dev, fido_cred_t *cred, const char *pin)
901*0afa8e06SEd Maste {
902*0afa8e06SEd Maste 	HWND			 w;
903*0afa8e06SEd Maste 	struct winhello_cred	*ctx;
904*0afa8e06SEd Maste 	int			 r = FIDO_ERR_INTERNAL;
905*0afa8e06SEd Maste 
906*0afa8e06SEd Maste 	(void)dev;
907*0afa8e06SEd Maste 
908*0afa8e06SEd Maste 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
909*0afa8e06SEd Maste 		fido_log_debug("%s: calloc", __func__);
910*0afa8e06SEd Maste 		goto fail;
911*0afa8e06SEd Maste 	}
912*0afa8e06SEd Maste 	if ((w = GetForegroundWindow()) == NULL) {
913*0afa8e06SEd Maste 		fido_log_debug("%s: GetForegroundWindow", __func__);
914*0afa8e06SEd Maste 		goto fail;
915*0afa8e06SEd Maste 	}
916*0afa8e06SEd Maste 	if ((r = translate_fido_cred(ctx, cred, pin)) != FIDO_OK) {
917*0afa8e06SEd Maste 		fido_log_debug("%s: translate_fido_cred", __func__);
918*0afa8e06SEd Maste 		goto fail;
919*0afa8e06SEd Maste 	}
920*0afa8e06SEd Maste 	if ((r = winhello_make_cred(w, ctx)) != FIDO_OK) {
921*0afa8e06SEd Maste 		fido_log_debug("%s: winhello_make_cred", __func__);
922*0afa8e06SEd Maste 		goto fail;
923*0afa8e06SEd Maste 	}
924*0afa8e06SEd Maste 	if ((r = translate_winhello_cred(cred, ctx->att)) != FIDO_OK) {
925*0afa8e06SEd Maste 		fido_log_debug("%s: translate_winhello_cred", __func__);
926*0afa8e06SEd Maste 		goto fail;
927*0afa8e06SEd Maste 	}
928*0afa8e06SEd Maste 
929*0afa8e06SEd Maste 	r = FIDO_OK;
930*0afa8e06SEd Maste fail:
931*0afa8e06SEd Maste 	winhello_cred_free(ctx);
932*0afa8e06SEd Maste 
933*0afa8e06SEd Maste 	return r;
934*0afa8e06SEd Maste }
935