Skip to content

Commit f9b4c22

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

File tree

1 file changed

+56
-12
lines changed

1 file changed

+56
-12
lines changed

src/electronic-id.cpp

Lines changed: 56 additions & 12 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,6 +109,44 @@ const std::map<byte_vector, ElectronicIDConstructor> SUPPORTED_ATRS {
112109
constructor<ElectronicID::Type::CzeEID>},
113110
};
114111

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+
bool matchATRWithMask(const byte_vector& atr, const byte_vector& pattern, const byte_vector& mask)
128+
{
129+
if (atr.size() != pattern.size() || pattern.size() != mask.size()) {
130+
return false;
131+
}
132+
for (size_t i = 0; i < atr.size(); ++i) {
133+
if ((atr[i] & mask[i]) != (pattern[i] & mask[i])) {
134+
return false;
135+
}
136+
}
137+
return true;
138+
}
139+
140+
std::optional<ElectronicIDConstructor> findMaskedATR(const byte_vector& atr)
141+
{
142+
for (const auto& entry : MASKED_ATRS) {
143+
if (matchATRWithMask(atr, entry.pattern, entry.mask)) {
144+
return entry.constructor;
145+
}
146+
}
147+
return std::nullopt;
148+
}
149+
115150
inline std::string byteVectorToHexString(const byte_vector& bytes)
116151
{
117152
std::ostringstream hexStringBuilder;
@@ -139,20 +174,29 @@ namespace electronic_id
139174

140175
bool isCardSupported(const pcsc_cpp::byte_vector& atr)
141176
{
142-
return SUPPORTED_ATRS.count(atr);
177+
if (SUPPORTED_ATRS.count(atr)) {
178+
return true;
179+
}
180+
181+
// If exact ATR match is not found, fall back to masked ATR lookup.
182+
return findMaskedATR(atr).has_value();
143183
}
144184

145185
ElectronicID::ptr getElectronicID(const pcsc_cpp::Reader& reader)
146186
{
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");
187+
if (auto it = SUPPORTED_ATRS.find(reader.cardAtr); it != SUPPORTED_ATRS.end()) {
188+
return it->second(reader);
155189
}
190+
191+
// If exact ATR match is not found, fall back to masked ATR lookup.
192+
if (auto eIDConstructor = findMaskedATR(reader.cardAtr)) {
193+
return (*eIDConstructor)(reader);
194+
}
195+
196+
// It should be verified that the card is supported with isCardSupported() before
197+
// calling getElectronicID(), so it is a programming error to reach this point.
198+
THROW(ProgrammingError,
199+
"Card with ATR '" + byteVectorToHexString(reader.cardAtr) + "' is not supported");
156200
}
157201

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

0 commit comments

Comments
 (0)