|
|
@@ -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";
|
|
|
@@ -30,6 +34,70 @@ 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));
|
|
|
+ furi_string_free(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 +105,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 +124,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 +156,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 +230,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 +400,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;
|
|
|
}
|