I'm working on a Qt5 with Win32 project on Win7 using VS2010. I need to implement a simple OTP configuration on our YubiKeys such that they all have a fixed secret key that identifies the YubiKey as a known key. I have gotten to the point where after importing the YubiClientAPI.dll library, I am able to detect if a key is inserted, get the key's serial#, perform an OTP challenge/response, and get current buffer. The results seem to complement the results I get when I run the "Sample YubiClientAPI MFC test container" application compiled from the Samples folder.
I have a couple things that I need to figure out about using the API in order to complete my project. But the most important question I have is that, since my secret 16 byte key is constant on the YubiKey, and I keep getting back different byte strings every time I do a challenge, what do I do on the client end to get a constant expected string back that I can use for recognition. I am assuming that there is something I'm doing wrong. In the code below, you will see that I am randomizing the challenge string (see comment starting with "NOTE:"). I have also commented the randomization out so that the challenge string is all zeroes. Doesn't make a difference in the variability of the response string.
I am also not sure how the "Private Identity 6 byte Hex" field is used in the authentication process? Read the documentation, and I see when I run the "YubiKey Personalization Tool" that I can set that, but don't know how that affects the resulting response I get, and what it has to do with my client-side authentication process.
If anyone can just give me an indication of what I should be looking at to figure this out, I would be very grateful.
Code:
#include <iomanip>
#include <sstream>
// NOTE: import done in header file
//#import <YubiClientAPI.dll> no_namespace, named_guids
#define RESPONSE_LENGTH 16
#define CHALLENGE_LENGTH 6
#define CONFIG1 0
#define CONFIG2 1
TestingYubikeyAPI::TestingYubikeyAPI(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
HRESULT hr = CoCreateInstance(CLSID_YubiClient, 0, CLSCTX_ALL, IID_IYubiClient, reinterpret_cast<void **>(&m_yubiClient));
if (FAILED(hr))
{
_com_error er(hr);
setValid(false);
}
else
setValid(true);
QObject::connect(ui.m_getOtpConf1PushButton, &QPushButton::clicked, this, &TestingYubikeyAPI::onGetOtpConfig1Clicked);
}
void TestingYubikeyAPI::onGetOtpConfig1Clicked()
{
BYTE challenge[CHALLENGE_LENGTH];
BYTE response[RESPONSE_LENGTH];
memset(challenge, 0, sizeof(challenge));
memset(response, 0, sizeof(response));
// NOTE: randomizing challenge
BCryptGenRandom(NULL, challenge, CHALLENGE_LENGTH, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
variant_t va;
std::ostringstream os;
std::stringstream os2;
os << std::hex << std::setfill('0');
for (DWORD i = 0; i < CHALLENGE_LENGTH; i++)
{
os << std::setw(2) << (int)challenge[i];
}
_bstr_t bstr(os.str().c_str());
TCHAR buf[1024];
va.bstrVal = bstr;
va.vt = VT_BSTR;
m_yubiClient->PutdataEncoding(ycENCODING_BYTE_ARRAY);
m_yubiClient->PutdataBuffer(va);
ycRETCODE ret = m_yubiClient->GetotpChallenge(CONFIG1, ycCALL_BLOCKING);
if (ret == ycRETCODE_OK)
{
getCurrentBuffer(challenge, 64);
}
else
{
ui.m_outputTextEdit->append(QString("Got No Data: retcode = %1").arg(translateRetCode(ret)));
}
}
void TestingYubikeyAPI::getCurrentBuffer(BYTE* pChallenge, int len)
{
BYTE HUGEP *pb;
long lbound, hbound;
QString outstr;
SafeArrayGetLBound(m_yubiClient->dataBuffer.parray, 1, &lbound);
SafeArrayGetUBound(m_yubiClient->dataBuffer.parray, 1, &hbound);
SafeArrayAccessData(m_yubiClient->dataBuffer.parray, (void **)&pb);
for (; lbound <= hbound; lbound++)
{
outstr = QString("%1%2 ").arg(outstr).arg((uint)pb[lbound], 2, 16, QLatin1Char('0'));
}
SafeArrayUnaccessData(m_yubiClient->dataBuffer.parray);
ui.m_outputTextEdit->append(QString("Got Data: %1").arg(outstr));
}