Add new card parsers (#1503)
* Add the "Two cities" parser * Add plantain and plantain4k parsers * Add new parsers to the supported list * United card PoC * Fix nfc device not sleeping * Completely read the 4K troika variants * Correct naming * Update to reflect upstream changes * Add support for MfUl info * Fix parsers * Card type detection fixes * Remove debug info * Fixes for the verification of cards * nfc: fix verification for supported cards * nfc: remove unused vars * Improve card reading reliability and fix plantain * plantain: change log level Co-authored-by: gornekich <n.gorbadey@gmail.com>
This commit is contained in:
		
							parent
							
								
									d003db0404
								
							
						
					
					
						commit
						9f3b80e606
					
				| @ -47,7 +47,9 @@ void nfc_scene_device_info_on_enter(void* context) { | |||||||
|             } |             } | ||||||
|             string_clear(country_name); |             string_clear(country_name); | ||||||
|         } |         } | ||||||
|     } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { |     } else if( | ||||||
|  |         dev_data->protocol == NfcDeviceProtocolMifareClassic || | ||||||
|  |         dev_data->protocol == NfcDeviceProtocolMifareUl) { | ||||||
|         string_set(temp_str, nfc->dev->dev_data.parsed_data); |         string_set(temp_str, nfc->dev->dev_data.parsed_data); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -34,6 +34,9 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { | |||||||
|         nfc); |         nfc); | ||||||
| 
 | 
 | ||||||
