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