525 lines
12 KiB
C++
525 lines
12 KiB
C++
/*!
|
|
* Commands.cpp
|
|
*
|
|
* \copyright Copyright (c) 2014 Governikus GmbH & Co. KG
|
|
*/
|
|
|
|
#include "Commands.h"
|
|
|
|
#include "asn1/ASN1Util.h"
|
|
#include "FileRef.h"
|
|
#include "SecureMessagingResponse.h"
|
|
|
|
#include <QLoggingCategory>
|
|
|
|
using namespace governikus;
|
|
|
|
|
|
Q_DECLARE_LOGGING_CATEGORY(card)
|
|
|
|
|
|
/*
|
|
* The base class CommandApduBuilder
|
|
*/
|
|
CommandApduBuilder::CommandApduBuilder()
|
|
{
|
|
}
|
|
|
|
|
|
CommandApduBuilder::~CommandApduBuilder()
|
|
{
|
|
}
|
|
|
|
|
|
/*
|
|
* SelectBuilder
|
|
*/
|
|
SelectBuilder::SelectBuilder(const FileRef& pFileRef)
|
|
: CommandApduBuilder()
|
|
, mFileRef(pFileRef)
|
|
{
|
|
}
|
|
|
|
|
|
CommandApdu SelectBuilder::build()
|
|
{
|
|
static const char INS = char(0xA4);
|
|
return CommandApdu(CommandApdu::CLA, INS, mFileRef.type, static_cast<char>(P2::NONE), mFileRef.path);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetChallengeBuilder
|
|
*/
|
|
|
|
GetChallengeBuilder::GetChallengeBuilder()
|
|
{
|
|
}
|
|
|
|
|
|
CommandApdu GetChallengeBuilder::build()
|
|
{
|
|
static const char INS = char(0x84);
|
|
return CommandApdu(CommandApdu::CLA, INS, 0, 0, QByteArray(), 0x08);
|
|
}
|
|
|
|
|
|
/*
|
|
* GetChallengeResponse
|
|
*/
|
|
|
|
GetChallengeResponse::GetChallengeResponse()
|
|
: ResponseApdu()
|
|
{
|
|
}
|
|
|
|
|
|
GetChallengeResponse::~GetChallengeResponse()
|
|
{
|
|
}
|
|
|
|
|
|
QByteArray GetChallengeResponse::getChallenge() const
|
|
{
|
|
if (getDataLength() != 8)
|
|
{
|
|
qCCritical(card) << "Challenge has wrong size. Expect 8 bytes, got " << getDataLength();
|
|
}
|
|
return getData();
|
|
}
|
|
|
|
|
|
/*
|
|
* MSEBuilder
|
|
*/
|
|
|
|
MSEBuilder::MSEBuilder(P1 p1, P2 p2)
|
|
: CommandApduBuilder()
|
|
, mP1(p1)
|
|
, mP2(p2)
|
|
, mAuxiliaryData()
|
|
, mOid()
|
|
, mPublicKey()
|
|
, mPrivateKey()
|
|
, mEphemeralPublicKey()
|
|
, mChat()
|
|
{
|
|
}
|
|
|
|
|
|
void MSEBuilder::setAuxiliaryData(const QByteArray& pData)
|
|
{
|
|
mAuxiliaryData = pData;
|
|
}
|
|
|
|
|
|
void MSEBuilder::setOid(const QByteArray& pData)
|
|
{
|
|
static const char TAG_OID = char(0x80);
|
|
mOid = Asn1Util::encode(TAG_OID, pData);
|
|
}
|
|
|
|
|
|
void MSEBuilder::setPublicKey(const QByteArray& pData)
|
|
{
|
|
static const char TAG_PUBLIC_KEY = char(0x83);
|
|
mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, pData);
|
|
}
|
|
|
|
|
|
void MSEBuilder::setPublicKey(PACE_PIN_ID pPin)
|
|
{
|
|
static const char TAG_PUBLIC_KEY = char(0x83);
|
|
QByteArray data;
|
|
data += Enum<PACE_PIN_ID>::getValue(pPin);
|
|
mPublicKey = Asn1Util::encode(TAG_PUBLIC_KEY, data);
|
|
}
|
|
|
|
|
|
void MSEBuilder::setPrivateKey(const QByteArray& pData)
|
|
{
|
|
static const char TAG_PRIVATE_KEY = char(0x84);
|
|
mPrivateKey = Asn1Util::encode(TAG_PRIVATE_KEY, pData);
|
|
}
|
|
|
|
|
|
void MSEBuilder::setEphemeralPublicKey(const QByteArray& pData)
|
|
{
|
|
static const char TAG_EPHEMERAL_PUBLIC_KEY = char(0x91);
|
|
mEphemeralPublicKey = Asn1Util::encode(TAG_EPHEMERAL_PUBLIC_KEY, pData);
|
|
}
|
|
|
|
|
|
void MSEBuilder::setChat(const QByteArray& pData)
|
|
{
|
|
mChat = pData;
|
|
}
|
|
|
|
|
|
CommandApdu MSEBuilder::build()
|
|
{
|
|
static const char INS = 0x22;
|
|
|
|
QByteArray data;
|
|
data += mOid;
|
|
data += mPublicKey;
|
|
data += mPrivateKey;
|
|
data += mAuxiliaryData;
|
|
data += mEphemeralPublicKey;
|
|
data += mChat;
|
|
|
|
return CommandApdu(CommandApdu::CLA, INS, static_cast<char>(mP1), static_cast<char>(mP2), data);
|
|
}
|
|
|
|
|
|
/*
|
|
* PSOBuilder
|
|
*/
|
|
PSOBuilder::PSOBuilder(P1 p1, P2 p2)
|
|
: CommandApduBuilder()
|
|
, mP1(p1)
|
|
, mP2(p2)
|
|
, mCertificateBody()
|
|
, mSignature()
|
|
{
|
|
}
|
|
|
|
|
|
void PSOBuilder::setCertificateBody(const QByteArray& pData)
|
|
{
|
|
mCertificateBody = pData;
|
|
}
|
|
|
|
|
|
void PSOBuilder::setSignature(const QByteArray& pData)
|
|
{
|
|
mSignature = pData;
|
|
}
|
|
|
|
|
|
CommandApdu PSOBuilder::build()
|
|
{
|
|
static const int INS = 0x2a;
|
|
|
|
QByteArray data;
|
|
data += mCertificateBody;
|
|
data += mSignature;
|
|
|
|
return CommandApdu(CommandApdu::CLA, INS, char(mP1), char(mP2), data);
|
|
}
|
|
|
|
|
|
/*
|
|
* EABuilder
|
|
*/
|
|
EABuilder::EABuilder()
|
|
: CommandApduBuilder()
|
|
, mSignature()
|
|
{
|
|
}
|
|
|
|
|
|
void EABuilder::setSignature(const QByteArray& pData)
|
|
{
|
|
mSignature = pData;
|
|
}
|
|
|
|
|
|
CommandApdu EABuilder::build()
|
|
{
|
|
static const char INS = char(0x82);
|
|
return CommandApdu(CommandApdu::CLA, INS, 0, 0, mSignature);
|
|
}
|
|
|
|
|
|
/*
|
|
* GABuilder
|
|
*/
|
|
GABuilder::GABuilder(char pClassByte)
|
|
: CommandApduBuilder()
|
|
, mClassByte(pClassByte)
|
|
, mCaEphemeralPublicKey()
|
|
, mPaceMappingData()
|
|
, mPaceEphemeralPublicKey()
|
|
, mPaceAuthenticationToken()
|
|
{
|
|
}
|
|
|
|
|
|
void GABuilder::setCaEphemeralPublicKey(const QByteArray& pData)
|
|
{
|
|
static const char TAG_EPHEMERAL_PUBLIC_KEY = char(0x80);
|
|
mCaEphemeralPublicKey = Asn1Util::encode(TAG_EPHEMERAL_PUBLIC_KEY, pData);
|
|
}
|
|
|
|
|
|
void GABuilder::setPaceMappingData(const QByteArray& pData)
|
|
{
|
|
static const char TAG_PACE_MAPPING_DATA = char(0x81);
|
|
mPaceMappingData = Asn1Util::encode(TAG_PACE_MAPPING_DATA, pData);
|
|
}
|
|
|
|
|
|
void GABuilder::setPaceEphemeralPublicKey(const QByteArray& pData)
|
|
{
|
|
static const char TAG_PACE_EPHEMERAL_PUBLIC_KEY = char(0x83);
|
|
mPaceEphemeralPublicKey = Asn1Util::encode(TAG_PACE_EPHEMERAL_PUBLIC_KEY, pData);
|
|
}
|
|
|
|
|
|
void GABuilder::setPaceAuthenticationToken(const QByteArray& pData)
|
|
{
|
|
static const char TAG_PACE_AUTHENTICATION_TOKEN = char(0x85);
|
|
mPaceAuthenticationToken = Asn1Util::encode(TAG_PACE_AUTHENTICATION_TOKEN, pData);
|
|
}
|
|
|
|
|
|
CommandApdu GABuilder::build()
|
|
{
|
|
static const char INS = char(0x86);
|
|
static const char TAG_DYNAMIC_AUTHENTICATION_DATA = 0x7C;
|
|
|
|
QByteArray data;
|
|
if (!mCaEphemeralPublicKey.isNull())
|
|
{
|
|
data += mCaEphemeralPublicKey;
|
|
}
|
|
else if (!mPaceMappingData.isNull())
|
|
{
|
|
data += mPaceMappingData;
|
|
}
|
|
else if (!mPaceEphemeralPublicKey.isNull())
|
|
{
|
|
data += mPaceEphemeralPublicKey;
|
|
}
|
|
else if (!mPaceAuthenticationToken.isNull())
|
|
{
|
|
data += mPaceAuthenticationToken;
|
|
}
|
|
data = Asn1Util::encode(TAG_DYNAMIC_AUTHENTICATION_DATA, data);
|
|
|
|
return CommandApdu(mClassByte, INS, 0, 0, data, Apdu::SHORT_MAX_LE);
|
|
}
|
|
|
|
|
|
ReadBinaryBuilder::ReadBinaryBuilder(uint pOffset, int pLe)
|
|
: CommandApduBuilder()
|
|
, mOffset(pOffset)
|
|
, mLe(pLe)
|
|
{
|
|
}
|
|
|
|
|
|
CommandApdu ReadBinaryBuilder::build()
|
|
{
|
|
static const char INS = char(0xB0);
|
|
return CommandApdu(CommandApdu::CLA, INS, static_cast<char>((mOffset & 0xff00) >> 8), static_cast<char>(mOffset & 0xff), QByteArray(), mLe);
|
|
}
|
|
|
|
|
|
ResetRetryCounterBuilder::ResetRetryCounterBuilder(const QByteArray& pPin)
|
|
: CommandApduBuilder()
|
|
, mPin(pPin)
|
|
{
|
|
}
|
|
|
|
|
|
CommandApdu ResetRetryCounterBuilder::build()
|
|
{
|
|
static const char INS = 0x2c;
|
|
// P1: 2 (change), 3 (unblock)
|
|
char p1 = mPin.isNull() ? 3 : 2;
|
|
// P2: 3 (PIN) (2 (CAN) -- not used)
|
|
// data: new PIN, when changing
|
|
return CommandApdu(CommandApdu::CLA, INS, p1, 3, mPin);
|
|
}
|
|
|
|
|
|
QByteArray PinModifyBuilder::createChangeEidPinCommandData(quint8 pTimeoutSeconds) const
|
|
{
|
|
// According to ISO-7816-4, 7.5.10 RESET RETRY COUNTER command
|
|
QByteArray abData = QByteArrayLiteral("002C0203");
|
|
|
|
return createCommandData(pTimeoutSeconds, 0x00, 0x01, 0x02, QByteArray::fromHex(abData));
|
|
}
|
|
|
|
|
|
QByteArray PinModifyBuilder::createCommandData(quint8 pTimeoutSeconds, char pMsgIndex1, char pMsgIndex2, char pMsgIndex3, const QByteArray& pAbData) const
|
|
{
|
|
// as defined in PC/SC, Part 10 "IFDs with Secure PIN Entry Capabilities"
|
|
QByteArray command;
|
|
// bTimeOut (timeout in seconds)
|
|
command += static_cast<char>(pTimeoutSeconds);
|
|
// bTimeOut2 (timeout in seconds after first key pressed)
|
|
command += static_cast<char>(pTimeoutSeconds);
|
|
// bmFormatString (PIN format): system unit is bytes (0x80), ASCII format (0x02)
|
|
command += char(0x82);
|
|
// bmPINBlockString (PIN block size and length info): PIN not in APDU command
|
|
command += char(0x00);
|
|
// bmPINLengthFormat (format of PIN length field in APDU command): PIN not in APDU command
|
|
command += char(0x00);
|
|
// bInsertionOffsetOld (insertion position offset for old PIN)
|
|
command += char(0x00);
|
|
// bInsertionOffsetNew BYTE (insertion position offset for new PIN)
|
|
command += char(0x00);
|
|
// wPINMaxExtraDigit USHORT (0xXXYY, min (XX) and max (length) of new PIN)
|
|
command += 0x06;
|
|
command += 0x06;
|
|
// bConfirmPIN (PIN confirmation options): confirm new PIN (0x01)
|
|
command += 0x01;
|
|
// bEntryValidationCondition (new PIN validation options): validation key pressed (0x02)
|
|
command += 0x02;
|
|
// bNumberMessage (number of display messages to be sent)
|
|
command += 0x02;
|
|
// wLangId (language ID for display messages): German (0x0407)
|
|
command += 0x07;
|
|
command += 0x04;
|
|
// bMsgIndex1 (index (into reader table) of first message to display)
|
|
command += pMsgIndex1;
|
|
// bMsgIndex2 (index (into reader table) of second message to display)
|
|
command += pMsgIndex2;
|
|
// bMsgIndex3 (index (into reader table) of third message to display)
|
|
command += pMsgIndex3;
|
|
// bTeoPrologue (T1 only: I-block prologue field to use): fill with 0
|
|
command += char(0x00);
|
|
command += char(0x00);
|
|
command += char(0x00);
|
|
|
|
if (pAbData.size() > 0xFF)
|
|
{
|
|
qCCritical(card) << "abData size bigger than 0xFF currently not supported.";
|
|
Q_ASSERT(pAbData.size() <= 0xFF);
|
|
return QByteArray();
|
|
}
|
|
// ulDataLength (length of the APDU to be sent to ICC)
|
|
command += static_cast<char>(pAbData.size());
|
|
command += char(0x00);
|
|
command += char(0x00);
|
|
command += char(0x00);
|
|
command += pAbData;
|
|
|
|
return command;
|
|
}
|
|
|
|
|
|
CommandApdu PinModifyBuilder::createCommandDataCcid(quint8 pTimeoutSeconds) const
|
|
{
|
|
// According to TR-03119 the command data has to be the full PC_to_RDR_Secure structure
|
|
// According to Reiner SCT the firmware is implemented in such a way, that the command
|
|
// data is expected as abPINOperationDataStucture
|
|
|
|
// according to DWG_Smart-Card_CCID_Rev110.pdf
|
|
QByteArray abPINDataStructure;
|
|
|
|
// bTimeOut (timeout in seconds)
|
|
abPINDataStructure += static_cast<char>(pTimeoutSeconds);
|
|
// bmFormatString (PIN format): system unit is bytes (0x80), ASCII format (0x02)
|
|
abPINDataStructure += char(0x82);
|
|
// bmPINBlockString (PIN block size and length info): PIN not in APDU command
|
|
abPINDataStructure += char(0x00);
|
|
// bmPINLengthFormat (format of PIN length field in APDU command): PIN not in APDU command
|
|
abPINDataStructure += char(0x00);
|
|
// bInsertionOffsetOld (insertion position offset for old PIN)
|
|
abPINDataStructure += char(0x00);
|
|
// bInsertionOffsetNew BYTE (insertion position offset for new PIN)
|
|
abPINDataStructure += char(0x00);
|
|
// wPINMaxExtraDigit USHORT (0xXXYY, min (XX) and max (length) of new PIN)
|
|
abPINDataStructure += char(0x06);
|
|
abPINDataStructure += char(0x06);
|
|
// bConfirmPIN (PIN confirmation options): confirm new PIN (0x01)
|
|
abPINDataStructure += char(0x01);
|
|
// bEntryValidationCondition (new PIN validation options): validation key pressed (0x02)
|
|
abPINDataStructure += char(0x02);
|
|
// bNumberMessage (number of display messages to be sent)
|
|
abPINDataStructure += char(0x02);
|
|
// wLangId (language ID for display messages): German (0x0407)
|
|
abPINDataStructure += char(0x07);
|
|
abPINDataStructure += char(0x04);
|
|
// bMsgIndex1 (index (into reader table) of first message to display)
|
|
abPINDataStructure += char(0x01);
|
|
// bMsgIndex2 (index (into reader table) of second message to display)
|
|
abPINDataStructure += char(0x02);
|
|
// bMsgIndex3 (index (into reader table) of third message to display)
|
|
abPINDataStructure += char(0x00);
|
|
// bTeoPrologue (T1 only: I-block prologue field to use): fill with 0
|
|
abPINDataStructure += char(0x00);
|
|
abPINDataStructure += char(0x00);
|
|
abPINDataStructure += char(0x00);
|
|
// abData (APDU to be sent to ICC)
|
|
abPINDataStructure += char(0x00); // CLA: command
|
|
abPINDataStructure += char(0x2c); // INS: Reset Retry Counter
|
|
abPINDataStructure += char(0x02); // P1: new PIN/CAN
|
|
abPINDataStructure += char(0x03); // P2: PIN
|
|
QByteArray abPINOperationDataStucture;
|
|
abPINOperationDataStucture += char(0x01); //bPINOperation
|
|
abPINOperationDataStucture += abPINDataStructure; //abPINDataStructure
|
|
|
|
// boxing command according to TR-03119
|
|
return CommandApdu(char(0xFF), char(0x9A), 0x04, 0x10, abPINOperationDataStucture);
|
|
}
|
|
|
|
|
|
void PinModifyOutput::parse(const QByteArray& pData)
|
|
{
|
|
if (pData.size() != 2)
|
|
{
|
|
mReturnCode = CardReturnCode::UNKNOWN;
|
|
}
|
|
|
|
const int errorCode = static_cast<quint8>(pData.at(0)) << 8 | static_cast<quint8>(pData.at(1));
|
|
switch (errorCode)
|
|
{
|
|
case 0x6400:
|
|
// operation timed out
|
|
mReturnCode = CardReturnCode::INPUT_TIME_OUT;
|
|
break;
|
|
|
|
case 0x6401:
|
|
// operation canceled by "Cancel" button
|
|
mReturnCode = CardReturnCode::CANCELLATION_BY_USER;
|
|
break;
|
|
|
|
case 0x6402:
|
|
// the two new PIN entries don't match
|
|
mReturnCode = CardReturnCode::NEW_PIN_MISMATCH;
|
|
break;
|
|
|
|
case 0x6403:
|
|
// entered PIN too short/long
|
|
mReturnCode = CardReturnCode::NEW_PIN_INVALID_LENGTH;
|
|
break;
|
|
|
|
case 0x6b80:
|
|
// invalid parameter in passed structure
|
|
mReturnCode = CardReturnCode::COMMAND_FAILED;
|
|
break;
|
|
|
|
case 0x6982:
|
|
// terminal is not authorized to unblock or change the PIN
|
|
mReturnCode = CardReturnCode::UNKNOWN;
|
|
break;
|
|
|
|
case 0x9000:
|
|
// success
|
|
mReturnCode = CardReturnCode::OK;
|
|
break;
|
|
|
|
default:
|
|
qCDebug(card) << "unknown error:" << pData.toHex();
|
|
mReturnCode = CardReturnCode::UNKNOWN;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void PinModifyOutput::parseFromCcid(const QByteArray& pData)
|
|
{
|
|
parse(pData);
|
|
}
|
|
|
|
|
|
CardReturnCode PinModifyOutput::getReturnCode() const
|
|
{
|
|
return mReturnCode;
|
|
}
|