Skip to content

Commit

Permalink
CommandAPDU reuse data object to avoid PIN copy
Browse files Browse the repository at this point in the history
WE2-1007

Signed-off-by: Raul Metsma <[email protected]>
  • Loading branch information
metsma committed Aug 6, 2024
1 parent 261c0d0 commit 1165e3a
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 70 deletions.
45 changes: 15 additions & 30 deletions lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,53 +140,38 @@ struct ResponseApdu
};

/** Struct that wraps command APDUs. */
struct CommandApdu
struct CommandApdu : public byte_vector
{
byte_type cla;
byte_type ins;
byte_type p1;
byte_type p2;
unsigned short le;
// Lc is data.size()
byte_vector data;

static const size_t MAX_DATA_SIZE = 255;
static const unsigned short LE_UNUSED = std::numeric_limits<unsigned short>::max();

CommandApdu(byte_type c, byte_type i, byte_type pp1, byte_type pp2, byte_vector d = {},
unsigned short l = LE_UNUSED) :
cla(c), ins(i), p1(pp1), p2(pp2), le(l), data(std::move(d))
// Case 1
CommandApdu(byte_type cls, byte_type ins, byte_type p1, byte_type p2) :
byte_vector {cls, ins, p1, p2}
{
}

CommandApdu(const CommandApdu& other, byte_vector d) :
CommandApdu(other.cla, other.ins, other.p1, other.p2, std::move(d), other.le)
// Case 2
CommandApdu(byte_type cls, byte_type ins, byte_type p1, byte_type p2, byte_type le) :
byte_vector {cls, ins, p1, p2, le}
{
}

constexpr bool isLeSet() const noexcept { return le != LE_UNUSED; }

byte_vector toBytes() const
// Case 3/4
CommandApdu(byte_type cls, byte_type ins, byte_type p1, byte_type p2, byte_vector data,
unsigned short le = LE_UNUSED) : byte_vector {std::move(data)}
{
if (data.size() > MAX_DATA_SIZE) {
if (size() > MAX_DATA_SIZE) {
throw std::invalid_argument("Command chaining not supported");
}
insert(begin(), {cls, ins, p1, p2, static_cast<byte_type>(size())});

auto bytes = byte_vector {cla, ins, p1, p2};

if (!data.empty()) {
bytes.push_back(static_cast<byte_type>(data.size()));
bytes.insert(bytes.end(), data.cbegin(), data.cend());
}

if (isLeSet()) {
if (le != LE_UNUSED) {
// TODO: EstEID spec: the maximum value of Le is 0xFE
if (le > ResponseApdu::MAX_DATA_SIZE)
throw std::invalid_argument("LE larger than response size");
bytes.push_back(static_cast<byte_type>(le));
push_back(static_cast<byte_type>(le));
}

return bytes;
}
};

Expand Down Expand Up @@ -295,7 +280,7 @@ void transmitApduWithExpectedResponse(const SmartCard& card, const CommandApdu&
size_t readDataLengthFromAsn1(const SmartCard& card);

/** Read lenght bytes from currently selected binary file in blockLength-sized chunks. */
byte_vector readBinary(const SmartCard& card, const size_t length, const size_t blockLength);
byte_vector readBinary(const SmartCard& card, const size_t length, byte_type blockLength);

// Errors.

Expand Down
4 changes: 2 additions & 2 deletions lib/libpcsc-cpp/src/SmartCard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ ResponseApdu SmartCard::transmit(const CommandApdu& command) const
THROW(std::logic_error, "Call SmartCard::transmit() inside a transaction");
}

return card->transmitBytes(command.toBytes());
return card->transmitBytes(command);
}

ResponseApdu SmartCard::transmitCTL(const CommandApdu& command, uint16_t lang, uint8_t minlen) const
Expand All @@ -327,7 +327,7 @@ ResponseApdu SmartCard::transmitCTL(const CommandApdu& command, uint16_t lang, u
THROW(std::logic_error, "Call SmartCard::transmit() inside a transaction");
}

return card->transmitBytesCTL(command.toBytes(), lang, minlen);
return card->transmitBytesCTL(command, lang, minlen);
}

} // namespace pcsc_cpp
23 changes: 9 additions & 14 deletions lib/libpcsc-cpp/src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ class UnexpectedResponseError : public Error
const ResponseApdu& response, const char* file, const int line,
const char* callerFunctionName) :
Error("transmitApduWithExpectedResponse(): Unexpected response to command '"s
+ bytes2hexstr(command.toBytes()) + "' - expected '"s
+ bytes2hexstr(expectedResponseBytes) + "', got '"s + bytes2hexstr(response.toBytes())
+ " in " + removeAbsolutePathPrefix(file) + ':' + std::to_string(line) + ':'
+ bytes2hexstr(command) + "' - expected '"s + bytes2hexstr(expectedResponseBytes)
+ "', got '"s + bytes2hexstr(response.toBytes()) + " in "
+ removeAbsolutePathPrefix(file) + ':' + std::to_string(line) + ':'
+ callerFunctionName)
{
}
Expand Down Expand Up @@ -101,7 +101,7 @@ size_t readDataLengthFromAsn1(const SmartCard& card)
// p1 - offset size first byte, 0
// p2 - offset size second byte, 0
// le - number of bytes to read, need 4 bytes from start for length
const auto readBinary4Bytes = CommandApdu {0x00, 0xb0, 0x00, 0x00, byte_vector(), 0x04};
const CommandApdu readBinary4Bytes {0x00, 0xb0, 0x00, 0x00, 0x04};

