Skip to content

Commit e517837

Browse files
committed
Thales card support
IB-8171 Signed-off-by: Raul Metsma <[email protected]>
1 parent 573b0c1 commit e517837

File tree

9 files changed

+133
-75
lines changed

9 files changed

+133
-75
lines changed

lib/libpcsc-cpp/src/SmartCard.cpp

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,6 @@ constexpr SmartCard::Protocol convertToSmartCardProtocol(const DWORD protocol)
5757
}
5858
}
5959

60-
std::pair<SCARDHANDLE, DWORD> connectToCard(const SCARDCONTEXT ctx, const string_t& readerName)
61-
{
62-
const unsigned requestedProtocol =
63-
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; // Let PCSC auto-select protocol.
64-
DWORD protocolOut = SCARD_PROTOCOL_UNDEFINED;
65-
SCARDHANDLE cardHandle = 0;
66-
67-
SCard(Connect, ctx, readerName.c_str(), DWORD(SCARD_SHARE_SHARED), requestedProtocol,
68-
&cardHandle, &protocolOut);
69-
70-
return {cardHandle, protocolOut};
71-
}
72-
7360
template <class K, class V = uint32_t, class D, size_t dsize, typename Func>
7461
constexpr std::map<K, V> parseTLV(const std::array<D, dsize>& data, DWORD size, Func transform)
7562
{
@@ -96,9 +83,13 @@ namespace pcsc_cpp
9683
class CardImpl
9784
{
9885
public:
99-
explicit CardImpl(std::pair<SCARDHANDLE, DWORD> cardParams) :
100-
cardHandle(cardParams.first), _protocol {cardParams.second, sizeof(SCARD_IO_REQUEST)}
86+
explicit CardImpl(const SCARDCONTEXT ctx, const string_t& readerName)
10187
{
88+
const unsigned requestedProtocol =
89+
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1; // Let PCSC auto-select protocol.
90+
SCard(Connect, ctx, readerName.c_str(), DWORD(SCARD_SHARE_SHARED), requestedProtocol,
91+
&cardHandle, &_protocol.dwProtocol);
92+
10293
// TODO: debug("Protocol: " + to_string(protocol()))
10394
try {
10495
DWORD size = 0;
@@ -124,7 +115,7 @@ class CardImpl
124115
}
125116
}
126117

127-
~CardImpl()
118+
~CardImpl() noexcept
128119
{
129120
if (cardHandle) {
130121
// Cannot throw in destructor, so cannot use the SCard() macro here.
@@ -161,6 +152,9 @@ class CardImpl
161152

162153
auto response = toResponse(std::move(responseBytes), responseLength);
163154

155+
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
156+
getResponseWithLE(response, commandBytes);
157+
}
164158
if (response.sw1 == ResponseApdu::MORE_DATA_AVAILABLE) {
165159
getMoreResponseData(response);
166160
}
@@ -213,8 +207,8 @@ class CardImpl
213207
DWORD protocol() const { return _protocol.dwProtocol; }
214208

215209
private:
216-
SCARDHANDLE cardHandle;
217-
const SCARD_IO_REQUEST _protocol;
210+
SCARDHANDLE cardHandle {};
211+
SCARD_IO_REQUEST _protocol {SCARD_PROTOCOL_UNDEFINED, sizeof(SCARD_IO_REQUEST)};
218212
std::map<DRIVER_FEATURES, uint32_t> features;
219213
uint32_t id_vendor {};
220214
uint32_t id_product {};
@@ -232,25 +226,20 @@ class CardImpl
232226

233227
// Let expected errors through for handling in upper layers or in if blocks below.
234228
switch (response.sw1) {
235-
case ResponseApdu::OK:
236-
case ResponseApdu::MORE_DATA_AVAILABLE: // See the if block after next.
237-
case ResponseApdu::VERIFICATION_FAILED:
238-
case ResponseApdu::VERIFICATION_CANCELLED:
239-
case ResponseApdu::WRONG_LENGTH:
240-
case ResponseApdu::COMMAND_NOT_ALLOWED:
241-
case ResponseApdu::WRONG_PARAMETERS:
242-
case ResponseApdu::WRONG_LE_LENGTH: // See next if block.
243-
break;
229+
using enum ResponseApdu::Status;
230+
case OK:
231+
case MORE_DATA_AVAILABLE:
232+
case WRONG_LE_LENGTH:
233+
case VERIFICATION_FAILED:
234+
case VERIFICATION_CANCELLED:
235+
case WRONG_LENGTH:
236+
case COMMAND_NOT_ALLOWED:
237+
case WRONG_PARAMETERS:
238+
return response;
244239
default:
245240
THROW(Error,
246241
"Error response: '" + response + "', protocol " + std::to_string(protocol()));
247242
}
248-
249-
if (response.sw1 == ResponseApdu::WRONG_LE_LENGTH) {
250-
THROW(Error, "Wrong LE length (SW1=0x6C) in response, please set LE");
251-
}
252-
253-
return response;
254243
}
255244

256245
void getMoreResponseData(ResponseApdu& response) const
@@ -269,6 +258,13 @@ class CardImpl
269258
response.sw1 = ResponseApdu::OK;
270259
response.sw2 = 0;
271260
}
261+
262+
void getResponseWithLE(ResponseApdu& response, byte_vector command) const
263+
{
264+
size_t pos = command.size() <= 5 ? 4 : 5 + command[4];
265+
command[pos] = response.sw2;
266+
response = transmitBytes(command);
267+
}
272268
};
273269

