@@ -41,8 +41,6 @@ using namespace std::string_literals;
4141namespace
4242{
4343
44- using ElectronicIDConstructor = std::function<ElectronicID::ptr(const Reader&)>;
45-
4644template <typename T>
4745constexpr auto constructor (const Reader& reader)
4846{
@@ -94,9 +92,6 @@ const std::map<byte_vector, ElectronicIDConstructor> SUPPORTED_ATRS {
9492 0x04 , 0x44 , 0xec , 0xc1 , 0x73 , 0x94 , 0x01 , 0x80 , 0x82 , 0x90 , 0x00 , 0x12 },
9593 constructor<ElectronicID::Type::HrvEID>},
9694 // BelEID
97- {{0x3b , 0x98 , 0x13 , 0x40 , 0x0a , 0xa5 , 0x03 , 0x01 , 0x01 , 0x01 , 0xad , 0x13 , 0x11 },
98- constructor<ElectronicID::Type::BelEID>},
99- // BelEID
10095 {{0x3B , 0x98 , 0x94 , 0x40 , 0x0A , 0xA5 , 0x03 , 0x01 , 0x01 , 0x01 , 0xAD , 0x13 , 0x10 },
10196 constructor<ElectronicID::Type::BelEID>},
10297 // BelEID
@@ -112,7 +107,22 @@ const std::map<byte_vector, ElectronicIDConstructor> SUPPORTED_ATRS {
112107 constructor<ElectronicID::Type::CzeEID>},
113108};
114109
115- inline std::string byteVectorToHexString (const byte_vector& bytes)
110+ // Holds ATR pattern, mask, and constructor for variable ATR cards.
111+ struct MaskedATREntry
112+ {
113+ byte_vector pattern;
114+ byte_vector mask;
115+ ElectronicIDConstructor constructor;
116+ };
117+
118+ const std::vector<MaskedATREntry> MASKED_ATRS = {
119+ // BelEID v1.7
120+ {{0x3b , 0x98 , 0x13 , 0x40 , 0x0a , 0xa5 , 0x03 , 0x01 , 0x01 , 0x01 , 0xad , 0x13 , 0x11 },
121+ {0xff , 0xff , 0x00 , 0xff , 0x00 , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0x00 },
122+ constructor<ElectronicID::Type::BelEID>},
123+ };
124+
125+ std::string byteVectorToHexString (const byte_vector& bytes)
116126{
117127 std::ostringstream hexStringBuilder;
118128
@@ -125,6 +135,26 @@ inline std::string byteVectorToHexString(const byte_vector& bytes)
125135 return hexStringBuilder.str ();
126136}
127137
138+ bool matchATRWithMask (const byte_vector& atr, const byte_vector& pattern, const byte_vector& mask)
139+ {
140+ if (pattern.size () != mask.size ()) {
141+ THROW (ProgrammingError,
142+ " MaskedATREntry '" + byteVectorToHexString (pattern)
143+ + " ' pattern size does not match mask size" );
144+ }
145+
146+ if (atr.size () != pattern.size ()) {
147+ return false ;
148+ }
149+ for (size_t i = 0 ; i < atr.size (); ++i) {
150+ if ((atr[i] & mask[i]) != (pattern[i] & mask[i])) {
151+ return false ;
152+ }
153+ }
154+
155+ return true ;
156+ }
157+
128158const auto SUPPORTED_ALGORITHMS = std::map<std::string, HashAlgorithm> {
129159 {" SHA-224" s, HashAlgorithm::SHA224}, {" SHA-256" s, HashAlgorithm::SHA256},
130160 {" SHA-384" s, HashAlgorithm::SHA384}, {" SHA-512" s, HashAlgorithm::SHA512},
@@ -137,22 +167,41 @@ const auto SUPPORTED_ALGORITHMS = std::map<std::string, HashAlgorithm> {
137167namespace electronic_id
138168{
139169
170+ std::optional<ElectronicIDConstructor> findMaskedATR (const byte_vector& atr)
171+ {
172+ for (const auto & entry : MASKED_ATRS) {
173+ if (matchATRWithMask (atr, entry.pattern , entry.mask )) {
174+ return entry.constructor ;
175+ }
176+ }
177+ return std::nullopt ;
178+ }
179+
140180bool isCardSupported (const pcsc_cpp::byte_vector& atr)
141181{
142- return SUPPORTED_ATRS.count (atr);
182+ if (SUPPORTED_ATRS.count (atr)) {
183+ return true ;
184+ }
185+
186+ // If exact ATR match is not found, fall back to masked ATR lookup.
187+ return findMaskedATR (atr).has_value ();
143188}
144189
145190ElectronicID::ptr getElectronicID (const pcsc_cpp::Reader& reader)
146191{
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" );
192+ if (auto it = SUPPORTED_ATRS.find (reader.cardAtr ); it != SUPPORTED_ATRS.end ()) {
193+ return it->second (reader);
155194 }
195+
196+ // If exact ATR match is not found, fall back to masked ATR lookup.
197+ if (auto eIDConstructor = findMaskedATR (reader.cardAtr )) {
198+ return (*eIDConstructor)(reader);
199+ }
200+
201+ // It should be verified that the card is supported with isCardSupported() before
202+ // calling getElectronicID(), so it is a programming error to reach this point.
203+ THROW (ProgrammingError,
204+ " Card with ATR '" + byteVectorToHexString (reader.cardAtr ) + " ' is not supported" );
156205}
157206
158207bool ElectronicID::isSupportedSigningHashAlgorithm (const HashAlgorithm hashAlgo) const
0 commit comments