auto response = card.transmit(readBinary4Bytes);

Expand Down Expand Up @@ -135,24 +135,19 @@ size_t readDataLengthFromAsn1(const SmartCard& card)
return length;
}

byte_vector readBinary(const SmartCard& card, const size_t length, const size_t blockLength)
byte_vector readBinary(const SmartCard& card, const size_t length, byte_type blockLength)
{
size_t blockLengthVar = blockLength;
auto lengthCounter = length;
auto resultBytes = byte_vector {};
auto readBinary = CommandApdu {0x00, 0xb0, 0x00, 0x00};

for (size_t offset = 0; lengthCounter != 0;
offset += blockLengthVar, lengthCounter -= blockLengthVar) {
offset += blockLength, lengthCounter -= blockLength) {

if (blockLengthVar > lengthCounter) {
blockLengthVar = lengthCounter;
if (blockLength > lengthCounter) {
blockLength = byte_type(lengthCounter);
}

readBinary.p1 = HIBYTE(offset);
readBinary.p2 = LOBYTE(offset);
readBinary.le = static_cast<byte_type>(blockLengthVar);

CommandApdu readBinary {0x00, 0xb0, HIBYTE(offset), LOBYTE(offset), blockLength};
auto response = card.transmit(readBinary);

resultBytes.insert(resultBytes.end(), response.data.cbegin(), response.data.cend());
Expand Down
4 changes: 2 additions & 2 deletions src/electronic-ids/pcsc/FinEID.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ byte_vector FinEIDv3::sign(const HashAlgorithm hashAlgo, const byte_vector& hash
byte_vector tlv {0x90, byte_type(hash.size())};
tlv.insert(tlv.cend(), hash.cbegin(), hash.cend());

const CommandApdu computeSignature {{0x00, 0x2A, 0x90, 0xA0}, std::move(tlv)};
const CommandApdu computeSignature {0x00, 0x2A, 0x90, 0xA0, std::move(tlv)};
const auto response = card->transmit(computeSignature);

if (response.sw1 == ResponseApdu::WRONG_LENGTH) {
Expand All @@ -156,7 +156,7 @@ byte_vector FinEIDv3::sign(const HashAlgorithm hashAlgo, const byte_vector& hash
"Command COMPUTE SIGNATURE failed with error " + bytes2hexstr(response.toBytes()));
}

const CommandApdu getSignature {0x00, 0x2A, 0x9E, 0x9A, {}, LE};
const CommandApdu getSignature {0x00, 0x2A, 0x9E, 0x9A, LE};
const auto signature = card->transmit(getSignature);

if (signature.sw1 == ResponseApdu::WRONG_LENGTH) {
Expand Down
27 changes: 5 additions & 22 deletions src/electronic-ids/pcsc/pcsc-common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace electronic_id
inline pcsc_cpp::byte_vector getCertificate(pcsc_cpp::SmartCard& card,
const pcsc_cpp::CommandApdu& selectCertFileCmd)
{
static const size_t MAX_LE_VALUE = 0xb5;
static const pcsc_cpp::byte_type MAX_LE_VALUE = 0xb5;

transmitApduWithExpectedResponse(card, selectCertFileCmd);

Expand All @@ -54,17 +54,16 @@ inline void verifyPin(pcsc_cpp::SmartCard& card, pcsc_cpp::byte_type p2,
pcsc_cpp::byte_vector&& pin, uint8_t pinMinLength, size_t paddingLength,
pcsc_cpp::byte_type paddingChar)
{
const pcsc_cpp::CommandApdu VERIFY_PIN {0x00, 0x20, 0x00, p2};
pcsc_cpp::ResponseApdu response;

if (card.readerHasPinPad()) {
const pcsc_cpp::CommandApdu verifyPin {VERIFY_PIN,
const pcsc_cpp::CommandApdu verifyPin {0x00, 0x20, 0x00, p2,
addPaddingToPin({}, paddingLength, paddingChar)};
response = card.transmitCTL(verifyPin, 0, pinMinLength);

} else {
const pcsc_cpp::CommandApdu verifyPin {
VERIFY_PIN, addPaddingToPin(std::move(pin), paddingLength, paddingChar)};
0x00, 0x20, 0x00, p2, addPaddingToPin(std::move(pin), paddingLength, paddingChar)};

response = card.transmit(verifyPin);
}
Expand Down Expand Up @@ -119,15 +118,7 @@ inline pcsc_cpp::byte_vector internalAuthenticate(pcsc_cpp::SmartCard& card,
const pcsc_cpp::byte_vector& hash,
const std::string& cardType)
{
static const pcsc_cpp::CommandApdu INTERNAL_AUTHENTICATE {0x00, 0x88, 0x00, 0x00};

pcsc_cpp::CommandApdu internalAuth {INTERNAL_AUTHENTICATE, hash};
// LE is needed in case of protocol T1.
// TODO: Implement this in libpcsc-cpp.
if (card.protocol() == pcsc_cpp::SmartCard::Protocol::T1) {
internalAuth.le = 0;
}

pcsc_cpp::CommandApdu internalAuth {0x00, 0x88, 0x00, 0x00, hash, 0};
const auto response = card.transmit(internalAuth);

if (response.sw1 == pcsc_cpp::ResponseApdu::WRONG_LENGTH) {
Expand All @@ -148,15 +139,7 @@ inline pcsc_cpp::byte_vector computeSignature(pcsc_cpp::SmartCard& card,
const pcsc_cpp::byte_vector& hash,
const std::string& cardType)
{
static const pcsc_cpp::CommandApdu COMPUTE_SIGNATURE {0x00, 0x2A, 0x9E, 0x9A};

pcsc_cpp::CommandApdu computeSignature {COMPUTE_SIGNATURE, hash};
// LE is needed in case of protocol T1.
// TODO: Implement this in libpcsc-cpp.
if (card.protocol() == pcsc_cpp::SmartCard::Protocol::T1) {
computeSignature.le = 0;
}

pcsc_cpp::CommandApdu computeSignature {0x00, 0x2A, 0x9E, 0x9A, hash, 0};
const auto response = card.transmit(computeSignature);

if (response.sw1 == pcsc_cpp::ResponseApdu::WRONG_LENGTH) {
Expand Down

0 comments on commit 1165e3a

Please sign in to comment.