Skip to content

Commit c22b3b5

Browse files
committed
Merge branch 'nfc-parsers' into dev
2 parents 1daa2fa + 1b45b8a commit c22b3b5

File tree

1 file changed

+98
-24
lines changed
  • applications/main/nfc/plugins/supported_cards

1 file changed

+98
-24
lines changed

applications/main/nfc/plugins/supported_cards/kazan.c

+98-24
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
* You should have received a copy of the GNU General Public License
1717
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1818
*/
19+
#include "core/core_defines.h"
1920
#include "core/log.h"
21+
#include "core/string.h"
2022
#include "nfc_supported_card_plugin.h"
2123

2224
#include "protocols/mf_classic/mf_classic.h"
@@ -36,7 +38,7 @@ typedef struct {
3638
uint64_t b;
3739
} MfClassicKeyPair;
3840

39-
static const MfClassicKeyPair kazan_1k_keys[] = {
41+
static const MfClassicKeyPair kazan_1k_keys_standart[] = {
4042
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
4143
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
4244
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
@@ -55,22 +57,51 @@ static const MfClassicKeyPair kazan_1k_keys[] = {
5557
{.a = 0xFFFFFFFFFFFF, .b = 0xFFFFFFFFFFFF},
5658
};
5759

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+
5879
enum SubscriptionType {
5980
SUBSCRIPTION_TYPE_UNKNOWN,
6081
SUBSCRIPTION_TYPE_PURSE,
61-
SUBSCRIPTION_TYPE_ABONNEMENT,
82+
SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME,
83+
SUBSCRIPTION_TYPE_ABONNEMENT_BY_TRIPS,
6284
};
6385

64-
enum SubscriptionType get_subscription_type(uint8_t value) {
86+
enum SubscriptionType get_subscription_type(uint8_t value, FuriString* tariff_name) {
6587
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;
6891
case 0x67:
92+
furi_string_printf(tariff_name, "Ground electric transport. 1 month");
93+
return SUBSCRIPTION_TYPE_ABONNEMENT_BY_TIME;
6994
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;
71100
case 0x53:
101+
furi_string_printf(tariff_name, "Standart purse");
72102
return SUBSCRIPTION_TYPE_PURSE;
73103
default:
104+
furi_string_printf(tariff_name, "Unknown");
74105
return SUBSCRIPTION_TYPE_UNKNOWN;
75106
}
76107
}
@@ -79,25 +110,25 @@ static bool kazan_verify(Nfc* nfc) {
79110
bool verified = false;
80111

81112
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);
86117

87118
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);
89121

90122
MfClassicAuthContext auth_context;
91123
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);
93125
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);
95127
break;
96128
}
97129

98130
verified = true;
99131
} while(false);
100-
101132
return verified;
102133
}
103134

@@ -122,18 +153,40 @@ static bool kazan_read(Nfc* nfc, NfcDevice* device) {
122153
.key_a_mask = 0,
123154
.key_b_mask = 0,
124155
};
156+
157+
MfClassicDeviceKeys keys_old = {
158+
.key_a_mask = 0,
159+
.key_b_mask = 0,
160+
};
161+
125162
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);
127167
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);
129174
FURI_BIT_SET(keys.key_b_mask, i);
175+
FURI_BIT_SET(keys_old.key_b_mask, i);
130176
}
131177

132178
error = mf_classic_poller_sync_read(nfc, &keys, data);
133179
if(error != MfClassicErrorNone) {
134-
FURI_LOG_W(TAG, "Failed to read data");
180+
FURI_LOG_W(TAG, "Failed to read data: standart keys");
135181
break;
136182
}
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+
}
137190

138191
nfc_device_set_data(device, NfcProtocolMfClassic, data);
139192

@@ -153,25 +206,30 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
153206
bool parsed = false;
154207

155208
do {
209+
const uint8_t verification_sector_number = 10;
156210
const uint8_t ticket_sector_number = 8;
157211
const uint8_t balance_sector_number = 9;
158212

159213
// Verify keys
160214
MfClassicKeyPair keys = {};
161215
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);
163217

164218
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));
166219

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+
}
168224

169225
// Parse data
170226
uint8_t start_block_num = mf_classic_get_first_block_num_of_sector(ticket_sector_number);
171227

172228
const uint8_t* block_start_ptr = &data->block[start_block_num].data[6];
173229

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);
175233

176234
FuriHalRtcDateTime valid_from;
177235
valid_from.year = 2000 + block_start_ptr[1];
@@ -222,10 +280,25 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
222280
valid_to.year);
223281
}
224282

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) {
226298
furi_string_cat_printf(
227299
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),
229302
trip_counter,
230303
valid_from.day,
231304
valid_from.month,
@@ -238,7 +311,8 @@ static bool kazan_parse(const NfcDevice* device, FuriString* parsed_data) {
238311
if(subscription_type == SUBSCRIPTION_TYPE_UNKNOWN) {
239312
furi_string_cat_printf(
240313
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),
242316
trip_counter,
243317
valid_from.day,
244318
valid_from.month,

0 commit comments

Comments
 (0)