Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ add_library(${PROJECT_NAME}
target_include_directories(${PROJECT_NAME}
PUBLIC
include
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
)

find_package(OpenSSL 3.0.0 REQUIRED)
Expand Down Expand Up @@ -73,7 +74,6 @@ set(MOCK_TEST_EXE lib${PROJECT_NAME}-test-mock)
add_executable(${MOCK_TEST_EXE}
tests/common/selectcard.hpp
tests/common/verify.hpp
tests/mock/atrs.hpp
tests/mock/select-certificate-script.hpp
tests/mock/select-certificate-script-EST-IDEMIA.hpp
tests/mock/select-certificate-script-FIN-V3.hpp
Expand Down
14 changes: 4 additions & 10 deletions include/electronic-id/electronic-id.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@

#include "enums.hpp"

#include <functional>
#include <optional>
#include <set>

namespace electronic_id
Expand Down Expand Up @@ -100,25 +98,21 @@ class ElectronicID
* By default, this function does nothing. It serves as an extension point for
* Pkcs11ElectronicID which needs to release the PKCS#11 module before the application exits to
* prevent potential crashes. */
virtual void release() const {}
virtual void release() const { }

virtual std::string name() const = 0;
virtual Type type() const = 0;

virtual pcsc_cpp::SmartCard const& smartcard() const { return card; }

protected:
ElectronicID(pcsc_cpp::SmartCard&& _card) noexcept : card(std::move(_card)) {}
ElectronicID(pcsc_cpp::SmartCard&& _card) noexcept : card(std::move(_card)) { }

pcsc_cpp::SmartCard card;
};

using ElectronicIDConstructor = std::function<ElectronicID::ptr(const pcsc_cpp::Reader&)>;

std::optional<ElectronicIDConstructor> findMaskedATR(const pcsc_cpp::byte_vector& atr);

bool isCardSupported(const pcsc_cpp::byte_vector& atr);

/** Returns an electronic ID instance for a supported card in the reader, or nullptr if the card is
* not supported. */
ElectronicID::ptr getElectronicID(const pcsc_cpp::Reader& reader);

/** Automatic card selection that either returns a vector of electronic ID pointers with available
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <vector>
#include <stdexcept>
#include <cstdint>
#include <span>

#ifdef _WIN32
using MOCK_LONG = int32_t;
Expand All @@ -44,7 +45,9 @@ class PcscMockError : public std::runtime_error
class PcscMock
{
public:
using byte_vector = std::vector<unsigned char>;
using byte_type = unsigned char;
using byte_vector = std::vector<byte_type>;
using byte_span = std::span<const byte_type>;
// An APDU script is a list of request-response APDU pairs.
using ApduScript = std::vector<std::pair<byte_vector, byte_vector>>;
// Define local string type so that we can use wstring in Windows if needed.
Expand Down Expand Up @@ -97,10 +100,10 @@ class PcscMock
self._stepCount = 0;
}

static const byte_vector& atr() { return instance()._atr; }
static void setAtr(const byte_vector& atr) { instance()._atr = atr; }
static byte_span atr() { return instance()._atr; }
static void setAtr(byte_span atr) { instance()._atr = std::move(atr); }

static const byte_vector DEFAULT_CARD_ATR;
static constexpr byte_type DEFAULT_CARD_ATR[] {0x1, 0x2, 0x3, 0x4};
static const string_t DEFAULT_READER_NAME;

static const byte_vector DEFAULT_COMMAND_APDU;
Expand All @@ -125,7 +128,7 @@ class PcscMock

std::set<std::string> _recordedCalls;
std::map<std::string, MOCK_LONG> _scardCallReturnValues;
byte_vector _atr = DEFAULT_CARD_ATR;
byte_span _atr = DEFAULT_CARD_ATR;
ApduScript _script = DEFAULT_SCRIPT;
size_t _stepCount = 0;
};
1 change: 0 additions & 1 deletion lib/libpcsc-cpp/tests/lib/libpcsc-mock/src/pcsc-mock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ PcscMock::byte_vector PcscMock::responseForApduCommand(const PcscMock::byte_vect
return response;
}

const PcscMock::byte_vector PcscMock::DEFAULT_CARD_ATR {0x1, 0x2, 0x3, 0x4};
#ifdef _WIN32
const PcscMock::string_t PcscMock::DEFAULT_READER_NAME {L"PcscMock-reader"s};
#else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include <winscard.h>
#endif

#include <algorithm>
#include <span>
#include <vector>
#include <string>

Expand Down Expand Up @@ -66,9 +68,8 @@ TEST(scard_mock_test, testScardCalls)
readerName = readerStates[0].szReader;
EXPECT_EQ(readerName, PcscMock::DEFAULT_READER_NAME);

auto atrBuf = readerStates[0].rgbAtr;
vector<unsigned char> atr(atrBuf, atrBuf + readerStates[0].cbAtr);
EXPECT_EQ(atr, PcscMock::DEFAULT_CARD_ATR);
EXPECT_TRUE(std::ranges::equal(std::span(readerStates[0].rgbAtr, readerStates[0].cbAtr),
PcscMock::DEFAULT_CARD_ATR));

auto protocol = SCARD_PROTOCOL_T0;
DWORD protocolOut = SCARD_PROTOCOL_UNDEFINED;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#include <gtest/gtest.h>

#include <algorithm>

using namespace pcsc_cpp;

namespace
Expand All @@ -46,7 +48,7 @@ TEST(pcsc_cpp_test, connectToCardSuccess)
{
auto card = connectToCard();

EXPECT_EQ(card.atr(), PcscMock::DEFAULT_CARD_ATR);
EXPECT_TRUE(std::ranges::equal(card.atr(), PcscMock::DEFAULT_CARD_ATR));
EXPECT_EQ(card.protocol(), SmartCard::Protocol::T1);
}

Expand Down
4 changes: 2 additions & 2 deletions src/availableSupportedCards.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ std::vector<ElectronicID::ptr> availableSupportedCards()
continue;
}
seenCard = true;
if (isCardSupported(reader.cardAtr)) {
cards.push_back(getElectronicID(reader));
if (auto eid = getElectronicID(reader)) {
cards.push_back(std::move(eid));
}
}

Expand Down
109 changes: 37 additions & 72 deletions src/electronic-id.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@

#include "magic_enum/magic_enum.hpp"

#include <algorithm>
#include <map>
#include <numeric>
#include <span>

using namespace pcsc_cpp;
using namespace electronic_id;
Expand All @@ -39,56 +41,42 @@ using namespace std::string_literals;
namespace
{

using ElectronicIDConstructor = std::function<ElectronicID::ptr(const Reader&)>;

template <typename T>
constexpr auto constructor(const Reader& reader)
ElectronicID::ptr constructor(const Reader& reader)
{
return std::make_unique<T>(reader.connectToCard());
return std::make_shared<T>(reader.connectToCard());
}

template <ElectronicID::Type value>
constexpr auto constructor(const Reader& /*reader*/)
ElectronicID::ptr constructor(const Reader& /*reader*/)
{
return std::make_unique<Pkcs11ElectronicID>(value);
return std::make_shared<Pkcs11ElectronicID>(value);
}

