mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
parent
3b3bc42e10
commit
be6835e42f
@ -8443,11 +8443,11 @@ Example: JBSWY3DPEHPK3PXP</source>
|
|||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Hardware key was not found or is misconfigured.</source>
|
<source>Failed to complete a challenge-response, the PCSC error code was: %1</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
<message>
|
<message>
|
||||||
<source>Failed to complete a challenge-response, the PCSC error code was: %1</source>
|
<source>Hardware key was not found or is not configured.</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
</context>
|
</context>
|
||||||
|
@ -34,8 +34,10 @@
|
|||||||
// Windows winscard and Linux pcsc-lite use unsigned long
|
// Windows winscard and Linux pcsc-lite use unsigned long
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
typedef uint32_t SCUINT;
|
typedef uint32_t SCUINT;
|
||||||
|
typedef uint32_t RETVAL;
|
||||||
#else
|
#else
|
||||||
typedef unsigned long SCUINT;
|
typedef unsigned long SCUINT;
|
||||||
|
typedef long RETVAL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This namescape contains static wrappers for the smart card API
|
// This namescape contains static wrappers for the smart card API
|
||||||
@ -49,11 +51,11 @@ namespace
|
|||||||
* @param context Smartcard API context, valid or not
|
* @param context Smartcard API context, valid or not
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t ensureValidContext(SCARDCONTEXT& context)
|
RETVAL ensureValidContext(SCARDCONTEXT& context)
|
||||||
{
|
{
|
||||||
// This check only tests if the handle pointer is valid in memory
|
// This check only tests if the handle pointer is valid in memory
|
||||||
// but it does not actually verify that it works
|
// but it does not actually verify that it works
|
||||||
int32_t rv = SCardIsValidContext(context);
|
auto rv = SCardIsValidContext(context);
|
||||||
|
|
||||||
// If the handle is broken, create it
|
// If the handle is broken, create it
|
||||||
// This happens e.g. on application launch
|
// This happens e.g. on application launch
|
||||||
@ -69,7 +71,7 @@ namespace
|
|||||||
rv = SCardListReaders(context, nullptr, nullptr, &dwReaders);
|
rv = SCardListReaders(context, nullptr, nullptr, &dwReaders);
|
||||||
// On windows, USB hot-plugging causes the underlying API server to die
|
// On windows, USB hot-plugging causes the underlying API server to die
|
||||||
// So on every USB unplug event, the API context has to be recreated
|
// So on every USB unplug event, the API context has to be recreated
|
||||||
if (rv == static_cast<int32_t>(SCARD_E_SERVICE_STOPPED)) {
|
if (rv == SCARD_E_SERVICE_STOPPED) {
|
||||||
// Dont care if the release works since the handle might be broken
|
// Dont care if the release works since the handle might be broken
|
||||||
SCardReleaseContext(context);
|
SCardReleaseContext(context);
|
||||||
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, nullptr, nullptr, &context);
|
rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, nullptr, nullptr, &context);
|
||||||
@ -94,7 +96,7 @@ namespace
|
|||||||
|
|
||||||
// Read size of required string buffer
|
// Read size of required string buffer
|
||||||
// OSX does not support auto-allocate
|
// OSX does not support auto-allocate
|
||||||
int32_t rv = SCardListReaders(context, nullptr, nullptr, &dwReaders);
|
auto rv = SCardListReaders(context, nullptr, nullptr, &dwReaders);
|
||||||
if (rv != SCARD_S_SUCCESS) {
|
if (rv != SCARD_S_SUCCESS) {
|
||||||
return readers_list;
|
return readers_list;
|
||||||
}
|
}
|
||||||
@ -131,18 +133,16 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t getCardStatus(SCARDHANDLE handle, SCUINT& dwProt, const SCARD_IO_REQUEST*& pioSendPci)
|
RETVAL getCardStatus(SCARDHANDLE handle, SCUINT& dwProt, const SCARD_IO_REQUEST*& pioSendPci)
|
||||||
{
|
{
|
||||||
int32_t rv = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
|
||||||
|
|
||||||
uint8_t pbAtr[MAX_ATR_SIZE] = {0}; // ATR record
|
|
||||||
char pbReader[MAX_READERNAME] = {0}; // Name of the reader the card is placed in
|
char pbReader[MAX_READERNAME] = {0}; // Name of the reader the card is placed in
|
||||||
SCUINT dwAtrLen = sizeof(pbAtr); // ATR record size
|
|
||||||
SCUINT dwReaderLen = sizeof(pbReader); // String length of the reader name
|
SCUINT dwReaderLen = sizeof(pbReader); // String length of the reader name
|
||||||
SCUINT dwState = 0; // Unused. Contents differ depending on API implementation.
|
SCUINT dwState = 0; // Unused. Contents differ depending on API implementation.
|
||||||
|
uint8_t pbAtr[MAX_ATR_SIZE] = {0}; // ATR record
|
||||||
|
SCUINT dwAtrLen = sizeof(pbAtr); // ATR record size
|
||||||
|
|
||||||
if ((rv = SCardStatus(handle, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen))
|
auto rv = SCardStatus(handle, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
||||||
== SCARD_S_SUCCESS) {
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
switch (dwProt) {
|
switch (dwProt) {
|
||||||
case SCARD_PROTOCOL_T0:
|
case SCARD_PROTOCOL_T0:
|
||||||
pioSendPci = SCARD_PCI_T0;
|
pioSendPci = SCARD_PCI_T0;
|
||||||
@ -152,7 +152,7 @@ namespace
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// This should not happen during normal use
|
// This should not happen during normal use
|
||||||
rv = static_cast<int32_t>(SCARD_E_PROTO_MISMATCH);
|
rv = SCARD_E_PROTO_MISMATCH;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -172,20 +172,20 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t transactRetry(SCARDHANDLE handle, const std::function<int32_t()>& atomic_action)
|
RETVAL transactRetry(SCARDHANDLE handle, const std::function<RETVAL()>& atomic_action)
|
||||||
{
|
{
|
||||||
int32_t rv = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
|
||||||
|
|
||||||
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
||||||
const SCARD_IO_REQUEST* pioSendPci = nullptr;
|
const SCARD_IO_REQUEST* pioSendPci = nullptr;
|
||||||
if ((rv = getCardStatus(handle, dwProt, pioSendPci)) == SCARD_S_SUCCESS) {
|
auto rv = getCardStatus(handle, dwProt, pioSendPci);
|
||||||
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
// Begin a transaction. This locks out any other process from interfacing with the card
|
// Begin a transaction. This locks out any other process from interfacing with the card
|
||||||
if ((rv = SCardBeginTransaction(handle)) == SCARD_S_SUCCESS) {
|
rv = SCardBeginTransaction(handle);
|
||||||
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 4; i > 0; i--) { // 3 tries for reconnecting after reset
|
for (i = 4; i > 0; i--) { // 3 tries for reconnecting after reset
|
||||||
// Run the lambda payload and store its return code
|
// Run the lambda payload and store its return code
|
||||||
int32_t rv_act = atomic_action();
|
RETVAL rv_act = atomic_action();
|
||||||
if (rv_act == static_cast<int32_t>(SCARD_W_RESET_CARD)) {
|
if (rv_act == SCARD_W_RESET_CARD) {
|
||||||
// The card was reset during the transmission.
|
// The card was reset during the transmission.
|
||||||
SCUINT dwProt_new = SCARD_PROTOCOL_UNDEFINED;
|
SCUINT dwProt_new = SCARD_PROTOCOL_UNDEFINED;
|
||||||
// Acknowledge the reset and reestablish the connection and handle
|
// Acknowledge the reset and reestablish the connection and handle
|
||||||
@ -207,7 +207,7 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
rv = static_cast<int32_t>(SCARD_W_RESET_CARD);
|
rv = SCARD_W_RESET_CARD;
|
||||||
qDebug("Smardcard was reset and failed to reconnect after 3 tries");
|
qDebug("Smardcard was reset and failed to reconnect after 3 tries");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,26 +231,24 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t transmit(SCARDHANDLE handle,
|
RETVAL transmit(SCARDHANDLE handle,
|
||||||
const uint8_t* pbSendBuffer,
|
const uint8_t* pbSendBuffer,
|
||||||
SCUINT dwSendLength,
|
SCUINT dwSendLength,
|
||||||
uint8_t* pbRecvBuffer,
|
uint8_t* pbRecvBuffer,
|
||||||
SCUINT& dwRecvLength)
|
SCUINT& dwRecvLength)
|
||||||
{
|
{
|
||||||
int32_t rv = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
|
||||||
|
|
||||||
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
||||||
const SCARD_IO_REQUEST* pioSendPci = nullptr;
|
const SCARD_IO_REQUEST* pioSendPci = nullptr;
|
||||||
if ((rv = getCardStatus(handle, dwProt, pioSendPci)) == SCARD_S_SUCCESS) {
|
auto rv = getCardStatus(handle, dwProt, pioSendPci);
|
||||||
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
// Write to and read from the card
|
// Write to and read from the card
|
||||||
// pioRecvPci is nullptr because we do not expect any PCI response header
|
// pioRecvPci is nullptr because we do not expect any PCI response header
|
||||||
if ((rv = SCardTransmit(
|
rv = SCardTransmit(handle, pioSendPci, pbSendBuffer, dwSendLength, nullptr, pbRecvBuffer, &dwRecvLength);
|
||||||
handle, pioSendPci, pbSendBuffer, dwSendLength, nullptr, pbRecvBuffer, &dwRecvLength))
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
== SCARD_S_SUCCESS) {
|
|
||||||
if (dwRecvLength < 2) {
|
if (dwRecvLength < 2) {
|
||||||
// Any valid response should be at least 2 bytes (response status)
|
// Any valid response should be at least 2 bytes (response status)
|
||||||
// However the protocol itself could fail
|
// However the protocol itself could fail
|
||||||
rv = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
rv = SCARD_E_UNEXPECTED;
|
||||||
} else {
|
} else {
|
||||||
if (pbRecvBuffer[dwRecvLength - 2] == SW_OK_HIGH && pbRecvBuffer[dwRecvLength - 1] == SW_OK_LOW) {
|
if (pbRecvBuffer[dwRecvLength - 2] == SW_OK_HIGH && pbRecvBuffer[dwRecvLength - 1] == SW_OK_LOW) {
|
||||||
rv = SCARD_S_SUCCESS;
|
rv = SCARD_S_SUCCESS;
|
||||||
@ -258,14 +256,14 @@ namespace
|
|||||||
&& pbRecvBuffer[dwRecvLength - 1] == SW_PRECOND_LOW) {
|
&& pbRecvBuffer[dwRecvLength - 1] == SW_PRECOND_LOW) {
|
||||||
// This happens if the key requires eg. a button press or if the applet times out
|
// This happens if the key requires eg. a button press or if the applet times out
|
||||||
// Solution: Re-present the card to the reader
|
// Solution: Re-present the card to the reader
|
||||||
rv = static_cast<int32_t>(SCARD_W_CARD_NOT_AUTHENTICATED);
|
rv = SCARD_W_CARD_NOT_AUTHENTICATED;
|
||||||
} else if ((pbRecvBuffer[dwRecvLength - 2] == SW_NOTFOUND_HIGH
|
} else if ((pbRecvBuffer[dwRecvLength - 2] == SW_NOTFOUND_HIGH
|
||||||
&& pbRecvBuffer[dwRecvLength - 1] == SW_NOTFOUND_LOW)
|
&& pbRecvBuffer[dwRecvLength - 1] == SW_NOTFOUND_LOW)
|
||||||
|| pbRecvBuffer[dwRecvLength - 2] == SW_UNSUP_HIGH) {
|
|| pbRecvBuffer[dwRecvLength - 2] == SW_UNSUP_HIGH) {
|
||||||
// This happens eg. during a select command when the AID is not found
|
// This happens eg. during a select command when the AID is not found
|
||||||
rv = static_cast<int32_t>(SCARD_E_FILE_NOT_FOUND);
|
rv = SCARD_E_FILE_NOT_FOUND;
|
||||||
} else {
|
} else {
|
||||||
rv = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
rv = SCARD_E_UNEXPECTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -281,7 +279,7 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t selectApplet(const SCardAID& handle)
|
RETVAL selectApplet(const SCardAID& handle)
|
||||||
{
|
{
|
||||||
uint8_t pbSendBuffer_head[5] = {
|
uint8_t pbSendBuffer_head[5] = {
|
||||||
CLA_ISO, INS_SELECT, SEL_APP_AID, 0, static_cast<uint8_t>(handle.second.size())};
|
CLA_ISO, INS_SELECT, SEL_APP_AID, 0, static_cast<uint8_t>(handle.second.size())};
|
||||||
@ -292,7 +290,7 @@ namespace
|
|||||||
0}; // 3 bytes version, 1 byte program counter, other stuff for various implementations, 2 bytes status
|
0}; // 3 bytes version, 1 byte program counter, other stuff for various implementations, 2 bytes status
|
||||||
SCUINT dwRecvLength = 12;
|
SCUINT dwRecvLength = 12;
|
||||||
|
|
||||||
int32_t rv = transmit(handle.first, pbSendBuffer, 5 + handle.second.size(), pbRecvBuffer, dwRecvLength);
|
auto rv = transmit(handle.first, pbSendBuffer, 5 + handle.second.size(), pbRecvBuffer, dwRecvLength);
|
||||||
|
|
||||||
delete[] pbSendBuffer;
|
delete[] pbSendBuffer;
|
||||||
|
|
||||||
@ -312,7 +310,7 @@ namespace
|
|||||||
{
|
{
|
||||||
for (const auto& aid : aid_codes) {
|
for (const auto& aid : aid_codes) {
|
||||||
// Ensure the transmission is retransmitted after card resets
|
// Ensure the transmission is retransmitted after card resets
|
||||||
int32_t rv = transactRetry(handle, [&handle, &aid]() {
|
auto rv = transactRetry(handle, [&handle, &aid]() {
|
||||||
// Try to select the card using the specified AID
|
// Try to select the card using the specified AID
|
||||||
return selectApplet({handle, aid});
|
return selectApplet({handle, aid});
|
||||||
});
|
});
|
||||||
@ -333,28 +331,27 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t getSerial(const SCardAID& handle, unsigned int& serial)
|
RETVAL getSerial(const SCardAID& handle, unsigned int& serial)
|
||||||
{
|
{
|
||||||
// Ensure the transmission is retransmitted after card resets
|
// Ensure the transmission is retransmitted after card resets
|
||||||
return transactRetry(handle.first, [&handle, &serial]() {
|
return transactRetry(handle.first, [&handle, &serial]() {
|
||||||
int32_t rv_l = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
|
||||||
|
|
||||||
// Ensure that the card is always selected before sending the command
|
// Ensure that the card is always selected before sending the command
|
||||||
if ((rv_l = selectApplet(handle)) != SCARD_S_SUCCESS) {
|
auto rv = selectApplet(handle);
|
||||||
return rv_l;
|
if (rv != SCARD_S_SUCCESS) {
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t pbSendBuffer[5] = {CLA_ISO, INS_API_REQ, CMD_GET_SERIAL, 0, 6};
|
uint8_t pbSendBuffer[5] = {CLA_ISO, INS_API_REQ, CMD_GET_SERIAL, 0, 6};
|
||||||
uint8_t pbRecvBuffer[6] = {0}; // 4 bytes serial, 2 bytes status
|
uint8_t pbRecvBuffer[6] = {0}; // 4 bytes serial, 2 bytes status
|
||||||
SCUINT dwRecvLength = 6;
|
SCUINT dwRecvLength = 6;
|
||||||
|
|
||||||
rv_l = transmit(handle.first, pbSendBuffer, 5, pbRecvBuffer, dwRecvLength);
|
rv = transmit(handle.first, pbSendBuffer, 5, pbRecvBuffer, dwRecvLength);
|
||||||
if (rv_l == SCARD_S_SUCCESS && dwRecvLength >= 4) {
|
if (rv == SCARD_S_SUCCESS && dwRecvLength >= 4) {
|
||||||
// The serial number is encoded MSB first
|
// The serial number is encoded MSB first
|
||||||
serial = (pbRecvBuffer[0] << 24) + (pbRecvBuffer[1] << 16) + (pbRecvBuffer[2] << 8) + (pbRecvBuffer[3]);
|
serial = (pbRecvBuffer[0] << 24) + (pbRecvBuffer[1] << 16) + (pbRecvBuffer[2] << 8) + (pbRecvBuffer[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv_l;
|
return rv;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,16 +365,18 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t openKeySerial(const unsigned int target_serial,
|
RETVAL openKeySerial(const unsigned int target_serial,
|
||||||
SCARDCONTEXT& context,
|
SCARDCONTEXT& context,
|
||||||
const QList<QByteArray>& aid_codes,
|
const QList<QByteArray>& aid_codes,
|
||||||
SCardAID* handle)
|
SCardAID* handle)
|
||||||
{
|
{
|
||||||
// Ensure the Smartcard API handle is still valid
|
// Ensure the Smartcard API handle is still valid
|
||||||
ensureValidContext(context);
|
auto rv = ensureValidContext(context);
|
||||||
|
if (rv != SCARD_S_SUCCESS) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
int32_t rv = SCARD_S_SUCCESS;
|
auto readers_list = getReaders(context);
|
||||||
QList<QString> readers_list = getReaders(context);
|
|
||||||
|
|
||||||
// Iterate all connected readers
|
// Iterate all connected readers
|
||||||
foreach (const QString& reader_name, readers_list) {
|
foreach (const QString& reader_name, readers_list) {
|
||||||
@ -392,40 +391,34 @@ namespace
|
|||||||
|
|
||||||
if (rv == SCARD_S_SUCCESS) {
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
// Read the ATR record of the card
|
// Read the ATR record of the card
|
||||||
uint8_t pbAtr[MAX_ATR_SIZE] = {0};
|
|
||||||
char pbReader[MAX_READERNAME] = {0};
|
char pbReader[MAX_READERNAME] = {0};
|
||||||
SCUINT dwAtrLen = sizeof(pbAtr);
|
|
||||||
SCUINT dwReaderLen = sizeof(pbReader);
|
SCUINT dwReaderLen = sizeof(pbReader);
|
||||||
SCUINT dwState = 0, dwProt = SCARD_PROTOCOL_UNDEFINED;
|
SCUINT dwState = 0;
|
||||||
|
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
||||||
|
uint8_t pbAtr[MAX_ATR_SIZE] = {0};
|
||||||
|
SCUINT dwAtrLen = sizeof(pbAtr);
|
||||||
|
|
||||||
rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
||||||
if (rv == SCARD_S_SUCCESS) {
|
if (rv == SCARD_S_SUCCESS && (dwProt == SCARD_PROTOCOL_T0 || dwProt == SCARD_PROTOCOL_T1)) {
|
||||||
if (dwProt == SCARD_PROTOCOL_T0 || dwProt == SCARD_PROTOCOL_T1) {
|
// Find which AID to use
|
||||||
// Find which AID to use
|
SCardAID satr;
|
||||||
SCardAID satr;
|
if (findAID(hCard, aid_codes, satr)) {
|
||||||
if (findAID(hCard, aid_codes, satr)) {
|
unsigned int serial = 0;
|
||||||
unsigned int serial = 0;
|
// Read the serial number of the card
|
||||||
// Read the serial number of the card
|
getSerial(satr, serial);
|
||||||
getSerial(satr, serial);
|
if (serial == target_serial) {
|
||||||
if (serial == target_serial) {
|
handle->first = satr.first;
|
||||||
handle->first = satr.first;
|
handle->second = satr.second;
|
||||||
handle->second = satr.second;
|
return SCARD_S_SUCCESS;
|
||||||
return SCARD_S_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
rv = static_cast<int32_t>(SCARD_E_PROTO_MISMATCH);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
|
SCardDisconnect(hCard, SCARD_LEAVE_CARD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rv != SCARD_S_SUCCESS) {
|
return SCARD_E_NO_SMARTCARD;
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<int32_t>(SCARD_E_NO_SMARTCARD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@ -438,27 +431,27 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t getStatus(const SCardAID& handle, uint8_t version[3])
|
RETVAL getStatus(const SCardAID& handle, uint8_t version[3])
|
||||||
{
|
{
|
||||||
// Ensure the transmission is retransmitted after card resets
|
// Ensure the transmission is retransmitted after card resets
|
||||||
return transactRetry(handle.first, [&handle, &version]() {
|
return transactRetry(handle.first, [&handle, &version]() {
|
||||||
int32_t rv_l = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
auto rv = selectApplet(handle);
|
||||||
|
|
||||||
// Ensure that the card is always selected before sending the command
|
// Ensure that the card is always selected before sending the command
|
||||||
if ((rv_l = selectApplet(handle)) != SCARD_S_SUCCESS) {
|
if (rv != SCARD_S_SUCCESS) {
|
||||||
return rv_l;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t pbSendBuffer[5] = {CLA_ISO, INS_STATUS, 0, 0, 6};
|
uint8_t pbSendBuffer[5] = {CLA_ISO, INS_STATUS, 0, 0, 6};
|
||||||
uint8_t pbRecvBuffer[8] = {0}; // 4 bytes serial, 2 bytes other stuff, 2 bytes status
|
uint8_t pbRecvBuffer[8] = {0}; // 4 bytes serial, 2 bytes other stuff, 2 bytes status
|
||||||
SCUINT dwRecvLength = 8;
|
SCUINT dwRecvLength = 8;
|
||||||
|
|
||||||
rv_l = transmit(handle.first, pbSendBuffer, 5, pbRecvBuffer, dwRecvLength);
|
rv = transmit(handle.first, pbSendBuffer, 5, pbRecvBuffer, dwRecvLength);
|
||||||
if (rv_l == SCARD_S_SUCCESS && dwRecvLength >= 3) {
|
if (rv == SCARD_S_SUCCESS && dwRecvLength >= 3) {
|
||||||
memcpy(version, pbRecvBuffer, 3);
|
memcpy(version, pbRecvBuffer, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv_l;
|
return rv;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -475,15 +468,15 @@ namespace
|
|||||||
*
|
*
|
||||||
* @return SCARD_S_SUCCESS on success
|
* @return SCARD_S_SUCCESS on success
|
||||||
*/
|
*/
|
||||||
int32_t getHMAC(const SCardAID& handle, uint8_t slot_cmd, const uint8_t input[64], uint8_t output[20])
|
RETVAL getHMAC(const SCardAID& handle, uint8_t slot_cmd, const uint8_t input[64], uint8_t output[20])
|
||||||
{
|
{
|
||||||
// Ensure the transmission is retransmitted after card resets
|
// Ensure the transmission is retransmitted after card resets
|
||||||
return transactRetry(handle.first, [&handle, &slot_cmd, &input, &output]() {
|
return transactRetry(handle.first, [&handle, &slot_cmd, &input, &output]() {
|
||||||
int32_t rv_l = static_cast<int32_t>(SCARD_E_UNEXPECTED);
|
auto rv = selectApplet(handle);
|
||||||
|
|
||||||
// Ensure that the card is always selected before sending the command
|
// Ensure that the card is always selected before sending the command
|
||||||
if ((rv_l = selectApplet(handle)) != SCARD_S_SUCCESS) {
|
if (rv != SCARD_S_SUCCESS) {
|
||||||
return rv_l;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t pbSendBuffer[5 + 64] = {CLA_ISO, INS_API_REQ, slot_cmd, 0, 64};
|
uint8_t pbSendBuffer[5 + 64] = {CLA_ISO, INS_API_REQ, slot_cmd, 0, 64};
|
||||||
@ -491,19 +484,19 @@ namespace
|
|||||||
uint8_t pbRecvBuffer[22] = {0}; // 20 bytes hmac, 2 bytes status
|
uint8_t pbRecvBuffer[22] = {0}; // 20 bytes hmac, 2 bytes status
|
||||||
SCUINT dwRecvLength = 22;
|
SCUINT dwRecvLength = 22;
|
||||||
|
|
||||||
rv_l = transmit(handle.first, pbSendBuffer, 5 + 64, pbRecvBuffer, dwRecvLength);
|
rv = transmit(handle.first, pbSendBuffer, 5 + 64, pbRecvBuffer, dwRecvLength);
|
||||||
if (rv_l == SCARD_S_SUCCESS && dwRecvLength >= 20) {
|
if (rv == SCARD_S_SUCCESS && dwRecvLength >= 20) {
|
||||||
memcpy(output, pbRecvBuffer, 20);
|
memcpy(output, pbRecvBuffer, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If transmission is successful but no data is returned
|
// If transmission is successful but no data is returned
|
||||||
// then the slot is probably not configured for HMAC-SHA1
|
// then the slot is probably not configured for HMAC-SHA1
|
||||||
// but for OTP or nothing instead
|
// but for OTP or nothing instead
|
||||||
if (rv_l == SCARD_S_SUCCESS && dwRecvLength != 22) {
|
if (rv == SCARD_S_SUCCESS && dwRecvLength != 22) {
|
||||||
return static_cast<int32_t>(SCARD_E_FILE_NOT_FOUND);
|
return SCARD_E_FILE_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rv_l;
|
return rv;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -555,7 +548,7 @@ void YubiKeyInterfacePCSC::findValidKeys()
|
|||||||
m_foundKeys.clear();
|
m_foundKeys.clear();
|
||||||
|
|
||||||
// Connect to each reader and look for cards
|
// Connect to each reader and look for cards
|
||||||
QList<QString> readers_list = getReaders(m_sc_context);
|
auto readers_list = getReaders(m_sc_context);
|
||||||
foreach (const QString& reader_name, readers_list) {
|
foreach (const QString& reader_name, readers_list) {
|
||||||
|
|
||||||
/* Some Yubikeys present their PCSC interface via USB as well
|
/* Some Yubikeys present their PCSC interface via USB as well
|
||||||
@ -571,71 +564,69 @@ void YubiKeyInterfacePCSC::findValidKeys()
|
|||||||
|
|
||||||
SCARDHANDLE hCard;
|
SCARDHANDLE hCard;
|
||||||
SCUINT dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
|
SCUINT dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
|
||||||
int32_t rv = SCardConnect(m_sc_context,
|
auto rv = SCardConnect(m_sc_context,
|
||||||
reader_name.toStdString().c_str(),
|
reader_name.toStdString().c_str(),
|
||||||
SCARD_SHARE_SHARED,
|
SCARD_SHARE_SHARED,
|
||||||
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
||||||
&hCard,
|
&hCard,
|
||||||
&dwActiveProtocol);
|
&dwActiveProtocol);
|
||||||
|
|
||||||
if (rv == SCARD_S_SUCCESS) {
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
// Read the potocol and the ATR record
|
// Read the potocol and the ATR record
|
||||||
uint8_t pbAtr[MAX_ATR_SIZE] = {0};
|
|
||||||
char pbReader[MAX_READERNAME] = {0};
|
char pbReader[MAX_READERNAME] = {0};
|
||||||
SCUINT dwAtrLen = sizeof(pbAtr);
|
|
||||||
SCUINT dwReaderLen = sizeof(pbReader);
|
SCUINT dwReaderLen = sizeof(pbReader);
|
||||||
SCUINT dwState = 0, dwProt = SCARD_PROTOCOL_UNDEFINED;
|
SCUINT dwState = 0;
|
||||||
rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
SCUINT dwProt = SCARD_PROTOCOL_UNDEFINED;
|
||||||
if (rv == SCARD_S_SUCCESS) {
|
uint8_t pbAtr[MAX_ATR_SIZE] = {0};
|
||||||
// Check for a valid protocol
|
SCUINT dwAtrLen = sizeof(pbAtr);
|
||||||
if (dwProt == SCARD_PROTOCOL_T0 || dwProt == SCARD_PROTOCOL_T1) {
|
|
||||||
// Find which AID to use
|
|
||||||
SCardAID satr;
|
|
||||||
if (findAID(hCard, m_aid_codes, satr)) {
|
|
||||||
// Build the UI name using the display name found in the ATR map
|
|
||||||
QByteArray atr = QByteArray(reinterpret_cast<char*>(pbAtr), dwAtrLen);
|
|
||||||
QString name = "Unknown Key";
|
|
||||||
if (m_atr_names.contains(atr)) {
|
|
||||||
name = m_atr_names.value(atr);
|
|
||||||
}
|
|
||||||
// Add the firmware version and the serial number
|
|
||||||
uint8_t version[3] = {0};
|
|
||||||
getStatus(satr, version);
|
|
||||||
name += QString(" v%1.%2.%3")
|
|
||||||
.arg(QString::number(version[0]),
|
|
||||||
QString::number(version[1]),
|
|
||||||
QString::number(version[2]));
|
|
||||||
unsigned int serial = 0;
|
|
||||||
getSerial(satr, serial);
|
|
||||||
|
|
||||||
/* This variable indicates that the key is locked / timed out.
|
rv = SCardStatus(hCard, pbReader, &dwReaderLen, &dwState, &dwProt, pbAtr, &dwAtrLen);
|
||||||
When using the key via NFC, the user has to re-present the key to clear the timeout.
|
if (rv == SCARD_S_SUCCESS && (dwProt == SCARD_PROTOCOL_T0 || dwProt == SCARD_PROTOCOL_T1)) {
|
||||||
Also, the key can be programmatically reset (see below).
|
// Find which AID to use
|
||||||
When using the key via USB (where the Yubikey presents as a PCSC reader in itself),
|
SCardAID satr;
|
||||||
the non-HMAC-SHA1 slots (eg. OTP) are incorrectly recognized as locked HMAC-SHA1 slots.
|
if (findAID(hCard, m_aid_codes, satr)) {
|
||||||
Due to this conundrum, we exclude "locked" keys from the key enumeration,
|
// Build the UI name using the display name found in the ATR map
|
||||||
but only if the reader is the "virtual yubikey reader device".
|
QByteArray atr(reinterpret_cast<char*>(pbAtr), dwAtrLen);
|
||||||
This also has the nice side effect of de-duplicating interfaces when a key
|
QString name("Unknown Key");
|
||||||
Is connected via USB and also accessible via PCSC */
|
if (m_atr_names.contains(atr)) {
|
||||||
bool wouldBlock = false;
|
name = m_atr_names.value(atr);
|
||||||
/* When the key is Used via NFC, the lock state / time-out is cleared when
|
}
|
||||||
The smartcard connection is re-established / the applet is selected
|
// Add the firmware version and the serial number
|
||||||
So the next call to performTestChallenge actually clears the lock.
|
uint8_t version[3] = {0};
|
||||||
Due to this, the key is unlocked and we display it as such.
|
getStatus(satr, version);
|
||||||
When the key times out in the time between the key listing and
|
name += QString(" v%1.%2.%3")
|
||||||
the database unlock /save, an intercation request will be displayed. */
|
.arg(QString::number(version[0]),
|
||||||
for (int slot = 1; slot <= 2; ++slot) {
|
QString::number(version[1]),
|
||||||
if (performTestChallenge(&satr, slot, &wouldBlock)) {
|
QString::number(version[2]));
|
||||||
auto display = tr("(PCSC) %1 [%2] Challenge-Response - Slot %3")
|
|
||||||
.arg(name, QString::number(serial), QString::number(slot));
|
unsigned int serial = 0;
|
||||||
m_foundKeys.insert(serial, {slot, display});
|
getSerial(satr, serial);
|
||||||
}
|
|
||||||
|
/* This variable indicates that the key is locked / timed out.
|
||||||
|
When using the key via NFC, the user has to re-present the key to clear the timeout.
|
||||||
|
Also, the key can be programmatically reset (see below).
|
||||||
|
When using the key via USB (where the Yubikey presents as a PCSC reader in itself),
|
||||||
|
the non-HMAC-SHA1 slots (eg. OTP) are incorrectly recognized as locked HMAC-SHA1 slots.
|
||||||
|
Due to this conundrum, we exclude "locked" keys from the key enumeration,
|
||||||
|
but only if the reader is the "virtual yubikey reader device".
|
||||||
|
This also has the nice side effect of de-duplicating interfaces when a key
|
||||||
|
Is connected via USB and also accessible via PCSC */
|
||||||
|
bool wouldBlock = false;
|
||||||
|
/* When the key is used via NFC, the lock state / time-out is cleared when
|
||||||
|
the smartcard connection is re-established / the applet is selected
|
||||||
|
so the next call to performTestChallenge actually clears the lock.
|
||||||
|
Due to this the key is unlocked, and we display it as such.
|
||||||
|
When the key times out in the time between the key listing and
|
||||||
|
the database unlock /save, an interaction request will be displayed. */
|
||||||
|
for (int slot = 1; slot <= 2; ++slot) {
|
||||||
|
if (performTestChallenge(&satr, slot, &wouldBlock)) {
|
||||||
|
auto display = tr("(PCSC) %1 [%2] Challenge-Response - Slot %3")
|
||||||
|
.arg(name, QString::number(serial), QString::number(slot));
|
||||||
|
m_foundKeys.insert(serial, {slot, display});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rv = SCardDisconnect(hCard, SCARD_LEAVE_CARD);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,8 +639,8 @@ bool YubiKeyInterfacePCSC::testChallenge(YubiKeySlot slot, bool* wouldBlock)
|
|||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
SCardAID hCard;
|
SCardAID hCard;
|
||||||
int32_t rv = openKeySerial(slot.first, m_sc_context, m_aid_codes, &hCard);
|
|
||||||
|
|
||||||
|
auto rv = openKeySerial(slot.first, m_sc_context, m_aid_codes, &hCard);
|
||||||
if (rv == SCARD_S_SUCCESS) {
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
ret = performTestChallenge(&hCard, slot.second, wouldBlock);
|
ret = performTestChallenge(&hCard, slot.second, wouldBlock);
|
||||||
SCardDisconnect(hCard.first, SCARD_LEAVE_CARD);
|
SCardDisconnect(hCard.first, SCARD_LEAVE_CARD);
|
||||||
@ -694,7 +685,7 @@ YubiKeyInterfacePCSC::challenge(YubiKeySlot slot, const QByteArray& challenge, B
|
|||||||
SCardAID hCard;
|
SCardAID hCard;
|
||||||
int tries = 20; // 5 seconds, test every 250 ms
|
int tries = 20; // 5 seconds, test every 250 ms
|
||||||
while (tries > 0) {
|
while (tries > 0) {
|
||||||
int32_t rv = openKeySerial(slot.first, m_sc_context, m_aid_codes, &hCard);
|
auto rv = openKeySerial(slot.first, m_sc_context, m_aid_codes, &hCard);
|
||||||
// Key with specified serial number is found
|
// Key with specified serial number is found
|
||||||
if (rv == SCARD_S_SUCCESS) {
|
if (rv == SCARD_S_SUCCESS) {
|
||||||
auto ret = performChallenge(&hCard, slot.second, true, challenge, response);
|
auto ret = performChallenge(&hCard, slot.second, true, challenge, response);
|
||||||
@ -758,19 +749,16 @@ YubiKey::ChallengeResult YubiKeyInterfacePCSC::performChallenge(void* key,
|
|||||||
paddedChallenge.append(QByteArray(padLen, padLen));
|
paddedChallenge.append(QByteArray(padLen, padLen));
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char* c;
|
auto c = reinterpret_cast<const unsigned char*>(paddedChallenge.constData());
|
||||||
unsigned char* r;
|
auto r = reinterpret_cast<unsigned char*>(response.data());
|
||||||
c = reinterpret_cast<const unsigned char*>(paddedChallenge.constData());
|
|
||||||
r = reinterpret_cast<unsigned char*>(response.data());
|
|
||||||
|
|
||||||
int32_t rv = getHMAC(*static_cast<SCardAID*>(key), yk_cmd, c, r);
|
|
||||||
|
|
||||||
|
auto rv = getHMAC(*static_cast<SCardAID*>(key), yk_cmd, c, r);
|
||||||
if (rv != SCARD_S_SUCCESS) {
|
if (rv != SCARD_S_SUCCESS) {
|
||||||
if (rv == static_cast<int32_t>(SCARD_W_CARD_NOT_AUTHENTICATED)) {
|
if (rv == SCARD_W_CARD_NOT_AUTHENTICATED) {
|
||||||
m_error = tr("Hardware key is locked or timed out. Unlock or re-present it to continue.");
|
m_error = tr("Hardware key is locked or timed out. Unlock or re-present it to continue.");
|
||||||
return YubiKey::ChallengeResult::YCR_WOULDBLOCK;
|
return YubiKey::ChallengeResult::YCR_WOULDBLOCK;
|
||||||
} else if (rv == static_cast<int32_t>(SCARD_E_FILE_NOT_FOUND)) {
|
} else if (rv == SCARD_E_FILE_NOT_FOUND) {
|
||||||
m_error = tr("Hardware key was not found or is misconfigured.");
|
m_error = tr("Hardware key was not found or is not configured.");
|
||||||
} else {
|
} else {
|
||||||
m_error =
|
m_error =
|
||||||
tr("Failed to complete a challenge-response, the PCSC error code was: %1").arg(QString::number(rv));
|
tr("Failed to complete a challenge-response, the PCSC error code was: %1").arg(QString::number(rv));
|
||||||
|
Loading…
Reference in New Issue
Block a user