@@ -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+
115150inline std::string byteVectorToHexString (const byte_vector& bytes)
116151{
117152 std::ostringstream hexStringBuilder;
@@ -139,20 +174,29 @@ namespace electronic_id
139174
140175bool 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
145185ElectronicID::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
158202bool ElectronicID::isSupportedSigningHashAlgorithm (const HashAlgorithm hashAlgo) const
0 commit comments