274270
SmartCard::TransactionGuard::TransactionGuard(const CardImpl& card, bool& inProgress) :
@@ -290,7 +286,7 @@ SmartCard::TransactionGuard::~TransactionGuard() noexcept
290286

291287
SmartCard::SmartCard(ContextPtr context, string_t readerName, byte_vector atr) :
292288
ctx(std::move(context)),
293-
card(std::make_unique<CardImpl>(connectToCard(ctx->handle(), readerName))),
289+
card(std::make_unique<CardImpl>(context->handle(), readerName)),
294290
_readerName(std::move(readerName)), _atr(std::move(atr)),
295291
_protocol(convertToSmartCardProtocol(card->protocol()))
296292
{

src/electronic-id.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ const std::map<byte_vector, ElectronicIDConstructor, VectorComparator> SUPPORTED
6060
{{0x3b, 0xdc, 0x96, 0x00, 0x80, 0xb1, 0xfe, 0x45, 0x1f, 0x83, 0x00, 0x12,
6161
0x23, 0x3f, 0x54, 0x65, 0x49, 0x44, 0x32, 0x0f, 0x90, 0x00, 0xc3},
6262
constructor<EstEIDIDEMIAV1>},
63+
// EstEID Thales v1.0
64+
{{0x3b, 0xff, 0x96, 0x00, 0x00, 0x80, 0x31, 0xfe, 0x43, 0x80, 0x31, 0xb8, 0x53,
65+
0x65, 0x49, 0x44, 0x64, 0xb0, 0x85, 0x05, 0x10, 0x12, 0x23, 0x3f, 0x1d},
66+
constructor<EstEIDTHALES>},
6367
// FinEID v3.0
6468
{{0x3B, 0x7F, 0x96, 0x00, 0x00, 0x80, 0x31, 0xB8, 0x65, 0xB0,
6569
0x85, 0x03, 0x00, 0xEF, 0x12, 0x00, 0xF6, 0x82, 0x90, 0x00},

src/electronic-ids/pcsc/EIDIDEMIA.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,7 @@ byte_vector EIDIDEMIA::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector&
8383
auto [keyId, isECC] = authKeyRef();
8484
selectSecurityEnv(*card, 0xA4, isECC ? 0x04 : 0x02, keyId, name());
8585

86-
verifyPin(*card, AUTH_PIN_REFERENCE, std::move(pin), authPinMinMaxLength().first,
87-
authPinMinMaxLength().second, PIN_PADDING_CHAR);
86+
verifyPin(*card, AUTH_PIN_REFERENCE, std::move(pin), authPinMinMaxLength(), PIN_PADDING_CHAR);
8887

8988
return internalAuthenticate(*card,
9089
authSignatureAlgorithm().isRSAWithPKCS1Padding()
@@ -111,8 +110,8 @@ ElectronicID::Signature EIDIDEMIA::signWithSigningKeyImpl(byte_vector&& pin,
111110
selectADF2();
112111
auto [keyRef, isECC] = signKeyRef();
113112
selectSecurityEnv(*card, 0xB6, isECC ? 0x54 : 0x42, keyRef, name());
114-
verifyPin(*card, SIGN_PIN_REFERENCE, std::move(pin), signingPinMinMaxLength().first,
115-
signingPinMinMaxLength().second, PIN_PADDING_CHAR);
113+
verifyPin(*card, SIGN_PIN_REFERENCE, std::move(pin), signingPinMinMaxLength(),
114+
PIN_PADDING_CHAR);
116115
auto tmp = hash;
117116
if (isECC) {
118117
constexpr size_t ECDSA384_INPUT_LENGTH = 384 / 8;

src/electronic-ids/pcsc/FinEID.cpp

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@
2222

2323
#include "FinEID.hpp"
2424

25+
#include "../TLV.hpp"
26+
2527
#include "pcsc-common.hpp"
2628

29+
#include <array>
30+
2731
// FINEID specification:
2832
// App 3.0:
2933
// https://dvv.fi/documents/16079645/17324923/S1v30.pdf/0bad6ff1-1617-1b1f-ab49-56a2f36ecd38/S1v30.pdf
@@ -44,15 +48,19 @@ namespace
4448
const auto SELECT_MAIN_AID = CommandApdu::select(
4549
0x04, {0xa0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4b, 0x43, 0x53, 0x2d, 0x31, 0x35});
4650
const auto SELECT_AUTH_CERT_FILE = CommandApdu::selectEF(0x08, {0x43, 0x31});
51+
const auto SELECT_AUTH_CERT_FILE_EST = CommandApdu::selectEF(0x08, {0xAD, 0xF1, 0x34, 0x11});
4752
const auto SELECT_SIGN_CERT_FILE_V3 = CommandApdu::selectEF(0x08, {0x50, 0x16, 0x43, 0x35});
4853
const auto SELECT_SIGN_CERT_FILE_V4 = CommandApdu::selectEF(0x08, {0x50, 0x16, 0x43, 0x32});
54+
const auto SELECT_SIGN_CERT_FILE_EST = CommandApdu::selectEF(0x08, {0xAD, 0xF2, 0x34, 0x21});
4955

5056
constexpr byte_type PIN_PADDING_CHAR = 0x00;
5157
constexpr byte_type AUTH_PIN_REFERENCE = 0x11;
58+
constexpr byte_type AUTH_PIN_REFERENCE_EST = 0x81;
5259
constexpr byte_type SIGNING_PIN_REFERENCE = 0x82;
5360
constexpr byte_type AUTH_KEY_REFERENCE = 0x01;
5461
constexpr byte_type SIGNING_KEY_REFERENCE_V3 = 0x03;
5562
constexpr byte_type SIGNING_KEY_REFERENCE_V4 = 0x02;
63+
constexpr byte_type SIGNING_KEY_REFERENCE_EST = 0x05;
5664
constexpr byte_type ECDSA_ALGO = 0x04;
5765
constexpr byte_type RSA_PSS_ALGO = 0x05;
5866

@@ -71,7 +79,7 @@ byte_vector FinEIDv3::getCertificateImpl(const CertificateType type) const
7179
byte_vector FinEIDv3::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const
7280
{
7381
return sign(authSignatureAlgorithm().hashAlgorithm(), hash, std::move(pin), AUTH_PIN_REFERENCE,
74-
authPinMinMaxLength(), AUTH_KEY_REFERENCE, RSA_PSS_ALGO, 0x00);
82+
authPinMinMaxLength(), AUTH_KEY_REFERENCE, RSA_PSS_ALGO);
7583
}
7684

7785
ElectronicID::PinRetriesRemainingAndMax FinEIDv3::authPinRetriesLeftImpl() const
@@ -88,7 +96,7 @@ ElectronicID::Signature FinEIDv3::signWithSigningKeyImpl(byte_vector&& pin, cons
8896
const HashAlgorithm hashAlgo) const
8997
{
9098
return {sign(hashAlgo, hash, std::move(pin), SIGNING_PIN_REFERENCE, signingPinMinMaxLength(),
91-
SIGNING_KEY_REFERENCE_V3, ECDSA_ALGO, 0x40),
99+
SIGNING_KEY_REFERENCE_V3, ECDSA_ALGO),
92100
{SignatureAlgorithm::ES, hashAlgo}};
93101
}
94102

@@ -99,7 +107,7 @@ ElectronicID::PinRetriesRemainingAndMax FinEIDv3::signingPinRetriesLeftImpl() co
99107

100108
byte_vector FinEIDv3::sign(const HashAlgorithm hashAlgo, const byte_vector& hash, byte_vector&& pin,
101109
byte_type pinReference, PinMinMaxLength pinMinMaxLength,
102-
byte_type keyReference, byte_type signatureAlgo, byte_type LE) const
110+
byte_type keyReference, byte_type signatureAlgo) const
103111
{
104112
if (signatureAlgo != ECDSA_ALGO && hashAlgo.isSHA3()) {
105113
THROW(ArgumentFatalError, "No OID for algorithm " + std::string(hashAlgo));
@@ -127,8 +135,7 @@ byte_vector FinEIDv3::sign(const HashAlgorithm hashAlgo, const byte_vector& hash
127135
THROW(ArgumentFatalError, "No OID for algorithm " + std::string(hashAlgo));
128136
}
129137

130-
verifyPin(*card, pinReference, std::move(pin), pinMinMaxLength.first, pinMinMaxLength.second,
131-
PIN_PADDING_CHAR);
138+
verifyPin(*card, pinReference, std::move(pin), pinMinMaxLength, PIN_PADDING_CHAR);
132139
// Select security environment for COMPUTE SIGNATURE.
133140
selectSecurityEnv(*card, 0xB6, signatureAlgo, keyReference, name());
134141

@@ -146,8 +153,8 @@ byte_vector FinEIDv3::sign(const HashAlgorithm hashAlgo, const byte_vector& hash
146153
THROW(SmartCardError, "Command COMPUTE SIGNATURE failed with error " + response);
147154
}
148155

149-
const CommandApdu getSignature {0x00, 0x2A, 0x9E, 0x9A, LE};
150-
const auto signature = card->transmit(getSignature);
156+
const CommandApdu getSignature {0x00, 0x2A, 0x9E, 0x9A, 0x00};
157+
auto signature = card->transmit(getSignature);
151158

152159
if (signature.sw1 == ResponseApdu::WRONG_LENGTH) {
153160
THROW(SmartCardError, "Wrong data length in command GET SIGNATURE argument: " + response);
@@ -156,7 +163,7 @@ byte_vector FinEIDv3::sign(const HashAlgorithm hashAlgo, const byte_vector& hash
156163
THROW(SmartCardError, "Command GET SIGNATURE failed with error " + signature);
157164
}
158165

159-
return signature.data;
166+
return std::move(signature.data);
160167
}
161168

162169
ElectronicID::PinRetriesRemainingAndMax FinEIDv3::pinRetriesLeft(byte_type pinReference) const
@@ -168,12 +175,13 @@ ElectronicID::PinRetriesRemainingAndMax FinEIDv3::pinRetriesLeft(byte_type pinRe
168175
if (!response.isOK()) {
169176
THROW(SmartCardError, "Command GET DATA failed with error " + response);
170177
}
171-
if (response.data.size() < 21) {
172-
THROW(SmartCardError,
173-
"Command GET DATA failed: received data size " + std::to_string(response.data.size())
174-
+ " is less than the expected size of the PIN remaining retries offset 21");
178+
if (TLV tlv(response.data); tlv.tag == 0xA0) {
179+
if (TLV info = tlv[0xdf21]) {
180+
return {*info.begin, maximumPinRetries()};
181+
}
175182
}
176-
return {uint8_t(response.data[20]), int8_t(5)};
183+
THROW(SmartCardError,
184+
"Command GET DATA failed: received data does not contain the PIN remaining retries info");
177185
}
178186

179187
byte_vector FinEIDv4::getCertificateImpl(const CertificateType type) const
@@ -186,14 +194,41 @@ byte_vector FinEIDv4::getCertificateImpl(const CertificateType type) const
186194
byte_vector FinEIDv4::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const
187195
{
188196
return sign(authSignatureAlgorithm().hashAlgorithm(), hash, std::move(pin), AUTH_PIN_REFERENCE,
189-
authPinMinMaxLength(), AUTH_KEY_REFERENCE, ECDSA_ALGO, 0x60);
197+
authPinMinMaxLength(), AUTH_KEY_REFERENCE, ECDSA_ALGO);
190198
}
191199

192200
ElectronicID::Signature FinEIDv4::signWithSigningKeyImpl(byte_vector&& pin, const byte_vector& hash,
193201
const HashAlgorithm hashAlgo) const
194202
{
195203
return {sign(hashAlgo, hash, std::move(pin), SIGNING_PIN_REFERENCE, signingPinMinMaxLength(),
196-
SIGNING_KEY_REFERENCE_V4, ECDSA_ALGO, 0x60),
204+
SIGNING_KEY_REFERENCE_V4, ECDSA_ALGO),
205+
{SignatureAlgorithm::ES, hashAlgo}};
206+
}
207+
208+
byte_vector EstEIDTHALES::getCertificateImpl(const CertificateType type) const
209+
{
210+
transmitApduWithExpectedResponse(*card, SELECT_MAIN_AID);
211+
return readFile(
212+
*card, type.isAuthentication() ? SELECT_AUTH_CERT_FILE_EST : SELECT_SIGN_CERT_FILE_EST);
213+
}
214+
215+
byte_vector EstEIDTHALES::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const
216+
{
217+
return sign(authSignatureAlgorithm().hashAlgorithm(), hash, std::move(pin),
218+
AUTH_PIN_REFERENCE_EST, authPinMinMaxLength(), AUTH_KEY_REFERENCE, ECDSA_ALGO);
219+
}
220+
221+
ElectronicID::PinRetriesRemainingAndMax EstEIDTHALES::authPinRetriesLeftImpl() const
222+
{
223+
return pinRetriesLeft(AUTH_PIN_REFERENCE_EST);
224+
}
225+
226+
ElectronicID::Signature EstEIDTHALES::signWithSigningKeyImpl(byte_vector&& pin,
227+
const byte_vector& hash,
228+
const HashAlgorithm hashAlgo) const
229+
{
230+
return {sign(hashAlgo, hash, std::move(pin), SIGNING_PIN_REFERENCE, signingPinMinMaxLength(),
231+
SIGNING_KEY_REFERENCE_EST, ECDSA_ALGO),
197232
{SignatureAlgorithm::ES, hashAlgo}};
198233
}
199234

src/electronic-ids/pcsc/FinEID.hpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace electronic_id
3030
class FinEIDv3 : public PcscElectronicID
3131
{
3232
public:
33-
FinEIDv3(pcsc_cpp::SmartCard::ptr _card) : PcscElectronicID(std::move(_card)) {}
33+
using PcscElectronicID::PcscElectronicID;
3434

3535
protected:
3636
byte_vector getCertificateImpl(const CertificateType type) const override;
@@ -56,17 +56,18 @@ class FinEIDv3 : public PcscElectronicID
5656

5757
byte_vector sign(const HashAlgorithm hashAlgo, const byte_vector& hash, byte_vector&& pin,
5858
byte_type pinReference, PinMinMaxLength pinMinMaxLength,
59-
byte_type keyReference, byte_type signatureAlgo, byte_type LE) const;
59+
byte_type keyReference, byte_type signatureAlgo) const;
6060

61+
virtual int8_t maximumPinRetries() const { return 5; }
6162
PinRetriesRemainingAndMax pinRetriesLeft(byte_type pinReference) const;
6263
};
6364

6465
class FinEIDv4 : public FinEIDv3
6566
{
6667
public:
67-
FinEIDv4(pcsc_cpp::SmartCard::ptr _card) : FinEIDv3(std::move(_card)) {}
68+
using FinEIDv3::FinEIDv3;
6869

69-
private:
70+
protected:
7071
JsonWebSignatureAlgorithm authSignatureAlgorithm() const override
7172
{
7273
return JsonWebSignatureAlgorithm::ES384;
@@ -82,4 +83,27 @@ class FinEIDv4 : public FinEIDv3
8283
const HashAlgorithm hashAlgo) const override;
8384
};
8485

86+
class EstEIDTHALES : public FinEIDv4
87+
{
88+
public:
89+
using FinEIDv4::FinEIDv4;
90+
91+
protected:
92+
byte_vector getCertificateImpl(const CertificateType type) const override;
93+
94+
PinRetriesRemainingAndMax authPinRetriesLeftImpl() const override;
95+
96+
PinMinMaxLength signingPinMinMaxLength() const override { return {5, 12}; }
97+
98+
std::string name() const override { return "EstEIDTHALES"; }
99+
Type type() const override { return EstEID; }
100+
101+
byte_vector signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const override;
102+
103+
Signature signWithSigningKeyImpl(byte_vector&& pin, const byte_vector& hash,
104+
const HashAlgorithm hashAlgo) const override;
105+
106+
int8_t maximumPinRetries() const override { return 3; }
107+
};
108+
85109
} // namespace electronic_id

0 commit comments

Comments
 (0)