Skip to content

Commit

Permalink
Picopass: Save unknown blocks with ?? (#196)
Browse files Browse the repository at this point in the history
Co-authored-by: あく <[email protected]>
  • Loading branch information
bettse and skotopes authored Apr 8, 2024
1 parent 88d65f7 commit 50c6e35
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 26 deletions.
85 changes: 70 additions & 15 deletions picopass/picopass_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <picopass_icons.h>

#include <toolbox/protocols/protocol_dict.h>
#include <toolbox/hex.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#include <lfrfid/lfrfid_dict_file.h>

Expand All @@ -15,6 +16,7 @@ static const uint32_t picopass_file_version = 1;

const uint8_t picopass_iclass_decryptionkey[] =
{0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36};
const char unknown_block[] = "?? ?? ?? ?? ?? ?? ?? ??";

PicopassDevice* picopass_device_alloc() {
PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice));
Expand Down Expand Up @@ -169,6 +171,7 @@ static bool picopass_device_save_file(
if(dev->format == PicopassDeviceSaveFormatPartial) {
// Clear key that may have been set when doing key tests for legacy
memset(card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0, PICOPASS_BLOCK_LEN);
card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].valid = false;
}

do {
Expand Down Expand Up @@ -203,13 +206,21 @@ static bool picopass_device_save_file(
PICOPASS_MAX_APP_LIMIT;
for(size_t i = 0; i < app_limit; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_write_hex(
file,
furi_string_get_cstr(temp_str),
card_data[i].data,
PICOPASS_BLOCK_LEN)) {
block_saved = false;
break;
if(card_data[i].valid) {
if(!flipper_format_write_hex(
file,
furi_string_get_cstr(temp_str),
card_data[i].data,
PICOPASS_BLOCK_LEN)) {
block_saved = false;
break;
}
} else {
if(!flipper_format_write_string_cstr(
file, furi_string_get_cstr(temp_str), unknown_block)) {
block_saved = false;
break;
}
}
}
if(!block_saved) break;
Expand Down Expand Up @@ -246,6 +257,19 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) {
return false;
}

bool picopass_hex_str_to_uint8(const char* value_str, uint8_t* value) {
furi_check(value_str);
furi_check(value);

bool parse_success = false;
while(*value_str && value_str[1]) {
parse_success = hex_char_to_uint8(*value_str, value_str[1], value++);
if(!parse_success) break;
value_str += 3;
}
return parse_success;
}

static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, bool show_dialog) {
bool parsed = false;
FlipperFormat* file = flipper_format_file_alloc(dev->storage);
Expand All @@ -260,43 +284,69 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo
}

do {
picopass_device_data_clear(&dev->dev_data);
if(!flipper_format_file_open_existing(file, furi_string_get_cstr(path))) break;

// Read and verify file header
uint32_t version = 0;
if(!flipper_format_read_header(file, temp_str, &version)) break;
if(furi_string_cmp_str(temp_str, picopass_file_header) ||
if(!furi_string_equal_str(temp_str, picopass_file_header) ||
(version != picopass_file_version)) {
deprecated_version = true;
break;
}

FuriString* block_str = furi_string_alloc();
// Parse header blocks
bool block_read = true;
for(size_t i = 0; i < 6; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_hex(
file, furi_string_get_cstr(temp_str), card_data[i].data, PICOPASS_BLOCK_LEN)) {
if(!flipper_format_read_string(file, furi_string_get_cstr(temp_str), block_str)) {
block_read = false;
break;
}
if(furi_string_equal_str(block_str, unknown_block)) {
FURI_LOG_D(TAG, "Block %i: %s (unknown)", i, furi_string_get_cstr(block_str));
card_data[i].valid = false;
memset(card_data[i].data, 0, PICOPASS_BLOCK_LEN);
} else {
FURI_LOG_D(TAG, "Block %i: %s (hex)", i, furi_string_get_cstr(block_str));
if(!picopass_hex_str_to_uint8(furi_string_get_cstr(block_str), card_data[i].data)) {
block_read = false;
break;
}
card_data[i].valid = true;
}
}

size_t app_limit = card_data[PICOPASS_CONFIG_BLOCK_INDEX].data[0];
// Fix for unpersonalized cards that have app_limit set to 0xFF
if(app_limit > PICOPASS_MAX_APP_LIMIT) app_limit = PICOPASS_MAX_APP_LIMIT;
for(size_t i = 6; i < app_limit; i++) {
furi_string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_hex(
file, furi_string_get_cstr(temp_str), card_data[i].data, PICOPASS_BLOCK_LEN)) {
if(!flipper_format_read_string(file, furi_string_get_cstr(temp_str), block_str)) {
block_read = false;
break;
}
if(furi_string_equal_str(block_str, unknown_block)) {
FURI_LOG_D(TAG, "Block %i: %s (unknown)", i, furi_string_get_cstr(block_str));
card_data[i].valid = false;
memset(card_data[i].data, 0, PICOPASS_BLOCK_LEN);
} else {
FURI_LOG_D(TAG, "Block %i: %s (hex)", i, furi_string_get_cstr(block_str));
if(!picopass_hex_str_to_uint8(furi_string_get_cstr(block_str), card_data[i].data)) {
block_read = false;
break;
}
card_data[i].valid = true;
}
}
if(!block_read) break;

picopass_device_parse_credential(card_data, pacs);
picopass_device_parse_wiegand(pacs->credential, pacs);
if(card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].valid) {
picopass_device_parse_credential(card_data, pacs);
picopass_device_parse_wiegand(pacs);
}

parsed = true;
} while(false);
Expand Down Expand Up @@ -371,10 +421,14 @@ void picopass_device_data_clear(PicopassDeviceData* dev_data) {
memset(dev_data->card_data[i].data, 0, sizeof(dev_data->card_data[i].data));
dev_data->card_data[i].valid = false;
}

