Skip to content

Commit 051ae3e

Browse files
metsmamrts
authored andcommitted
Read certificate file ref from DCOD file
Fixes: #119 Signed-off-by: Raul Metsma <raul@metsma.ee>
1 parent f576dcc commit 051ae3e

File tree

12 files changed

+295
-147
lines changed

12 files changed

+295
-147
lines changed

.github/workflows/codeql.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ jobs:
2424
run: sudo apt update -qq && sudo apt install --no-install-recommends -y cmake libgtest-dev libpcsclite-dev
2525

2626
- name: Initialize CodeQL
27-
uses: github/codeql-action/init@v2
27+
uses: github/codeql-action/init@v3
2828
with:
2929
languages: cpp
3030
queries: +security-and-quality
3131

3232
- name: Autobuild
33-
uses: github/codeql-action/autobuild@v2
33+
uses: github/codeql-action/autobuild@v3
3434

3535
- name: Perform CodeQL Analysis
36-
uses: github/codeql-action/analyze@v2
36+
uses: github/codeql-action/analyze@v3

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.16.0)
1+
cmake_minimum_required(VERSION 3.22)
22
if(POLICY CMP0092)
33
cmake_policy(SET CMP0092 NEW)
44
endif()
@@ -16,10 +16,10 @@ add_library(${PROJECT_NAME}
1616
src/electronic-ids/common.hpp
1717
src/electronic-ids/common.cpp
1818
src/electronic-ids/scope.hpp
19+
src/electronic-ids/TLV.hpp
1920
src/electronic-ids/x509.hpp
2021
src/electronic-ids/pcsc/EIDIDEMIA.cpp
2122
src/electronic-ids/pcsc/EIDIDEMIA.hpp
22-
src/electronic-ids/pcsc/EstEIDIDEMIA.cpp
2323
src/electronic-ids/pcsc/EstEIDIDEMIA.hpp
2424
src/electronic-ids/pcsc/FinEID.cpp
2525
src/electronic-ids/pcsc/FinEID.hpp

lib/libpcsc-cpp/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.16)
1+
cmake_minimum_required(VERSION 3.22)
22

33
project(pcsc-cpp)
44

lib/libpcsc-cpp/tests/lib/libpcsc-mock/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
cmake_minimum_required(VERSION 3.10.0)
1+
cmake_minimum_required(VERSION 3.22)
22
if(POLICY CMP0092)
33
cmake_policy(SET CMP0092 NEW)
44
endif()

src/electronic-ids/TLV.hpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright (c) 2025 Estonian Information System Authority
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in all
12+
* copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+
* SOFTWARE.
21+
*/
22+
23+
#pragma once
24+
25+
#include "pcsc-cpp/pcsc-cpp.hpp"
26+
#include "pcsc-cpp/pcsc-cpp-utils.hpp"
27+
28+
namespace electronic_id
29+
{
30+
31+
/**
32+
* Represents a single Tag-Length-Value structure used in DER-encoded eID card files.
33+
*
34+
* The constructor parses the tag and length from the provided byte range,
35+
* then adjusts its iterators so that `begin` and `end` reference only the value bytes.
36+
* If the TLV is empty, `operator bool()` returns false.
37+
*/
38+
struct TLV
39+
{
40+
using byte_vector = pcsc_cpp::byte_vector;
41+
uint16_t tag {};
42+
uint32_t length {};
43+
byte_vector::const_iterator begin;
44+
byte_vector::const_iterator end;
45+
46+
PCSC_CPP_CONSTEXPR_VECTOR explicit TLV(const byte_vector& data) :
47+
TLV(data.cbegin(), data.cend())
48+
{
49+
}
50+
51+
PCSC_CPP_CONSTEXPR_VECTOR TLV(byte_vector::const_iterator _begin,
52+
byte_vector::const_iterator _end) : begin(_begin), end(_end)
53+
{
54+
if (!*this) {
55+
return;
56+
}
57+
58+
tag = *begin++;
59+
if ((tag & 0x1F) == 0x1F) { // Multi-byte tag
60+
if (!*this) {
61+
THROW(std::invalid_argument, "Invalid TLV: Unexpected end of tag");
62+
}
63+
tag = (tag << 8) | (*begin++);
64+
}
65+
66+
if (!*this) {
67+
THROW(std::invalid_argument, "Invalid TLV: Missing length field");
68+
}
69+
70+
length = *begin++;
71+
if (length & 0x80) { // Extended length encoding
72+
auto num_bytes = uint8_t(length & 0x7F);
73+
if (num_bytes == 0 || num_bytes > 4 || std::distance(begin, end) < num_bytes) {
74+
THROW(std::invalid_argument, "Invalid TLV: Incorrect extended length encoding");
75+
}
76+
77+
length = 0;
78+
for (uint8_t i = 0; i < num_bytes; ++i) {
79+
length = (length << 8) | (*begin++);
80+
}
81+
}
82+
83+
if (std::distance(begin, end) < length) {
84+
THROW(std::invalid_argument, "Invalid TLV: Insufficient value data");
85+
}
86+
}
87+
88+
PCSC_CPP_CONSTEXPR_VECTOR TLV child() const { return {begin, begin + length}; }
89+
90+
PCSC_CPP_CONSTEXPR_VECTOR TLV& operator++() { return *this = {begin + length, end}; }
91+
92+
template <typename... Tags>
93+
static PCSC_CPP_CONSTEXPR_VECTOR TLV path(TLV tlv, uint16_t tag, Tags... tags)
94+
{
95+
for (; tlv; ++tlv) {
96+
if (tlv.tag == tag) {
97+
if constexpr (sizeof...(tags) > 0) {
98+
return path(tlv.child(), uint16_t(tags)...);
99+
}
100+
return tlv;
101+
}
102+
}
103+
return TLV({});
104+
}
105+
template <typename... Tags>
106+
static PCSC_CPP_CONSTEXPR_VECTOR TLV path(const byte_vector& data, uint16_t tag, Tags... tags)
107+
{
108+
return path(TLV(data), tag, tags...);
109+
}
110+
111+
constexpr operator bool() const noexcept { return begin != end; }
112+
};
113+
114+
} // namespace electronic_id

