Add challenge-response support for Nitrokey 3 (#9631)

Co-authored-by: Szczepan Zalega <szczepan@nitrokey.com>
This commit is contained in:
Jonathan White 2023-07-15 22:47:19 -04:00
parent f30604c6f6
commit 33b740ddd0
No known key found for this signature in database
GPG Key ID: 440FC65F2E0C6E01
2 changed files with 61 additions and 9 deletions

View File

@ -242,23 +242,68 @@ namespace
if (rv == SCARD_S_SUCCESS) {
// Write to and read from the card
// pioRecvPci is nullptr because we do not expect any PCI response header
const SCUINT dwRecvBufferSize = dwRecvLength;
rv = SCardTransmit(handle, pioSendPci, pbSendBuffer, dwSendLength, nullptr, pbRecvBuffer, &dwRecvLength);
if (dwRecvLength < 2) {
// Any valid response should be at least 2 bytes (response status)
// However the protocol itself could fail
return SCARD_E_UNEXPECTED;
}
uint8_t SW1 = pbRecvBuffer[dwRecvLength - 2];
// Check for the MoreDataAvailable SW1 code. If present, send GetResponse command repeatedly, until success
// SW, or filling the receiving buffer.
if (SW1 == SW_MORE_DATA_HIGH) {
while (true) {
if (dwRecvBufferSize < dwRecvLength) {
// No free buffer space remaining
return SCARD_E_UNEXPECTED;
}
// Overwrite Status Word in the receiving buffer
dwRecvLength -= 2;
SCUINT dwRecvLength_sr = dwRecvBufferSize - dwRecvLength; // at least 2 bytes for SW are available
const uint8_t bRecvDataSize =
qBound(static_cast<SCUINT>(0), dwRecvLength_sr - 2, static_cast<SCUINT>(255));
uint8_t pbSendBuffer_sr[] = {CLA_ISO, INS_GET_RESPONSE, 0, 0, bRecvDataSize};
rv = SCardTransmit(handle,
pioSendPci,
pbSendBuffer_sr,
sizeof pbSendBuffer_sr,
nullptr,
pbRecvBuffer + dwRecvLength,
&dwRecvLength_sr);
// Check if any new data are received. Break if the smart card's status is other than success,
// or no new bytes were received.
if (!(rv == SCARD_S_SUCCESS && dwRecvLength_sr >= 2)) {
break;
}
dwRecvLength += dwRecvLength_sr;
SW1 = pbRecvBuffer[dwRecvLength - 2];
// Break the loop if there is no continuation status
if (SW1 != SW_MORE_DATA_HIGH) {
break;
}
}
}
if (rv == SCARD_S_SUCCESS) {
if (dwRecvLength < 2) {
// Any valid response should be at least 2 bytes (response status)
// However the protocol itself could fail
rv = SCARD_E_UNEXPECTED;
} else {
if (pbRecvBuffer[dwRecvLength - 2] == SW_OK_HIGH && pbRecvBuffer[dwRecvLength - 1] == SW_OK_LOW) {
const uint8_t SW_HIGH = pbRecvBuffer[dwRecvLength - 2];
const uint8_t SW_LOW = pbRecvBuffer[dwRecvLength - 1];
if (SW_HIGH == SW_OK_HIGH && SW_LOW == SW_OK_LOW) {
rv = SCARD_S_SUCCESS;
} else if (pbRecvBuffer[dwRecvLength - 2] == SW_PRECOND_HIGH
&& pbRecvBuffer[dwRecvLength - 1] == SW_PRECOND_LOW) {
} else if (SW_HIGH == SW_PRECOND_HIGH && SW_LOW == SW_PRECOND_LOW) {
// This happens if the key requires eg. a button press or if the applet times out
// Solution: Re-present the card to the reader
rv = SCARD_W_CARD_NOT_AUTHENTICATED;
} else if ((pbRecvBuffer[dwRecvLength - 2] == SW_NOTFOUND_HIGH
&& pbRecvBuffer[dwRecvLength - 1] == SW_NOTFOUND_LOW)
|| pbRecvBuffer[dwRecvLength - 2] == SW_UNSUP_HIGH) {
} else if ((SW_HIGH == SW_NOTFOUND_HIGH && SW_LOW == SW_NOTFOUND_LOW) || SW_HIGH == SW_UNSUP_HIGH) {
// This happens eg. during a select command when the AID is not found
rv = SCARD_E_FILE_NOT_FOUND;
} else {
@ -285,9 +330,10 @@ namespace
auto pbSendBuffer = new uint8_t[5 + handle.second.size()];
memcpy(pbSendBuffer, pbSendBuffer_head, 5);
memcpy(pbSendBuffer + 5, handle.second.constData(), handle.second.size());
uint8_t pbRecvBuffer[12] = {
// Give it more space in case custom implementations have longer answer to select
uint8_t pbRecvBuffer[64] = {
0}; // 3 bytes version, 1 byte program counter, other stuff for various implementations, 2 bytes status
SCUINT dwRecvLength = 12;
SCUINT dwRecvLength = sizeof pbRecvBuffer;
auto rv = transmit(handle.first, pbSendBuffer, 5 + handle.second.size(), pbRecvBuffer, dwRecvLength);

View File

@ -24,6 +24,7 @@
#define CLA_ISO 0x00
#define INS_SELECT 0xA4
#define INS_GET_RESPONSE 0xC0
#define SEL_APP_AID 0x04
#define INS_API_REQ 0x01
#define INS_STATUS 0x03
@ -37,6 +38,7 @@
#define SW_NOTFOUND_HIGH 0x6A
#define SW_NOTFOUND_LOW 0x82
#define SW_UNSUP_HIGH 0x6D
#define SW_MORE_DATA_HIGH 0x61
typedef QPair<SCARDHANDLE, QByteArray> SCardAID;
@ -75,6 +77,7 @@ private:
// and also for compatible third-party ones. They will be tried one by one.
const QList<QByteArray> m_aid_codes = {
QByteArrayLiteral("\xA0\x00\x00\x05\x27\x20\x01"), // Yubico Yubikey
QByteArrayLiteral("\xA0\x00\x00\x05\x27\x21\x01"), // Yubico Yubikey OATH AID / Nitrokey 3 Secrets App
QByteArrayLiteral("\xA0\x00\x00\x06\x17\x00\x07\x53\x4E\xAF\x01") // Fidesmo development
};
@ -109,7 +112,10 @@ private:
{QByteArrayLiteral("\x3B\x80\x80\x01\x01"), "Fidesmo Card 2.0"},
{QByteArrayLiteral("\x3B\x8A\x80\x01\x00\x31\xC1\x73\xC8\x40\x00\x00\x90\x00\x90"), "VivoKey Apex"},
{QByteArrayLiteral("\x3B\x8D\x80\x01\x00\x31\xC1\x73\xC8\x40\x00\x52\xA5\x10\x00\x90\x00\x70"),
"Dangerous Things FlexSecure"}};
"Dangerous Things FlexSecure"},
{QByteArrayLiteral("\x3b\x8f\x01\x80\x5d\x4e\x69\x74\x72\x6f\x6b\x65\x79\x00\x00\x00\x00\x00\x6a"),
"Nitrokey 3"},
};
};
#endif // KEEPASSX_YUBIKEY_INTERFACE_PCSC_H