memset(dev_data->pacs.credential, 0, sizeof(dev_data->pacs.credential));
dev_data->pacs.legacy = false;
dev_data->pacs.se_enabled = false;
dev_data->pacs.elite_kdf = false;
dev_data->pacs.sio = false;
dev_data->pacs.pin_length = 0;
dev_data->pacs.bitLength = 0;
}

bool picopass_device_delete(PicopassDevice* dev, bool use_load_path) {
Expand Down Expand Up @@ -450,7 +504,8 @@ void picopass_device_parse_credential(PicopassBlock* card_data, PicopassPacs* pa
pacs->sio = (card_data[10].data[0] == 0x30); // rough check
}

void picopass_device_parse_wiegand(uint8_t* credential, PicopassPacs* pacs) {
void picopass_device_parse_wiegand(PicopassPacs* pacs) {
uint8_t* credential = pacs->credential;
uint32_t* halves = (uint32_t*)credential;
if(halves[0] == 0) {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
Expand Down
2 changes: 1 addition & 1 deletion picopass/picopass_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,5 @@ void picopass_device_set_loading_callback(
void* context);

void picopass_device_parse_credential(PicopassBlock* card_data, PicopassPacs* pacs);
void picopass_device_parse_wiegand(uint8_t* credential, PicopassPacs* pacs);
void picopass_device_parse_wiegand(PicopassPacs* pacs);
bool picopass_device_hid_csn(PicopassDevice* dev);
2 changes: 1 addition & 1 deletion picopass/protocol/picopass_listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ PicopassListenerCommand
uint8_t rmac[4] = {};
uint8_t tmac[4] = {};
const uint8_t* key = instance->data->card_data[instance->key_block_num].data;
bool no_key = picopass_is_memset(key, 0x00, PICOPASS_BLOCK_LEN);
bool no_key = !instance->data->card_data[instance->key_block_num].valid;
const uint8_t* rx_data = bit_buffer_get_data(buf);

if(no_key) {
Expand Down
2 changes: 1 addition & 1 deletion picopass/protocol/picopass_poller.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ NfcCommand picopass_poller_parse_credential_handler(PicopassPoller* instance) {
NfcCommand picopass_poller_parse_wiegand_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;

picopass_device_parse_wiegand(instance->data->pacs.credential, &instance->data->pacs);
picopass_device_parse_wiegand(&instance->data->pacs);
instance->state = PicopassPollerStateSuccess;
return command;
}
Expand Down
3 changes: 1 addition & 2 deletions picopass/scenes/picopass_scene_card_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ void picopass_scene_card_menu_on_enter(void* context) {
bool zero_config = picopass_is_memset(
card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data, 0x00, PICOPASS_BLOCK_LEN);
bool no_credential = picopass_is_memset(pacs->credential, 0x00, sizeof(pacs->credential));
bool no_key = picopass_is_memset(
card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN);
bool no_key = !card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].valid;

if(secured && zero_config) {
submenu_add_item(
Expand Down
3 changes: 1 addition & 2 deletions picopass/scenes/picopass_scene_device_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ bool picopass_scene_device_info_on_event(void* context, SceneManagerEvent event)
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
consumed = true;
consumed = scene_manager_previous_scene(picopass->scene_manager);
}
return consumed;
}
Expand Down
8 changes: 6 additions & 2 deletions picopass/scenes/picopass_scene_more_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ void picopass_scene_more_info_on_enter(void* context) {

for(size_t i = 0; i < app_limit; i++) {
for(size_t j = 0; j < PICOPASS_BLOCK_LEN; j += 2) {
furi_string_cat_printf(
str, "%02X%02X ", card_data[i].data[j], card_data[i].data[j + 1]);
if(card_data[i].valid) {
furi_string_cat_printf(
str, "%02X%02X ", card_data[i].data[j], card_data[i].data[j + 1]);
} else {
furi_string_cat_printf(str, "???? ");
}
}
}

Expand Down
3 changes: 1 addition & 2 deletions picopass/scenes/picopass_scene_read_card_success.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,7 @@ void picopass_scene_read_card_success_on_enter(void* context) {
furi_string_cat_printf(credential_str, " +SIO");
}

bool no_key = picopass_is_memset(
card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN);
bool no_key = !card_data[PICOPASS_SECURE_KD_BLOCK_INDEX].valid;

if(no_key) {
furi_string_cat_printf(key_str, "No Key: used NR-MAC");
Expand Down

0 comments on commit 50c6e35

Please sign in to comment.