Skip to content

Commit 53cb883

Browse files
committed
Read binary and add readFile
Signed-off-by: Raul Metsma <[email protected]>
1 parent f85a892 commit 53cb883

File tree

10 files changed

+564
-643
lines changed

10 files changed

+564
-643
lines changed

lib/libpcsc-cpp/include/pcsc-cpp/pcsc-cpp.hpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ struct CommandApdu
182182
constexpr operator const byte_vector&() const { return d; }
183183

184184
/**
185-
* A helper function to create a SELECT command APDU.
185+
* A helper function to create a SELECT FILE command APDU.
186186
*
187-
* The ISO 7816-4 Section 6.11 SELECT command has the form:
187+
* The ISO 7816-4 Section 6.11 SELECT FILE command has the form:
188188
* CLA = 0x00
189189
* INS = 0xA4
190190
* P1 = varies, see below.
@@ -193,6 +193,7 @@ struct CommandApdu
193193
*
194194
* The P1 parameter for the SELECT command controls the selection mode,
195195
* we use the following modes:
196+
* 0x02 = Select EF under current DF,
196197
* 0x04 = Select AID (application identifier),
197198
* direct selection by DF (dedicated file, directory) name.
198199
* 0x08 = Select from MF (master file, root directory).
@@ -203,6 +204,27 @@ struct CommandApdu
203204
return {0x00, 0xA4, p1, 0x0C, std::move(file)};
204205
}
205206

207+
static PCSC_CPP_CONSTEXPR_VECTOR CommandApdu selectEF(byte_type p1, byte_vector file)
208+
{
209+
return {0x00, 0xA4, p1, 0x04, std::move(file), 0x00};
210+
}
211+
212+
/**
213+
* A helper function to create a READ BINARY command APDU.
214+
*
215+
* The ISO 7816-4 Section 6.1 READ BINARY command has the form:
216+
* CLA = 0x00
217+
* INS = 0xB0
218+
* P1, P2 = if bit8=0 in P1, then P1||P2 is the offset of the first byte to be read in data units from the
219+
* beginning of the file.
220+
* Lc and Data field = Empty
221+
* Le = Number of bytes to be read
222+
*/
223+
static PCSC_CPP_CONSTEXPR_VECTOR CommandApdu readBinary(uint16_t pos, byte_type le)
224+
{
225+
return {0x00, 0xb0, byte_type(pos >> 8), byte_type(pos), le};
226+
}
227+
206228
byte_vector d;
207229
};
208230

@@ -276,11 +298,8 @@ std::vector<Reader> listReaders();
276298
/** Transmit APDU command and verify that expected response is received. */
277299
void transmitApduWithExpectedResponse(const SmartCard& card, const CommandApdu& command);
278300

279-
/** Read data length from currently selected file header, file must be ASN.1-encoded. */
280-
size_t readDataLengthFromAsn1(const SmartCard& card);
281-
282301
/** Read lenght bytes from currently selected binary file in blockLength-sized chunks. */
283-
byte_vector readBinary(const SmartCard& card, const size_t length, byte_type blockLength);
302+
byte_vector readBinary(const SmartCard& card, const uint16_t length, byte_type blockLength = 0x00);
284303

285304
// Errors.
286305

lib/libpcsc-cpp/src/utils.cpp

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -23,34 +23,16 @@
2323
#include "pcsc-cpp/pcsc-cpp.hpp"
2424
#include "pcsc-cpp/pcsc-cpp-utils.hpp"
2525

26-
#include <sstream>
26+
#include <algorithm>
2727
#include <iomanip>
28+
#include <sstream>
2829

2930
using namespace pcsc_cpp;
3031
using namespace std::string_literals;
3132

