16
16
* You should have received a copy of the GNU General Public License
17
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18
18
*/
19
+ #include "core/core_defines.h"
19
20
#include "core/log.h"
21
+ #include "core/string.h"
20
22
#include "nfc_supported_card_plugin.h"
21
23
22
24
#include "protocols/mf_classic/mf_classic.h"
@@ -36,7 +38,7 @@ typedef struct {
36
38
uint64_t b ;
37
39
} MfClassicKeyPair ;
38
40
39
- static const MfClassicKeyPair kazan_1k_keys [] = {
41
+ static const MfClassicKeyPair kazan_1k_keys_standart [] = {
40
42
{.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
41
43
{.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
42
44
{.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
@@ -55,22 +57,51 @@ static const MfClassicKeyPair kazan_1k_keys[] = {
55
57
{.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
56
58
};
57
59
60
+ static const MfClassicKeyPair kazan_1k_keys_old [] = {
61
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
62
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
63
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
64
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
65
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
66
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
67
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
68
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
69
+ {.a = 0x2058EAEE8446 , .b = 0xCB9B23815F87 },
70
+ {.a = 0x492F3744A1DC , .b = 0x6B770AADA274 },
71
+ {.a = 0xF7A545095C49 , .b = 0x6862FD600F78 },
72
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
73
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
74
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
75
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
76
+ {.a = 0xFFFFFFFFFFFF , .b = 0xFFFFFFFFFFFF },
77
+ };
78
+
58
79
enum SubscriptionType {
59
80
SUBSCRIPTION_TYPE_UNKNOWN ,
60
81
SUBSCRIPTION_TYPE_PURSE ,
61
- SUBSCRIPTION_TYPE_ABONNEMENT ,
82
+ SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME ,
83
+ SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS ,
62
84
};
63
85
64
- enum SubscriptionType get_subscription_type (uint8_t value ) {
86
+ enum SubscriptionType get_subscription_type (uint8_t value , FuriString * tariff_name ) {
65
87
switch (value ) {
66
- case 0 :
67
- case 0x60 :
88
+ case 0x51 :
89
+ furi_string_printf (tariff_name , "Social. Adult" );
90
+ return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME ;
68
91
case 0x67 :
92
+ furi_string_printf (tariff_name , "Ground electric transport. 1 month" );
93
+ return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME ;
69
94
case 0x0F :
70
- return SUBSCRIPTION_TYPE_ABONNEMENT ;
95
+ furi_string_printf (tariff_name , "Underground only" );
96
+ return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS ;
97
+ case 0x6D :
98
+ furi_string_printf (tariff_name , "Tram. 60 minutes. Transfer. 10 trips" );
99
+ return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS ;
71
100
case 0x53 :
101
+ furi_string_printf (tariff_name , "Standart purse" );
72
102
return SUBSCRIPTION_TYPE_PURSE ;
73
103
default :
104
+ furi_string_printf (tariff_name , "Unknown" );
74
105
return SUBSCRIPTION_TYPE_UNKNOWN ;
75
106
}
76
107
}
@@ -79,25 +110,25 @@ static bool kazan_verify(Nfc* nfc) {
79
110
bool verified = false;
80
111
81
112
do {
82
- const uint8_t ticket_sector_number = 8 ;
83
- const uint8_t ticket_block_number =
84
- mf_classic_get_first_block_num_of_sector (ticket_sector_number ) + 1 ;
85
- FURI_LOG_D (TAG , "Verifying sector %u" , ticket_sector_number );
113
+ const uint8_t verification_sector_number = 10 ;
114
+ const uint8_t verification_block_number =
115
+ mf_classic_get_first_block_num_of_sector (verification_sector_number ) + 1 ;
116
+ FURI_LOG_D (TAG , "Verifying sector %u" , verification_sector_number );
86
117
87
118
MfClassicKey key = {0 };
88
- nfc_util_num2bytes (kazan_1k_keys [ticket_sector_number ].a , COUNT_OF (key .data ), key .data );
119
+ nfc_util_num2bytes (
120
+ kazan_1k_keys_standart [verification_sector_number ].a , COUNT_OF (key .data ), key .data );
89
121
90
122
MfClassicAuthContext auth_context ;
91
123
MfClassicError error = mf_classic_poller_sync_auth (
92
- nfc , ticket_block_number , & key , MfClassicKeyTypeA , & auth_context );
124
+ nfc , verification_block_number , & key , MfClassicKeyTypeA , & auth_context );
93
125
if (error != MfClassicErrorNone ) {
94
- FURI_LOG_D (TAG , "Failed to read block %u: %d" , ticket_block_number , error );
126
+ FURI_LOG_D (TAG , "Failed to read block %u: %d" , verification_block_number , error );
95
127
break ;
96
128
}
97
129
98
130
verified = true;
99
131
} while (false);
100
-
101
132
return verified ;
102
133
}
103
134
@@ -122,18 +153,40 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
122
153
.key_a_mask = 0 ,
123
154
.key_b_mask = 0 ,
124
155
};
156
+
157
+ MfClassicDeviceKeys keys_old = {
158
+ .key_a_mask = 0 ,
159
+ .key_b_mask = 0 ,
160
+ };
161
+
125
162
for (size_t i = 0 ; i < mf_classic_get_total_sectors_num (data -> type ); i ++ ) {
126
- nfc_util_num2bytes (kazan_1k_keys [i ].a , sizeof (MfClassicKey ), keys .key_a [i ].data );
163
+ nfc_util_num2bytes (
164
+ kazan_1k_keys_standart [i ].a , sizeof (MfClassicKey ), keys .key_a [i ].data );
165
+ nfc_util_num2bytes (
166
+ kazan_1k_keys_old [i ].a , sizeof (MfClassicKey ), keys_old .key_a [i ].data );
127
167
FURI_BIT_SET (keys .key_a_mask , i );
128
- nfc_util_num2bytes (kazan_1k_keys [i ].b , sizeof (MfClassicKey ), keys .key_b [i ].data );
168
+ FURI_BIT_SET (keys_old .key_a_mask , i );
169
+
170
+ nfc_util_num2bytes (
171
+ kazan_1k_keys_standart [i ].b , sizeof (MfClassicKey ), keys .key_b [i ].data );
172
+ nfc_util_num2bytes (
173
+ kazan_1k_keys_old [i ].b , sizeof (MfClassicKey ), keys_old .key_b [i ].data );
129
174
FURI_BIT_SET (keys .key_b_mask , i );
175
+ FURI_BIT_SET (keys_old .key_b_mask , i );
130
176
}
131
177
132
178
error = mf_classic_poller_sync_read (nfc , & keys , data );
133
179
if (error != MfClassicErrorNone ) {
134
- FURI_LOG_W (TAG , "Failed to read data" );
180
+ FURI_LOG_W (TAG , "Failed to read data: standart keys " );
135
181
break ;
136
182
}
183
+ if (!mf_classic_is_card_read (data )) {
184
+ error = mf_classic_poller_sync_read (nfc , & keys_old , data );
185
+ if (error != MfClassicErrorNone ) {
186
+ FURI_LOG_W (TAG , "Failed to read data: old keys" );
187
+ break ;
188
+ }
189
+ }
137
190
138
191
nfc_device_set_data (device , NfcProtocolMfClassic , data );
139
192
@@ -153,25 +206,30 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
153
206
bool parsed = false;
154
207
155
208
do {
209
+ const uint8_t verification_sector_number = 10 ;
156
210
const uint8_t ticket_sector_number = 8 ;
157
211
const uint8_t balance_sector_number = 9 ;
158
212
159
213
// Verify keys
160
214
MfClassicKeyPair keys = {};
161
215
const MfClassicSectorTrailer * sec_tr =
162
- mf_classic_get_sector_trailer_by_sector (data , ticket_sector_number );
216
+ mf_classic_get_sector_trailer_by_sector (data , verification_sector_number );
163
217
164
218
keys .a = nfc_util_bytes2num (sec_tr -> key_a .data , COUNT_OF (sec_tr -> key_a .data ));
165
- keys .b = nfc_util_bytes2num (sec_tr -> key_b .data , COUNT_OF (sec_tr -> key_b .data ));
166
219
167
- if ((keys .a != 0xE954024EE754 ) && (keys .b != 0x0CD464CDC100 )) break ;
220
+ if (keys .a != 0xF7A545095C49 ) {
221
+ FURI_LOG_D (TAG , "Parser: Failed to verify key a: %llu" , keys .a );
222
+ break ;
223
+ }
168
224
169
225
// Parse data
170
226
uint8_t start_block_num = mf_classic_get_first_block_num_of_sector (ticket_sector_number );
171
227
172
228
const uint8_t * block_start_ptr = & data -> block [start_block_num ].data [6 ];
173
229
174
- enum SubscriptionType subscription_type = get_subscription_type (block_start_ptr [0 ]);
230
+ FuriString * tariff_name = furi_string_alloc ();
231
+ enum SubscriptionType subscription_type =
232
+ get_subscription_type (block_start_ptr [0 ], tariff_name );
175
233
176
234
FuriHalRtcDateTime valid_from ;
177
235
valid_from .year = 2000 + block_start_ptr [1 ];
@@ -222,10 +280,25 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
222
280
valid_to .year );
223
281
}
224
282
225
- if (subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT ) {
283
+ if (subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS ) {
284
+ furi_string_cat_printf (
285
+ parsed_data ,
286
+ "Type: abonnement\nTariff: %s\nTrips left: %lu\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u" ,
287
+ furi_string_get_cstr (tariff_name ),
288
+ trip_counter ,
289
+ valid_from .day ,
290
+ valid_from .month ,
291
+ valid_from .year ,
292
+ valid_to .day ,
293
+ valid_to .month ,
294
+ valid_to .year );
295
+ }
296
+
297
+ if (subscription_type == SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME ) {
226
298
furi_string_cat_printf (
227
299
parsed_data ,
228
- "Type: abonnement\nTrips left: %lu\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u" ,
300
+ "Type: abonnement\nTariff: %s\nTotal valid time: %lu days\nCard valid:\nfrom: %02u.%02u.%u\nto: %02u.%02u.%u" ,
301
+ furi_string_get_cstr (tariff_name ),
229
302
trip_counter ,
230
303
valid_from .day ,
231
304
valid_from .month ,
@@ -238,7 +311,8 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
238
311
if (subscription_type == SUBSCRIPTION_TYPE_UNKNOWN ) {
239
312
furi_string_cat_printf (
240
313
parsed_data ,
241
- "Type: unknown\nBalance: %lu RUR\nValid from: %02u.%02u.%u\nValid to: %02u.%02u.%u" ,
314
+ "Type: unknown\nTariff: %s\nCounter: %lu\nValid from: %02u.%02u.%u\nValid to: %02u.%02u.%u" ,
315
+ furi_string_get_cstr (tariff_name ),
242
316
trip_counter ,
243
317
valid_from .day ,
244
318
valid_from .month ,
0 commit comments