@@ -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+
128170const 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
140182bool 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
145192ElectronicID::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
158209bool ElectronicID::isSupportedSigningHashAlgorithm (const HashAlgorithm hashAlgo) const
0 commit comments