Hi
I am writing FIDO2 C++ based application using WebAuthn.dll for "YUBIKEY 5 NFC" (External authenticator) using the following WebAutheN APIs of Microsoft from the
https://github.com/microsoft/webauthn/blob/master/webauthn.h
HRESULT WINAPI WebAuthNAuthenticatorMakeCredential(
_In_ HWND hWnd,
_In_ PCWEBAUTHN_RP_ENTITY_INFORMATION pRpInformation,
_In_ PCWEBAUTHN_USER_ENTITY_INFORMATION pUserInformation,
_In_ PCWEBAUTHN_COSE_CREDENTIAL_PARAMETERS pPubKeyCredParams,
_In_ PCWEBAUTHN_CLIENT_DATA pWebAuthNClientData,
_In_opt_ PCWEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS pWebAuthNMakeCredentialOptions,
_Outptr_result_maybenull_ PWEBAUTHN_CREDENTIAL_ATTESTATION *ppWebAuthNCredentialAttestation);
HRESULT WINAPI WebAuthNAuthenticatorGetAssertion(
_In_ HWND hWnd,
_In_ LPCWSTR pwszRpId,
_In_ PCWEBAUTHN_CLIENT_DATA pWebAuthNClientData,
_In_opt_ PCWEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS pWebAuthNGetAssertionOptions,
_Outptr_result_maybenull_ PWEBAUTHN_ASSERTION *ppWebAuthNAssertion);
I am able to get the registration successfully with the IdP (Identity Provider) using C++ API WebAuthNAuthenticatorMakeCredential() in my code.
But when it comes to Authentication, i am facing problem with the api WebAuthNAuthenticatorGetAssertion() as follows:
"This Security Key doesn't look familiar. Please try a different one."
Below is my code :
============
HWND hWnd = GetForegroundWindow();
LPCWSTR pwszRpId = L"tenet.domain.com";
//My client data in json format is as below...
sClientData64 = {"type":"webauthn.get","challenge":"MKB9ApSESppXbU-oVW_M","origin":"https://tenet.domain.com","crossOrigin":true};
WEBAUTHN_CLIENT_DATA oClientData_in = { WEBAUTHN_CLIENT_DATA_CURRENT_VERSION,
static_cast<DWORD>(sClientData64.length()),
(PBYTE)(sClientData64.data()),
WEBAUTHN_HASH_ALGORITHM_SHA_256
};
I tried to manipulate the values as in the following link: https://github.com/Yubico/python-fido2/blob/master/fido2/win_api.py to implement
the structure "WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS" below:
My allow list code is as follows:
====================
std::vector<WEBAUTHN_CREDENTIAL_EX> allowCredentials;
WEBAUTHN_CREDENTIAL_EX* pAllowCredentials = nullptr;
std::vector<WEBAUTHN_CREDENTIAL_EX*> allowCredentialsPtrs;
WEBAUTHN_CREDENTIAL_LIST allowCredentialList = { 0 };
WEBAUTHN_CREDENTIAL_LIST* pAllowCredentialList = nullptr;
DWORD dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_4; // the value is 4
if(dwversion is >= 4)
{
DWORD winTransports = 0;
std::string sAuthenticatorType = "usb";
int nTransport = 0;
if(sAuthenticatorType == "usb")
{
nTransport = 1;
}
if (nTransport & U2F_AUTHENTICATOR_TRANSPORT_USB)
{
winTransports |= WEBAUTHN_CTAP_TRANSPORT_USB;
}
WEBAUTHN_CREDENTIAL_EX webCredEx = { WEBAUTHN_CREDENTIAL_EX_CURRENT_VERSION,
static_cast<DWORD>(sCredentialId.length()),
((BYTE*)(sCredentialId.c_str())),
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY,
winTransports
};
allowCredentials.push_back(webCredEx);
pAllowCredentials = allowCredentials.data();
for (DWORD i = 0; i < allowCredentials.size(); i++)
{
allowCredentialsPtrs.push_back(&pAllowCredentials[i]);
}
allowCredentialList.cCredentials = allowCredentials.size(); // original
allowCredentialList.ppCredentials = allowCredentialsPtrs.data(); // original
pAllowCredentialList = &allowCredentialList; // my AllowCredentialList value
}
// Credentials is as follows:
==================
WEBAUTHN_CREDENTIALS webCredentials = {0,NULL};
DWORD dwVersion = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_VERSION_3; // the value is 3
if(dwversion is >= 3)
{
std::vector<WEBAUTHN_CREDENTIAL> vCredential;
std::vector<WEBAUTHN_CREDENTIAL*> pCredential;
WEBAUTHN_CREDENTIAL webAuthCredential = {WEBAUTHN_CREDENTIAL_CURRENT_VERSION,
static_cast<DWORD>(sCredentialId.length()),
((BYTE*)(sCredentialId.c_str())),
WEBAUTHN_CREDENTIAL_TYPE_PUBLIC_KEY
};
WEBAUTHN_CREDENTIAL *pwebCredential;
vCredential.push_back(webAuthCredential);
pwebCredential = vCredential.data();
for (DWORD i = 0; i < vCredential.size(); i++)
{
pCredential.push_back(&pwebCredential[i]);
}
webCredentials.cCredentials = vCredential.size();
webCredentials.pCredentials = pwebCredential;
}
// filling this structure based on the dwVersion.. as the code above.
WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS oWebAuthNGetAssertionOptions = {
dwVersion, // version
10000, // time in milliseconds
webCredentials, //WEBAUTHN_CREDENTIALS
{0, NULL}, //WEBAUTHN_EXTENSIONS
dwAuthenticatorAttachement, // for Platform (Windows Hello) vs Cross platform authenticator (Yubikey)
winUserVerificationReq, // user Verification Required (preferred)
0, // dwFlags
NULL, // as json data received it is null
pbU2fAppIdUsed, //(FALSE) this is a pointer
NULL, // pCancellationId
pAllowCredentialList // allowList if (dwversion is >= 4) else allowlist is null
};
WEBAUTHN_ASSERTION* pWebAuthNAssertion = nullptr; // this is an output parameter,
// on calling, I get a dialog popup saying " This Security key doesn't look familiar. please try a different one."
hResult = WebAuthNAuthenticatorGetAssertion(hWnd, pwszRpId &oClientData_in, &oWebAuthNAssertionOptions, &pWebAuthNAssertion);
I took the python code for reference from the https://github.com/Yubico/python-fido2/blob/master/fido2/win_api.py
if (dwVersion >= 3)
self.pCancellationId = cancellationId (NULL)
if self.dwVersion >= 4:
clist = WebAuthNCredentialList(credentials)
self.pAllowCredentialList = ctypes.pointer(clist)
else:
self.CredentialList = WebAuthNCredentials(credentials)
Please let me know, if any thing has to added in my code.
Thankyou.