diff --git a/.github/workflows/cmake-linux-fedora.yml b/.github/workflows/cmake-linux-fedora.yml index 068901f..e382f40 100644 --- a/.github/workflows/cmake-linux-fedora.yml +++ b/.github/workflows/cmake-linux-fedora.yml @@ -2,20 +2,14 @@ name: CMake (Fedora Linux) on: [push, pull_request] -env: - BUILD_TYPE: RelWithDebInfo - jobs: build: runs-on: ubuntu-latest - continue-on-error: ${{ matrix.experimental }} + continue-on-error: ${{ matrix.container == 'fedora:rawhide' }} strategy: matrix: - container: ['fedora:latest'] - experimental: [false] - include: - - container: 'fedora:rawhide' - experimental: true + container: ['fedora:latest', 'fedora:rawhide'] + build_type: [Release, RelWithDebInfo] container: image: ${{ matrix.container }} @@ -27,10 +21,10 @@ jobs: - uses: actions/checkout@v4 - name: Configure CMake - run: cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -S . -B build + run: cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -S . -B build - name: Build - run: cmake --build build --config $BUILD_TYPE -j $(nproc) + run: cmake --build build --config ${{ matrix.build_type }} -j $(nproc) - name: Test - run: ctest -V -C $BUILD_TYPE --test-dir build + run: ctest -V -C ${{ matrix.build_type }} --test-dir build diff --git a/include/electronic-id/electronic-id.hpp b/include/electronic-id/electronic-id.hpp index 9a745cc..c5a6e88 100644 --- a/include/electronic-id/electronic-id.hpp +++ b/include/electronic-id/electronic-id.hpp @@ -42,7 +42,7 @@ class ElectronicID using byte_type = pcsc_cpp::byte_type; using Signature = std::pair; - enum Type { + enum Type : uint8_t { EstEID, FinEID, LatEID, @@ -125,7 +125,6 @@ class CardInfo const pcsc_cpp::Reader& reader() const { return _reader; } const ElectronicID& eid() const { return *_eid; } - const ElectronicID::ptr eidPtr() const { return _eid; } private: pcsc_cpp::Reader _reader; @@ -216,7 +215,7 @@ class MsCryptoApiError : public Error class AutoSelectFailed : public Error { public: - enum class Reason { + enum class Reason : uint8_t { SERVICE_NOT_RUNNING, NO_READERS, SINGLE_READER_NO_CARD, @@ -241,7 +240,7 @@ class VerifyPinFailed : public Error template using observer_ptr = T*; - enum class Status { + enum class Status : uint8_t { RETRY_ALLOWED, INVALID_PIN_LENGTH, PIN_ENTRY_TIMEOUT, diff --git a/lib/libpcsc-cpp/CMakeLists.txt b/lib/libpcsc-cpp/CMakeLists.txt index 74ac159..2ca5dbb 100644 --- a/lib/libpcsc-cpp/CMakeLists.txt +++ b/lib/libpcsc-cpp/CMakeLists.txt @@ -9,10 +9,8 @@ add_library(${PROJECT_NAME} include/${PROJECT_NAME}/${PROJECT_NAME}.hpp include/${PROJECT_NAME}/${PROJECT_NAME}-utils.hpp include/${PROJECT_NAME}/comp_winscard.hpp - include/flag-set-cpp/flag_set.hpp include/magic_enum/magic_enum.hpp src/Context.hpp - src/Reader.cpp src/SCardCall.hpp src/SmartCard.cpp src/listReaders.cpp diff --git a/lib/libpcsc-cpp/include/flag-set-cpp/flag_set.hpp b/lib/libpcsc-cpp/include/flag-set-cpp/flag_set.hpp deleted file mode 100644 index cfb9293..0000000 --- a/lib/libpcsc-cpp/include/flag-set-cpp/flag_set.hpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (c) 2019 Arnaud Kapp (Xaqq), Barry Revzin, Mart Somermaa - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// flag_set is a type-safe class for using enums as flags in C++14 with an underlying std::bitset. -// See https://github.com/mrts/flag-set-cpp - -#pragma once - -#include -#include -#include -#include - -template -class flag_set -{ -public: - flag_set() = default; - - explicit flag_set(const T& val) { flags.set(static_cast(val)); } - - // Binary operations. - - flag_set& operator&=(const T& val) noexcept - { - bool tmp = flags.test(static_cast(val)); - flags.reset(); - flags.set(static_cast(val), tmp); - return *this; - } - - flag_set& operator&=(const flag_set& o) noexcept - { - flags &= o.flags; - return *this; - } - - flag_set& operator|=(const T& val) noexcept - { - flags.set(static_cast(val)); - return *this; - } - - flag_set& operator|=(const flag_set& o) noexcept - { - flags |= o.flags; - return *this; - } - - // The resulting bitset can contain at most 1 bit. - flag_set operator&(const T& val) const - { - flag_set ret(*this); - ret &= val; - - assert(ret.flags.count() <= 1); - return ret; - } - - flag_set operator&(const flag_set& val) const - { - flag_set ret(*this); - ret.flags &= val.flags; - - return ret; - } - - // The resulting bitset contains at least 1 bit. - flag_set operator|(const T& val) const - { - flag_set ret(*this); - ret |= val; - - assert(ret.flags.count() >= 1); - return ret; - } - - flag_set operator|(const flag_set& val) const - { - flag_set ret(*this); - ret.flags |= val.flags; - - return ret; - } - - flag_set operator~() const - { - flag_set cp(*this); - cp.flags.flip(); - - return cp; - } - - // The bitset evaluates to true if any bit is set. - explicit operator bool() const { return flags.any(); } - - // Methods from std::bitset. - - bool operator==(const flag_set& o) const { return flags == o.flags; } - - std::size_t size() const { return flags.size(); } - - std::size_t count() const { return flags.count(); } - - flag_set& set() - { - flags.set(); - return *this; - } - - flag_set& reset() - { - flags.reset(); - return *this; - } - - flag_set& flip() - { - flags.flip(); - return *this; - } - - flag_set& set(const T& val, bool value = true) - { - flags.set(static_cast(val), value); - return *this; - } - - flag_set& reset(const T& val) - { - flags.reset(static_cast(val)); - return *this; - } - - flag_set& flip(const T& val) - { - flags.flip(static_cast(val)); - return *this; - } - - constexpr bool operator[](const T& val) const { return flags[static_cast(val)]; } - - std::string to_string() const { return flags.to_string(); } - - // Operator for outputting to stream. - friend std::ostream& operator<<(std::ostream& stream, const flag_set& self) - { - return stream << self.flags; - } - -private: - using u_type = std::underlying_type_t; - - // _ is last value sentinel and must be present in enum T. - std::bitset(T::_)> flags; -}; diff --git a/lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp b/lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp index c16a5bd..7ee3b09 100644 --- a/lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp +++ b/lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp @@ -22,11 +22,11 @@ #pragma once -#include "flag-set-cpp/flag_set.hpp" - #include #include #include +#include +#include #include // The rule of five (C++ Core guidelines C.21). @@ -254,39 +254,14 @@ class SmartCard }; /** Reader provides card reader information, status and gives access to the smart card in it. */ -class Reader +struct Reader { -public: - enum class Status { - UNAWARE, - IGNORE, - CHANGED, - UNKNOWN, - UNAVAILABLE, - EMPTY, - PRESENT, - ATRMATCH, - EXCLUSIVE, - INUSE, - MUTE, - UNPOWERED, - _ - }; - - Reader(ContextPtr context, string_t name, byte_vector cardAtr, flag_set status); - SmartCard::ptr connectToCard() const { return std::make_unique(ctx, name, cardAtr); } - bool isCardInserted() const { return status[Status::PRESENT]; } - - std::string statusString() const; - + const ContextPtr ctx; const string_t name; const byte_vector cardAtr; - const flag_set status; - -private: - ContextPtr ctx; + const bool isCardPresent; }; /** diff --git a/lib/libpcsc-cpp/src/Reader.cpp b/lib/libpcsc-cpp/src/Reader.cpp deleted file mode 100644 index 88cb6e6..0000000 --- a/lib/libpcsc-cpp/src/Reader.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2020-2024 Estonian Information System Authority - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "pcsc-cpp/pcsc-cpp.hpp" - -#include "magic_enum/magic_enum.hpp" - -#include - -namespace pcsc_cpp -{ - -Reader::Reader(ContextPtr c, string_t n, byte_vector atr, flag_set s) : - name(std::move(n)), cardAtr(std::move(atr)), status(s), ctx(std::move(c)) -{ -} - -std::string Reader::statusString() const -{ - std::vector result; - - for (auto statusValue = int(Reader::Status::UNAWARE); statusValue < int(Reader::Status::_); - ++statusValue) { - if (status[Reader::Status(statusValue)]) { - result.emplace_back(magic_enum::enum_name(Reader::Status(statusValue))); - } - } - - return std::accumulate(std::begin(result), std::end(result), std::string(), - [](const std::string& resultStr, const std::string& item) { - return resultStr.empty() ? item : resultStr + "," + item; - }); -} - -} // namespace pcsc_cpp diff --git a/lib/libpcsc-cpp/src/listReaders.cpp b/lib/libpcsc-cpp/src/listReaders.cpp index 14c3d97..b9ffb2d 100644 --- a/lib/libpcsc-cpp/src/listReaders.cpp +++ b/lib/libpcsc-cpp/src/listReaders.cpp @@ -65,37 +65,6 @@ std::vector getReaderStates(const SCARDCONTEXT ctx, const str return readerStates; } -inline flag_set flagSetFromReaderState(const DWORD readerState) -{ - if (!readerState) { - return flag_set {Reader::Status::UNAWARE}; - } - - static const std::map READER_STATUS_MAP = { - // SCARD_STATE_UNAWARE is zero and covered with (!readerState) above. - {SCARD_STATE_IGNORE, Reader::Status::IGNORE}, - {SCARD_STATE_CHANGED, Reader::Status::CHANGED}, - {SCARD_STATE_UNKNOWN, Reader::Status::UNKNOWN}, - {SCARD_STATE_UNAVAILABLE, Reader::Status::UNAVAILABLE}, - {SCARD_STATE_EMPTY, Reader::Status::EMPTY}, - {SCARD_STATE_PRESENT, Reader::Status::PRESENT}, - {SCARD_STATE_ATRMATCH, Reader::Status::ATRMATCH}, - {SCARD_STATE_EXCLUSIVE, Reader::Status::EXCLUSIVE}, - {SCARD_STATE_INUSE, Reader::Status::INUSE}, - {SCARD_STATE_MUTE, Reader::Status::MUTE}, - {SCARD_STATE_UNPOWERED, Reader::Status::UNPOWERED}}; - - auto result = flag_set {}; - - for (const auto& [key, value] : READER_STATUS_MAP) { - if (readerState & key) { - result.set(value); - } - } - - return result; -} - string_t populateReaderNames(const SCARDCONTEXT ctx) { // Buffer length is in characters, not bytes. @@ -121,12 +90,14 @@ std::vector listReaders() try { auto readerNames = populateReaderNames(ctx->handle()); - - for (const auto& readerState : getReaderStates(ctx->handle(), readerNames)) { - readers.emplace_back( - ctx, readerState.szReader, - byte_vector {readerState.rgbAtr, std::next(readerState.rgbAtr, readerState.cbAtr)}, - flagSetFromReaderState(readerState.dwEventState)); + auto readerStates = getReaderStates(ctx->handle(), readerNames); + readers.reserve(readerStates.size()); + for (const auto& readerState : readerStates) { + readers.push_back( + {ctx, readerState.szReader, + byte_vector {std::begin(readerState.rgbAtr), + std::next(std::begin(readerState.rgbAtr), readerState.cbAtr)}, + bool(readerState.dwEventState & SCARD_STATE_PRESENT)}); } } catch (const ScardNoReadersError& /* e */) { } diff --git a/lib/libpcsc-cpp/tests/integration/test-pcsc-cpp.cpp b/lib/libpcsc-cpp/tests/integration/test-pcsc-cpp.cpp index e1e9e6d..8d3ca74 100644 --- a/lib/libpcsc-cpp/tests/integration/test-pcsc-cpp.cpp +++ b/lib/libpcsc-cpp/tests/integration/test-pcsc-cpp.cpp @@ -33,12 +33,11 @@ TEST(pcsc_cpp_test, listReaders) auto readers = listReaders(); for (const auto& reader : readers) { #ifdef _WIN32 - std::wcout << L"Reader name: '" << reader.name << L"', status: '" - << std::wstring(reader.statusString().cbegin(), reader.statusString().cend()) - << L"'" << std::endl; + std::wcout << L"Reader name: '" << reader.name << L"', card status: '" + << (reader.isCardPresent ? L"PRESENT" : L"ABSENT") << L"'" << std::endl; #else - std::cout << "Reader name: '" << reader.name << "', status: '" << reader.statusString() - << "'" << std::endl; + std::cout << "Reader name: '" << reader.name << "', card status: '" + << (reader.isCardPresent ? "PRESENT" : "ABSENT") << "'" << std::endl; #endif } } diff --git a/lib/libpcsc-cpp/tests/mock/test-select-card-reader-and-card.cpp b/lib/libpcsc-cpp/tests/mock/test-select-card-reader-and-card.cpp index f61f899..c1238bc 100644 --- a/lib/libpcsc-cpp/tests/mock/test-select-card-reader-and-card.cpp +++ b/lib/libpcsc-cpp/tests/mock/test-select-card-reader-and-card.cpp @@ -38,7 +38,7 @@ TEST(pcsc_cpp_test, listReadersSuccess) #else EXPECT_EQ(readers[0].name, "PcscMock-reader"); #endif - EXPECT_EQ(readers[0].statusString(), "PRESENT"); + EXPECT_EQ(readers[0].isCardPresent, true); } TEST(pcsc_cpp_test, listReadersNoReaders) diff --git a/src/availableSupportedCards.cpp b/src/availableSupportedCards.cpp index 844ce99..f8118f4 100644 --- a/src/availableSupportedCards.cpp +++ b/src/availableSupportedCards.cpp @@ -53,7 +53,7 @@ std::vector availableSupportedCards() // The list may be empty, but we cannot throw yet due to the listMsCryptoApiElectronicIDs() // call in Windows. for (const auto& reader : readers) { - if (!reader.isCardInserted()) { + if (!reader.isCardPresent) { continue; } seenCard = true; diff --git a/src/electronic-id.cpp b/src/electronic-id.cpp index b1cbc11..8066dc2 100644 --- a/src/electronic-id.cpp +++ b/src/electronic-id.cpp @@ -26,8 +26,6 @@ #include "electronic-ids/pkcs11/Pkcs11ElectronicID.hpp" -#include "pcsc-cpp/pcsc-cpp-utils.hpp" - #include "magic_enum/magic_enum.hpp" #include @@ -53,7 +51,7 @@ constexpr auto constructor(const Reader& /*reader*/) } // Supported cards. -const std::map SUPPORTED_ATRS { +const std::map SUPPORTED_ATRS { // EstEID Idemia v1.0 {{0x3b, 0xdb, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45, 0x1f, 0x83, 0x00, 0x12, 0x23, 0x3f, 0x53, 0x65, 0x49, 0x44, 0x0f, 0x90, 0x00, 0xf1}, diff --git a/src/electronic-ids/TLV.hpp b/src/electronic-ids/TLV.hpp index 26b53d1..418de82 100644 --- a/src/electronic-ids/TLV.hpp +++ b/src/electronic-ids/TLV.hpp @@ -99,7 +99,7 @@ struct TLV PCSC_CPP_CONSTEXPR_VECTOR TLV operator[](uint32_t find) const { TLV tlv = child(); - for (; tlv && tlv.tag != find; ++tlv); + for (; tlv && tlv.tag != find; ++tlv) {} return tlv; } PCSC_CPP_CONSTEXPR_VECTOR TLV& operator++() { return *this = {begin + length, end}; } diff --git a/src/electronic-ids/common.hpp b/src/electronic-ids/common.hpp index 99ade7d..d122c8b 100644 --- a/src/electronic-ids/common.hpp +++ b/src/electronic-ids/common.hpp @@ -72,4 +72,14 @@ inline pcsc_cpp::byte_vector addRSAOID(const HashAlgorithm hashAlgo, return oidAndHash; } +// Workaround gcc 14.2 bug +struct VectorComparator +{ + PCSC_CPP_CONSTEXPR_VECTOR bool operator()(const std::vector& a, + const std::vector& b) const + { + return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); + } +}; + } // namespace electronic_id diff --git a/src/electronic-ids/ms-cryptoapi/listMsCryptoApiElectronicIDs.cpp b/src/electronic-ids/ms-cryptoapi/listMsCryptoApiElectronicIDs.cpp index 0aa94ba..25d3202 100644 --- a/src/electronic-ids/ms-cryptoapi/listMsCryptoApiElectronicIDs.cpp +++ b/src/electronic-ids/ms-cryptoapi/listMsCryptoApiElectronicIDs.cpp @@ -51,7 +51,7 @@ std::vector listMsCryptoApiElectronicIDs() nullptr, L"Dummy reader for MS CryptoAPI tokens"s, {}, - flag_set {pcsc_cpp::Reader::Status::PRESENT}, + true, }; PCCERT_CONTEXT cert = nullptr; diff --git a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp index 72a704c..df747ee 100644 --- a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp +++ b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp @@ -34,8 +34,8 @@ using namespace electronic_id; struct LatEIDIDEMIAV2::Private { - std::map authCache; - std::map signCache; + std::map authCache; + std::map signCache; std::optional authKeyInfo; std::optional signKeyInfo; }; @@ -108,8 +108,8 @@ EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::signKeyRef() const return data->signKeyInfo.value(); } -const byte_vector& LatEIDIDEMIAV2::readEF_File(byte_vector file, - std::map& cache) const +template +const byte_vector& LatEIDIDEMIAV2::readEF_File(byte_vector file, C& cache) const { if (auto it = cache.find(file); it != cache.end()) { return it->second; @@ -126,8 +126,8 @@ const byte_vector& LatEIDIDEMIAV2::readEF_File(byte_vector file, readBinary(*card, size_t(*size.begin << 8) + *(size.begin + 1), 0xFF); } -const byte_vector& LatEIDIDEMIAV2::readDCODInfo(byte_type type, - std::map& cache) const +template +const byte_vector& LatEIDIDEMIAV2::readDCODInfo(byte_type type, C& cache) const { const auto info = readEF_File(EF_OD, cache); if (auto file = TLV::path(info, type, 0x30, 0x04); file && file.length == 2) { @@ -136,8 +136,8 @@ const byte_vector& LatEIDIDEMIAV2::readDCODInfo(byte_type type, THROW(SmartCardError, "EF.DCOD reference not found"); } -EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::readPrKDInfo(byte_type keyID, - std::map& cache) const +template +EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::readPrKDInfo(byte_type keyID, C& cache) const { auto info = readDCODInfo(PRIV_FILE_REF, cache); if (info.empty()) { diff --git a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp index 5c74ed2..3ae397d 100644 --- a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp +++ b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp @@ -51,11 +51,12 @@ class LatEIDIDEMIAV2 : public EIDIDEMIA KeyInfo authKeyRef() const override; KeyInfo signKeyRef() const override; - const byte_vector& readEF_File(byte_vector file, - std::map& cache) const; - const byte_vector& readDCODInfo(byte_type type, - std::map& cache) const; - KeyInfo readPrKDInfo(byte_type keyID, std::map& cache) const; + template + const byte_vector& readEF_File(byte_vector file, C& cache) const; + template + const byte_vector& readDCODInfo(byte_type type, C& cache) const; + template + KeyInfo readPrKDInfo(byte_type keyID, C& cache) const; struct Private; std::unique_ptr data;