Skip to content

Commit dfae43b

Browse files
committed
Add support for masked ATRs to support BelEID v1_7
WE2-1040 Signed-off-by: Mart Somermaa <[email protected]>
1 parent 7991e0e commit dfae43b

File tree

1 file changed

+64
-13
lines changed

1 file changed

+64
-13
lines changed

src/electronic-id.cpp

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,6 @@ const std::map<byte_vector, ElectronicIDConstructor> SUPPORTED_ATRS {
9494
0x04, 0x44, 0xec, 0xc1, 0x73, 0x94, 0x01, 0x80, 0x82, 0x90, 0x00, 0x12},
9595
constructor<ElectronicID::Type::HrvEID>},
9696
// BelEID
97-
{{0x3b, 0x98, 0x13, 0x40, 0x0a, 0xa5, 0x03, 0x01, 0x01, 0x01, 0xad, 0x13, 0x11},
98-
constructor<ElectronicID::Type::BelEID>},
99-
// BelEID
10097
{{0x3B, 0x98, 0x94, 0x40, 0x0A, 0xA5, 0x03, 0x01, 0x01, 0x01, 0xAD, 0x13, 0x10},
10198
constructor<ElectronicID::Type::BelEID>},
10299
// BelEID
@@ -112,7 +109,22 @@ const std::map<byte_vector, ElectronicIDConstructor> SUPPORTED_ATRS {
112109
constructor<ElectronicID::Type::CzeEID>},
113110
};
114111

115-
inline std::string byteVectorToHexString(const byte_vector& bytes)
112+
// Holds ATR pattern, mask, and constructor for variable ATR cards.
113+
struct MaskedATREntry
114+
{
115+
byte_vector pattern;
116+
byte_vector mask;
117+
ElectronicIDConstructor constructor;
118+
};
119+
120+
const std::vector<MaskedATREntry> MASKED_ATRS = {
121+
// BelEID v1.7
122+
{{0x3b, 0x98, 0x13, 0x40, 0x0a, 0xa5, 0x03, 0x01, 0x01, 0x01, 0xad, 0x13, 0x11},
123+
{0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00},
124+
constructor<ElectronicID::Type::BelEID>},
125+
};
126+
127+
std::string byteVectorToHexString(const byte_vector& bytes)
116128
{
117129
std::ostringstream hexStringBuilder;
118130

@@ -125,6 +137,36 @@ inline std::string byteVectorToHexString(const byte_vector& bytes)
125137
return hexStringBuilder.str();
126138
}
127139

140+
bool matchATRWithMask(const byte_vector& atr, const byte_vector& pattern, const byte_vector& mask)
141+
{
142+
if (pattern.size() != mask.size()) {
143+
THROW(ProgrammingError,
144+
"MaskedATREntry '" + byteVectorToHexString(pattern)
145+
+ "' pattern size does not match mask size");
146+
}
147+
148+
if (atr.size() != pattern.size()) {
149+
return false;
150+
}
151+
for (size_t i = 0; i < atr.size(); ++i) {
152+
if ((atr[i] & mask[i]) != (pattern[i] & mask[i])) {
153+
return false;
154+
}
155+
}
156+
157+
return true;
158+
}
159+
160+
std::optional<ElectronicIDConstructor> findMaskedATR(const byte_vector& atr)
161+
{
162+
for (const auto& entry : MASKED_ATRS) {
163+
if (matchATRWithMask(atr, entry.pattern, entry.mask)) {
164+
return entry.constructor;
165+
}
166+
}
167+
return std::nullopt;
168+
}
169+
128170
const auto SUPPORTED_ALGORITHMS = std::map<std::string, HashAlgorithm> {
129171
{"SHA-224"s, HashAlgorithm::SHA224}, {"SHA-256"s, HashAlgorithm::SHA256},
130172
{"SHA-384"s, HashAlgorithm::SHA384}, {"SHA-512"s, HashAlgorithm::SHA512},
@@ -139,20 +181,29 @@ namespace electronic_id
139181

140182
bool isCardSupported(const pcsc_cpp::byte_vector& atr)
141183
{
142-
return SUPPORTED_ATRS.count(atr);
184+
if (SUPPORTED_ATRS.count(atr)) {
185+
return true;
186+
}
187+
188+
// If exact ATR match is not found, fall back to masked ATR lookup.
189+
return findMaskedATR(atr).has_value();
143190
}
144191

145192
ElectronicID::ptr getElectronicID(const pcsc_cpp::Reader& reader)
146193
{
147-
try {
148-
const auto& eidConstructor = SUPPORTED_ATRS.at(reader.cardAtr);
149-
return eidConstructor(reader);
150-
} catch (const std::out_of_range&) {
151-
// It should be verified that the card is supported with isCardSupported() before
152-
// calling getElectronicID(), so it is a programming error if out_of_range occurs here.
153-
THROW(ProgrammingError,
154-
"Card with ATR '" + byteVectorToHexString(reader.cardAtr) + "' is not supported");
194+
if (auto it = SUPPORTED_ATRS.find(reader.cardAtr); it != SUPPORTED_ATRS.end()) {
195+
return it->second(reader);
155196
}
197+
198+
// If exact ATR match is not found, fall back to masked ATR lookup.
199+
if (auto eIDConstructor = findMaskedATR(reader.cardAtr)) {
200+
return (*eIDConstructor)(reader);
201+
}
202+
203+
// It should be verified that the card is supported with isCardSupported() before
204+
// calling getElectronicID(), so it is a programming error to reach this point.
205+
THROW(ProgrammingError,
206+
"Card with ATR '" + byteVectorToHexString(reader.cardAtr) + "' is not supported");
156207
}
157208

158209
bool ElectronicID::isSupportedSigningHashAlgorithm(const HashAlgorithm hashAlgo) const

0 commit comments

Comments
 (0)