|     string_t temp_str; |     string_t temp_str; | ||||||
|  |     if(string_size(nfc->dev->dev_data.parsed_data)) { | ||||||
|  |         string_init_set(temp_str, nfc->dev->dev_data.parsed_data); | ||||||
|  |     } else { | ||||||
|         string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); |         string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true)); | ||||||
|         string_cat_printf(temp_str, "UID:"); |         string_cat_printf(temp_str, "UID:"); | ||||||
|         for(size_t i = 0; i < data->uid_len; i++) { |         for(size_t i = 0; i < data->uid_len; i++) { | ||||||
| @ -44,6 +47,7 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) { | |||||||
|         if(mf_ul_data->data_read != mf_ul_data->data_size) { |         if(mf_ul_data->data_read != mf_ul_data->data_size) { | ||||||
|             string_cat_printf(temp_str, "\nPassword-protected pages!"); |             string_cat_printf(temp_str, "\nPassword-protected pages!"); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|     widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); |     widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str)); | ||||||
|     string_clear(temp_str); |     string_clear(temp_str); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -91,7 +91,9 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) { | |||||||
|             bool application_info_present = false; |             bool application_info_present = false; | ||||||
|             if(dev_data->protocol == NfcDeviceProtocolEMV) { |             if(dev_data->protocol == NfcDeviceProtocolEMV) { | ||||||
|                 application_info_present = true; |                 application_info_present = true; | ||||||
|             } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) { |             } else if( | ||||||
|  |                 dev_data->protocol == NfcDeviceProtocolMifareClassic || | ||||||
|  |                 dev_data->protocol == NfcDeviceProtocolMifareUl) { | ||||||
|                 application_info_present = nfc_supported_card_verify_and_parse(dev_data); |                 application_info_present = nfc_supported_card_verify_and_parse(dev_data); | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -123,7 +123,25 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         // Read card
 |         // Try to read supported card
 | ||||||
|  |         FURI_LOG_I(TAG, "Trying to read a supported card ..."); | ||||||
|  |         for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { | ||||||
|  |             if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareUl) { | ||||||
|  |                 if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { | ||||||
|  |                     if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { | ||||||
|  |                         read_success = true; | ||||||
|  |                         nfc_supported_card[i].parse(nfc_worker->dev_data); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } else { | ||||||
|  |                     furi_hal_nfc_sleep(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(read_success) break; | ||||||
|  |         furi_hal_nfc_sleep(); | ||||||
|  | 
 | ||||||
|  |         // Otherwise, try to read as usual
 | ||||||
|         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; |         if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break; | ||||||
|         if(!mf_ul_read_card(tx_rx, &reader, &data)) break; |         if(!mf_ul_read_card(tx_rx, &reader, &data)) break; | ||||||
|         // Copy data
 |         // Copy data
 | ||||||
| @ -149,14 +167,17 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont | |||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         // Try to read supported card
 |         // Try to read supported card
 | ||||||
|         FURI_LOG_I(TAG, "Try read supported card ..."); |         FURI_LOG_I(TAG, "Trying to read a supported card ..."); | ||||||
|         for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { |         for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) { | ||||||
|             if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { |             if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) { | ||||||
|                 if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { |                 if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) { | ||||||
|                     if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { |                     if(nfc_supported_card[i].read(nfc_worker, tx_rx)) { | ||||||
|                         read_success = true; |                         read_success = true; | ||||||
|                         nfc_supported_card[i].parse(nfc_worker->dev_data); |                         nfc_supported_card[i].parse(nfc_worker->dev_data); | ||||||
|  |                         break; | ||||||
|                     } |                     } | ||||||
|  |                 } else { | ||||||
|  |                     furi_hal_nfc_sleep(); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
							
								
								
									
										113
									
								
								lib/nfc/parsers/all_in_one.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								lib/nfc/parsers/all_in_one.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | #include "all_in_one.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <nfc_worker_i.h> | ||||||
|  | 
 | ||||||
|  | #include "furi_hal.h" | ||||||
|  | 
 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_UNKNOWN 0 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_A 1 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_D 2 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_E2 3 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_E3 4 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_E5 5 | ||||||
|  | #define ALL_IN_ONE_LAYOUT_2 6 | ||||||
|  | 
 | ||||||
|  | uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) { | ||||||
|  |     // I absolutely hate what's about to happen here.
 | ||||||
|  | 
 | ||||||
|  |     // Switch on the second half of the third byte of page 5
 | ||||||
|  |     FURI_LOG_I("all_in_one", "Layout byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 2]); | ||||||
|  |     FURI_LOG_I( | ||||||
|  |         "all_in_one", "Layout half-byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 3] & 0x0F); | ||||||
|  |     switch(dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F) { | ||||||
|  |     // If it is A, the layout type is a type A layout
 | ||||||
|  |     case 0x0A: | ||||||
|  |         return ALL_IN_ONE_LAYOUT_A; | ||||||
|  |     case 0x0D: | ||||||
|  |         return ALL_IN_ONE_LAYOUT_D; | ||||||
|  |     case 0x02: | ||||||
|  |         return ALL_IN_ONE_LAYOUT_2; | ||||||
|  |     default: | ||||||
|  |         FURI_LOG_I( | ||||||
|  |             "all_in_one", | ||||||
|  |             "Unknown layout type: %d", | ||||||
|  |             dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F); | ||||||
|  |         return ALL_IN_ONE_LAYOUT_UNKNOWN; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     UNUSED(nfc_worker); | ||||||
|  |     // If this is a all_in_one pass, first 2 bytes of page 4 are 0x45 0xD9
 | ||||||
|  |     MfUltralightReader reader = {}; | ||||||
|  |     MfUltralightData data = {}; | ||||||
|  | 
 | ||||||
|  |     if(!mf_ul_read_card(tx_rx, &reader, &data)) { | ||||||
|  |         return false; | ||||||
|  |     } else { | ||||||
|  |         if(data.data[4 * 4] == 0x45 && data.data[4 * 4 + 1] == 0xD9) { | ||||||
|  |             FURI_LOG_I("all_in_one", "Pass verified"); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     MfUltralightReader reader = {}; | ||||||
|  |     MfUltralightData data = {}; | ||||||
|  |     if(!mf_ul_read_card(tx_rx, &reader, &data)) { | ||||||
|  |         return false; | ||||||
|  |     } else { | ||||||
|  |         memcpy(&nfc_worker->dev_data->mf_ul_data, &data, sizeof(data)); | ||||||
|  |         FURI_LOG_I("all_in_one", "Card read"); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool all_in_one_parser_parse(NfcDeviceData* dev_data) { | ||||||
|  |     if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) { | ||||||
|  |         FURI_LOG_I("all_in_one", "Pass not verified"); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // If the layout is a then the ride count is stored in the first byte of page 8
 | ||||||
|  |     uint8_t ride_count = 0; | ||||||
|  |     uint32_t serial = 0; | ||||||
|  |     if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) { | ||||||
|  |         ride_count = dev_data->mf_ul_data.data[4 * 8]; | ||||||
|  |     } else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) { | ||||||
|  |         // If the layout is D, the ride count is stored in the second byte of page 9
 | ||||||
|  |         ride_count = dev_data->mf_ul_data.data[4 * 9 + 1]; | ||||||
|  |         // I hate this with a burning passion.
 | ||||||
|  | 
 | ||||||
|  |         // The number starts at the second half of the third byte on page 4, and is 32 bits long
 | ||||||
|  |         // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
 | ||||||
|  |         // B8 17 A2 A4 BD becomes 81 7A 2A 4B
 | ||||||
|  |         serial = (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | | ||||||
|  |                  dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | | ||||||
|  |                  dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | | ||||||
|  |                  dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | | ||||||
|  |                  (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); | ||||||
|  |     } else { | ||||||
|  |         FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data)); | ||||||
|  |         ride_count = 137; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // I hate this with a burning passion.
 | ||||||
|  | 
 | ||||||
|  |     // The number starts at the second half of the third byte on page 4, and is 32 bits long
 | ||||||
|  |     // So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
 | ||||||
|  |     // B8 17 A2 A4 BD becomes 81 7A 2A 4B
 | ||||||
|  |     serial = | ||||||
|  |         (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 | | ||||||
|  |         dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | dev_data->mf_ul_data.data[4 * 4 + 4] << 12 | | ||||||
|  |         dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4); | ||||||
|  | 
 | ||||||
|  |     // Format string for rides count
 | ||||||
|  |     string_printf( | ||||||
|  |         dev_data->parsed_data, "\e#All-In-One\nNumber: %u\nRides left: %u", serial, ride_count); | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								lib/nfc/parsers/all_in_one.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/nfc/parsers/all_in_one.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool all_in_one_parser_parse(NfcDeviceData* dev_data); | ||||||
| @ -1,14 +1,54 @@ | |||||||
| #include "nfc_supported_card.h" | #include "nfc_supported_card.h" | ||||||
| 
 | 
 | ||||||
| #include "troyka_parser.h" | #include "plantain_parser.h" | ||||||
|  | #include "troika_parser.h" | ||||||
|  | #include "plantain_4k_parser.h" | ||||||
|  | #include "troika_4k_parser.h" | ||||||
|  | #include "two_cities.h" | ||||||
|  | #include "all_in_one.h" | ||||||
| 
 | 
 | ||||||
| NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { | NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = { | ||||||
|     [NfcSupportedCardTypeTroyka] = |     [NfcSupportedCardTypePlantain] = | ||||||
|         { |         { | ||||||
|             .protocol = NfcDeviceProtocolMifareClassic, |             .protocol = NfcDeviceProtocolMifareClassic, | ||||||
|             .verify = troyka_parser_verify, |             .verify = plantain_parser_verify, | ||||||
|             .read = troyka_parser_read, |             .read = plantain_parser_read, | ||||||
|             .parse = troyka_parser_parse, |             .parse = plantain_parser_parse, | ||||||
|  |         }, | ||||||
|  |     [NfcSupportedCardTypeTroika] = | ||||||
|  |         { | ||||||
|  |             .protocol = NfcDeviceProtocolMifareClassic, | ||||||
|  |             .verify = troika_parser_verify, | ||||||
|  |             .read = troika_parser_read, | ||||||
|  |             .parse = troika_parser_parse, | ||||||
|  |         }, | ||||||
|  |     [NfcSupportedCardTypePlantain4K] = | ||||||
|  |         { | ||||||
|  |             .protocol = NfcDeviceProtocolMifareClassic, | ||||||
|  |             .verify = plantain_4k_parser_verify, | ||||||
|  |             .read = plantain_4k_parser_read, | ||||||
|  |             .parse = plantain_4k_parser_parse, | ||||||
|  |         }, | ||||||
|  |     [NfcSupportedCardTypeTroika4K] = | ||||||
|  |         { | ||||||
|  |             .protocol = NfcDeviceProtocolMifareClassic, | ||||||
|  |             .verify = troika_4k_parser_verify, | ||||||
|  |             .read = troika_4k_parser_read, | ||||||
|  |             .parse = troika_4k_parser_parse, | ||||||
|  |         }, | ||||||
|  |     [NfcSupportedCardTypeTwoCities] = | ||||||
|  |         { | ||||||
|  |             .protocol = NfcDeviceProtocolMifareClassic, | ||||||
|  |             .verify = two_cities_parser_verify, | ||||||
|  |             .read = two_cities_parser_read, | ||||||
|  |             .parse = two_cities_parser_parse, | ||||||
|  |         }, | ||||||
|  |     [NfcSupportedCardTypeAllInOne] = | ||||||
|  |         { | ||||||
|  |             .protocol = NfcDeviceProtocolMifareUl, | ||||||
|  |             .verify = all_in_one_parser_verify, | ||||||
|  |             .read = all_in_one_parser_read, | ||||||
|  |             .parse = all_in_one_parser_parse, | ||||||
|         }, |         }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,7 +7,12 @@ | |||||||
| #include <m-string.h> | #include <m-string.h> | ||||||
| 
 | 
 | ||||||
| typedef enum { | typedef enum { | ||||||
|     NfcSupportedCardTypeTroyka, |     NfcSupportedCardTypePlantain, | ||||||
|  |     NfcSupportedCardTypeTroika, | ||||||
|  |     NfcSupportedCardTypePlantain4K, | ||||||
|  |     NfcSupportedCardTypeTroika4K, | ||||||
|  |     NfcSupportedCardTypeTwoCities, | ||||||
|  |     NfcSupportedCardTypeAllInOne, | ||||||
| 
 | 
 | ||||||
|     NfcSupportedCardTypeEnd, |     NfcSupportedCardTypeEnd, | ||||||
| } NfcSupportedCardType; | } NfcSupportedCardType; | ||||||
|  | |||||||
							
								
								
									
										153
									
								
								lib/nfc/parsers/plantain_4k_parser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										153
									
								
								lib/nfc/parsers/plantain_4k_parser.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,153 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | #include "plantain_parser.h" // For luhn and string_push_uint64 | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <nfc_worker_i.h> | ||||||
|  | 
 | ||||||
|  | #include "furi_hal.h" | ||||||
|  | 
 | ||||||
|  | static const MfClassicAuthContext plantain_keys_4k[] = { | ||||||
|  |     {.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF}, | ||||||
|  |     {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, | ||||||
|  |     {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, | ||||||
|  |     {.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, | ||||||
|  |     {.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, | ||||||
|  |     {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, | ||||||
|  |     {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, | ||||||
|  |     {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, | ||||||
|  |     {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf}, | ||||||
|  |     {.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a}, | ||||||
|  |     {.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b}, | ||||||
|  |     {.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3}, | ||||||
|  |     {.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22}, | ||||||
|  |     {.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056}, | ||||||
|  |     {.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510}, | ||||||
|  |     {.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf}, | ||||||
|  |     {.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439}, | ||||||
|  |     {.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406}, | ||||||
|  |     {.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3}, | ||||||
|  |     {.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA}, | ||||||
|  |     {.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f}, | ||||||
|  |     {.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3}, | ||||||
|  |     {.sector = 30, .key_a = 0x0eb23cc8110b, .key_b = 0x04dc35277635}, | ||||||
|  |     {.sector = 31, .key_a = 0xbc4580b7f20b, .key_b = 0xd0a4131fb290}, | ||||||
|  |     {.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023}, | ||||||
|  |     {.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9}, | ||||||
|  |     {.sector = 34, .key_a = 0xfd8705e721b0, .key_b = 0x296fc317a513}, | ||||||
|  |     {.sector = 35, .key_a = 0x22052b480d11, .key_b = 0xe19504c39461}, | ||||||
|  |     {.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7}, | ||||||
|  |     {.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a}, | ||||||
|  |     {.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7}, | ||||||
|  |     {.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     UNUSED(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint8_t sector = 8; | ||||||
|  |     uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); | ||||||
|  |     FURI_LOG_D("Plant4K", "Verifying sector %d", sector); | ||||||
|  |     if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { | ||||||
|  |         FURI_LOG_D("Plant4K", "Sector %d verified", sector); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     MfClassicReader reader = {}; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); | ||||||
|  |     for(size_t i = 0; i < COUNT_OF(plantain_keys_4k); i++) { | ||||||
|  |         mf_classic_reader_add_sector( | ||||||
|  |             &reader, | ||||||
|  |             plantain_keys_4k[i].sector, | ||||||
|  |             plantain_keys_4k[i].key_a, | ||||||
|  |             plantain_keys_4k[i].key_b); | ||||||
|  |         FURI_LOG_T("plant4k", "Added sector %d", plantain_keys_4k[i].sector); | ||||||
|  |     } | ||||||
|  |     for(int i = 0; i < 5; i++) { | ||||||
|  |         if(mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool plantain_4k_parser_parse(NfcDeviceData* dev_data) { | ||||||
|  |     MfClassicData* data = &dev_data->mf_classic_data; | ||||||
|  | 
 | ||||||
|  |     // Verify key
 | ||||||
|  |     MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); | ||||||
|  |     uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); | ||||||
|  |     if(key != plantain_keys_4k[8].key_a) return false; | ||||||
|  | 
 | ||||||
|  |     // Point to block 0 of sector 4, value 0
 | ||||||
|  |     uint8_t* temp_ptr = &data->block[4 * 4].value[0]; | ||||||
|  |     // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
 | ||||||
|  |     // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
 | ||||||
|  |     uint32_t balance = | ||||||
|  |         ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; | ||||||
|  |     // Read card number
 | ||||||
|  |     // Point to block 0 of sector 0, value 0
 | ||||||
|  |     temp_ptr = &data->block[0 * 4].value[0]; | ||||||
|  |     // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
 | ||||||
|  |     // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal
 | ||||||
|  |     uint8_t card_number_arr[7]; | ||||||
|  |     for(size_t i = 0; i < 7; i++) { | ||||||
|  |         card_number_arr[i] = temp_ptr[6 - i]; | ||||||
|  |     } | ||||||
|  |     // Copy card number to uint64_t
 | ||||||
|  |     uint64_t card_number = 0; | ||||||
|  |     for(size_t i = 0; i < 7; i++) { | ||||||
|  |         card_number = (card_number << 8) | card_number_arr[i]; | ||||||
|  |     } | ||||||
|  |     // Convert card number to string
 | ||||||
|  |     string_t card_number_str; | ||||||
|  |     string_init(card_number_str); | ||||||
|  |     // Should look like "361301047292848684"
 | ||||||
|  |     // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead
 | ||||||
|  |     string_push_uint64(card_number, card_number_str); | ||||||
|  |     // Add suffix with luhn checksum (1 digit) to the card number string
 | ||||||
|  |     string_t card_number_suffix; | ||||||
|  |     string_init(card_number_suffix); | ||||||
|  | 
 | ||||||
|  |     // The number to calculate the checksum on doesn't fit into uint64_t, idk
 | ||||||
|  |     //uint8_t luhn_checksum = plantain_calculate_luhn(card_number);
 | ||||||
|  | 
 | ||||||
|  |     // // Convert luhn checksum to string
 | ||||||
|  |     // string_t luhn_checksum_str;
 | ||||||
|  |     // string_init(luhn_checksum_str);
 | ||||||
|  |     // string_push_uint64(luhn_checksum, luhn_checksum_str);
 | ||||||
|  | 
 | ||||||
|  |     string_cat_printf(card_number_suffix, "-"); | ||||||
|  |     // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum);
 | ||||||
|  |     string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); | ||||||
|  |     // Free all not needed strings
 | ||||||
|  |     string_clear(card_number_suffix); | ||||||
|  |     // string_clear(luhn_checksum_str);
 | ||||||
|  | 
 | ||||||
|  |     string_printf( | ||||||
|  |         dev_data->parsed_data, | ||||||
|  |         "\e#Plantain\nN:%s\nBalance:%d\n", | ||||||
|  |         string_get_cstr(card_number_str), | ||||||
|  |         balance); | ||||||
|  |     string_clear(card_number_str); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								lib/nfc/parsers/plantain_4k_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/nfc/parsers/plantain_4k_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool plantain_4k_parser_parse(NfcDeviceData* dev_data); | ||||||
							
								
								
									
										147
									
								
								lib/nfc/parsers/plantain_parser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								lib/nfc/parsers/plantain_parser.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,147 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <nfc_worker_i.h> | ||||||
|  | 
 | ||||||
|  | #include "furi_hal.h" | ||||||
|  | 
 | ||||||
|  | static const MfClassicAuthContext plantain_keys[] = { | ||||||
|  |     {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, | ||||||
|  |     {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, | ||||||
|  |     {.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, | ||||||
|  |     {.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, | ||||||
|  |     {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, | ||||||
|  |     {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, | ||||||
|  |     {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, | ||||||
|  |     {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     UNUSED(nfc_worker); | ||||||
|  |     if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint8_t sector = 8; | ||||||
|  |     uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); | ||||||
|  |     FURI_LOG_D("Plant", "Verifying sector %d", sector); | ||||||
|  |     if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) { | ||||||
|  |         FURI_LOG_D("Plant", "Sector %d verified", sector); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     MfClassicReader reader = {}; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); | ||||||
|  |     for(size_t i = 0; i < COUNT_OF(plantain_keys); i++) { | ||||||
|  |         mf_classic_reader_add_sector( | ||||||
|  |             &reader, plantain_keys[i].sector, plantain_keys[i].key_a, plantain_keys[i].key_b); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void string_push_uint64(uint64_t input, string_t output) { | ||||||
|  |     const uint8_t base = 10; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         char c = input % base; | ||||||
|  |         input /= base; | ||||||
|  | 
 | ||||||
|  |         if(c < 10) | ||||||
|  |             c += '0'; | ||||||
|  |         else | ||||||
|  |             c += 'A' - 10; | ||||||
|  |         string_push_back(output, c); | ||||||
|  |     } while(input); | ||||||
|  | 
 | ||||||
|  |     // reverse string
 | ||||||
|  |     for(uint8_t i = 0; i < string_size(output) / 2; i++) { | ||||||
|  |         char c = string_get_char(output, i); | ||||||
|  |         string_set_char(output, i, string_get_char(output, string_size(output) - i - 1)); | ||||||
|  |         string_set_char(output, string_size(output) - i - 1, c); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint8_t plantain_calculate_luhn(uint64_t number) { | ||||||
|  |     // No.
 | ||||||
|  |     UNUSED(number); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool plantain_parser_parse(NfcDeviceData* dev_data) { | ||||||
|  |     MfClassicData* data = &dev_data->mf_classic_data; | ||||||
|  | 
 | ||||||
|  |     // Verify key
 | ||||||
|  |     MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); | ||||||
|  |     uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); | ||||||
|  |     if(key != plantain_keys[8].key_a) return false; | ||||||
|  | 
 | ||||||
|  |     // Point to block 0 of sector 4, value 0
 | ||||||
|  |     uint8_t* temp_ptr = &data->block[4 * 4].value[0]; | ||||||
|  |     // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
 | ||||||
|  |     // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
 | ||||||
|  |     uint32_t balance = | ||||||
|  |         ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; | ||||||
|  |     // Read card number
 | ||||||
|  |     // Point to block 0 of sector 0, value 0
 | ||||||
|  |     temp_ptr = &data->block[0 * 4].value[0]; | ||||||
|  |     // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
 | ||||||
|  |     // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal
 | ||||||
|  |     uint8_t card_number_arr[7]; | ||||||
|  |     for(size_t i = 0; i < 7; i++) { | ||||||
|  |         card_number_arr[i] = temp_ptr[6 - i]; | ||||||
|  |     } | ||||||
|  |     // Copy card number to uint64_t
 | ||||||
|  |     uint64_t card_number = 0; | ||||||
|  |     for(size_t i = 0; i < 7; i++) { | ||||||
|  |         card_number = (card_number << 8) | card_number_arr[i]; | ||||||
|  |     } | ||||||
|  |     // Convert card number to string
 | ||||||
|  |     string_t card_number_str; | ||||||
|  |     string_init(card_number_str); | ||||||
|  |     // Should look like "361301047292848684"
 | ||||||
|  |     // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead
 | ||||||
|  |     string_push_uint64(card_number, card_number_str); | ||||||
|  |     // Add suffix with luhn checksum (1 digit) to the card number string
 | ||||||
|  |     string_t card_number_suffix; | ||||||
|  |     string_init(card_number_suffix); | ||||||
|  | 
 | ||||||
|  |     // The number to calculate the checksum on doesn't fit into uint64_t, idk
 | ||||||
|  |     //uint8_t luhn_checksum = plantain_calculate_luhn(card_number);
 | ||||||
|  | 
 | ||||||
|  |     // // Convert luhn checksum to string
 | ||||||
|  |     // string_t luhn_checksum_str;
 | ||||||
|  |     // string_init(luhn_checksum_str);
 | ||||||
|  |     // string_push_uint64(luhn_checksum, luhn_checksum_str);
 | ||||||
|  | 
 | ||||||
|  |     string_cat_printf(card_number_suffix, "-"); | ||||||
|  |     // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum);
 | ||||||
|  |     string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); | ||||||
|  |     // Free all not needed strings
 | ||||||
|  |     string_clear(card_number_suffix); | ||||||
|  |     // string_clear(luhn_checksum_str);
 | ||||||
|  | 
 | ||||||
|  |     string_printf( | ||||||
|  |         dev_data->parsed_data, | ||||||
|  |         "\e#Plantain\nN:%s\nBalance:%d\n", | ||||||
|  |         string_get_cstr(card_number_str), | ||||||
|  |         balance); | ||||||
|  |     string_clear(card_number_str); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										13
									
								
								lib/nfc/parsers/plantain_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								lib/nfc/parsers/plantain_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool plantain_parser_parse(NfcDeviceData* dev_data); | ||||||
|  | 
 | ||||||
|  | void string_push_uint64(uint64_t input, string_t output); | ||||||
|  | 
 | ||||||
|  | uint8_t plantain_calculate_luhn(uint64_t number); | ||||||
							
								
								
									
										104
									
								
								lib/nfc/parsers/troika_4k_parser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								lib/nfc/parsers/troika_4k_parser.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,104 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <nfc_worker_i.h> | ||||||
|  | 
 | ||||||
|  | static const MfClassicAuthContext troika_4k_keys[] = { | ||||||
|  |     {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, | ||||||
|  |     {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, | ||||||
|  |     {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5}, | ||||||
|  |     {.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698}, | ||||||
|  |     {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb}, | ||||||
|  |     {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, | ||||||
|  |     {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, | ||||||
|  |     {.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d}, | ||||||
|  |     {.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b}, | ||||||
|  |     {.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0}, | ||||||
|  |     {.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, | ||||||
|  |     {.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75}, | ||||||
|  |     {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408}, | ||||||
|  |     {.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df}, | ||||||
|  |     {.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9}, | ||||||
|  |     {.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4}, | ||||||
|  |     {.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216}, | ||||||
|  |     {.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba}, | ||||||
|  |     {.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252}, | ||||||
|  |     {.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01}, | ||||||
|  |     {.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994}, | ||||||
|  |     {.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6}, | ||||||
|  |     {.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32}, | ||||||
|  |     {.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3}, | ||||||
|  |     {.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5}, | ||||||
|  |     {.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a}, | ||||||
|  |     {.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80}, | ||||||
|  |     {.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d}, | ||||||
|  |     {.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2}, | ||||||
|  |     {.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324}, | ||||||
|  |     {.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae}, | ||||||
|  |     {.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda}, | ||||||
|  |     {.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d}, | ||||||
|  |     {.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465}, | ||||||
|  |     {.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4}, | ||||||
|  |     {.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint8_t sector = 11; | ||||||
|  |     uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); | ||||||
|  |     FURI_LOG_D("Troika", "Verifying sector %d", sector); | ||||||
|  |     if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { | ||||||
|  |         FURI_LOG_D("Troika", "Sector %d verified", sector); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     MfClassicReader reader = {}; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); | ||||||
|  |     for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) { | ||||||
|  |         mf_classic_reader_add_sector( | ||||||
|  |             &reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool troika_4k_parser_parse(NfcDeviceData* dev_data) { | ||||||
|  |     MfClassicData* data = &dev_data->mf_classic_data; | ||||||
|  | 
 | ||||||
|  |     // Verify key
 | ||||||
|  |     MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); | ||||||
|  |     uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); | ||||||
|  |     if(key != troika_4k_keys[4].key_a) return false; | ||||||
|  | 
 | ||||||
|  |     // Verify card type
 | ||||||
|  |     if(data->type != MfClassicType4k) return false; | ||||||
|  | 
 | ||||||
|  |     uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; | ||||||
|  |     uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25; | ||||||
|  |     temp_ptr = &data->block[8 * 4].value[3]; | ||||||
|  |     uint32_t number = 0; | ||||||
|  |     for(size_t i = 0; i < 4; i++) { | ||||||
|  |         number <<= 8; | ||||||
|  |         number |= temp_ptr[i]; | ||||||
|  |     } | ||||||
|  |     number >>= 4; | ||||||
|  | 
 | ||||||
|  |     string_printf(dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								lib/nfc/parsers/troika_4k_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/nfc/parsers/troika_4k_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool troika_4k_parser_parse(NfcDeviceData* dev_data); | ||||||
| @ -3,7 +3,7 @@ | |||||||
| #include <gui/modules/widget.h> | #include <gui/modules/widget.h> | ||||||
| #include <nfc_worker_i.h> | #include <nfc_worker_i.h> | ||||||
| 
 | 
 | ||||||
| static const MfClassicAuthContext troyka_keys[] = { | static const MfClassicAuthContext troika_keys[] = { | ||||||
|     {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, |     {.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58}, | ||||||
|     {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, |     {.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880}, | ||||||
|     {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, |     {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
| @ -22,42 +22,50 @@ static const MfClassicAuthContext troyka_keys[] = { | |||||||
|     {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, |     {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|     furi_assert(nfc_worker); |     furi_assert(nfc_worker); | ||||||
|     UNUSED(nfc_worker); |     UNUSED(nfc_worker); | ||||||
|  |     if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     MfClassicAuthContext auth_ctx = { |     uint8_t sector = 11; | ||||||
|         .key_a = MF_CLASSIC_NO_KEY, |     uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); | ||||||
|         .key_b = MF_CLASSIC_NO_KEY, |     FURI_LOG_D("Troika", "Verifying sector %d", sector); | ||||||
|         .sector = 8, |     if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) { | ||||||
|     }; |         FURI_LOG_D("Troika", "Sector %d verified", sector); | ||||||
|     return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333); |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|     furi_assert(nfc_worker); |     furi_assert(nfc_worker); | ||||||
| 
 | 
 | ||||||
|     MfClassicReader reader = {}; |     MfClassicReader reader = {}; | ||||||
|     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|     reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); |     reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); | ||||||
| 
 | 
 | ||||||
|     for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) { |     for(size_t i = 0; i < COUNT_OF(troika_keys); i++) { | ||||||
|         mf_classic_reader_add_sector( |         mf_classic_reader_add_sector( | ||||||
|             &reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b); |             &reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; |     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool troyka_parser_parse(NfcDeviceData* dev_data) { | bool troika_parser_parse(NfcDeviceData* dev_data) { | ||||||
|     MfClassicData* data = &dev_data->mf_classic_data; |     MfClassicData* data = &dev_data->mf_classic_data; | ||||||
|     bool troyka_parsed = false; |     bool troika_parsed = false; | ||||||
| 
 | 
 | ||||||
|     do { |     do { | ||||||
|         // Verify key
 |         // Verify key
 | ||||||
|         MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); |         MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8); | ||||||
|         uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); |         uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); | ||||||
|         if(key != troyka_keys[8].key_a) break; |         if(key != troika_keys[8].key_a) break; | ||||||
|  | 
 | ||||||
|  |         // Verify card type
 | ||||||
|  |         if(data->type != MfClassicType1k) break; | ||||||
| 
 | 
 | ||||||
|         // Parse data
 |         // Parse data
 | ||||||
|         uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; |         uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5]; | ||||||
| @ -71,9 +79,9 @@ bool troyka_parser_parse(NfcDeviceData* dev_data) { | |||||||
|         number >>= 4; |         number >>= 4; | ||||||
| 
 | 
 | ||||||
|         string_printf( |         string_printf( | ||||||
|             dev_data->parsed_data, "\e#Troyka\nNum: %ld\nBalance: %d rur.", number, balance); |             dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance); | ||||||
|         troyka_parsed = true; |         troika_parsed = true; | ||||||
|     } while(false); |     } while(false); | ||||||
| 
 | 
 | ||||||
|     return troyka_parsed; |     return troika_parsed; | ||||||
| } | } | ||||||
							
								
								
									
										9
									
								
								lib/nfc/parsers/troika_parser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/nfc/parsers/troika_parser.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool troika_parser_parse(NfcDeviceData* dev_data); | ||||||
| @ -1,9 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include "nfc_supported_card.h" |  | ||||||
| 
 |  | ||||||
| bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); |  | ||||||
| 
 |  | ||||||
| bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); |  | ||||||
| 
 |  | ||||||
| bool troyka_parser_parse(NfcDeviceData* dev_data); |  | ||||||
							
								
								
									
										171
									
								
								lib/nfc/parsers/two_cities.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								lib/nfc/parsers/two_cities.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,171 @@ | |||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | #include "plantain_parser.h" // For plantain-specific stuff | ||||||
|  | 
 | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | #include <nfc_worker_i.h> | ||||||
|  | 
 | ||||||
|  | #include "furi_hal.h" | ||||||
|  | 
 | ||||||
|  | static const MfClassicAuthContext two_cities_keys_4k[] = { | ||||||
|  |     {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b}, | ||||||
|  |     {.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a}, | ||||||
|  |     {.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81}, | ||||||
|  |     {.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763}, | ||||||
|  |     {.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb}, | ||||||
|  |     {.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56}, | ||||||
|  |     {.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26}, | ||||||
|  |     {.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, | ||||||
|  |     {.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf}, | ||||||
|  |     {.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a}, | ||||||
|  |     {.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b}, | ||||||
|  |     {.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3}, | ||||||
|  |     {.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22}, | ||||||
|  |     {.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056}, | ||||||
|  |     {.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510}, | ||||||
|  |     {.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf}, | ||||||
|  |     {.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439}, | ||||||
|  |     {.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406}, | ||||||
|  |     {.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3}, | ||||||
|  |     {.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA}, | ||||||
|  |     {.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f}, | ||||||
|  |     {.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3}, | ||||||
|  |     {.sector = 30, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7}, | ||||||
|  |     {.sector = 31, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3}, | ||||||
|  |     {.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023}, | ||||||
|  |     {.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9}, | ||||||
|  |     {.sector = 34, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 35, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99}, | ||||||
|  |     {.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7}, | ||||||
|  |     {.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a}, | ||||||
|  |     {.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7}, | ||||||
|  |     {.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  |     UNUSED(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) { | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     uint8_t sector = 4; | ||||||
|  |     uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector); | ||||||
|  |     FURI_LOG_D("2cities", "Verifying sector %d", sector); | ||||||
|  |     if(mf_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) { | ||||||
|  |         FURI_LOG_D("2cities", "Sector %d verified", sector); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) { | ||||||
|  |     furi_assert(nfc_worker); | ||||||
|  | 
 | ||||||
|  |     MfClassicReader reader = {}; | ||||||
|  |     FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data; | ||||||
|  |     reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak); | ||||||
|  |     for(size_t i = 0; i < COUNT_OF(two_cities_keys_4k); i++) { | ||||||
|  |         mf_classic_reader_add_sector( | ||||||
|  |             &reader, | ||||||
|  |             two_cities_keys_4k[i].sector, | ||||||
|  |             two_cities_keys_4k[i].key_a, | ||||||
|  |             two_cities_keys_4k[i].key_b); | ||||||
|  |         FURI_LOG_T("2cities", "Added sector %d", two_cities_keys_4k[i].sector); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool two_cities_parser_parse(NfcDeviceData* dev_data) { | ||||||
|  |     MfClassicData* data = &dev_data->mf_classic_data; | ||||||
|  | 
 | ||||||
|  |     // Verify key
 | ||||||
|  |     MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4); | ||||||
|  |     uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); | ||||||
|  |     if(key != two_cities_keys_4k[4].key_a) return false; | ||||||
|  | 
 | ||||||
|  |     // =====
 | ||||||
|  |     // PLANTAIN
 | ||||||
|  |     // =====
 | ||||||
|  | 
 | ||||||
|  |     // Point to block 0 of sector 4, value 0
 | ||||||
|  |     uint8_t* temp_ptr = &data->block[4 * 4].value[0]; | ||||||
|  |     // Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
 | ||||||
|  |     // 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
 | ||||||
|  |     uint32_t balance = | ||||||
|  |         ((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100; | ||||||
|  |     // Read card number
 | ||||||
|  |     // Point to block 0 of sector 0, value 0
 | ||||||
|  |     temp_ptr = &data->block[0 * 4].value[0]; | ||||||
|  |     // Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
 | ||||||
|  |     // 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal
 | ||||||
|  |     uint8_t card_number_arr[7]; | ||||||
|  |     for(size_t i = 0; i < 7; i++) { | ||||||
|  |         card_number_arr[i] = temp_ptr[6 - i]; | ||||||
|  |     } | ||||||
|  |     // Copy card number to uint64_t
 | ||||||
|  |     uint64_t card_number = 0; | ||||||
|  |     for(size_t i = 0; i < 7; i++) { | ||||||
|  |         card_number = (card_number << 8) | card_number_arr[i]; | ||||||
|  |     } | ||||||
|  |     // Convert card number to string
 | ||||||
|  |     string_t card_number_str; | ||||||
|  |     string_init(card_number_str); | ||||||
|  |     // Should look like "361301047292848684"
 | ||||||
|  |     // %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead
 | ||||||
|  |     string_push_uint64(card_number, card_number_str); | ||||||
|  |     // Add suffix with luhn checksum (1 digit) to the card number string
 | ||||||
|  |     string_t card_number_suffix; | ||||||
|  |     string_init(card_number_suffix); | ||||||
|  | 
 | ||||||
|  |     // The number to calculate the checksum on doesn't fit into uint64_t, idk
 | ||||||
|  |     //uint8_t luhn_checksum = two_cities_calculate_luhn(card_number);
 | ||||||
|  | 
 | ||||||
|  |     // // Convert luhn checksum to string
 | ||||||
|  |     // string_t luhn_checksum_str;
 | ||||||
|  |     // string_init(luhn_checksum_str);
 | ||||||
|  |     // string_push_uint64(luhn_checksum, luhn_checksum_str);
 | ||||||
|  | 
 | ||||||
|  |     string_cat_printf(card_number_suffix, "-"); | ||||||
|  |     // FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum);
 | ||||||
|  |     string_cat_printf(card_number_str, string_get_cstr(card_number_suffix)); | ||||||
|  |     // Free all not needed strings
 | ||||||
|  |     string_clear(card_number_suffix); | ||||||
|  |     // string_clear(luhn_checksum_str);
 | ||||||
|  | 
 | ||||||
|  |     // =====
 | ||||||
|  |     // --PLANTAIN--
 | ||||||
|  |     // =====
 | ||||||
|  |     // TROIKA
 | ||||||
|  |     // =====
 | ||||||
|  | 
 | ||||||
|  |     uint8_t* troika_temp_ptr = &data->block[8 * 4 + 1].value[5]; | ||||||
|  |     uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25; | ||||||
|  |     troika_temp_ptr = &data->block[8 * 4].value[3]; | ||||||
|  |     uint32_t troika_number = 0; | ||||||
|  |     for(size_t i = 0; i < 4; i++) { | ||||||
|  |         troika_number <<= 8; | ||||||
|  |         troika_number |= troika_temp_ptr[i]; | ||||||
|  |     } | ||||||
|  |     troika_number >>= 4; | ||||||
|  | 
 | ||||||
|  |     string_printf( | ||||||
|  |         dev_data->parsed_data, | ||||||
|  |         "\e#Troika+Plantain\nPN: %s\nPB: %d rur.\nTN: %d\nTB: %d rur.\n", | ||||||
|  |         string_get_cstr(card_number_str), | ||||||
|  |         balance, | ||||||
|  |         troika_number, | ||||||
|  |         troika_balance); | ||||||
|  |     string_clear(card_number_str); | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								lib/nfc/parsers/two_cities.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								lib/nfc/parsers/two_cities.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "nfc_supported_card.h" | ||||||
|  | 
 | ||||||
|  | bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx); | ||||||
|  | 
 | ||||||
|  | bool two_cities_parser_parse(NfcDeviceData* dev_data); | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Astra
						Astra