struct ATREntry
{
std::span<const byte_type> atr;
ElectronicID::ptr (*constructor)(const pcsc_cpp::Reader&);

bool operator==(const pcsc_cpp::byte_vector& other) const
{
return std::ranges::equal(atr, other);
}
};

// Supported cards.
const std::map<byte_vector, ElectronicIDConstructor, VectorComparator> SUPPORTED_ATRS {
// EstEID Idemia Cosmo 8.1/8.2
{{0x3b, 0xdb, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45, 0x1f, 0x83, 0x00,
0x12, 0x23, 0x3f, 0x53, 0x65, 0x49, 0x44, 0x0f, 0x90, 0x00, 0xf1},
constructor<EstEIDIDEMIAV1>},
// EstEID Idemia Cosmo X
{{0x3b, 0xdc, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45, 0x1f, 0x83, 0x00, 0x12,
0x23, 0x3f, 0x54, 0x65, 0x49, 0x44, 0x32, 0x0f, 0x90, 0x00, 0xc3},
constructor<EstEIDIDEMIAV1>},
// EstEID Thales v1.0
{{0x3b, 0xff, 0x96, 0x00, 0x00, 0x80, 0x31, 0xfe, 0x43, 0x80, 0x31, 0xb8, 0x53,
0x65, 0x49, 0x44, 0x64, 0xb0, 0x85, 0x05, 0x10, 0x12, 0x23, 0x3f, 0x1d},
constructor<EstEIDThales>},
// FinEID v3.1
{{0x3B, 0x7F, 0x96, 0x00, 0x00, 0x80, 0x31, 0xB8, 0x65, 0xB0,
0x85, 0x04, 0x02, 0x1B, 0x12, 0x00, 0xF6, 0x82, 0x90, 0x00},
constructor<FinEIDv3>},
// LatEID Idemia Cosmo 8.1/8.2
{{0x3b, 0xdb, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45, 0x1f, 0x83, 0x00,
0x12, 0x42, 0x8f, 0x53, 0x65, 0x49, 0x44, 0x0f, 0x90, 0x00, 0x20},
constructor<LatEIDIDEMIAV2>},
// LatEID Idemia Cosmo X
{{0x3b, 0xdc, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45, 0x1f, 0x83, 0x00, 0x12,
0x42, 0x8f, 0x54, 0x65, 0x49, 0x44, 0x32, 0x0f, 0x90, 0x00, 0x12},
constructor<LatEIDIDEMIAV2>},
// HrvEID
{{0x3b, 0xff, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x45, 0x00, 0x31, 0xb9, 0x64,
0x04, 0x44, 0xec, 0xc1, 0x73, 0x94, 0x01, 0x80, 0x82, 0x90, 0x00, 0x12},
constructor<ElectronicID::Type::HrvEID>},
// BelEID - https://github.com/Fedict/eid-mw/wiki/Applet-1.8
{{0x3b, 0x7f, 0x96, 0x00, 0x00, 0x80, 0x31, 0x80, 0x65, 0xb0,
0x85, 0x04, 0x01, 0x20, 0x12, 0x0f, 0xff, 0x82, 0x90, 0x00},
constructor<ElectronicID::Type::BelEID>},
// CzeEID
{{0x3b, 0x7e, 0x94, 0x00, 0x00, 0x80, 0x25, 0xd2, 0x03, 0x10, 0x01, 0x00, 0x56, 0x00, 0x00,
0x00, 0x02, 0x02, 0x00},
constructor<ElectronicID::Type::CzeEID>},
constexpr ATREntry SUPPORTED_ATRS[] {
{EstEIDIDEMIAV1::ATR_COSMO_8, constructor<EstEIDIDEMIAV1>},
{EstEIDIDEMIAV1::ATR_COSMO_X, constructor<EstEIDIDEMIAV1>},
{EstEIDThales::ATR, constructor<EstEIDThales>},
{FinEIDv3::ATR, constructor<FinEIDv3>},
{LatEIDIDEMIAV2::ATR_COSMO_8, constructor<LatEIDIDEMIAV2>},
{LatEIDIDEMIAV2::ATR_COSMO_X, constructor<LatEIDIDEMIAV2>},
{Pkcs11ElectronicID::HrvEID_ATR, constructor<Pkcs11ElectronicID::Type::HrvEID>},
{Pkcs11ElectronicID::BelEID_ATR, constructor<Pkcs11ElectronicID::Type::BelEID>},
{Pkcs11ElectronicID::CzeEID_ATR, constructor<Pkcs11ElectronicID::Type::CzeEID>},
};

// Holds ATR pattern, mask, and constructor for variable ATR cards.
Expand Down Expand Up @@ -120,11 +108,7 @@ struct MaskedATREntry

const std::vector<MaskedATREntry> MASKED_ATRS = {
// FinEID Thales v4.0/v4.1
{{0x3B, 0x7F, 0x96, 0x00, 0x00, 0x80, 0x31, 0xB8, 0x65, 0xB0,
0x85, 0x05, 0x00, 0x11, 0x12, 0x24, 0x60, 0x82, 0x90, 0x00},
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
constructor<FinEIDv4>},
{FinEIDv4::ATR, FinEIDv4::MASK, constructor<FinEIDv4>},
// BelEID v1.7
{{0x3b, 0x98, 0x13, 0x40, 0x0a, 0xa5, 0x03, 0x01, 0x01, 0x01, 0xad, 0x13, 0x11},
{0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00},
Expand Down Expand Up @@ -221,39 +205,20 @@ const auto SUPPORTED_ALGORITHMS = std::map<std::string_view, HashAlgorithm> {
namespace electronic_id
{

std::optional<ElectronicIDConstructor> findMaskedATR(const byte_vector& atr)
{
if (auto i = std::find(MASKED_ATRS.cbegin(), MASKED_ATRS.cend(), atr);
i != MASKED_ATRS.cend()) {
return i->constructor;
}
return std::nullopt;
}

bool isCardSupported(const pcsc_cpp::byte_vector& atr)
{
if (SUPPORTED_ATRS.contains(atr)) {
return true;
}

// If exact ATR match is not found, fall back to masked ATR lookup.
return findMaskedATR(atr).has_value();
}

ElectronicID::ptr getElectronicID(const pcsc_cpp::Reader& reader)
{
if (auto it = SUPPORTED_ATRS.find(reader.cardAtr); it != SUPPORTED_ATRS.end()) {
return it->second(reader);
if (auto it = std::find(std::begin(SUPPORTED_ATRS), std::end(SUPPORTED_ATRS), reader.cardAtr);
it != std::end(SUPPORTED_ATRS)) {
return it->constructor(reader);
}

// If exact ATR match is not found, fall back to masked ATR lookup.
if (auto eIDConstructor = findMaskedATR(reader.cardAtr)) {
return (*eIDConstructor)(reader);
if (auto i = std::find(MASKED_ATRS.cbegin(), MASKED_ATRS.cend(), reader.cardAtr);
i != MASKED_ATRS.cend()) {
return i->constructor(reader);
}

// It should be verified that the card is supported with isCardSupported() before
// calling getElectronicID(), so it is a programming error to reach this point.
THROW(ProgrammingError, "Card with ATR '" + reader.cardAtr + "' is not supported");
return nullptr;
}

bool ElectronicID::isSupportedSigningHashAlgorithm(const HashAlgorithm hashAlgo) const
Expand Down
7 changes: 7 additions & 0 deletions src/electronic-ids/pcsc/EstEIDIDEMIA.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ class EstEIDIDEMIAV1 : public EIDIDEMIA
public:
using EIDIDEMIA::EIDIDEMIA;

static constexpr byte_type ATR_COSMO_8[] {0x3b, 0xdb, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45,
0x1f, 0x83, 0x00, 0x12, 0x23, 0x3f, 0x53, 0x65,
0x49, 0x44, 0x0f, 0x90, 0x00, 0xf1};
static constexpr byte_type ATR_COSMO_X[] {0x3b, 0xdc, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45,
0x1f, 0x83, 0x00, 0x12, 0x23, 0x3f, 0x54, 0x65,
0x49, 0x44, 0x32, 0x0f, 0x90, 0x00, 0xc3};

private:
constexpr PinMinMaxLength signingPinMinMaxLength() const override { return {5, 12}; }
std::string name() const override { return "EstEID IDEMIA v1"; }
Expand Down
4 changes: 4 additions & 0 deletions src/electronic-ids/pcsc/EstEIDThales.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class EstEIDThales : public EIDThales
public:
using EIDThales::EIDThales;

static constexpr byte_type ATR[] {0x3b, 0xff, 0x96, 0x00, 0x00, 0x80, 0x31, 0xfe, 0x43,
0x80, 0x31, 0xb8, 0x53, 0x65, 0x49, 0x44, 0x64, 0xb0,
0x85, 0x05, 0x10, 0x12, 0x23, 0x3f, 0x1d};

protected:
std::string name() const override { return "EstEIDThales"; }
Type type() const override { return EstEID; }
Expand Down
8 changes: 8 additions & 0 deletions src/electronic-ids/pcsc/FinEID.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ class FinEIDv4 : public EIDThales
public:
using EIDThales::EIDThales;

static constexpr byte_type ATR[] {0x3B, 0x7F, 0x96, 0x00, 0x00, 0x80, 0x31, 0xB8, 0x65, 0xB0,
0x85, 0x05, 0x00, 0x11, 0x12, 0x24, 0x60, 0x82, 0x90, 0x00};
static constexpr byte_type MASK[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

protected:
std::string name() const override { return "FinEID v4"; }
Type type() const override { return FinEID; }
Expand All @@ -54,6 +59,9 @@ class FinEIDv3 : public FinEIDv4
public:
using FinEIDv4::FinEIDv4;

static constexpr byte_type ATR[] {0x3B, 0x7F, 0x96, 0x00, 0x00, 0x80, 0x31, 0xB8, 0x65, 0xB0,
0x85, 0x04, 0x02, 0x1B, 0x12, 0x00, 0xF6, 0x82, 0x90, 0x00};

protected:
std::string name() const override { return "FinEID v3"; }
constexpr JsonWebSignatureAlgorithm authSignatureAlgorithm() const override
Expand Down
7 changes: 7 additions & 0 deletions src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class LatEIDIDEMIAV2 : public EIDIDEMIA
~LatEIDIDEMIAV2() override;
PCSC_CPP_DISABLE_COPY_MOVE(LatEIDIDEMIAV2);

static constexpr byte_type ATR_COSMO_8[] {0x3b, 0xdb, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45,
0x1f, 0x83, 0x00, 0x12, 0x42, 0x8f, 0x53, 0x65,
0x49, 0x44, 0x0f, 0x90, 0x00, 0x20};
static constexpr byte_type ATR_COSMO_X[] {0x3b, 0xdc, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45,
0x1f, 0x83, 0x00, 0x12, 0x42, 0x8f, 0x54, 0x65,
0x49, 0x44, 0x32, 0x0f, 0x90, 0x00, 0x12};

private:
byte_vector getCertificateImpl(const SmartCard::Session& session,
const CertificateType type) const override;
Expand Down
11 changes: 11 additions & 0 deletions src/electronic-ids/pkcs11/Pkcs11ElectronicID.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ class Pkcs11ElectronicID : public ElectronicID
public:
explicit Pkcs11ElectronicID(ElectronicID::Type type);

static constexpr byte_type HrvEID_ATR[] {0x3b, 0xff, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe, 0x45,
0x00, 0x31, 0xb9, 0x64, 0x04, 0x44, 0xec, 0xc1, 0x73,
0x94, 0x01, 0x80, 0x82, 0x90, 0x00, 0x12};
// https://github.com/Fedict/eid-mw/wiki/Applet-1.8
static constexpr byte_type BelEID_ATR[] {0x3b, 0x7f, 0x96, 0x00, 0x00, 0x80, 0x31,
0x80, 0x65, 0xb0, 0x85, 0x04, 0x01, 0x20,
0x12, 0x0f, 0xff, 0x82, 0x90, 0x00};
static constexpr byte_type CzeEID_ATR[] {0x3b, 0x7e, 0x94, 0x00, 0x00, 0x80, 0x25,
0xd2, 0x03, 0x10, 0x01, 0x00, 0x56, 0x00,
0x00, 0x00, 0x02, 0x02, 0x00};

private:
bool allowsUsingLettersAndSpecialCharactersInPin() const override
{
Expand Down
Loading
Loading