32-
#ifdef HIBYTE
33-
#undef HIBYTE
34-
#endif
35-
#ifdef LOBYTE
36-
#undef LOBYTE
37-
#endif
38-
39-
constexpr byte_type HIBYTE(size_t w) noexcept
40-
{
41-
return static_cast<byte_type>((w >> 8) & 0xff);
42-
}
43-
constexpr byte_type LOBYTE(size_t w) noexcept
44-
{
45-
return static_cast<byte_type>(w & 0xff);
46-
}
47-
4833
namespace
4934
{
5035

51-
const byte_type DER_SEQUENCE_TYPE_TAG = 0x30;
52-
const byte_type DER_TWO_BYTE_LENGTH = 0x82;
53-
5436
class UnexpectedResponseError : public Error
5537
{
5638
public:
@@ -93,68 +75,25 @@ void transmitApduWithExpectedResponse(const SmartCard& card, const CommandApdu&
9375
}
9476
}
9577

96-
size_t readDataLengthFromAsn1(const SmartCard& card)
97-
{
98-
// p1 - offset size first byte, 0
99-
// p2 - offset size second byte, 0
100-
// le - number of bytes to read, need 4 bytes from start for length
101-
const CommandApdu readBinary4Bytes {0x00, 0xb0, 0x00, 0x00, 0x04};
102-
103-
auto response = card.transmit(readBinary4Bytes);
104-
105-
// Verify expected DER header, first byte must be SEQUENCE.
106-
if (response.data[0] != DER_SEQUENCE_TYPE_TAG) {
107-
// TODO: more specific exception
108-
THROW(Error,
109-
"readDataLengthFromAsn1(): First byte must be SEQUENCE (0x30), but is "s
110-
+ int2hexstr(response.data[0]));
111-
}
112-
113-
// TODO: support other lenghts besides 2.
114-
// Assume 2-byte length, so second byte must be 0x82.
115-
if (response.data[1] != DER_TWO_BYTE_LENGTH) {
116-
// TODO: more specific exception
117-
THROW(Error,
118-
"readDataLengthFromAsn1(): Second byte must be two-byte length indicator "s
119-
"(0x82), but is "s
120-
+ int2hexstr(response.data[1]));
121-
}
122-
123-
// Read 2-byte length field at offset 2 and 3 and add the 4 DER length bytes.
124-
const auto length = size_t((response.data[2] << 8) + response.data[3] + 4);
125-
if (length < 128 || length > 0x0f00) {
126-
// TODO: more specific exception
127-
THROW(Error,
128-
"readDataLengthFromAsn1(): Unexpected data length in DER header: "s
129-
+ std::to_string(length));
130-
}
131-
132-
return length;
133-
}
134-
135-
byte_vector readBinary(const SmartCard& card, const size_t length, byte_type blockLength)
78+
byte_vector readBinary(const SmartCard& card, const uint16_t length, byte_type blockLength)
13679
{
137-
auto lengthCounter = length;
13880
auto resultBytes = byte_vector {};
139-
140-
for (size_t offset = 0; lengthCounter != 0;
141-
offset += blockLength, lengthCounter -= blockLength) {
142-
143-
if (blockLength > lengthCounter) {
144-
blockLength = byte_type(lengthCounter);
81+
while (resultBytes.size() < length) {
82+
byte_type chunck = byte_type(std::min<size_t>(length - resultBytes.size(), blockLength));
83+
auto response =
84+
card.transmit(CommandApdu::readBinary(uint16_t(resultBytes.size()), chunck));
85+
if (chunck > 0 && response.data.size() != chunck) {
86+
THROW(Error,
87+
"readBinary(): Invalid length received: "s + std::to_string(response.data.size())
88+
+ " excpected: " + std::to_string(chunck));
14589
}
146-
147-
CommandApdu readBinary {0x00, 0xb0, HIBYTE(offset), LOBYTE(offset), blockLength};
148-
auto response = card.transmit(readBinary);
149-
15090
resultBytes.insert(resultBytes.end(), response.data.cbegin(), response.data.cend());
15191
}
152-
15392
if (resultBytes.size() != length) {
154-
// TODO: more specific exception
155-
THROW(Error, "readBinary(): Invalid length: "s + std::to_string(resultBytes.size()));
93+
THROW(Error,
94+
"readBinary(): Invalid length received: "s + std::to_string(resultBytes.size())
95+
+ " excpected: " + std::to_string(length));
15696
}
157-
15897
return resultBytes;
15998
}
16099

src/electronic-ids/pcsc/EIDIDEMIA.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ const auto ADF1_AID = CommandApdu::select(
4646
const auto ADF2_AID = CommandApdu::select(0x04,
4747
{0x51, 0x53, 0x43, 0x44, 0x20, 0x41, 0x70, 0x70, 0x6C,
4848
0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E});
49-
const auto AUTH_CERT = CommandApdu::select(0x09, {0xAD, 0xF1, 0x34, 0x01});
50-
const auto SIGN_CERT = CommandApdu::select(0x09, {0xAD, 0xF2, 0x34, 0x1F});
49+
const auto AUTH_CERT = CommandApdu::selectEF(0x09, {0xAD, 0xF1, 0x34, 0x01});
50+
const auto SIGN_CERT = CommandApdu::selectEF(0x09, {0xAD, 0xF2, 0x34, 0x1F});
5151

5252
} // namespace
5353

@@ -69,7 +69,7 @@ void EIDIDEMIA::selectADF2() const
6969
byte_vector EIDIDEMIA::getCertificateImpl(const CertificateType type) const
7070
{
7171
selectMain();
72-
return electronic_id::getCertificate(*card, type.isAuthentication() ? AUTH_CERT : SIGN_CERT);
72+
return readFile(*card, type.isAuthentication() ? AUTH_CERT : SIGN_CERT, 0xC0);
7373
}
7474

7575
EIDIDEMIA::KeyInfo EIDIDEMIA::authKeyRef() const

src/electronic-ids/pcsc/FinEID.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@ namespace
4343

4444
const auto SELECT_MAIN_AID = CommandApdu::select(
4545
0x04, {0xa0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4b, 0x43, 0x53, 0x2d, 0x31, 0x35});
46-
const auto SELECT_AUTH_CERT_FILE = CommandApdu::select(0x08, {0x43, 0x31});
47-
const auto SELECT_SIGN_CERT_FILE_V3 = CommandApdu::select(0x08, {0x50, 0x16, 0x43, 0x35});
48-
const auto SELECT_SIGN_CERT_FILE_V4 = CommandApdu::select(0x08, {0x50, 0x16, 0x43, 0x32});
46+
const auto SELECT_AUTH_CERT_FILE = CommandApdu::selectEF(0x08, {0x43, 0x31});
47+
const auto SELECT_SIGN_CERT_FILE_V3 = CommandApdu::selectEF(0x08, {0x50, 0x16, 0x43, 0x35});
48+
const auto SELECT_SIGN_CERT_FILE_V4 = CommandApdu::selectEF(0x08, {0x50, 0x16, 0x43, 0x32});
4949

5050
constexpr byte_type PIN_PADDING_CHAR = 0x00;
5151
constexpr byte_type AUTH_PIN_REFERENCE = 0x11;
@@ -64,8 +64,8 @@ namespace electronic_id
6464
byte_vector FinEIDv3::getCertificateImpl(const CertificateType type) const
6565
{
6666
transmitApduWithExpectedResponse(*card, SELECT_MAIN_AID);
67-
return electronic_id::getCertificate(
68-
*card, type.isAuthentication() ? SELECT_AUTH_CERT_FILE : SELECT_SIGN_CERT_FILE_V3);
67+
return readFile(*card,
68+
type.isAuthentication() ? SELECT_AUTH_CERT_FILE : SELECT_SIGN_CERT_FILE_V3);
6969
}
7070

7171
byte_vector FinEIDv3::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const
@@ -179,8 +179,8 @@ ElectronicID::PinRetriesRemainingAndMax FinEIDv3::pinRetriesLeft(byte_type pinRe
179179
byte_vector FinEIDv4::getCertificateImpl(const CertificateType type) const
180180
{
181181
transmitApduWithExpectedResponse(*card, SELECT_MAIN_AID);
182-
return electronic_id::getCertificate(
183-
*card, type.isAuthentication() ? SELECT_AUTH_CERT_FILE : SELECT_SIGN_CERT_FILE_V4);
182+
return readFile(*card,
183+
type.isAuthentication() ? SELECT_AUTH_CERT_FILE : SELECT_SIGN_CERT_FILE_V4);
184184
}
185185

186186
byte_vector FinEIDv4::signWithAuthKeyImpl(byte_vector&& pin, const byte_vector& hash) const

src/electronic-ids/pcsc/LatEIDIDEMIAv2.cpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ byte_vector LatEIDIDEMIAV2::getCertificateImpl(const CertificateType type) const
6363
auto info =
6464
readDCODInfo(CERT_FILE_REF, type.isAuthentication() ? data->authCache : data->signCache);
6565
if (TLV id = TLV::path(info, 0x30, 0xA1, 0x30, 0x30, 0x04)) {
66-
return electronic_id::getCertificate(*card, CommandApdu::select(0x02, {id.begin, id.end}));
66+
return readFile(*card, CommandApdu::selectEF(0x02, {id.begin, id.end}), 0xC0);
6767
}
6868
THROW(SmartCardError, "EF.CD reference not found");
6969
}
@@ -114,24 +114,20 @@ const byte_vector& LatEIDIDEMIAV2::readEF_File(byte_vector file, C& cache) const
114114
if (auto it = cache.find(file); it != cache.end()) {
115115
return it->second;
116116
}
117-
auto response = card->transmit({0x00, 0xA4, 0x02, 0x04, file, 0x00});
118-
if (!response.isOK()) {
119-
THROW(SmartCardError, "Failed to read EF file");
120-
}
121-
TLV size = TLV::path(response.data, 0x62, 0x80);
122-
if (!size || size.length != 2) {
123-
THROW(SmartCardError, "Failed to read EF file length");
124-
}
125-
return cache[std::move(file)] =
126-
readBinary(*card, size_t(*size.begin << 8) + *(size.begin + 1), 0xFF);
117+
return cache[std::move(file)] = readFile(*card, CommandApdu::selectEF(0x02, file));
127118
}
128119

129120
template <class C>
130121
const byte_vector& LatEIDIDEMIAV2::readDCODInfo(byte_type type, C& cache) const
131122
{
132123
const auto info = readEF_File(EF_OD, cache);
133-
if (auto file = TLV::path(info, type, 0x30, 0x04); file && file.length == 2) {
134-
return readEF_File({file.begin, file.end}, cache);
124+
for (TLV ref(info); ref; ++ref) {
125+
if (ref.tag != type) {
126+
continue;
127+
}
128+
if (auto file = ref[0x30][0x04]; file && file.length == 2) {
129+
return readEF_File({file.begin, file.end}, cache);
130+
}
135131
}
136132
THROW(SmartCardError, "EF.DCOD reference not found");
137133
}
@@ -144,7 +140,7 @@ EIDIDEMIA::KeyInfo LatEIDIDEMIAV2::readPrKDInfo(byte_type keyID, C& cache) const
144140
THROW(SmartCardError, "EF.PrKD reference not found");
145141
}
146142
TLV prKD(info);
147-
TLV key = TLV::path(prKD.child(), 0x30);
143+
TLV key = prKD[0x30];
148144
key = TLV::path(++key, 0x30, 0x02);
149145
return {key.length == 2 ? *std::next(key.begin) : keyID, prKD.tag == 0xA0};
150146
}

0 commit comments

Comments
 (0)