src/electronic-ids/pcsc/EIDIDEMIA.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace
3333
constexpr byte_type PIN_PADDING_CHAR = 0xFF;
3434
constexpr byte_type AUTH_PIN_REFERENCE = 0x01;
3535
constexpr byte_type SIGN_PIN_REFERENCE = 0x85;
36+
constexpr byte_type DEFAULT_AUTH_KEY_ID = 0x81;
37+
constexpr byte_type DEFAULT_SIGN_KEY_ID = 0x9F;
3638

3739
const auto MAIN_AID = CommandApdu::select(0x04,
3840
{0xA0, 0x00, 0x00, 0x00, 0x77, 0x01, 0x08, 0x00, 0x07,
@@ -47,6 +49,11 @@ const auto SIGN_CERT = CommandApdu::select(0x09, {0xAD, 0xF2, 0x34, 0x1F});
4749

4850
} // namespace
4951

52+
void EIDIDEMIA::selectMain() const
53+
{
54+
transmitApduWithExpectedResponse(*card, MAIN_AID);
55+
}
56+
5057
void EIDIDEMIA::selectADF1() const
5158
{
5259
transmitApduWithExpectedResponse(*card, ADF1_AID);
@@ -59,14 +66,20 @@ void EIDIDEMIA::selectADF2() const
5966

6067
byte_vector EIDIDEMIA::getCertificateImpl(const CertificateType type) const
6168
{
62-
transmitApduWithExpectedResponse(*card, MAIN_AID);
69+
selectMain();
6370
return electronic_id::getCertificate(*card, type.isAuthentication() ? AUTH_CERT : SIGN_CERT);
6471
}
6572

73+
EIDIDEMIA::KeyInfo EIDIDEMIA::authKeyRef() const
74+
{
75+
return {DEFAULT_AUTH_KEY_ID, true};
76+
}
77+
6678
byte_vector EIDIDEMIA::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const
6779
{
6880
selectADF1();
69-
selectAuthSecurityEnv();
81+
auto [keyId, isECC] = authKeyRef();
82+
selectSecurityEnv(*card, 0xA4, isECC ? 0x04 : 0x02, keyId, name());
7083

7184
verifyPin(*card, AUTH_PIN_REFERENCE, std::move(pin), authPinMinMaxLength().first,
7285
authPinMinMaxLength().second, PIN_PADDING_CHAR);
@@ -80,18 +93,23 @@ byte_vector EIDIDEMIA::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector&
8093

8194
ElectronicID::PinRetriesRemainingAndMax EIDIDEMIA::authPinRetriesLeftImpl() const
8295
{
83-
transmitApduWithExpectedResponse(*card, MAIN_AID);
96+
selectMain();
8497
return pinRetriesLeft(AUTH_PIN_REFERENCE);
8598
}
8699

100+
EIDIDEMIA::KeyInfo EIDIDEMIA::signKeyRef() const
101+
{
102+
return {DEFAULT_SIGN_KEY_ID, true};
103+
}
104+
87105
ElectronicID::Signature EIDIDEMIA::signWithSigningKeyImpl(byte_vector&& pin,
88106
const byte_vector& hash,
89107
const HashAlgorithm hashAlgo) const
90108
{
91109
selectADF2();
92-
pcsc_cpp::byte_type algo = selectSignSecurityEnv();
110+
auto [keyRef, isECC] = signKeyRef();
111+
selectSecurityEnv(*card, 0xB6, isECC ? 0x54 : 0x42, keyRef, name());
93112
auto tmp = hash;
94-
bool isECC = algo == 0x54;
95113
if (isECC) {
96114
constexpr size_t ECDSA384_INPUT_LENGTH = 384 / 8;
97115
if (tmp.size() < ECDSA384_INPUT_LENGTH) {

src/electronic-ids/pcsc/EIDIDEMIA.hpp

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,22 +30,37 @@ namespace electronic_id
3030
class EIDIDEMIA : public PcscElectronicID
3131
{
3232
public:
33+
struct KeyInfo
34+
{
35+
byte_type id;
36+
bool isECC;
37+
};
38+
3339
explicit EIDIDEMIA(pcsc_cpp::SmartCard::ptr _card) : PcscElectronicID(std::move(_card)) {}
3440

3541
protected:
3642
byte_vector getCertificateImpl(const CertificateType type) const override;
3743

3844
PinRetriesRemainingAndMax authPinRetriesLeftImpl() const override;
39-
virtual void selectAuthSecurityEnv() const = 0;
45+
JsonWebSignatureAlgorithm authSignatureAlgorithm() const override
46+
{
47+
return JsonWebSignatureAlgorithm::ES384;
48+
}
49+
virtual KeyInfo authKeyRef() const;
4050
byte_vector signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const override;
4151

4252
PinRetriesRemainingAndMax signingPinRetriesLeftImpl() const override;
43-
virtual pcsc_cpp::byte_type selectSignSecurityEnv() const = 0;
53+
const std::set<SignatureAlgorithm>& supportedSigningAlgorithms() const override
54+
{
55+
return ELLIPTIC_CURVE_SIGNATURE_ALGOS();
56+
}
57+
virtual KeyInfo signKeyRef() const;
4458
Signature signWithSigningKeyImpl(byte_vector&& pin, const byte_vector& hash,
4559
const HashAlgorithm hashAlgo) const override;
4660

4761
PinRetriesRemainingAndMax pinRetriesLeft(byte_type pinReference) const;
4862

63+
void selectMain() const;
4964
void selectADF1() const;
5065
void selectADF2() const;
5166
};

src/electronic-ids/pcsc/EstEIDIDEMIA.cpp

Lines changed: 0 additions & 45 deletions
This file was deleted.

src/electronic-ids/pcsc/EstEIDIDEMIA.hpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,22 @@
2424

2525
#include "EIDIDEMIA.hpp"
2626

27+
// ESTEID specification:
28+
// https://installer.id.ee/media/id2019/TD-ID1-Chip-App.pdf
29+
2730
namespace electronic_id
2831
{
2932

3033
class EstEIDIDEMIAV1 : public EIDIDEMIA
3134
{
3235
public:
33-
explicit EstEIDIDEMIAV1(pcsc_cpp::SmartCard::ptr _card) : EIDIDEMIA(std::move(_card)) {}
36+
using EIDIDEMIA::EIDIDEMIA;
3437

3538
private:
36-
JsonWebSignatureAlgorithm authSignatureAlgorithm() const override
37-
{
38-
return JsonWebSignatureAlgorithm::ES384;
39-
}
4039
PinMinMaxLength authPinMinMaxLength() const override { return {4, 12}; }
41-
42-
const std::set<SignatureAlgorithm>& supportedSigningAlgorithms() const override;
4340
PinMinMaxLength signingPinMinMaxLength() const override { return {5, 12}; }
44-
4541
std::string name() const override { return "EstEID IDEMIA v1"; }
4642
Type type() const override { return EstEID; }
47-
48-
void selectAuthSecurityEnv() const override;
49-
pcsc_cpp::byte_type selectSignSecurityEnv() const override;
5043
};
5144

5245
} // namespace electronic_id

0 commit comments

Comments
 (0)