diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index e2cd092..efb8ef8 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -24,13 +24,13 @@ jobs: run: sudo apt update -qq && sudo apt install --no-install-recommends -y cmake libgtest-dev libpcsclite-dev - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: cpp queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 13cc00d..a707500 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16.0) +cmake_minimum_required(VERSION 3.22) if(POLICY CMP0092) cmake_policy(SET CMP0092 NEW) endif() @@ -16,10 +16,10 @@ add_library(${PROJECT_NAME} src/electronic-ids/common.hpp src/electronic-ids/common.cpp src/electronic-ids/scope.hpp + src/electronic-ids/TLV.hpp src/electronic-ids/x509.hpp src/electronic-ids/pcsc/EIDIDEMIA.cpp src/electronic-ids/pcsc/EIDIDEMIA.hpp - src/electronic-ids/pcsc/EstEIDIDEMIA.cpp src/electronic-ids/pcsc/EstEIDIDEMIA.hpp src/electronic-ids/pcsc/FinEID.cpp src/electronic-ids/pcsc/FinEID.hpp diff --git a/lib/libpcsc-cpp/CMakeLists.txt b/lib/libpcsc-cpp/CMakeLists.txt index 849d8b2..74ac159 100644 --- a/lib/libpcsc-cpp/CMakeLists.txt +++ b/lib/libpcsc-cpp/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.22) project(pcsc-cpp) diff --git a/lib/libpcsc-cpp/tests/lib/libpcsc-mock/CMakeLists.txt b/lib/libpcsc-cpp/tests/lib/libpcsc-mock/CMakeLists.txt index be7541e..35fe8e2 100644 --- a/lib/libpcsc-cpp/tests/lib/libpcsc-mock/CMakeLists.txt +++ b/lib/libpcsc-cpp/tests/lib/libpcsc-mock/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.0) +cmake_minimum_required(VERSION 3.22) if(POLICY CMP0092) cmake_policy(SET CMP0092 NEW) endif() diff --git a/src/electronic-ids/TLV.hpp b/src/electronic-ids/TLV.hpp new file mode 100644 index 0000000..9f00032 --- /dev/null +++ b/src/electronic-ids/TLV.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2025 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. + */ + +#pragma once + +#include "pcsc-cpp/pcsc-cpp.hpp" + +namespace electronic_id +{ + +struct TLV +{ + using byte_vector = pcsc_cpp::byte_vector; + uint16_t tag {}; + uint32_t length {}; + byte_vector::const_iterator begin; + byte_vector::const_iterator end; + + PCSC_CPP_CONSTEXPR_VECTOR explicit TLV(const byte_vector& data) : + TLV(data.cbegin(), data.cend()) + { + } + + PCSC_CPP_CONSTEXPR_VECTOR TLV(byte_vector::const_iterator _begin, + byte_vector::const_iterator _end) : begin(_begin), end(_end) + { + if (!*this) { + return; + } + + tag = *begin++; + if ((tag & 0x1F) == 0x1F) { // Multi-byte tag + if (!*this) { + throw std::invalid_argument("Invalid TLV: Unexpected end of tag"); + } + tag = (tag << 8) | (*begin++); + } + + if (!*this) { + throw std::invalid_argument("Invalid TLV: Missing length field"); + } + + length = *begin++; + if (length & 0x80) { // Extended length encoding + auto num_bytes = uint8_t(length & 0x7F); + if (num_bytes == 0 || num_bytes > 4 || std::distance(begin, end) < num_bytes) { + throw std::invalid_argument("Invalid TLV: Incorrect extended length encoding"); + } + + length = 0; + for (uint8_t i = 0; i < num_bytes; ++i) { + length = (length << 8) | (*begin++); + } + } + + if (std::distance(begin, end) < length) { + throw std::invalid_argument("Invalid TLV: Insufficient value data"); + } + } + + PCSC_CPP_CONSTEXPR_VECTOR TLV child() const { return {begin, begin + length}; } + + PCSC_CPP_CONSTEXPR_VECTOR TLV& operator++() { return *this = {begin + length, end}; } + + template + static PCSC_CPP_CONSTEXPR_VECTOR TLV path(TLV tlv, uint16_t tag, Tags... tags) + { + for (; tlv; ++tlv) { + if (tlv.tag == tag) { + if constexpr (sizeof...(tags) > 0) + return path(tlv.child(), uint16_t(tags)...); + return tlv; + } + } + return TLV({}); + } + template + static PCSC_CPP_CONSTEXPR_VECTOR TLV path(const byte_vector& data, uint16_t tag, Tags... tags) + { + return path(TLV(data), tag, tags...); + } + + constexpr operator bool() const noexcept { return begin != end; } +}; + +} // namespace electronic_id diff --git a/src/electronic-ids/pcsc/EIDIDEMIA.cpp b/src/electronic-ids/pcsc/EIDIDEMIA.cpp index 74dd589..aacd37a 100644 --- a/src/electronic-ids/pcsc/EIDIDEMIA.cpp +++ b/src/electronic-ids/pcsc/EIDIDEMIA.cpp @@ -33,6 +33,8 @@ namespace constexpr byte_type PIN_PADDING_CHAR = 0xFF; constexpr byte_type AUTH_PIN_REFERENCE = 0x01; constexpr byte_type SIGN_PIN_REFERENCE = 0x85; +constexpr byte_type DEFAULT_AUTH_KEY_ID = 0x81; +constexpr byte_type DEFAULT_SIGN_KEY_ID = 0x9F; const auto MAIN_AID = CommandApdu::select(0x04, {0xA0, 0x00, 0x00, 0x00, 0x77, 0x01, 0x08, 0x00, 0x07, @@ -47,6 +49,11 @@ const auto SIGN_CERT = CommandApdu::select(0x09, {0xAD, 0xF2, 0x34, 0x1F}); } // namespace +void EIDIDEMIA::selectMain() const +{ + transmitApduWithExpectedResponse(*card, MAIN_AID); +} + void EIDIDEMIA::selectADF1() const { transmitApduWithExpectedResponse(*card, ADF1_AID); @@ -59,14 +66,20 @@ void EIDIDEMIA::selectADF2() const byte_vector EIDIDEMIA::getCertificateImpl(const CertificateType type) const { - transmitApduWithExpectedResponse(*card, MAIN_AID); + selectMain(); return electronic_id::getCertificate(*card, type.isAuthentication() ? AUTH_CERT : SIGN_CERT); } +EIDIDEMIA::KeyInfo EIDIDEMIA::authKeyRef() const +{ + return {DEFAULT_AUTH_KEY_ID, true}; +} + byte_vector EIDIDEMIA::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const { selectADF1(); - selectAuthSecurityEnv(); + auto [keyId, isECC] = authKeyRef(); + selectSecurityEnv(*card, 0xA4, isECC ? 0x04 : 0x02, keyId, name()); verifyPin(*card, AUTH_PIN_REFERENCE, std::move(pin), authPinMinMaxLength().first, authPinMinMaxLength().second, PIN_PADDING_CHAR); @@ -80,18 +93,23 @@ byte_vector EIDIDEMIA::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& ElectronicID::PinRetriesRemainingAndMax EIDIDEMIA::authPinRetriesLeftImpl() const { - transmitApduWithExpectedResponse(*card, MAIN_AID); + selectMain(); return pinRetriesLeft(AUTH_PIN_REFERENCE); } +EIDIDEMIA::KeyInfo EIDIDEMIA::signKeyRef() const +{ + return {DEFAULT_SIGN_KEY_ID, true}; +} + ElectronicID::Signature EIDIDEMIA::signWithSigningKeyImpl(byte_vector&& pin, const byte_vector& hash, const HashAlgorithm hashAlgo) const { selectADF2(); - pcsc_cpp::byte_type algo = selectSignSecurityEnv(); + auto [keyRef, isECC] = signKeyRef(); + selectSecurityEnv(*card, 0xB6, isECC ? 0x54 : 0x42, keyRef, name()); auto tmp = hash; - bool isECC = algo == 0x54; if (isECC) { constexpr size_t ECDSA384_INPUT_LENGTH = 384 / 8; if (tmp.size() < ECDSA384_INPUT_LENGTH) { diff --git a/src/electronic-ids/pcsc/EIDIDEMIA.hpp b/src/electronic-ids/pcsc/EIDIDEMIA.hpp index f34e8b7..b1d5381 100644 --- a/src/electronic-ids/pcsc/EIDIDEMIA.hpp +++ b/src/electronic-ids/pcsc/EIDIDEMIA.hpp @@ -30,22 +30,37 @@ namespace electronic_id class EIDIDEMIA : public PcscElectronicID { public: + struct KeyInfo + { + byte_type id; + bool isECC; + }; + explicit EIDIDEMIA(pcsc_cpp::SmartCard::ptr _card) : PcscElectronicID(std::move(_card)) {} protected: byte_vector getCertificateImpl(const CertificateType type) const override; PinRetriesRemainingAndMax authPinRetriesLeftImpl() const override; - virtual void selectAuthSecurityEnv() const = 0; + JsonWebSignatureAlgorithm authSignatureAlgorithm() const override + { + return JsonWebSignatureAlgorithm::ES384; + } + virtual KeyInfo authKeyRef() const; byte_vector signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const override; PinRetriesRemainingAndMax signingPinRetriesLeftImpl() const override; - virtual pcsc_cpp::byte_type selectSignSecurityEnv() const = 0; + const std::set& supportedSigningAlgorithms() const override + { + return ELLIPTIC_CURVE_SIGNATURE_ALGOS(); + } + virtual KeyInfo signKeyRef() const; Signature signWithSigningKeyImpl(byte_vector&& pin, const byte_vector& hash, const HashAlgorithm hashAlgo) const override; PinRetriesRemainingAndMax pinRetriesLeft(byte_type pinReference) const; + void selectMain() const; void selectADF1() const; void selectADF2() const; }; diff --git a/src/electronic-ids/pcsc/EstEIDIDEMIA.cpp b/src/electronic-ids/pcsc/EstEIDIDEMIA.cpp deleted file mode 100644 index a43f654..0000000 --- a/src/electronic-ids/pcsc/EstEIDIDEMIA.cpp +++ /dev/null @@ -1,45 +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 "EstEIDIDEMIA.hpp" - -#include "pcsc-common.hpp" - -// ESTEID specification: -// https://installer.id.ee/media/id2019/TD-ID1-Chip-App.pdf - -using namespace electronic_id; - -void EstEIDIDEMIAV1::selectAuthSecurityEnv() const -{ - selectSecurityEnv(*card, 0xA4, 0x04, 0x81, name()); -} - -pcsc_cpp::byte_type EstEIDIDEMIAV1::selectSignSecurityEnv() const -{ - return selectSecurityEnv(*card, 0xB6, 0x54, 0x9f, name()); -} - -const std::set& EstEIDIDEMIAV1::supportedSigningAlgorithms() const -{ - return ELLIPTIC_CURVE_SIGNATURE_ALGOS(); -} diff --git a/src/electronic-ids/pcsc/EstEIDIDEMIA.hpp b/src/electronic-ids/pcsc/EstEIDIDEMIA.hpp index dc93110..e4cdaf1 100644 --- a/src/electronic-ids/pcsc/EstEIDIDEMIA.hpp +++ b/src/electronic-ids/pcsc/EstEIDIDEMIA.hpp @@ -24,29 +24,22 @@ #include "EIDIDEMIA.hpp" +// ESTEID specification: +// https://installer.id.ee/media/id2019/TD-ID1-Chip-App.pdf + namespace electronic_id { class EstEIDIDEMIAV1 : public EIDIDEMIA { public: - explicit EstEIDIDEMIAV1(pcsc_cpp::SmartCard::ptr _card) : EIDIDEMIA(std::move(_card)) {} + using EIDIDEMIA::EIDIDEMIA; private: - JsonWebSignatureAlgorithm authSignatureAlgorithm() const override - { - return JsonWebSignatureAlgorithm::ES384; - } PinMinMaxLength authPinMinMaxLength() const override { return {4, 12}; } - - const std::set& supportedSigningAlgorithms() const override; PinMinMaxLength signingPinMinMaxLength() const override { return {5, 12}; } - std::string name() const override { return "EstEID IDEMIA v1"; } Type type() const override { return EstEID; } - - void selectAuthSecurityEnv() const override; - pcsc_cpp::byte_type selectSignSecurityEnv() const override; }; } // namespace electronic_id diff --git a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp index 8d69a37..416060c 100644 --- a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp +++ b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp @@ -22,6 +22,8 @@ #include "LatEIDIDEMIAv2.hpp" +#include "../TLV.hpp" + #include "pcsc-common.hpp" #include @@ -30,65 +32,21 @@ using namespace pcsc_cpp; using namespace electronic_id; -struct KeyInfo -{ - bool isECC; - byte_type id; -}; - struct LatEIDIDEMIAV2::Private { + std::map authCache; + std::map signCache; std::optional authKeyInfo; std::optional signKeyInfo; }; namespace { -constexpr byte_type DEFAULT_AUTH_KEY_ID = 0x81; -constexpr byte_type DEFAULT_SIGN_KEY_ID = 0x9F; -inline byte_vector readEF_File(const SmartCard& card, const byte_vector& file) -{ - auto response = card.transmit({0x00, 0xA4, 0x02, 0x04, file, 0x00}); - if (!response.isOK()) { - THROW(SmartCardError, "Failed to read EF file"); - } - static constexpr std::array findLength {0x80, 0x02}; - auto pos = std::search(response.data.cbegin(), response.data.cend(), findLength.cbegin(), - findLength.cend()); - if (pos == response.data.cend()) { - THROW(SmartCardError, "Failed to read EF file length"); - } - pos += byte_vector::difference_type(findLength.size()); - return readBinary(card, size_t(*pos << 8) + *(pos + 1), 0xFF); -} +const byte_vector EF_OD {0x50, 0x31}; +constexpr byte_type PRIV_FILE_REF = 0xA0; +constexpr byte_type CERT_FILE_REF = 0xA4; -inline byte_vector readEF_PrKD(const SmartCard& card) -{ - static const byte_vector EF_OD {0x50, 0x31}; - const auto info = readEF_File(card, EF_OD); - static constexpr std::array file {0xA0, 0x06, 0x30, 0x04, 0x04, 0x02}; - auto pos = std::search(info.cbegin(), info.cend(), file.cbegin(), file.cend()); - if (pos == info.cend()) { - THROW(SmartCardError, "EF.PrKD reference not found"); - } - pos += byte_vector::difference_type(file.size()); - return readEF_File(card, {*pos, *(pos + 1)}); -} - -inline KeyInfo readPrKDInfo(const SmartCard& card, byte_type keyID) -{ - const auto data = readEF_PrKD(card); - if (data.empty()) { - return {false, keyID}; - } - static constexpr std::array needle {0x02, 0x02, 0x00}; - if (auto pos = std::search(data.cbegin(), data.cend(), needle.cbegin(), needle.cend()); - pos != data.cend()) { - return {data[0] == 0xA0, *(pos + byte_vector::difference_type(needle.size()))}; - } - return {data[0] == 0xA0, keyID}; -} } // namespace LatEIDIDEMIAV2::LatEIDIDEMIAV2(pcsc_cpp::SmartCard::ptr _card) : @@ -98,12 +56,23 @@ LatEIDIDEMIAV2::LatEIDIDEMIAV2(pcsc_cpp::SmartCard::ptr _card) : LatEIDIDEMIAV2::~LatEIDIDEMIAV2() = default; +byte_vector LatEIDIDEMIAV2::getCertificateImpl(const CertificateType type) const +{ + selectMain(); + type.isAuthentication() ? selectADF1() : selectADF2(); + auto info = readDCODInfo(CERT_FILE_REF, type.isAuthentication() ? data->authCache : data->signCache); + if (TLV id = TLV::path(info, 0x30, 0xA1, 0x30, 0x30, 0x04)) { + return electronic_id::getCertificate(*card, CommandApdu::select(0x02, {id.begin, id.end})); + } + THROW(SmartCardError, "EF.CD reference not found"); +} + JsonWebSignatureAlgorithm LatEIDIDEMIAV2::authSignatureAlgorithm() const { if (!data->authKeyInfo.has_value()) { auto transactionGuard = card->beginTransaction(); selectADF1(); - data->authKeyInfo = readPrKDInfo(*card, DEFAULT_AUTH_KEY_ID); + authKeyRef(); } return data->authKeyInfo->isECC ? JsonWebSignatureAlgorithm::ES384 : JsonWebSignatureAlgorithm::RS256; @@ -114,7 +83,7 @@ const std::set& LatEIDIDEMIAV2::supportedSigningAlgorithms() if (!data->signKeyInfo.has_value()) { auto transactionGuard = card->beginTransaction(); selectADF2(); - data->signKeyInfo = readPrKDInfo(*card, DEFAULT_SIGN_KEY_ID); + signKeyRef(); } const static std::set RS256_SIGNATURE_ALGO { {SignatureAlgorithm::RS256}, @@ -122,20 +91,56 @@ const std::set& LatEIDIDEMIAV2::supportedSigningAlgorithms() return data->signKeyInfo->isECC ? ELLIPTIC_CURVE_SIGNATURE_ALGOS() : RS256_SIGNATURE_ALGO; } -void LatEIDIDEMIAV2::selectAuthSecurityEnv() const +EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::authKeyRef() const { if (!data->authKeyInfo.has_value()) { - data->authKeyInfo = readPrKDInfo(*card, DEFAULT_AUTH_KEY_ID); + data->authKeyInfo = readPrKDInfo(EIDIDEMIA::authKeyRef().id, data->authCache); } - selectSecurityEnv(*card, 0xA4, data->authKeyInfo->isECC ? 0x04 : 0x02, data->authKeyInfo->id, - name()); + return data->authKeyInfo.value(); } -byte_type LatEIDIDEMIAV2::selectSignSecurityEnv() const +EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::signKeyRef() const { if (!data->signKeyInfo.has_value()) { - data->signKeyInfo = readPrKDInfo(*card, DEFAULT_SIGN_KEY_ID); + data->signKeyInfo = readPrKDInfo(EIDIDEMIA::signKeyRef().id, data->signCache); + } + return data->signKeyInfo.value(); +} + +const byte_vector& LatEIDIDEMIAV2::readEF_File(byte_vector file, std::map& cache) const +{ + if (auto it = cache.find(file); it != cache.end()) { + return it->second; + } + auto response = card->transmit({0x00, 0xA4, 0x02, 0x04, file, 0x00}); + if (!response.isOK()) { + THROW(SmartCardError, "Failed to read EF file"); + } + TLV size = TLV::path(response.data, 0x62, 0x80); + if (!size || size.length != 2) { + THROW(SmartCardError, "Failed to read EF file length"); + } + return cache[std::move(file)] = + readBinary(*card, size_t(*size.begin << 8) + *(size.begin + 1), 0xFF); +} + +const byte_vector& LatEIDIDEMIAV2::readDCODInfo(byte_type type, std::map &cache) const +{ + const auto info = readEF_File(EF_OD, cache); + if (auto file = TLV::path(info, type, 0x30, 0x04); file && file.length == 2) { + return readEF_File({file.begin, file.end}, cache); + } + THROW(SmartCardError, "EF.DCOD reference not found"); +} + +EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::readPrKDInfo(byte_type keyID, std::map& cache) const +{ + auto info = readDCODInfo(PRIV_FILE_REF, cache); + if (info.empty()) { + THROW(SmartCardError, "EF.PrKD reference not found"); } - return selectSecurityEnv(*card, 0xB6, data->signKeyInfo->isECC ? 0x54 : 0x42, - data->signKeyInfo->id, name()); + TLV prKD(info); + TLV key = TLV::path(prKD.child(), 0x30); + key = TLV::path(++key, 0x30, 0x02); + return {key.length == 2 ? *std::next(key.begin) : keyID, prKD.tag == 0xA0}; } diff --git a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp index 7140c19..26a7321 100644 --- a/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp +++ b/src/electronic-ids/pcsc/LatEIDIDEMIAv2.hpp @@ -24,6 +24,8 @@ #include "EIDIDEMIA.hpp" +#include + namespace electronic_id { @@ -35,6 +37,8 @@ class LatEIDIDEMIAV2 : public EIDIDEMIA PCSC_CPP_DISABLE_COPY_MOVE(LatEIDIDEMIAV2); private: + byte_vector getCertificateImpl(const CertificateType type) const override; + JsonWebSignatureAlgorithm authSignatureAlgorithm() const override; PinMinMaxLength authPinMinMaxLength() const override { return {4, 12}; } @@ -44,8 +48,12 @@ class LatEIDIDEMIAV2 : public EIDIDEMIA std::string name() const override { return "LatEID IDEMIA v2"; } Type type() const override { return LatEID; } - void selectAuthSecurityEnv() const override; - pcsc_cpp::byte_type selectSignSecurityEnv() const override; + 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; struct Private; std::unique_ptr data; diff --git a/tests/mock/select-certificate-script-LAT-V2.hpp b/tests/mock/select-certificate-script-LAT-V2.hpp index 85de729..cebb39b 100644 --- a/tests/mock/select-certificate-script-LAT-V2.hpp +++ b/tests/mock/select-certificate-script-LAT-V2.hpp @@ -30,8 +30,27 @@ const PcscMock::ApduScript LATEID_IDEMIA_V2_SELECT_AUTH_CERTIFICATE_AND_AUTHENTI 0x08, 0x00, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, 0x00}, {0x90, 0x00}}, + // Select ADF1 + {{0x00, 0xA4, 0x04, 0x0C, 0x0d, 0xe8, 0x28, 0xbd, 0x08, 0x0f, 0xf2, 0x50, 0x4f, 0x54, 0x20, + 0x41, 0x57, 0x50}, + {0x90, 0x00}}, + + // Read authentication certificate file id + {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x50, 0x31, 0x00}, + {0x62, 0x04, 0x80, 0x02, 0x00, 0x10, 0x90, 0x00}}, + + {{0x00, 0xB0, 0x00, 0x00, 0x10}, + {0xA4, 0x06, 0x30, 0x04, 0x04, 0x02, 0x70, 0x05, 0xA0, 0x06, 0x30, 0x04, 0x04, 0x02, 0x70, + 0x01, 0x90, 0x00}}, + + {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x70, 0x05, 0x00}, + {0x62, 0x04, 0x80, 0x02, 0x00, 0x0C, 0x90, 0x00}}, + + {{0x00, 0xB0, 0x00, 0x00, 0x0C}, + {0x30, 0x0A, 0xA1, 0x08, 0x30, 0x06, 0x30, 0x04, 0x04, 0x02, 0x34, 0x01, 0x90, 0x00}}, + // Select authentication certificate file. - {{0x00, 0xA4, 0x09, 0x0C, 0x04, 0xAD, 0xF1, 0x34, 0x01}, {0x90, 0x00}}, + {{0x00, 0xA4, 0x02, 0x0C, 0x02, 0x34, 0x01}, {0x90, 0x00}}, // Read data length. {{0x00, 0xb0, 0x00, 0x00, 0x04}, {0x30, 0x82, 0x06, 0xc1, 0x90, 0x00}}, @@ -203,14 +222,12 @@ const PcscMock::ApduScript LATEID_IDEMIA_V2_SELECT_AUTH_CERTIFICATE_AND_AUTHENTI 0x41, 0x57, 0x50}, {0x90, 0x00}}, - // Detect if card is updated - {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x50, 0x31, 0x00}, {0x80, 0x02, 0x00, 0x08, 0x90, 0x00}}, - - {{0x00, 0xB0, 0x00, 0x00, 0x08}, {0xA0, 0x06, 0x30, 0x04, 0x04, 0x02, 0x70, 0x01, 0x90, 0x00}}, + // Read private key info + {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x70, 0x01, 0x00}, + {0x62, 0x04, 0x80, 0x02, 0x00, 0x0A, 0x90, 0x00}}, - {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x70, 0x01, 0x00}, {0x80, 0x02, 0x00, 0x05, 0x90, 0x00}}, - - {{0x00, 0xB0, 0x00, 0x00, 0x05}, {0x30, 0x02, 0x02, 0x00, 0x81, 0x90, 0x00}}, + {{0x00, 0xB0, 0x00, 0x00, 0x0A}, + {0x30, 0x08, 0x30, 0x00, 0x30, 0x04, 0x02, 0x02, 0x00, 0x81, 0x90, 0x00}}, // Select AWP AID. {{0x00, 0xA4, 0x04, 0x0C, 0x0D, 0xe8, 0x28, 0xbd, 0x08, 0x0f, 0xf2, 0x50, 0x4f, 0x54, 0x20, @@ -255,8 +272,27 @@ const PcscMock::ApduScript LATEID_IDEMIA_V2_SELECT_SIGN_CERTIFICATE_AND_SIGNING 0x08, 0x00, 0x07, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x01, 0x00}, {0x90, 0x00}}, + // Select main ADF2. + {{0x00, 0xA4, 0x04, 0x0C, 0x10, 0x51, 0x53, 0x43, 0x44, 0x20, 0x41, + 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E}, + {0x90, 0x00}}, + + // Read authentication certificate file id + {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x50, 0x31, 0x00}, + {0x62, 0x04, 0x80, 0x02, 0x00, 0x10, 0x90, 0x00}}, + + {{0x00, 0xB0, 0x00, 0x00, 0x10}, + {0xA4, 0x06, 0x30, 0x04, 0x04, 0x02, 0x70, 0x05, 0xA0, 0x06, 0x30, 0x04, 0x04, 0x02, 0x70, + 0x01, 0x90, 0x00}}, + + {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x70, 0x05, 0x00}, + {0x62, 0x04, 0x80, 0x02, 0x00, 0x0C, 0x90, 0x00}}, + + {{0x00, 0xB0, 0x00, 0x00, 0x0C}, + {0x30, 0x0A, 0xA1, 0x08, 0x30, 0x06, 0x30, 0x04, 0x04, 0x02, 0x34, 0x1F, 0x90, 0x00}}, + // Select signing certificate file. - {{0x00, 0xA4, 0x09, 0x0C, 0x04, 0xAD, 0xF2, 0x34, 0x1F}, {0x90, 0x00}}, + {{0x00, 0xA4, 0x02, 0x0C, 0x02, 0x34, 0x1F}, {0x90, 0x00}}, // Read data length. {{0x00, 0xb0, 0x00, 0x00, 0x04}, {0x30, 0x82, 0x08, 0x48, 0x90, 0x00}}, @@ -459,14 +495,12 @@ const PcscMock::ApduScript LATEID_IDEMIA_V2_SELECT_SIGN_CERTIFICATE_AND_SIGNING 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E}, {0x90, 0x00}}, - // Detect if card is updated - {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x50, 0x31, 0x00}, {0x80, 0x02, 0x00, 0x08, 0x90, 0x00}}, - - {{0x00, 0xB0, 0x00, 0x00, 0x08}, {0xA0, 0x06, 0x30, 0x04, 0x04, 0x02, 0x70, 0x01, 0x90, 0x00}}, - - {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x70, 0x01, 0x00}, {0x80, 0x02, 0x00, 0x05, 0x90, 0x00}}, + // Read private key info + {{0x00, 0xA4, 0x02, 0x04, 0x02, 0x70, 0x01, 0x00}, + {0x62, 0x04, 0x80, 0x02, 0x00, 0x0A, 0x90, 0x00}}, - {{0x00, 0xB0, 0x00, 0x00, 0x05}, {0x30, 0x02, 0x02, 0x00, 0x9F, 0x90, 0x00}}, + {{0x00, 0xB0, 0x00, 0x00, 0x0A}, + {0x30, 0x08, 0x30, 0x00, 0x30, 0x04, 0x02, 0x02, 0x00, 0x9F, 0x90, 0x00}}, // Select QSCD AID. {{0x00, 0xA4, 0x04, 0x0C, 0x10, 0x51, 0x53, 0x43, 0x44, 0x20, 0x41,