From 17e059f4827314c6a9f7a64fe9545995a3c2f3d1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Fri, 22 Sep 2023 22:03:21 +0300 Subject: [PATCH] update picopass --- picopass_device.c | 135 +++++++++++++--------- picopass_device.h | 11 +- picopass_worker.c | 4 +- scenes/picopass_scene_card_menu.c | 14 +-- scenes/picopass_scene_device_info.c | 14 +-- scenes/picopass_scene_read_card_success.c | 12 +- 6 files changed, 99 insertions(+), 91 deletions(-) diff --git a/picopass_device.c b/picopass_device.c index 73f79eca727..bbd77124395 100644 --- a/picopass_device.c +++ b/picopass_device.c @@ -4,6 +4,10 @@ #include #include +#include +#include +#include + #define TAG "PicopassDevice" static const char* picopass_file_header = "Flipper Picopass device"; @@ -30,6 +34,69 @@ void picopass_device_set_name(PicopassDevice* dev, const char* name) { strlcpy(dev->dev_name, name, PICOPASS_DEV_NAME_MAX_LEN); } +static bool picopass_device_save_file_lfrfid(PicopassDevice* dev, FuriString* file_path) { + furi_assert(dev); + PicopassPacs* pacs = &dev->dev_data.pacs; + ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax); + ProtocolId protocol = LFRFIDProtocolHidGeneric; + + bool result = false; + uint64_t target = 0; + uint64_t sentinel = 1ULL << pacs->bitLength; + memcpy(&target, pacs->credential, RFAL_PICOPASS_BLOCK_LEN); + target = __builtin_bswap64(target); + FURI_LOG_D(TAG, "Original (%d): %016llx", pacs->bitLength, target); + + if(pacs->bitLength == 26) { + //3 bytes + protocol = LFRFIDProtocolH10301; + // Remove parity + target = (target >> 1) & 0xFFFFFF; + // Reverse order since it'll get reversed again + target = __builtin_bswap64(target) >> (64 - 24); + } else if(pacs->bitLength < 44) { + // https://gist.github.com/blark/e8f125e402f576bdb7e2d7b3428bdba6 + protocol = LFRFIDProtocolHidGeneric; + if(pacs->bitLength <= 36) { + uint64_t header = 1ULL << 37; + target = __builtin_bswap64((target | sentinel | header) << 4) >> (64 - 48); + } else { + target = __builtin_bswap64((target | sentinel) << 4) >> (64 - 48); + } + } else { + //8 bytes + protocol = LFRFIDProtocolHidExGeneric; + target = __builtin_bswap64(target); + } + + size_t data_size = protocol_dict_get_data_size(dict, protocol); + uint8_t* data = malloc(data_size); + if(data_size < 8) { + memcpy(data, (void*)&target, data_size); + } else { + // data_size 12 for LFRFIDProtocolHidExGeneric + memcpy(data + 4, (void*)&target, 8); + } + + protocol_dict_set_data(dict, protocol, data, data_size); + free(data); + + FuriString* briefStr; + briefStr = furi_string_alloc(); + protocol_dict_render_brief_data(dict, briefStr, protocol); + FURI_LOG_D(TAG, "LFRFID Brief: %s", furi_string_get_cstr(briefStr)); + + result = lfrfid_dict_file_save(dict, protocol, furi_string_get_cstr(file_path)); + if(result) { + FURI_LOG_D(TAG, "Written: %d", result); + } else { + FURI_LOG_D(TAG, "Failed to write"); + } + + protocol_dict_free(dict); + return result; +} + static bool picopass_device_save_file( PicopassDevice* dev, const char* dev_name, @@ -37,6 +104,7 @@ static bool picopass_device_save_file( const char* extension, bool use_load_path) { furi_assert(dev); + FURI_LOG_D(TAG, "Save File"); bool saved = false; FlipperFormat* file = flipper_format_file_alloc(dev->storage); @@ -55,30 +123,18 @@ static bool picopass_device_save_file( // First remove picopass device file if it was saved furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension); } - // Open file - if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; if(dev->format == PicopassDeviceSaveFormatHF) { - uint32_t fc = pacs->record.FacilityCode; - uint32_t cn = pacs->record.CardNumber; + // Open file + if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; + // Write header if(!flipper_format_write_header_cstr(file, picopass_file_header, picopass_file_version)) break; - if(pacs->record.valid) { - if(!flipper_format_write_uint32(file, "Facility Code", &fc, 1)) break; - if(!flipper_format_write_uint32(file, "Card Number", &cn, 1)) break; - if(!flipper_format_write_hex( - file, "Credential", pacs->credential, RFAL_PICOPASS_BLOCK_LEN)) - break; - if(pacs->pin_length > 0) { - if(!flipper_format_write_hex( - file, "PIN\t\t", pacs->pin0, RFAL_PICOPASS_BLOCK_LEN)) - break; - if(!flipper_format_write_hex( - file, "PIN(cont.)\t", pacs->pin1, RFAL_PICOPASS_BLOCK_LEN)) - break; - } - } + if(!flipper_format_write_hex( + file, "Credential", pacs->credential, RFAL_PICOPASS_BLOCK_LEN)) + break; + // TODO: Add elite if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break; bool block_saved = true; @@ -99,20 +155,7 @@ static bool picopass_device_save_file( } if(!block_saved) break; } else if(dev->format == PicopassDeviceSaveFormatLF) { - const char* lf_header = "Flipper RFID key"; - // Write header - if(!flipper_format_write_header_cstr(file, lf_header, 1)) break; - if(!flipper_format_write_comment_cstr( - file, - "This was generated from the Picopass plugin and may not match current lfrfid")) - break; - // When lfrfid supports more formats, update this - if(!flipper_format_write_string_cstr(file, "Key type", "H10301")) break; - uint8_t H10301[3] = {0}; - H10301[0] = pacs->record.FacilityCode; - H10301[1] = pacs->record.CardNumber >> 8; - H10301[2] = pacs->record.CardNumber & 0x00FF; - if(!flipper_format_write_hex(file, "Data", H10301, 3)) break; + saved = picopass_device_save_file_lfrfid(dev, temp_str); } saved = true; } while(0); @@ -186,7 +229,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo if(!block_read) break; if(picopass_device_parse_credential(AA1, pacs) != ERR_NONE) break; - if(picopass_device_parse_wiegand(pacs->credential, &pacs->record) != ERR_NONE) break; + if(picopass_device_parse_wiegand(pacs->credential, pacs) != ERR_NONE) break; parsed = true; } while(false); @@ -356,37 +399,23 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa return ERR_NONE; } -ReturnCode picopass_device_parse_wiegand(uint8_t* credential, PicopassWiegandRecord* record) { +ReturnCode picopass_device_parse_wiegand(uint8_t* credential, PicopassPacs* pacs) { uint32_t* halves = (uint32_t*)credential; if(halves[0] == 0) { uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); - record->bitLength = 31 - leading0s; + pacs->bitLength = 31 - leading0s; } else { uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); - record->bitLength = 63 - leading0s; + pacs->bitLength = 63 - leading0s; } - FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); // Remove sentinel bit from credential. Byteswapping to handle array of bytes vs 64bit value - uint64_t sentinel = __builtin_bswap64(1ULL << record->bitLength); + uint64_t sentinel = __builtin_bswap64(1ULL << pacs->bitLength); uint64_t swapped = 0; memcpy(&swapped, credential, sizeof(uint64_t)); swapped = swapped ^ sentinel; memcpy(credential, &swapped, sizeof(uint64_t)); - FURI_LOG_D(TAG, "PACS: (%d) %016llx", record->bitLength, swapped); + FURI_LOG_D(TAG, "PACS: (%d) %016llx", pacs->bitLength, swapped); - if(record->bitLength == 26) { - uint8_t* v4 = credential + 4; - uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); - - record->CardNumber = (bot >> 1) & 0xFFFF; - record->FacilityCode = (bot >> 17) & 0xFF; - FURI_LOG_D(TAG, "FC: %u CN: %u", record->FacilityCode, record->CardNumber); - record->valid = true; - } else { - record->CardNumber = 0; - record->FacilityCode = 0; - record->valid = false; - } return ERR_NONE; } diff --git a/picopass_device.h b/picopass_device.h index 4cd4ba83c43..99b84fedb22 100644 --- a/picopass_device.h +++ b/picopass_device.h @@ -72,13 +72,6 @@ typedef enum { PicopassEmulatorStateSelected, } PicopassEmulatorState; -typedef struct { - bool valid; - uint8_t bitLength; - uint8_t FacilityCode; - uint16_t CardNumber; -} PicopassWiegandRecord; - typedef struct { bool legacy; bool se_enabled; @@ -88,10 +81,10 @@ typedef struct { bool elite_kdf; uint8_t pin_length; PicopassEncryption encryption; + uint8_t bitLength; uint8_t credential[8]; uint8_t pin0[8]; uint8_t pin1[8]; - PicopassWiegandRecord record; } PicopassPacs; typedef struct { @@ -146,4 +139,4 @@ void picopass_device_set_loading_callback( void* context); ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs); -ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record); +ReturnCode picopass_device_parse_wiegand(uint8_t* credential, PicopassPacs* pacs); diff --git a/picopass_worker.c b/picopass_worker.c index 494362cfcdc..581a8058086 100644 --- a/picopass_worker.c +++ b/picopass_worker.c @@ -606,7 +606,7 @@ void picopass_worker_elite_dict_attack(PicopassWorker* picopass_worker) { break; } - err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); + err = picopass_device_parse_wiegand(pacs->credential, pacs); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); picopass_worker->callback(PicopassWorkerEventFail, picopass_worker->context); @@ -715,7 +715,7 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) { } if(nextState == PicopassWorkerEventSuccess) { - err = picopass_device_parse_wiegand(pacs->credential, &pacs->record); + err = picopass_device_parse_wiegand(pacs->credential, pacs); if(err != ERR_NONE) { FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err); nextState = PicopassWorkerEventFail; diff --git a/scenes/picopass_scene_card_menu.c b/scenes/picopass_scene_card_menu.c index fa4515db3e7..c42ae67dea7 100644 --- a/scenes/picopass_scene_card_menu.c +++ b/scenes/picopass_scene_card_menu.c @@ -20,14 +20,12 @@ void picopass_scene_card_menu_on_enter(void* context) { submenu_add_item( submenu, "Save", SubmenuIndexSave, picopass_scene_card_menu_submenu_callback, picopass); - if(picopass->dev->dev_data.pacs.record.valid) { - submenu_add_item( - submenu, - "Save as LF", - SubmenuIndexSaveAsLF, - picopass_scene_card_menu_submenu_callback, - picopass); - } + submenu_add_item( + submenu, + "Save as LFRFID", + SubmenuIndexSaveAsLF, + picopass_scene_card_menu_submenu_callback, + picopass); submenu_add_item( submenu, "Write", SubmenuIndexWrite, picopass_scene_card_menu_submenu_callback, picopass); submenu_add_item( diff --git a/scenes/picopass_scene_device_info.c b/scenes/picopass_scene_device_info.c index 60ed6ed948b..bb4751f8fb1 100644 --- a/scenes/picopass_scene_device_info.c +++ b/scenes/picopass_scene_device_info.c @@ -31,12 +31,12 @@ void picopass_scene_device_info_on_enter(void* context) { furi_string_cat_printf(csn_str, "%02X ", csn[i]); } - if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + if(pacs->bitLength == 0 || pacs->bitLength == 255) { // Neither of these are valid. Indicates the block was all 0x00 or all 0xff furi_string_cat_printf(wiegand_str, "Invalid PACS"); } else { - size_t bytesLength = pacs->record.bitLength / 8; - if(pacs->record.bitLength % 8 > 0) { + size_t bytesLength = pacs->bitLength / 8; + if(pacs->bitLength % 8 > 0) { // Add extra byte if there are bits remaining bytesLength++; } @@ -44,13 +44,7 @@ void picopass_scene_device_info_on_enter(void* context) { for(uint8_t i = RFAL_PICOPASS_BLOCK_LEN - bytesLength; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(credential_str, "%02X", pacs->credential[i]); } - - if(pacs->record.valid) { - furi_string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); - } else { - furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); - } + furi_string_cat_printf(wiegand_str, "%d bits", pacs->bitLength); if(pacs->sio) { furi_string_cat_printf(credential_str, " +SIO"); diff --git a/scenes/picopass_scene_read_card_success.c b/scenes/picopass_scene_read_card_success.c index f35ac85979e..eeb07185aa7 100644 --- a/scenes/picopass_scene_read_card_success.c +++ b/scenes/picopass_scene_read_card_success.c @@ -64,7 +64,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { "Menu", picopass_scene_read_card_success_widget_callback, picopass); - } else if(pacs->record.bitLength == 0 || pacs->record.bitLength == 255) { + } else if(pacs->bitLength == 0 || pacs->bitLength == 255) { // Neither of these are valid. Indicates the block was all 0x00 or all 0xff furi_string_cat_printf(wiegand_str, "Invalid PACS"); @@ -78,18 +78,12 @@ void picopass_scene_read_card_success_on_enter(void* context) { picopass_scene_read_card_success_widget_callback, picopass); } else { - size_t bytesLength = 1 + pacs->record.bitLength / 8; + size_t bytesLength = 1 + pacs->bitLength / 8; furi_string_set(credential_str, ""); for(uint8_t i = RFAL_PICOPASS_BLOCK_LEN - bytesLength; i < RFAL_PICOPASS_BLOCK_LEN; i++) { furi_string_cat_printf(credential_str, "%02X", pacs->credential[i]); } - - if(pacs->record.valid) { - furi_string_cat_printf( - wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber); - } else { - furi_string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength); - } + furi_string_cat_printf(wiegand_str, "%d bits", pacs->bitLength); if(pacs->sio) { furi_string_cat_printf(credential_str, " +SIO");