Skip to content

Commit

Permalink
update picopass
Browse files Browse the repository at this point in the history
  • Loading branch information
xMasterX committed Sep 22, 2023
1 parent 2eb1433 commit 17e059f
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 91 deletions.
135 changes: 82 additions & 53 deletions picopass_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <flipper_format/flipper_format.h>
#include <picopass_icons.h>

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

#define TAG "PicopassDevice"

static const char* picopass_file_header = "Flipper Picopass device";
Expand All @@ -30,13 +34,77 @@ 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,
const char* folder,
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);
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
11 changes: 2 additions & 9 deletions picopass_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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);
4 changes: 2 additions & 2 deletions picopass_worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
14 changes: 6 additions & 8 deletions scenes/picopass_scene_card_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
14 changes: 4 additions & 10 deletions scenes/picopass_scene_device_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,20 @@ 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++;
}
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");
Expand Down
12 changes: 3 additions & 9 deletions scenes/picopass_scene_read_card_success.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -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");
Expand Down

0 comments on commit 17e059f

Please sign in to comment.