Skip to content

Commit

Permalink
upd picopass
Browse files Browse the repository at this point in the history
  • Loading branch information
xMasterX committed Dec 7, 2023
1 parent b55c5ff commit c470295
Show file tree
Hide file tree
Showing 14 changed files with 433 additions and 47 deletions.
2 changes: 1 addition & 1 deletion base_pack/picopass/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ App(
],
stack_size=4 * 1024,
fap_description="App to communicate with NFC tags using the PicoPass(iClass) format",
fap_version="1.8",
fap_version="1.9",
fap_icon="125_10px.png",
fap_category="NFC",
fap_libs=["mbedtls"],
Expand Down
62 changes: 60 additions & 2 deletions base_pack/picopass/picopass_device.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,59 @@ void picopass_device_set_name(PicopassDevice* dev, const char* name) {
strlcpy(dev->dev_name, name, PICOPASS_DEV_NAME_MAX_LEN);
}

// For use with Seader's virtual card processing.
static bool picopass_device_save_file_seader(
PicopassDevice* dev,
FlipperFormat* file,
FuriString* file_path) {
furi_assert(dev);
PicopassPacs* pacs = &dev->dev_data.pacs;
PicopassBlock* AA1 = dev->dev_data.AA1;
bool result = false;

const char* seader_file_header = "Flipper Seader Credential";
const uint32_t seader_file_version = 1;

do {
FURI_LOG_D(
TAG,
"Save %s %ld to %s",
seader_file_header,
seader_file_version,
furi_string_get_cstr(file_path));
if(!flipper_format_file_open_always(file, furi_string_get_cstr(file_path))) break;
if(!flipper_format_write_header_cstr(file, seader_file_header, seader_file_version)) break;
if(!flipper_format_write_uint32(file, "Bits", (uint32_t*)&pacs->bitLength, 1)) break;
if(!flipper_format_write_hex(file, "Credential", pacs->credential, PICOPASS_BLOCK_LEN))
break;

FURI_LOG_D(TAG, "Pre-sio");
// Seader only captures 64 byte SIO so I'm going to leave it at that
uint8_t sio[64];

// TODO: save SR vs SE more properly
if(pacs->sio) { // SR
for(uint8_t i = 0; i < 8; i++) {
memcpy(sio + (i * 8), AA1[10 + i].data, PICOPASS_BLOCK_LEN);
}
if(!flipper_format_write_hex(file, "SIO", sio, sizeof(sio))) break;
} else if(pacs->se_enabled) { //SE
for(uint8_t i = 0; i < 8; i++) {
memcpy(sio + (i * 8), AA1[6 + i].data, PICOPASS_BLOCK_LEN);
}
if(!flipper_format_write_hex(file, "SIO", sio, sizeof(sio))) break;
}
FURI_LOG_D(TAG, "post sio");
if(!flipper_format_write_hex(
file, "Diversifier", AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN))
break;

result = true;
} while(false);

return result;
}

static bool picopass_device_save_file_lfrfid(PicopassDevice* dev, FuriString* file_path) {
furi_assert(dev);
PicopassPacs* pacs = &dev->dev_data.pacs;
Expand Down Expand Up @@ -151,11 +204,13 @@ static bool picopass_device_save_file(
}
}
if(!block_saved) break;
saved = true;
} else if(dev->format == PicopassDeviceSaveFormatLF) {
saved = picopass_device_save_file_lfrfid(dev, temp_str);
} else if(dev->format == PicopassDeviceSaveFormatSeader) {
saved = picopass_device_save_file_seader(dev, file, temp_str);
}
saved = true;
} while(0);
} while(false);

if(!saved) {
dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile");
Expand All @@ -171,6 +226,9 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) {
dev, dev_name, STORAGE_APP_DATA_PATH_PREFIX, PICOPASS_APP_EXTENSION, true);
} else if(dev->format == PicopassDeviceSaveFormatLF) {
return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true);
} else if(dev->format == PicopassDeviceSaveFormatSeader) {
return picopass_device_save_file(
dev, dev_name, EXT_PATH("apps_data/seader"), ".credential", true);
}

