mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Add challenge-response support for Nitrokey 3 (#9631)
Co-authored-by: Szczepan Zalega <szczepan@nitrokey.com>
This commit is contained in:
parent
f30604c6f6
commit
33b740ddd0
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user