Umarsh transport cards parser (#3277)
* Umarsh transport card parser added * Volna transport cards keys added Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									78b7310057
								
							
						
					
					
						commit
						0af74fb755
					
				| @ -92,6 +92,15 @@ App( | |||||||
|     sources=["plugins/supported_cards/aime.c"], |     sources=["plugins/supported_cards/aime.c"], | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | App( | ||||||
|  |     appid="umarsh_parser", | ||||||
|  |     apptype=FlipperAppType.PLUGIN, | ||||||
|  |     entry_point="umarsh_plugin_ep", | ||||||
|  |     targets=["f7"], | ||||||
|  |     requires=["nfc"], | ||||||
|  |     sources=["plugins/supported_cards/umarsh.c"], | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| App( | App( | ||||||
|     appid="nfc_start", |     appid="nfc_start", | ||||||
|     targets=["f7"], |     targets=["f7"], | ||||||
|  | |||||||
							
								
								
									
										155
									
								
								applications/main/nfc/plugins/supported_cards/umarsh.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								applications/main/nfc/plugins/supported_cards/umarsh.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,155 @@ | |||||||
|  | /*
 | ||||||
|  |  * Parser for Umarsh card (Russia). | ||||||
|  |  * | ||||||
|  |  * Copyright 2023 Leptoptilos <leptoptilos@icloud.com> | ||||||
|  |  * Thanks https://github.com/krolchonok for the provided dumps and their analysis
 | ||||||
|  |  * | ||||||
|  |  * Note: All meaningful data is stored in sectors 0, 8 and 12, reading data  | ||||||
|  |  * from which is possible only with the B key. The key B for these sectors  | ||||||
|  |  * is unique for each card. To get it, you should use a nested attack. | ||||||
|  |  * More info about Umarsh cards: https://github.com/metrodroid/metrodroid/wiki/Umarsh
 | ||||||
|  |  * | ||||||
|  |  * This program is free software: you can redistribute it and/or modify it | ||||||
|  |  * under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation, either version 3 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, but | ||||||
|  |  * WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | ||||||
|  |  * General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License | ||||||
|  |  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include "core/core_defines.h" | ||||||
|  | #include "nfc_supported_card_plugin.h" | ||||||
|  | 
 | ||||||
|  | #include "protocols/mf_classic/mf_classic.h" | ||||||
|  | #include <flipper_application/flipper_application.h> | ||||||
|  | 
 | ||||||
|  | #include <nfc/nfc_device.h> | ||||||
|  | #include <nfc/helpers/nfc_util.h> | ||||||
|  | #include <nfc/protocols/mf_classic/mf_classic_poller_sync.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <furi_hal_rtc.h> | ||||||
|  | 
 | ||||||
|  | #define TAG "Umarsh" | ||||||
|  | 
 | ||||||
|  | bool parse_datetime(uint16_t date, FuriHalRtcDateTime* result) { | ||||||
|  |     result->year = 2000 + (date >> 9); | ||||||
|  |     result->month = date >> 5 & 0x0F; | ||||||
|  |     result->day = date & 0x1F; | ||||||
|  |     return (date != 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool umarsh_parse(const NfcDevice* device, FuriString* parsed_data) { | ||||||
|  |     furi_assert(device); | ||||||
|  | 
 | ||||||
|  |     const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic); | ||||||
|  | 
 | ||||||
|  |     bool parsed = false; | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         // Verify card type
 | ||||||
|  |         if(data->type != MfClassicType1k) break; | ||||||
|  | 
 | ||||||
|  |         const uint8_t ticket_sector = 8; | ||||||
|  | 
 | ||||||
|  |         const uint8_t ticket_sector_start_block_number = | ||||||
|  |             mf_classic_get_first_block_num_of_sector(ticket_sector); | ||||||
|  | 
 | ||||||
|  |         // Validate specific for Umarsh ticket sector header
 | ||||||
|  |         const uint8_t* block_start_ptr = &data->block[ticket_sector_start_block_number].data[0]; | ||||||
|  | 
 | ||||||
|  |         const uint32_t header_part_0 = nfc_util_bytes2num(block_start_ptr, 4); | ||||||
|  |         const uint32_t header_part_1 = nfc_util_bytes2num(block_start_ptr + 4, 4); | ||||||
|  |         if((header_part_0 + header_part_1) != 0xFFFFFFFF) break; | ||||||
|  | 
 | ||||||
|  |         // Data parsing from block 1
 | ||||||
|  |         block_start_ptr = &data->block[ticket_sector_start_block_number + 1].data[0]; | ||||||
|  |         const uint16_t expiry_date = nfc_util_bytes2num(block_start_ptr + 1, 2); | ||||||
|  |         const uint8_t region_number = (((block_start_ptr[8] >> 5) & 0x07) << 4) | | ||||||
|  |                                       (block_start_ptr[12] & 0x0F); | ||||||
|  |         const uint8_t refill_counter = nfc_util_bytes2num(block_start_ptr + 7, 1); | ||||||
|  |         const uint32_t card_number = nfc_util_bytes2num(block_start_ptr + 8, 4) & 0x3FFFFFFF; | ||||||
|  | 
 | ||||||
|  |         if(card_number == 0) break; | ||||||
|  | 
 | ||||||
|  |         // Data parsing from block 2
 | ||||||
|  |         block_start_ptr = &data->block[ticket_sector_start_block_number + 2].data[0]; | ||||||
|  |         const uint16_t valid_to = nfc_util_bytes2num(block_start_ptr, 2); | ||||||
|  |         const uint32_t terminal_number = nfc_util_bytes2num(block_start_ptr + 3, 3); | ||||||
|  |         const uint16_t last_refill_date = nfc_util_bytes2num(block_start_ptr + 6, 2); | ||||||
|  |         const uint16_t balance_rub = (nfc_util_bytes2num(block_start_ptr + 8, 2)) & 0x7FFF; | ||||||
|  |         const uint8_t balance_kop = nfc_util_bytes2num(block_start_ptr + 10, 1) & 0x7F; | ||||||
|  | 
 | ||||||
|  |         FuriHalRtcDateTime expiry_datetime; | ||||||
|  |         bool is_expiry_datetime_valid = parse_datetime(expiry_date, &expiry_datetime); | ||||||
|  | 
 | ||||||
|  |         FuriHalRtcDateTime valid_to_datetime; | ||||||
|  |         bool is_valid_to_datetime_valid = parse_datetime(valid_to, &valid_to_datetime); | ||||||
|  | 
 | ||||||
|  |         FuriHalRtcDateTime last_refill_datetime; | ||||||
|  |         bool is_last_refill_datetime_valid = | ||||||
|  |             parse_datetime(last_refill_date, &last_refill_datetime); | ||||||
|  | 
 | ||||||
|  |         furi_string_cat_printf( | ||||||
|  |             parsed_data, | ||||||
|  |             "\e#Umarsh\nCard number: %lu\nRegion: %02u\nTerminal number: %lu\nRefill counter: %u\nBalance: %u.%02u RUR", | ||||||
|  |             card_number, | ||||||
|  |             region_number, | ||||||
|  |             terminal_number, | ||||||
|  |             refill_counter, | ||||||
|  |             balance_rub, | ||||||
|  |             balance_kop); | ||||||
|  | 
 | ||||||
|  |         if(is_expiry_datetime_valid) | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 parsed_data, | ||||||
|  |                 "\nExpires: %02u.%02u.%u", | ||||||
|  |                 expiry_datetime.day, | ||||||
|  |                 expiry_datetime.month, | ||||||
|  |                 expiry_datetime.year); | ||||||
|  |         if(is_valid_to_datetime_valid) | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 parsed_data, | ||||||
|  |                 "\nValid to: %02u.%02u.%u", | ||||||
|  |                 valid_to_datetime.day, | ||||||
|  |                 valid_to_datetime.month, | ||||||
|  |                 valid_to_datetime.year); | ||||||
|  |         if(is_last_refill_datetime_valid) | ||||||
|  |             furi_string_cat_printf( | ||||||
|  |                 parsed_data, | ||||||
|  |                 "\nLast refill: %02u.%02u.%u", | ||||||
|  |                 last_refill_datetime.day, | ||||||
|  |                 last_refill_datetime.month, | ||||||
|  |                 last_refill_datetime.year); | ||||||
|  | 
 | ||||||
|  |         parsed = true; | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     return parsed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Actual implementation of app<>plugin interface */ | ||||||
|  | static const NfcSupportedCardsPlugin umarsh_plugin = { | ||||||
|  |     .protocol = NfcProtocolMfClassic, | ||||||
|  |     .verify = NULL, | ||||||
|  |     .read = NULL, | ||||||
|  |     .parse = umarsh_parse, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Plugin descriptor to comply with basic plugin specification */ | ||||||
|  | static const FlipperAppPluginDescriptor umarsh_plugin_descriptor = { | ||||||
|  |     .appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, | ||||||
|  |     .ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, | ||||||
|  |     .entry_point = &umarsh_plugin, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Plugin entry point - must return a pointer to const descriptor  */ | ||||||
|  | const FlipperAppPluginDescriptor* umarsh_plugin_ep() { | ||||||
|  |     return &umarsh_plugin_descriptor; | ||||||
|  | } | ||||||
| @ -1312,3 +1312,7 @@ CE99FBC8BD26 | |||||||
| # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) | # PIK Comfort Moscow keys (ISBC Mifare Plus SE 1K) | ||||||
| 009FB42D98ED | 009FB42D98ED | ||||||
| 002E626E2820 | 002E626E2820 | ||||||
|  | 
 | ||||||
|  | # Volgograd (Russia) Volna transport cards keys | ||||||
|  | 2B787A063D5D | ||||||
|  | D37C8F1793F7 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Leptopt1los
						Leptopt1los