return false;
Expand Down
1 change: 1 addition & 0 deletions base_pack/picopass/picopass_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ typedef enum {
typedef enum {
PicopassDeviceSaveFormatHF,
PicopassDeviceSaveFormatLF,
PicopassDeviceSaveFormatSeader,
} PicopassDeviceSaveFormat;

typedef enum {
Expand Down
1 change: 1 addition & 0 deletions base_pack/picopass/picopass_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ enum PicopassCustomEvent {
PicopassCustomEventDictAttackUpdateView,
PicopassCustomEventLoclassGotMac,
PicopassCustomEventLoclassGotStandardKey,
PicopassCustomEventNrMacSaved,

PicopassCustomEventPollerSuccess,
PicopassCustomEventPollerFail,
Expand Down
90 changes: 77 additions & 13 deletions base_pack/picopass/protocol/picopass_listener.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "picopass_i.h"
#include "picopass_listener_i.h"
#include "picopass_keys.h"

Expand Down Expand Up @@ -299,6 +300,61 @@ PicopassListenerCommand
return command;
}

PicopassListenerCommand picopass_listener_save_mac(PicopassListener* instance, uint8_t* rx_data) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
Picopass* picopass = instance->context;

PicopassDevice* dev = picopass->dev;

const uint8_t* csn = instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data;
const uint8_t* epurse = instance->data->AA1[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data;

FuriString* temp_str = furi_string_alloc();
FuriString* filename = furi_string_alloc();
FlipperFormat* file = flipper_format_file_alloc(dev->storage);

for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(filename, "%02x", csn[i]);
}
furi_string_cat_printf(filename, "_");
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(filename, "%02x", epurse[i]);
}

furi_string_printf(
temp_str, "%s/%s%s", STORAGE_APP_DATA_PATH_PREFIX, furi_string_get_cstr(filename), ".mac");
do {
// Open file
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;

if(!flipper_format_write_hex(file, "NR-MAC", rx_data + 1, PICOPASS_BLOCK_LEN)) break;

FURI_LOG_D(
TAG,
"Saved nr-mac: %02x %02x %02x %02x %02x %02x %02x %02x",
// Skip command byte [0]
rx_data[1],
rx_data[2],
rx_data[3],
rx_data[4],
rx_data[5],
rx_data[6],
rx_data[7],
rx_data[8]);

notification_message(picopass->notifications, &sequence_double_vibro);
command = PicopassListenerCommandStop;
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventNrMacSaved);
} while(0);

furi_string_free(temp_str);
furi_string_free(filename);
flipper_format_free(file);

return command;
}

PicopassListenerCommand
picopass_listener_check_handler_emulation(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
Expand All @@ -310,23 +366,31 @@ PicopassListenerCommand
// Since nr isn't const in loclass_opt_doBothMAC_2() copy buffer
uint8_t rx_data[9] = {};
bit_buffer_write_bytes(buf, rx_data, sizeof(rx_data));
loclass_opt_doBothMAC_2(instance->cipher_state, &rx_data[1], rmac, tmac, key);
bool no_key = picopass_is_memset(key, 0x00, PICOPASS_BLOCK_LEN);

#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
if(memcmp(&rx_data[5], rmac, 4)) {
// Bad MAC from reader, do not send a response.
FURI_LOG_I(TAG, "Got bad MAC from reader");
// Reset the cipher state since we don't do it in READCHECK
picopass_listener_init_cipher_state(instance);
if(no_key) {
// We're emulating a partial dump of an iClass SE card and should capture the NR and MAC
command = picopass_listener_save_mac(instance, rx_data);
break;
}
} else {
loclass_opt_doBothMAC_2(instance->cipher_state, &rx_data[1], rmac, tmac, key);

#ifndef PICOPASS_DEBUG_IGNORE_BAD_RMAC
if(memcmp(&rx_data[5], rmac, PICOPASS_MAC_LEN)) {
// Bad MAC from reader, do not send a response.
FURI_LOG_I(TAG, "Got bad MAC from reader");
// Reset the cipher state since we don't do it in READCHECK
picopass_listener_init_cipher_state(instance);
break;
}
#endif

bit_buffer_copy_bytes(instance->tx_buffer, tmac, sizeof(tmac));
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_D(TAG, "Failed tx update response: %d", error);
break;
bit_buffer_copy_bytes(instance->tx_buffer, tmac, sizeof(tmac));
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_D(TAG, "Failed tx update response: %d", error);
break;
}
}

command = PicopassListenerCommandProcessed;
Expand Down
Loading

0 comments on commit c470295

Please sign in to comment.