-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dev' into hid-keyboard
- Loading branch information
Showing
5 changed files
with
309 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#include "nfc_supported_card_plugin.h" | ||
|
||
#include <flipper_application/flipper_application.h> | ||
#include <machine/endian.h> | ||
#include <nfc/protocols/st25tb/st25tb.h> | ||
|
||
#define TAG "MyKey" | ||
|
||
const uint32_t blankBlock18 = 0x480FCD8F, blankBlock19 = 0x070082C0; | ||
|
||
static bool mykey_is_blank(const St25tbData* data) { | ||
return data->blocks[0x18] == blankBlock18 && data->blocks[0x19] == blankBlock19; | ||
} | ||
|
||
static bool mykey_has_lockid(const St25tbData* data) { | ||
return (data->blocks[5] & 0xFF) == 0x7F; | ||
} | ||
|
||
static bool mykey_parse(const NfcDevice* device, FuriString* parsed_data) { | ||
furi_assert(device); | ||
furi_assert(parsed_data); | ||
|
||
const St25tbData* data = nfc_device_get_data(device, NfcProtocolSt25tb); | ||
|
||
if(data->type != St25tbType04k && data->type != St25tbTypeX4k) { | ||
FURI_LOG_D(TAG, "bad type"); | ||
return false; | ||
} | ||
|
||
for(int i = 0; i < 5; i++) { | ||
if(data->blocks[i] != 0xFFFFFFFF) { | ||
FURI_LOG_D(TAG, "bad otp block %d", i); | ||
return false; | ||
} | ||
} | ||
|
||
if((data->blocks[8] >> 16 & 0xFF) > 0x31 || (data->blocks[8] >> 8 & 0xFF) > 0x12) { | ||
FURI_LOG_D(TAG, "bad mfg date"); | ||
return false; | ||
} | ||
|
||
if(data->system_otp_block != 0xFEFFFFFF) { | ||
FURI_LOG_D(TAG, "bad sys otp block"); | ||
return false; | ||
} | ||
|
||
furi_string_cat(parsed_data, "\e#MyKey\n"); | ||
|
||
if(data->blocks[6] == 0) { // Tag is actually a MyKey but it has been bricked by a reader | ||
furi_string_cat(parsed_data, "\e#Bricked!\nBlock 6 is 0!"); | ||
return true; | ||
} | ||
|
||
bool is_blank = mykey_is_blank(data); | ||
furi_string_cat_printf(parsed_data, "Serial#: %08lX\n", __bswap32(data->blocks[7])); | ||
furi_string_cat_printf(parsed_data, "Blank: %s\n", is_blank ? "yes" : "no"); | ||
furi_string_cat_printf(parsed_data, "LockID: %s\n", mykey_has_lockid(data) ? "maybe" : "no"); | ||
|
||
uint32_t block8 = data->blocks[8]; | ||
furi_string_cat_printf( | ||
parsed_data, | ||
"Prod. date: %02lX/%02lX/%04lX", | ||
block8 >> 16 & 0xFF, | ||
block8 >> 8 & 0xFF, | ||
0x2000 + (block8 & 0xFF)); | ||
|
||
if(!is_blank) { | ||
furi_string_cat_printf( | ||
parsed_data, "\nOp. count: %ld\n", __bswap32(data->blocks[0x12] & 0xFFFFFF00)); | ||
|
||
uint32_t block3C = data->blocks[0x3C]; | ||
if(block3C == 0xFFFFFFFF) { | ||
furi_string_cat(parsed_data, "No history available!"); | ||
} else { | ||
block3C ^= data->blocks[0x07]; | ||
uint32_t startingOffset = ((block3C & 0x30000000) >> 28) | | ||
((block3C & 0x00100000) >> 18); | ||
furi_check(startingOffset < 8); | ||
for(int txnOffset = 8; txnOffset > 0; txnOffset--) { | ||
uint32_t txnBlock = | ||
__bswap32(data->blocks[0x34 + ((startingOffset + txnOffset) % 8)]); | ||
|
||
if(txnBlock == 0xFFFFFFFF) { | ||
break; | ||
} | ||
|
||
uint8_t day = txnBlock >> 27; | ||
uint8_t month = txnBlock >> 23 & 0xF; | ||
uint16_t year = 2000 + (txnBlock >> 16 & 0x7F); | ||
uint16_t credit = txnBlock & 0xFFFF; | ||
|
||
if(txnOffset == 8) { | ||
furi_string_cat_printf( | ||
parsed_data, "Current credit: %d.%02d euros\n", credit / 100, credit % 100); | ||
furi_string_cat(parsed_data, "Op. history (newest first):"); | ||
} | ||
|
||
furi_string_cat_printf( | ||
parsed_data, | ||
"\n %02d/%02d/%04d %d.%02d", | ||
day, | ||
month, | ||
year, | ||
credit / 100, | ||
credit % 100); | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
/* Actual implementation of app<>plugin interface */ | ||
static const NfcSupportedCardsPlugin mykey_plugin = { | ||
.protocol = NfcProtocolSt25tb, | ||
.verify = NULL, | ||
.read = NULL, | ||
.parse = mykey_parse, | ||
}; | ||
|
||
/* Plugin descriptor to comply with basic plugin specification */ | ||
static const FlipperAppPluginDescriptor mykey_plugin_descriptor = { | ||
.appid = NFC_SUPPORTED_CARD_PLUGIN_APP_ID, | ||
.ep_api_version = NFC_SUPPORTED_CARD_PLUGIN_API_VERSION, | ||
.entry_point = &mykey_plugin, | ||
}; | ||
|
||
/* Plugin entry point - must return a pointer to const descriptor */ | ||
const FlipperAppPluginDescriptor* mykey_plugin_ep() { | ||
return &mykey_plugin_descriptor; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/* | ||
* Parser for Umarsh card (Russia). | ||
* | ||
* Copyright 2023 Leptoptilos <[email protected]> | ||
* 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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters