Temporarily backport app updates from apps repo

This commit is contained in:
Willy-JL
2023-11-12 11:06:02 +00:00
parent 79e7f491fe
commit e309fa8a88
1498 changed files with 1325977 additions and 20227 deletions

View File

@@ -10,13 +10,14 @@ App(
],
stack_size=4 * 1024,
fap_description="App to communicate with NFC tags using the PicoPass(iClass) format",
fap_version="1.2",
fap_version="1.7",
fap_icon="125_10px.png",
fap_category="NFC",
fap_libs=["mbedtls"],
fap_private_libs=[
Lib(
name="loclass",
cflags=["-O3"],
),
],
fap_icon_assets="icons",

View File

@@ -110,12 +110,7 @@ static void init_opt_select_LUT(void) {
}
***********************************************************************************/
#define loclass_opt__select(x, y, r) \
(4 & ((((r) & ((r) << 2)) >> 5) ^ (((r) & ~((r) << 2)) >> 4) ^ (((r) | (r) << 2) >> 3))) | \
(2 & ((((r) | (r) << 2) >> 6) ^ (((r) | (r) << 2) >> 1) ^ ((r) >> 5) ^ (r) ^ (((x) ^ (y)) << 1))) | \
(1 & ((((r) & ~((r) << 2)) >> 4) ^ (((r) & ((r) << 2)) >> 3) ^ (r) ^ (x)))
static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
static inline void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y) {
uint16_t Tt = s->t & 0xc533;
Tt = Tt ^ (Tt >> 1);
Tt = Tt ^ (Tt >> 4);
@@ -133,16 +128,15 @@ static void loclass_opt_successor(const uint8_t* k, LoclassState_t* s, uint8_t y
s->b = s->b >> 1;
s->b |= (opt_B ^ s->r) << 7;
uint8_t opt_select = loclass_opt_select_LUT[s->r] & 0x04;
opt_select |= (loclass_opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02;
opt_select |= (loclass_opt_select_LUT[s->r] ^ Tt) & 0x01;
uint8_t Tt1 = Tt & 0x01;
uint8_t opt_select = loclass_opt_select_LUT[s->r] ^ Tt1 ^ ((Tt1 ^ (y & 0x01)) << 1);
uint8_t r = s->r;
s->r = (k[opt_select] ^ s->b) + s->l;
s->l = s->r + r;
}
static void loclass_opt_suc(
static inline void loclass_opt_suc(
const uint8_t* k,
LoclassState_t* s,
const uint8_t* in,
@@ -150,21 +144,22 @@ static void loclass_opt_suc(
bool add32Zeroes) {
for(int i = 0; i < length; i++) {
uint8_t head = in[i];
#pragma GCC unroll 8
for(int j = 0; j < 8; j++) {
loclass_opt_successor(k, s, head);
head >>= 1;
}
}
//For tag MAC, an additional 32 zeroes
// For tag MAC, an additional 32 zeroes
if(add32Zeroes) {
for(int i = 0; i < 16; i++) {
loclass_opt_successor(k, s, 0);
for(int i = 0; i < 32; i++) {
loclass_opt_successor(k, s, 0);
}
}
}
static void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
static inline void loclass_opt_output(const uint8_t* k, LoclassState_t* s, uint8_t* buffer) {
#pragma GCC unroll 4
for(uint8_t times = 0; times < 4; times++) {
uint8_t bout = 0;
bout |= (s->r & 0x4) >> 2;
@@ -280,22 +275,32 @@ void loclass_opt_doTagMAC_2(
loclass_opt_output(div_key_p, &_init, mac);
}
/**
* The second part of the tag MAC calculation, since the CC is already calculated into the state,
* this function is fed only the NR, and generates both the reader and tag MACs.
* @param _init - precalculated cipher state
* @param nr - the reader challenge
* @param rmac - where to store the reader MAC
* @param tmac - where to store the tag MAC
* @param div_key_p - the key to use
*/
void loclass_opt_doBothMAC_2(
LoclassState_t _init,
uint8_t* nr,
uint8_t rmac[4],
uint8_t tmac[4],
const uint8_t* div_key_p) {
loclass_opt_suc(div_key_p, &_init, nr, 4, false);
// Save internal state for reuse before outputting
LoclassState_t nr_state = _init;
loclass_opt_output(div_key_p, &_init, rmac);
// Feed the 32 0 bits for the tag mac
loclass_opt_suc(div_key_p, &nr_state, NULL, 0, true);
loclass_opt_output(div_key_p, &nr_state, tmac);
LoclassState_t* s = &_init;
loclass_opt_suc(div_key_p, s, nr, 4, false);
loclass_opt_output(div_key_p, s, rmac);
loclass_opt_output(div_key_p, s, tmac);
}
void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite) {
void loclass_iclass_calc_div_key(
const uint8_t* csn,
const uint8_t* key,
uint8_t* div_key,
bool elite) {
if(elite) {
uint8_t keytable[128] = {0};
uint8_t key_index[8] = {0};

View File

@@ -109,5 +109,9 @@ void loclass_opt_doBothMAC_2(
const uint8_t* div_key_p);
void loclass_doMAC_N(uint8_t* in_p, uint8_t in_size, uint8_t* div_key_p, uint8_t mac[4]);
void loclass_iclass_calc_div_key(uint8_t* csn, const uint8_t* key, uint8_t* div_key, bool elite);
void loclass_iclass_calc_div_key(
const uint8_t* csn,
const uint8_t* key,
uint8_t* div_key,
bool elite);
#endif // OPTIMIZED_CIPHER_H

View File

@@ -302,7 +302,7 @@ void loclass_hash0(uint64_t c, uint8_t k[8]) {
* @param key
* @param div_key
*/
void loclass_diversifyKey(uint8_t* csn, const uint8_t* key, uint8_t* div_key) {
void loclass_diversifyKey(const uint8_t* csn, const uint8_t* key, uint8_t* div_key) {
mbedtls_des_context loclass_ctx_enc;
// Prepare the DES key

View File

@@ -56,7 +56,7 @@ void loclass_hash0(uint64_t c, uint8_t k[8]);
* @param div_key
*/
void loclass_diversifyKey(uint8_t* csn, const uint8_t* key, uint8_t* div_key);
void loclass_diversifyKey(const uint8_t* csn, const uint8_t* key, uint8_t* div_key);
/**
* @brief Permutes a key from standard NIST format to Iclass specific format
* @param key

View File

@@ -38,6 +38,8 @@ void loclass_writer_free(LoclassWriter* instance) {
}
bool loclass_writer_write_start_stop(LoclassWriter* instance, bool start) {
furi_assert(instance != NULL);
FuriHalRtcDateTime curr_dt;
furi_hal_rtc_get_datetime(&curr_dt);
uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
@@ -97,4 +99,4 @@ bool loclass_writer_write_params(
bool write_success = stream_write_string(instance->file_stream, str);
furi_string_free(str);
return write_success;
}
}

View File

@@ -23,7 +23,6 @@ void picopass_tick_event_callback(void* context) {
Picopass* picopass_alloc() {
Picopass* picopass = malloc(sizeof(Picopass));
picopass->worker = picopass_worker_alloc();
picopass->view_dispatcher = view_dispatcher_alloc();
picopass->scene_manager = scene_manager_alloc(&picopass_scene_handlers, picopass);
view_dispatcher_enable_queue(picopass->view_dispatcher);
@@ -35,6 +34,8 @@ Picopass* picopass_alloc() {
view_dispatcher_set_tick_event_callback(
picopass->view_dispatcher, picopass_tick_event_callback, 100);
picopass->nfc = nfc_alloc();
// Picopass device
picopass->dev = picopass_device_alloc();
@@ -100,6 +101,8 @@ void picopass_free(Picopass* picopass) {
picopass_device_free(picopass->dev);
picopass->dev = NULL;
nfc_free(picopass->nfc);
// Submenu
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewMenu);
submenu_free(picopass->submenu);
@@ -130,10 +133,6 @@ void picopass_free(Picopass* picopass) {
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoclass);
loclass_free(picopass->loclass);
// Worker
picopass_worker_stop(picopass->worker);
picopass_worker_free(picopass->worker);
// View Dispatcher
view_dispatcher_free(picopass->view_dispatcher);
@@ -210,7 +209,7 @@ void picopass_show_loading_popup(void* context, bool show) {
static void picopass_migrate_from_old_folder() {
Storage* storage = furi_record_open(RECORD_STORAGE);
storage_common_migrate(storage, EXT_PATH("picopass"), STORAGE_APP_DATA_PATH_PREFIX);
storage_common_migrate(storage, "/ext/picopass", STORAGE_APP_DATA_PATH_PREFIX);
furi_record_close(RECORD_STORAGE);
}

View File

@@ -2,7 +2,12 @@
#include <toolbox/path.h>
#include <flipper_format/flipper_format.h>
#include "picopass_icons.h"
#include <picopass_icons.h>
#include <toolbox/protocols/protocol_dict.h>
#include <lfrfid/protocols/lfrfid_protocols.h>
#include <lfrfid/lfrfid_dict_file.h>
#include <assets_icons.h>
#define TAG "PicopassDevice"
@@ -31,6 +36,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, 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,
@@ -38,6 +107,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);
@@ -56,30 +126,17 @@ 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, PICOPASS_BLOCK_LEN))
break;
// TODO: Add elite
if(!flipper_format_write_comment_cstr(file, "Picopass blocks")) break;
bool block_saved = true;
@@ -90,30 +147,14 @@ static bool picopass_device_save_file(
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),
AA1[i].data,
RFAL_PICOPASS_BLOCK_LEN)) {
file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
block_saved = false;
break;
}
}
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);
@@ -131,7 +172,7 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) {
return picopass_device_save_file(
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, EXT_PATH("lfrfid"), ".rfid", true);
return picopass_device_save_file(dev, dev_name, ANY_PATH("lfrfid"), ".rfid", true);
}
return false;
@@ -167,7 +208,7 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo
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), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) {
file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
block_read = false;
break;
}
@@ -179,15 +220,15 @@ static bool picopass_device_load_data(PicopassDevice* dev, FuriString* path, boo
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), AA1[i].data, RFAL_PICOPASS_BLOCK_LEN)) {
file, furi_string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
block_read = false;
break;
}
}
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;
picopass_device_parse_credential(AA1, pacs);
picopass_device_parse_wiegand(pacs->credential, pacs);
parsed = true;
} while(false);
@@ -304,7 +345,7 @@ void picopass_device_set_loading_callback(
dev->loading_cb_ctx = context;
}
ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) {
void picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) {
uint8_t key[32] = {0};
memcpy(key, picopass_iclass_decryptionkey, sizeof(picopass_iclass_decryptionkey));
mbedtls_des3_context ctx;
@@ -312,40 +353,25 @@ ReturnCode picopass_device_decrypt(uint8_t* enc_data, uint8_t* dec_data) {
mbedtls_des3_set2key_dec(&ctx, key);
mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data);
mbedtls_des3_free(&ctx);
return ERR_NONE;
}
ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) {
ReturnCode err;
void picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs) {
pacs->biometrics = AA1[6].data[4];
pacs->pin_length = AA1[6].data[6] & 0x0F;
pacs->encryption = AA1[6].data[7];
if(pacs->encryption == PicopassDeviceEncryption3DES) {
FURI_LOG_D(TAG, "3DES Encrypted");
err = picopass_device_decrypt(AA1[7].data, pacs->credential);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
return err;
}
picopass_device_decrypt(AA1[7].data, pacs->credential);
err = picopass_device_decrypt(AA1[8].data, pacs->pin0);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
return err;
}
picopass_device_decrypt(AA1[8].data, pacs->pin0);
err = picopass_device_decrypt(AA1[9].data, pacs->pin1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
return err;
}
picopass_device_decrypt(AA1[9].data, pacs->pin1);
} else if(pacs->encryption == PicopassDeviceEncryptionNone) {
FURI_LOG_D(TAG, "No Encryption");
memcpy(pacs->credential, AA1[7].data, RFAL_PICOPASS_BLOCK_LEN);
memcpy(pacs->pin0, AA1[8].data, RFAL_PICOPASS_BLOCK_LEN);
memcpy(pacs->pin1, AA1[9].data, RFAL_PICOPASS_BLOCK_LEN);
memcpy(pacs->credential, AA1[7].data, PICOPASS_BLOCK_LEN);
memcpy(pacs->pin0, AA1[8].data, PICOPASS_BLOCK_LEN);
memcpy(pacs->pin1, AA1[9].data, PICOPASS_BLOCK_LEN);
} else if(pacs->encryption == PicopassDeviceEncryptionDES) {
FURI_LOG_D(TAG, "DES Encrypted");
} else {
@@ -353,41 +379,33 @@ ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pa
}
pacs->sio = (AA1[10].data[0] == 0x30); // rough check
return ERR_NONE;
}
ReturnCode picopass_device_parse_wiegand(uint8_t* credential, PicopassWiegandRecord* record) {
void 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);
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;
FURI_LOG_D(TAG, "PACS: (%d) %016llx", pacs->bitLength, swapped);
}
bool picopass_device_hid_csn(PicopassDevice* dev) {
furi_assert(dev);
PicopassBlock* AA1 = dev->dev_data.AA1;
uint8_t* csn = AA1[PICOPASS_CSN_BLOCK_INDEX].data;
// From Proxmark3 RRG sourcecode
bool isHidRange = (memcmp(csn + 5, "\xFF\x12\xE0", 3) == 0) && ((csn[4] & 0xF0) == 0xF0);
return isHidRange;
}

View File

@@ -12,6 +12,13 @@
#include <optimized_cipher.h>
#include "helpers/iclass_elite_dict.h"
#define LOCLASS_NUM_CSNS 9
#ifndef LOCLASS_NUM_PER_CSN
// Collect 2 MACs per CSN to account for keyroll modes by default
#define LOCLASS_NUM_PER_CSN 2
#endif
#define LOCLASS_MACS_TO_COLLECT (LOCLASS_NUM_CSNS * LOCLASS_NUM_PER_CSN)
#define PICOPASS_DEV_NAME_MAX_LEN 22
#define PICOPASS_READER_DATA_MAX_SIZE 64
#define PICOPASS_MAX_APP_LIMIT 32
@@ -38,8 +45,9 @@
// Read Access, 1 meanns anonymous read enabled, 0 means must auth to read applicaion
#define PICOPASS_FUSE_RA 0x01
#define PICOPASS_APP_FOLDER EXT_PATH("picopass")
#define PICOPASS_APP_FOLDER ANY_PATH("picopass")
#define PICOPASS_APP_EXTENSION ".picopass"
#define PICOPASS_APP_FILE_PREFIX "Picopass"
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
#define PICOPASS_DICT_KEY_BATCH_SIZE 10
@@ -69,15 +77,9 @@ typedef enum {
PicopassEmulatorStateIdle,
PicopassEmulatorStateActive,
PicopassEmulatorStateSelected,
PicopassEmulatorStateStopEmulation,
} PicopassEmulatorState;
typedef struct {
bool valid;
uint8_t bitLength;
uint8_t FacilityCode;
uint16_t CardNumber;
} PicopassWiegandRecord;
typedef struct {
bool legacy;
bool se_enabled;
@@ -87,20 +89,19 @@ 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 {
uint8_t data[RFAL_PICOPASS_BLOCK_LEN];
uint8_t data[PICOPASS_BLOCK_LEN];
} PicopassBlock;
typedef struct {
PicopassBlock AA1[PICOPASS_MAX_APP_LIMIT];
PicopassPacs pacs;
IclassEliteDictAttackData iclass_elite_dict_attack_data;
} PicopassDeviceData;
typedef struct {
@@ -109,6 +110,7 @@ typedef struct {
uint8_t key_block_num; // in loclass mode used to store csn#
bool loclass_mode;
bool loclass_got_std_key;
uint8_t loclass_mac_buffer[8 * LOCLASS_NUM_PER_CSN];
LoclassWriter* loclass_writer;
} PicopassEmulatorCtx;
@@ -144,5 +146,6 @@ void picopass_device_set_loading_callback(
PicopassLoadingCallback callback,
void* context);
ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs);
ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record);
void picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs);
void picopass_device_parse_wiegand(uint8_t* credential, PicopassPacs* pacs);
bool picopass_device_hid_csn(PicopassDevice* dev);

View File

@@ -1,7 +1,6 @@
#pragma once
#include "picopass.h"
#include "picopass_worker.h"
#include "picopass_device.h"
#include "rfal_picopass.h"
@@ -27,14 +26,20 @@
#include <storage/storage.h>
#include <lib/toolbox/path.h>
#include "picopass_icons.h"
#include <picopass_icons.h>
#include <assets_icons.h>
#include <nfc/nfc.h>
#include <nfc/helpers/nfc_dict.h>
#include "protocol/picopass_poller.h"
#include "protocol/picopass_listener.h"
#define PICOPASS_TEXT_STORE_SIZE 128
#define LOCLASS_NUM_CSNS 9
// Collect 2 MACs per CSN to account for keyroll modes
#define LOCLASS_MACS_TO_COLLECT (LOCLASS_NUM_CSNS * 2)
#define PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_elite_dict.txt")
#define PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_standard_dict.txt")
#define PICOPASS_ICLASS_ELITE_DICT_USER_NAME APP_DATA_PATH("assets/iclass_elite_dict_user.txt")
enum PicopassCustomEvent {
// Reserve first 100 events for button types and indexes, starting from 0
@@ -45,6 +50,12 @@ enum PicopassCustomEvent {
PicopassCustomEventByteInputDone,
PicopassCustomEventTextInputDone,
PicopassCustomEventDictAttackSkip,
PicopassCustomEventDictAttackUpdateView,
PicopassCustomEventLoclassGotMac,
PicopassCustomEventLoclassGotStandardKey,
PicopassCustomEventPollerSuccess,
PicopassCustomEventPollerFail,
};
typedef enum {
@@ -52,17 +63,37 @@ typedef enum {
EventTypeKey,
} EventType;
typedef struct {
const char* name;
uint16_t total_keys;
uint16_t current_key;
bool card_detected;
} PicopassDictAttackContext;
typedef struct {
uint8_t key_to_write[PICOPASS_BLOCK_LEN];
bool is_elite;
} PicopassWriteKeyContext;
typedef struct {
size_t macs_collected;
} PicopassLoclassContext;
struct Picopass {
PicopassWorker* worker;
ViewDispatcher* view_dispatcher;
Gui* gui;
NotificationApp* notifications;
SceneManager* scene_manager;
PicopassDevice* dev;
Nfc* nfc;
PicopassPoller* poller;
PicopassListener* listener;
NfcDict* dict;
char text_store[PICOPASS_TEXT_STORE_SIZE + 1];
FuriString* text_box_store;
uint8_t byte_input_store[RFAL_PICOPASS_BLOCK_LEN];
uint8_t byte_input_store[PICOPASS_BLOCK_LEN];
// Common Views
Submenu* submenu;
@@ -73,6 +104,10 @@ struct Picopass {
Widget* widget;
DictAttack* dict_attack;
Loclass* loclass;
PicopassDictAttackContext dict_attack_ctx;
PicopassWriteKeyContext write_key_context;
PicopassLoclassContext loclass_context;
};
typedef enum {

View File

@@ -2,9 +2,9 @@
#include "picopass_device.h"
extern const uint8_t picopass_iclass_key[RFAL_PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_factory_credit_key[RFAL_PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_factory_debit_key[RFAL_PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_xice_key[RFAL_PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_xicl_key[RFAL_PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_xics_key[RFAL_PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_iclass_key[PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_factory_credit_key[PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_factory_debit_key[PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_xice_key[PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_xicl_key[PICOPASS_BLOCK_LEN];
extern const uint8_t picopass_xics_key[PICOPASS_BLOCK_LEN];

File diff suppressed because it is too large Load Diff

View File

@@ -1,56 +0,0 @@
#pragma once
#include "picopass_device.h"
#include "picopass_keys.h"
typedef struct PicopassWorker PicopassWorker;
typedef enum {
// Init states
PicopassWorkerStateNone,
PicopassWorkerStateBroken,
PicopassWorkerStateReady,
// Main worker states
PicopassWorkerStateDetect,
PicopassWorkerStateWrite,
PicopassWorkerStateWriteKey,
PicopassWorkerStateEliteDictAttack,
PicopassWorkerStateEmulate,
PicopassWorkerStateLoclass,
// Transition
PicopassWorkerStateStop,
} PicopassWorkerState;
typedef enum {
// Reserve first 50 events for application events
PicopassWorkerEventReserved = 50,
// Picopass worker common events
PicopassWorkerEventSuccess,
PicopassWorkerEventFail,
PicopassWorkerEventNoCardDetected,
PicopassWorkerEventSeEnabled,
PicopassWorkerEventAborted,
PicopassWorkerEventCardDetected,
PicopassWorkerEventNewDictKeyBatch,
PicopassWorkerEventNoDictFound,
PicopassWorkerEventLoclassGotMac,
PicopassWorkerEventLoclassGotStandardKey,
} PicopassWorkerEvent;
typedef void (*PicopassWorkerCallback)(PicopassWorkerEvent event, void* context);
PicopassWorker* picopass_worker_alloc();
PicopassWorkerState picopass_worker_get_state(PicopassWorker* picopass_worker);
void picopass_worker_free(PicopassWorker* picopass_worker);
void picopass_worker_start(
PicopassWorker* picopass_worker,
PicopassWorkerState state,
PicopassDeviceData* dev_data,
PicopassWorkerCallback callback,
void* context);
void picopass_worker_stop(PicopassWorker* picopass_worker);

View File

@@ -1,36 +0,0 @@
#pragma once
#include "picopass_worker.h"
#include "loclass_writer.h"
#include "picopass_i.h"
#include <furi.h>
#include <lib/toolbox/stream/file_stream.h>
#include <furi_hal.h>
#include <stdlib.h>
#include <rfal_rf.h>
#include <platform.h>
struct PicopassWorker {
FuriThread* thread;
Storage* storage;
Stream* dict_stream;
PicopassDeviceData* dev_data;
PicopassWorkerCallback callback;
void* context;
PicopassWorkerState state;
};
void picopass_worker_change_state(PicopassWorker* picopass_worker, PicopassWorkerState state);
int32_t picopass_worker_task(void* context);
void picopass_worker_detect(PicopassWorker* picopass_worker);
void picopass_worker_write(PicopassWorker* picopass_worker);
void picopass_worker_write_key(PicopassWorker* picopass_worker);
void picopass_worker_emulate(PicopassWorker* picopass_worker, bool loclass_mode);

View File

@@ -0,0 +1,667 @@
#include "picopass_listener_i.h"
#include "picopass_keys.h"
#include <furi/furi.h>
#define PICOPASS_LISTENER_HAS_MASK(x, b) ((x & b) == b)
typedef enum {
PicopassListenerCommandProcessed,
PicopassListenerCommandSilent,
PicopassListenerCommandSendSoF,
PicopassListenerCommandStop,
} PicopassListenerCommand;
typedef PicopassListenerCommand (
*PicopassListenerCommandHandler)(PicopassListener* instance, BitBuffer* buf);
typedef struct {
uint8_t start_byte_cmd;
size_t cmd_len_bits;
PicopassListenerCommandHandler handler;
} PicopassListenerCmd;
// CSNs from Proxmark3 repo
static const uint8_t loclass_csns[LOCLASS_NUM_CSNS][PICOPASS_BLOCK_LEN] = {
{0x01, 0x0A, 0x0F, 0xFF, 0xF7, 0xFF, 0x12, 0xE0},
{0x0C, 0x06, 0x0C, 0xFE, 0xF7, 0xFF, 0x12, 0xE0},
{0x10, 0x97, 0x83, 0x7B, 0xF7, 0xFF, 0x12, 0xE0},
{0x13, 0x97, 0x82, 0x7A, 0xF7, 0xFF, 0x12, 0xE0},
{0x07, 0x0E, 0x0D, 0xF9, 0xF7, 0xFF, 0x12, 0xE0},
{0x14, 0x96, 0x84, 0x76, 0xF7, 0xFF, 0x12, 0xE0},
{0x17, 0x96, 0x85, 0x71, 0xF7, 0xFF, 0x12, 0xE0},
{0xCE, 0xC5, 0x0F, 0x77, 0xF7, 0xFF, 0x12, 0xE0},
{0xD2, 0x5A, 0x82, 0xF8, 0xF7, 0xFF, 0x12, 0xE0},
};
static void picopass_listener_reset(PicopassListener* instance) {
instance->state = PicopassListenerStateIdle;
}
static void picopass_listener_loclass_update_csn(PicopassListener* instance) {
// collect LOCLASS_NUM_PER_CSN nonces in a row for each CSN
const uint8_t* csn =
loclass_csns[(instance->key_block_num / LOCLASS_NUM_PER_CSN) % LOCLASS_NUM_CSNS];
memcpy(instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data, csn, sizeof(PicopassBlock));
uint8_t key[PICOPASS_BLOCK_LEN] = {};
loclass_iclass_calc_div_key(csn, picopass_iclass_key, key, false);
memcpy(instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data, key, sizeof(PicopassBlock));
picopass_listener_init_cipher_state_key(instance, key);
}
PicopassListenerCommand
picopass_listener_actall_handler(PicopassListener* instance, BitBuffer* buf) {
UNUSED(buf);
if(instance->state != PicopassListenerStateHalt) {
instance->state = PicopassListenerStateActive;
}
// nfc_set_fdt_listen_fc(instance->nfc, 1000);
return PicopassListenerCommandSendSoF;
}
PicopassListenerCommand picopass_listener_act_handler(PicopassListener* instance, BitBuffer* buf) {
UNUSED(buf);
PicopassListenerCommand command = PicopassListenerCommandSendSoF;
if(instance->state != PicopassListenerStateActive) {
command = PicopassListenerCommandSilent;
}
return command;
}
PicopassListenerCommand
picopass_listener_halt_handler(PicopassListener* instance, BitBuffer* buf) {
UNUSED(buf);
PicopassListenerCommand command = PicopassListenerCommandSendSoF;
// Technically we should go to StateHalt, but since we can't detect the field dropping we drop to idle instead
instance->state = PicopassListenerStateIdle;
return command;
}
PicopassListenerCommand
picopass_listener_identify_handler(PicopassListener* instance, BitBuffer* buf) {
UNUSED(buf);
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
if(instance->state != PicopassListenerStateActive) break;
picopass_listener_write_anticoll_csn(instance, instance->tx_buffer);
PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
FURI_LOG_D(TAG, "Error sending CSN: %d", error);
break;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_select_handler(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
if((instance->state == PicopassListenerStateHalt) ||
(instance->state == PicopassListenerStateIdle)) {
bit_buffer_copy_bytes(
instance->tmp_buffer,
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data,
sizeof(PicopassBlock));
} else {
picopass_listener_write_anticoll_csn(instance, instance->tmp_buffer);
}
const uint8_t* listener_uid = bit_buffer_get_data(instance->tmp_buffer);
const uint8_t* received_data = bit_buffer_get_data(buf);
if(memcmp(listener_uid, &received_data[1], PICOPASS_BLOCK_LEN) != 0) {
if(instance->state == PicopassListenerStateActive) {
instance->state = PicopassListenerStateIdle;
} else if(instance->state == PicopassListenerStateSelected) {
// Technically we should go to StateHalt, but since we can't detect the field dropping we drop to idle instead
instance->state = PicopassListenerStateIdle;
}
break;
}
instance->state = PicopassListenerStateSelected;
bit_buffer_copy_bytes(
instance->tx_buffer,
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data,
sizeof(PicopassBlock));
PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
FURI_LOG_D(TAG, "Error sending select response: %d", error);
break;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_read_handler(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
uint8_t block_num = bit_buffer_get_byte(buf, 1);
if(block_num > PICOPASS_MAX_APP_LIMIT) break;
bit_buffer_reset(instance->tx_buffer);
if((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX) ||
(block_num == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
bit_buffer_append_byte(instance->tx_buffer, 0xff);
}
} else {
bit_buffer_copy_bytes(
instance->tx_buffer, instance->data->AA1[block_num].data, sizeof(PicopassBlock));
}
PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
FURI_LOG_D(TAG, "Failed to tx read block response: %d", error);
break;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
static PicopassListenerCommand
picopass_listener_readcheck(PicopassListener* instance, BitBuffer* buf, uint8_t key_block_num) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
if(instance->state != PicopassListenerStateSelected) break;
uint8_t block_num = bit_buffer_get_byte(buf, 1);
if(block_num != PICOPASS_SECURE_EPURSE_BLOCK_INDEX) break;
// loclass mode doesn't do any card side crypto, just logs the readers crypto, so no-op in this mode
// we can also no-op if the key block is the same, CHECK re-inits if it failed already
if((instance->key_block_num != key_block_num) &&
(instance->mode != PicopassListenerModeLoclass)) {
instance->key_block_num = key_block_num;
picopass_listener_init_cipher_state(instance);
}
// DATA(8)
bit_buffer_copy_bytes(
instance->tx_buffer, instance->data->AA1[block_num].data, sizeof(PicopassBlock));
NfcError error = nfc_listener_tx(instance->nfc, instance->tx_buffer);
if(error != NfcErrorNone) {
FURI_LOG_D(TAG, "Failed to tx read check response: %d", error);
break;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_readcheck_kd_handler(PicopassListener* instance, BitBuffer* buf) {
return picopass_listener_readcheck(instance, buf, PICOPASS_SECURE_KD_BLOCK_INDEX);
}
PicopassListenerCommand
picopass_listener_readcheck_kc_handler(PicopassListener* instance, BitBuffer* buf) {
return picopass_listener_readcheck(instance, buf, PICOPASS_SECURE_KC_BLOCK_INDEX);
}
PicopassListenerCommand
picopass_listener_check_handler_loclass(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
NfcCommand callback_command = NfcCommandContinue;
// LOCLASS Reader attack mode
do {
#ifndef PICOPASS_DEBUG_IGNORE_LOCLASS_STD_KEY
// loclass mode stores the derived standard debit key in Kd to check
PicopassBlock key = instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX];
uint8_t rmac[4];
uint8_t rx_data[9] = {};
bit_buffer_write_bytes(buf, rx_data, sizeof(rx_data));
loclass_opt_doReaderMAC_2(instance->cipher_state, &rx_data[1], rmac, key.data);
if(!memcmp(&rx_data[5], rmac, 4)) {
// MAC from reader matches Standard Key, keyroll mode or non-elite keyed reader.
// Either way no point logging it.
FURI_LOG_W(TAG, "loclass: standard key detected during collection");
if(instance->callback) {
instance->event.type = PicopassListenerEventTypeLoclassGotStandardKey;
callback_command = instance->callback(instance->event, instance->context);
if(callback_command == NfcCommandStop) {
command = PicopassListenerCommandStop;
}
}
// Don't reset the state as the reader may try a different key next without going through anticoll
// The reader is always free to redo the anticoll if it wants to anyway
break;
}
#endif
// Save to buffer to defer flushing when we rotate CSN
memcpy(
instance->loclass_mac_buffer + ((instance->key_block_num % LOCLASS_NUM_PER_CSN) * 8),
&rx_data[1],
8);
// Rotate to the next CSN/attempt
instance->key_block_num++;
// CSN changed
if(instance->key_block_num % LOCLASS_NUM_PER_CSN == 0) {
// Flush NR-MACs for this CSN to SD card
for(int i = 0; i < LOCLASS_NUM_PER_CSN; i++) {
loclass_writer_write_params(
instance->writer,
instance->key_block_num + i - LOCLASS_NUM_PER_CSN,
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data,
instance->data->AA1[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data,
instance->loclass_mac_buffer + (i * 8),
instance->loclass_mac_buffer + (i * 8) + 4);
}
if(instance->key_block_num < LOCLASS_NUM_CSNS * LOCLASS_NUM_PER_CSN) {
picopass_listener_loclass_update_csn(instance);
// Only reset the state when we change to a new CSN for the same reason as when we get a standard key
instance->state = PicopassListenerStateIdle;
}
}
if(instance->callback) {
instance->event.type = PicopassListenerEventTypeLoclassGotMac;
instance->callback(instance->event, instance->context);
}
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_check_handler_emulation(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
uint8_t rmac[4] = {};
uint8_t tmac[4] = {};
const uint8_t* key = instance->data->AA1[instance->key_block_num].data;
// 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);
#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);
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;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_check_handler(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
if(instance->state != PicopassListenerStateSelected) break;
if(instance->mode == PicopassListenerModeLoclass) {
command = picopass_listener_check_handler_loclass(instance, buf);
} else {
command = picopass_listener_check_handler_emulation(instance, buf);
}
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_update_handler(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
if(instance->mode == PicopassListenerModeLoclass) break;
if(instance->state != PicopassListenerStateSelected) break;
PicopassBlock config_block = instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX];
bool pers_mode = PICOPASS_LISTENER_HAS_MASK(config_block.data[7], PICOPASS_FUSE_PERS);
const uint8_t* rx_data = bit_buffer_get_data(buf);
uint8_t block_num = rx_data[1];
if(block_num == PICOPASS_CSN_BLOCK_INDEX) break; // CSN is always read only
if(!pers_mode && PICOPASS_LISTENER_HAS_MASK(config_block.data[3], 0x80))
break; // Chip is in RO mode, no updated possible (even ePurse)
if(!pers_mode && (block_num == PICOPASS_SECURE_AIA_BLOCK_INDEX))
break; // AIA can only be set in personalisation mode
if(!pers_mode &&
((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX ||
block_num == PICOPASS_SECURE_KC_BLOCK_INDEX) &&
(!PICOPASS_LISTENER_HAS_MASK(config_block.data[7], PICOPASS_FUSE_CRYPT10))))
break;
if(block_num >= 6 && block_num <= 12) {
// bit0 is block6, up to bit6 being block12
if(!PICOPASS_LISTENER_HAS_MASK(config_block.data[3], (1 << (block_num - 6)))) {
// Block is marked as read-only, deny writing
break;
}
}
// TODO: Check CRC/SIGN depending on if in secure mode
// Check correct key
// -> Kd only allows decrementing e-Purse
// -> per-app controlled by key access config
// bool keyAccess = PICOPASS_LISTENER_HAS_MASK(config_block.data[5], 0x01);
// -> must auth with that key to change it
PicopassBlock new_block = {};
switch(block_num) {
case PICOPASS_CONFIG_BLOCK_INDEX:
new_block.data[0] = config_block.data[0]; // Applications Limit
new_block.data[1] = config_block.data[1] & rx_data[3]; // OTP
new_block.data[2] = config_block.data[2] & rx_data[4]; // OTP
new_block.data[3] = config_block.data[3] & rx_data[5]; // Block Write Lock
new_block.data[4] = config_block.data[4]; // Chip Config
new_block.data[5] = config_block.data[5]; // Memory Config
new_block.data[6] = rx_data[8]; // EAS
new_block.data[7] = config_block.data[7]; // Fuses
// Some parts allow w (but not e) if in persMode
if(pers_mode) {
new_block.data[0] &= rx_data[2]; // Applications Limit
new_block.data[4] &= rx_data[6]; // Chip Config
new_block.data[5] &= rx_data[7]; // Memory Config
new_block.data[7] &= rx_data[9]; // Fuses
} else {
// Fuses allows setting Crypt1/0 from 1 to 0 only during application mode
new_block.data[7] &= rx_data[9] | ~PICOPASS_FUSE_CRYPT10;
}
break;
case PICOPASS_SECURE_EPURSE_BLOCK_INDEX:
// ePurse updates swap first and second half of the block each update
memcpy(&new_block.data[4], &rx_data[2], 4);
memcpy(&new_block.data[0], &rx_data[6], 4);
break;
case PICOPASS_SECURE_KD_BLOCK_INDEX:
// fallthrough
case PICOPASS_SECURE_KC_BLOCK_INDEX:
if(!pers_mode) {
new_block = instance->data->AA1[block_num];
for(size_t i = 0; i < sizeof(PicopassBlock); i++) {
new_block.data[i] ^= rx_data[i + 2];
}
break;
}
// Use default case when in personalisation mode
// fallthrough
default:
memcpy(new_block.data, &rx_data[2], sizeof(PicopassBlock));
break;
}
instance->data->AA1[block_num] = new_block;
if((block_num == instance->key_block_num) ||
(block_num == PICOPASS_SECURE_EPURSE_BLOCK_INDEX)) {
picopass_listener_init_cipher_state(instance);
}
bit_buffer_reset(instance->tx_buffer);
if((block_num == PICOPASS_SECURE_KD_BLOCK_INDEX) ||
(block_num == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
// Key updates always return FF's
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
bit_buffer_append_byte(instance->tx_buffer, 0xff);
}
} else {
bit_buffer_copy_bytes(
instance->tx_buffer, instance->data->AA1[block_num].data, sizeof(PicopassBlock));
}
PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
FURI_LOG_D(TAG, "Failed to tx update response: %d", error);
break;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
PicopassListenerCommand
picopass_listener_read4_handler(PicopassListener* instance, BitBuffer* buf) {
PicopassListenerCommand command = PicopassListenerCommandSilent;
do {
if(instance->state != PicopassListenerStateSelected) break;
uint8_t block_start = bit_buffer_get_byte(buf, 1);
if(block_start + 4 >= PICOPASS_MAX_APP_LIMIT) break;
// TODO: Check CRC?
// TODO: Check auth?
bit_buffer_reset(instance->tx_buffer);
for(uint8_t i = block_start; i < block_start + 4; i++) {
if((i == PICOPASS_SECURE_KD_BLOCK_INDEX) || (i == PICOPASS_SECURE_KC_BLOCK_INDEX)) {
for(size_t j = 0; j < sizeof(PicopassBlock); j++) {
bit_buffer_append_byte(instance->tx_buffer, 0xff);
}
} else {
bit_buffer_append_bytes(
instance->tx_buffer, instance->data->AA1[i].data, sizeof(PicopassBlock));
}
}
PicopassError error = picopass_listener_send_frame(instance, instance->tx_buffer);
if(error != PicopassErrorNone) {
FURI_LOG_D(TAG, "Failed to tx read4 response: %d", error);
break;
}
command = PicopassListenerCommandProcessed;
} while(false);
return command;
}
static const PicopassListenerCmd picopass_listener_cmd_handlers[] = {
{
.start_byte_cmd = RFAL_PICOPASS_CMD_ACTALL,
.cmd_len_bits = 8,
.handler = picopass_listener_actall_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_ACT,
.cmd_len_bits = 8,
.handler = picopass_listener_act_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_HALT,
.cmd_len_bits = 8,
.handler = picopass_listener_halt_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_READ_OR_IDENTIFY,
.cmd_len_bits = 8,
.handler = picopass_listener_identify_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_SELECT,
.cmd_len_bits = 8 * 9,
.handler = picopass_listener_select_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_READ_OR_IDENTIFY,
.cmd_len_bits = 8 * 4,
.handler = picopass_listener_read_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_READCHECK_KD,
.cmd_len_bits = 8 * 2,
.handler = picopass_listener_readcheck_kd_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_READCHECK_KC,
.cmd_len_bits = 8 * 2,
.handler = picopass_listener_readcheck_kc_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_CHECK,
.cmd_len_bits = 8 * 9,
.handler = picopass_listener_check_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_UPDATE,
.cmd_len_bits = 8 * 14,
.handler = picopass_listener_update_handler,
},
{
.start_byte_cmd = RFAL_PICOPASS_CMD_READ4,
.cmd_len_bits = 8 * 4,
.handler = picopass_listener_read4_handler,
},
};
PicopassListener* picopass_listener_alloc(Nfc* nfc, const PicopassDeviceData* data) {
furi_assert(nfc);
furi_assert(data);
PicopassListener* instance = malloc(sizeof(PicopassListener));
instance->nfc = nfc;
instance->data = malloc(sizeof(PicopassDeviceData));
mempcpy(instance->data, data, sizeof(PicopassDeviceData));
instance->tx_buffer = bit_buffer_alloc(PICOPASS_LISTENER_BUFFER_SIZE_MAX);
instance->tmp_buffer = bit_buffer_alloc(PICOPASS_LISTENER_BUFFER_SIZE_MAX);
nfc_set_fdt_listen_fc(instance->nfc, PICOPASS_FDT_LISTEN_FC);
nfc_config(instance->nfc, NfcModeListener, NfcTechIso15693);
return instance;
}
void picopass_listener_free(PicopassListener* instance) {
furi_assert(instance);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->tmp_buffer);
free(instance->data);
if(instance->writer) {
loclass_writer_write_start_stop(instance->writer, false);
loclass_writer_free(instance->writer);
}
free(instance);
}
bool picopass_listener_set_mode(PicopassListener* instance, PicopassListenerMode mode) {
furi_assert(instance);
bool success = true;
instance->mode = mode;
if(instance->mode == PicopassListenerModeLoclass) {
instance->key_block_num = 0;
picopass_listener_loclass_update_csn(instance);
instance->writer = loclass_writer_alloc();
if(instance->writer) {
loclass_writer_write_start_stop(instance->writer, true);
} else {
success = false;
}
}
return success;
}
NfcCommand picopass_listener_start_callback(NfcEvent event, void* context) {
furi_assert(context);
NfcCommand command = NfcCommandContinue;
PicopassListener* instance = context;
BitBuffer* rx_buf = event.data.buffer;
PicopassListenerCommand picopass_cmd = PicopassListenerCommandSilent;
if(event.type == NfcEventTypeRxEnd) {
for(size_t i = 0; i < COUNT_OF(picopass_listener_cmd_handlers); i++) {
if(bit_buffer_get_size(rx_buf) != picopass_listener_cmd_handlers[i].cmd_len_bits) {
continue;
}
if(bit_buffer_get_byte(rx_buf, 0) !=
picopass_listener_cmd_handlers[i].start_byte_cmd) {
continue;
}
picopass_cmd = picopass_listener_cmd_handlers[i].handler(instance, rx_buf);
break;
}
if(picopass_cmd == PicopassListenerCommandSendSoF) {
nfc_iso15693_listener_tx_sof(instance->nfc);
} else if(picopass_cmd == PicopassListenerCommandStop) {
command = NfcCommandStop;
}
}
return command;
}
void picopass_listener_start(
PicopassListener* instance,
PicopassListenerCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
picopass_listener_reset(instance);
nfc_start(instance->nfc, picopass_listener_start_callback, instance);
}
void picopass_listener_stop(PicopassListener* instance) {
furi_assert(instance);
nfc_stop(instance->nfc);
}
const PicopassDeviceData* picopass_listener_get_data(PicopassListener* instance) {
furi_assert(instance);
return instance->data;
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <nfc/nfc.h>
#include "picopass_protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
PicopassListenerModeEmulation,
PicopassListenerModeLoclass,
} PicopassListenerMode;
typedef enum {
PicopassListenerEventTypeLoclassGotStandardKey,
PicopassListenerEventTypeLoclassGotMac,
} PicopassListenerEventType;
typedef struct {
PicopassListenerEventType type;
} PicopassListenerEvent;
typedef NfcCommand (*PicopassListenerCallback)(PicopassListenerEvent event, void* context);
typedef struct PicopassListener PicopassListener;
PicopassListener* picopass_listener_alloc(Nfc* nfc, const PicopassDeviceData* data);
void picopass_listener_free(PicopassListener* instance);
bool picopass_listener_set_mode(PicopassListener* instance, PicopassListenerMode mode);
void picopass_listener_start(
PicopassListener* instance,
PicopassListenerCallback callback,
void* context);
void picopass_listener_stop(PicopassListener* instance);
const PicopassDeviceData* picopass_listener_get_data(PicopassListener* instance);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,52 @@
#include "picopass_listener_i.h"
#include <furi/furi.h>
static PicopassError picopass_listener_process_error(NfcError error) {
PicopassError ret = PicopassErrorNone;
switch(error) {
case NfcErrorNone:
ret = PicopassErrorNone;
break;
default:
ret = PicopassErrorTimeout;
break;
}
return ret;
}
void picopass_listener_init_cipher_state_key(PicopassListener* instance, const uint8_t* key) {
uint8_t cc[PICOPASS_BLOCK_LEN] = {};
memcpy(
cc, instance->data->AA1[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data, sizeof(PicopassBlock));
instance->cipher_state = loclass_opt_doTagMAC_1(cc, key);
}
void picopass_listener_init_cipher_state(PicopassListener* instance) {
uint8_t key[PICOPASS_BLOCK_LEN] = {};
memcpy(key, instance->data->AA1[instance->key_block_num].data, sizeof(PicopassBlock));
picopass_listener_init_cipher_state_key(instance, key);
}
PicopassError picopass_listener_send_frame(PicopassListener* instance, BitBuffer* tx_buffer) {
iso13239_crc_append(Iso13239CrcTypePicopass, tx_buffer);
NfcError error = nfc_listener_tx(instance->nfc, tx_buffer);
return picopass_listener_process_error(error);
}
// from proxmark3 armsrc/iclass.c rotateCSN
PicopassError picopass_listener_write_anticoll_csn(PicopassListener* instance, BitBuffer* buffer) {
const uint8_t* uid = instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data;
bit_buffer_reset(buffer);
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
bit_buffer_append_byte(buffer, (uid[i] >> 3) | (uid[(i + 1) % 8] << 5));
}
return PicopassErrorNone;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include "picopass_listener.h"
#include <nfc/helpers/iso13239_crc.h>
#include <optimized_ikeys.h>
#include <optimized_cipher.h>
#include <loclass_writer.h>
#define TAG "PicopassListener"
#define PICOPASS_LISTENER_BUFFER_SIZE_MAX (255)
typedef enum {
PicopassListenerStateIdle,
PicopassListenerStateHalt,
PicopassListenerStateActive,
PicopassListenerStateSelected,
} PicopassListenerState;
struct PicopassListener {
Nfc* nfc;
PicopassDeviceData* data;
PicopassListenerState state;
LoclassState_t cipher_state;
PicopassListenerMode mode;
BitBuffer* tx_buffer;
BitBuffer* tmp_buffer;
uint8_t key_block_num;
LoclassWriter* writer;
uint8_t loclass_mac_buffer[8 * LOCLASS_NUM_PER_CSN];
PicopassListenerEvent event;
PicopassListenerCallback callback;
void* context;
};
void picopass_listener_init_cipher_state_key(PicopassListener* instance, const uint8_t* key);
void picopass_listener_init_cipher_state(PicopassListener* instance);
PicopassError picopass_listener_send_frame(PicopassListener* instance, BitBuffer* tx_buffer);
PicopassError picopass_listener_write_anticoll_csn(PicopassListener* instance, BitBuffer* buffer);

View File

@@ -0,0 +1,543 @@
#include "picopass_poller_i.h"
#include "../loclass/optimized_cipher.h"
#include <furi/furi.h>
#define TAG "Picopass"
typedef NfcCommand (*PicopassPollerStateHandler)(PicopassPoller* instance);
static void picopass_poller_reset(PicopassPoller* instance) {
instance->current_block = 0;
}
static void picopass_poller_prepare_read(PicopassPoller* instance) {
instance->app_limit = instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] <
PICOPASS_MAX_APP_LIMIT ?
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0] :
PICOPASS_MAX_APP_LIMIT;
instance->current_block = 2;
}
NfcCommand picopass_poller_request_mode_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->event.type = PicopassPollerEventTypeRequestMode;
command = instance->callback(instance->event, instance->context);
instance->mode = instance->event_data.req_mode.mode;
instance->state = PicopassPollerStateDetect;
return command;
}
NfcCommand picopass_poller_detect_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
PicopassError error = picopass_poller_actall(instance);
if(error == PicopassErrorNone) {
instance->state = PicopassPollerStateSelect;
instance->event.type = PicopassPollerEventTypeCardDetected;
command = instance->callback(instance->event, instance->context);
} else {
furi_delay_ms(100);
}
return command;
}
NfcCommand picopass_poller_select_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
do {
PicopassError error = picopass_poller_identify(instance, &instance->col_res_serial_num);
if(error != PicopassErrorNone) {
instance->state = PicopassPollerStateFail;
break;
}
error =
picopass_poller_select(instance, &instance->col_res_serial_num, &instance->serial_num);
if(error != PicopassErrorNone) {
instance->state = PicopassPollerStateFail;
break;
}
if(instance->mode == PicopassPollerModeRead) {
instance->state = PicopassPollerStatePreAuth;
} else {
instance->state = PicopassPollerStateAuth;
}
} while(false);
return command;
}
NfcCommand picopass_poller_pre_auth_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
PicopassError error = PicopassErrorNone;
do {
memcpy(
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data,
instance->serial_num.data,
sizeof(PicopassSerialNum));
FURI_LOG_D(
TAG,
"csn %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[0],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[1],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[2],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[3],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[4],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[5],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[6],
instance->data->AA1[PICOPASS_CSN_BLOCK_INDEX].data[7]);
PicopassBlock block = {};
error = picopass_poller_read_block(instance, 1, &block);
if(error != PicopassErrorNone) {
instance->state = PicopassPollerStateFail;
break;
}
memcpy(
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data,
block.data,
sizeof(PicopassBlock));
FURI_LOG_D(
TAG,
"config %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[1],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[2],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[3],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[4],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[5],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[6],
instance->data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[7]);
error = picopass_poller_read_block(instance, 5, &block);
if(error != PicopassErrorNone) {
instance->state = PicopassPollerStateFail;
break;
}
memcpy(
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
block.data,
sizeof(PicopassBlock));
FURI_LOG_D(
TAG,
"aia %02x%02x%02x%02x%02x%02x%02x%02x",
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[0],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[1],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[2],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[3],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[4],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[5],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[6],
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data[7]);
instance->state = PicopassPollerStateCheckSecurity;
} while(false);
return command;
}
NfcCommand picopass_poller_check_security(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
// Thank you proxmark!
PicopassBlock temp_block = {};
memset(temp_block.data, 0xff, sizeof(PicopassBlock));
instance->data->pacs.legacy =
(memcmp(
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
temp_block.data,
sizeof(PicopassBlock)) == 0);
temp_block.data[3] = 0x00;
temp_block.data[4] = 0x06;
instance->data->pacs.se_enabled =
(memcmp(
instance->data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data,
temp_block.data,
sizeof(PicopassBlock)) == 0);
if(instance->data->pacs.se_enabled) {
FURI_LOG_D(TAG, "SE enabled");
instance->state = PicopassPollerStateFail;
} else {
instance->state = PicopassPollerStateAuth;
}
return command;
}
NfcCommand picopass_poller_auth_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
do {
// Request key
instance->event.type = PicopassPollerEventTypeRequestKey;
command = instance->callback(instance->event, instance->context);
if(command != NfcCommandContinue) break;
if(!instance->event_data.req_key.is_key_provided) {
instance->state = PicopassPollerStateFail;
break;
}
FURI_LOG_D(
TAG,
"Try to %s auth with key %02x%02x%02x%02x%02x%02x%02x%02x",
instance->event_data.req_key.is_elite_key ? "elite" : "standard",
instance->event_data.req_key.key[0],
instance->event_data.req_key.key[1],
instance->event_data.req_key.key[2],
instance->event_data.req_key.key[3],
instance->event_data.req_key.key[4],
instance->event_data.req_key.key[5],
instance->event_data.req_key.key[6],
instance->event_data.req_key.key[7]);
PicopassReadCheckResp read_check_resp = {};
uint8_t* csn = instance->serial_num.data;
memset(instance->div_key, 0, sizeof(instance->div_key));
uint8_t* div_key = NULL;
if(instance->mode == PicopassPollerModeRead) {
div_key = instance->data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data;
} else {
div_key = instance->div_key;
}
uint8_t ccnr[12] = {};
PicopassMac mac = {};
PicopassError error = picopass_poller_read_check(instance, &read_check_resp);
if(error == PicopassErrorTimeout) {
instance->event.type = PicopassPollerEventTypeCardLost;
command = instance->callback(instance->event, instance->context);
instance->state = PicopassPollerStateDetect;
break;
} else if(error != PicopassErrorNone) {
FURI_LOG_E(TAG, "Read check failed: %d", error);
break;
}
memcpy(ccnr, read_check_resp.data, sizeof(PicopassReadCheckResp)); // last 4 bytes left 0
loclass_iclass_calc_div_key(
csn,
instance->event_data.req_key.key,
div_key,
instance->event_data.req_key.is_elite_key);
loclass_opt_doReaderMAC(ccnr, div_key, mac.data);
PicopassCheckResp check_resp = {};
error = picopass_poller_check(instance, &mac, &check_resp);
if(error == PicopassErrorNone) {
FURI_LOG_I(TAG, "Found key");
memcpy(instance->mac.data, mac.data, sizeof(PicopassMac));
if(instance->mode == PicopassPollerModeRead) {
memcpy(
instance->data->pacs.key, instance->event_data.req_key.key, PICOPASS_KEY_LEN);
instance->data->pacs.elite_kdf = instance->event_data.req_key.is_elite_key;
picopass_poller_prepare_read(instance);
instance->state = PicopassPollerStateReadBlock;
} else if(instance->mode == PicopassPollerModeWrite) {
instance->state = PicopassPollerStateWriteBlock;
} else {
instance->state = PicopassPollerStateWriteKey;
}
}
} while(false);
return command;
}
NfcCommand picopass_poller_read_block_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
do {
if(instance->current_block == instance->app_limit) {
instance->state = PicopassPollerStateParseCredential;
break;
}
if(instance->current_block == PICOPASS_SECURE_KD_BLOCK_INDEX) {
// Skip over Kd block which is populated earlier (READ of Kd returns all FF's)
instance->current_block++;
}
PicopassBlock block = {};
PicopassError error =
picopass_poller_read_block(instance, instance->current_block, &block);
if(error != PicopassErrorNone) {
FURI_LOG_E(TAG, "Failed to read block %d: %d", instance->current_block, error);
instance->state = PicopassPollerStateFail;
break;
}
FURI_LOG_D(
TAG,
"Block %d: %02x%02x%02x%02x%02x%02x%02x%02x",
instance->current_block,
block.data[0],
block.data[1],
block.data[2],
block.data[3],
block.data[4],
block.data[5],
block.data[6],
block.data[7]);
memcpy(
instance->data->AA1[instance->current_block].data, block.data, sizeof(PicopassBlock));
instance->current_block++;
} while(false);
return command;
}
NfcCommand picopass_poller_parse_credential_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
picopass_device_parse_credential(instance->data->AA1, &instance->data->pacs);
instance->state = PicopassPollerStateParseWiegand;
return command;
}
NfcCommand picopass_poller_parse_wiegand_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
picopass_device_parse_wiegand(instance->data->pacs.credential, &instance->data->pacs);
instance->state = PicopassPollerStateSuccess;
return command;
}
NfcCommand picopass_poller_write_block_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
PicopassError error = PicopassErrorNone;
do {
instance->event.type = PicopassPollerEventTypeRequestWriteBlock;
command = instance->callback(instance->event, instance->context);
if(command != NfcCommandContinue) break;
PicopassPollerEventDataRequestWriteBlock* write_block = &instance->event_data.req_write;
if(!write_block->perform_write) {
instance->state = PicopassPollerStateSuccess;
break;
}
FURI_LOG_D(TAG, "Writing %d block", write_block->block_num);
uint8_t data[9] = {};
data[0] = write_block->block_num;
memcpy(&data[1], write_block->block->data, PICOPASS_BLOCK_LEN);
loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
FURI_LOG_D(
TAG,
"loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
write_block->block_num,
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
data[8],
instance->mac.data[0],
instance->mac.data[1],
instance->mac.data[2],
instance->mac.data[3]);
error = picopass_poller_write_block(
instance, write_block->block_num, write_block->block, &instance->mac);
if(error != PicopassErrorNone) {
FURI_LOG_E(TAG, "Failed to write block %d. Error %d", write_block->block_num, error);
instance->state = PicopassPollerStateFail;
break;
}
} while(false);
return command;
}
NfcCommand picopass_poller_write_key_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
PicopassError error = PicopassErrorNone;
do {
instance->event.type = PicopassPollerEventTypeRequestWriteKey;
command = instance->callback(instance->event, instance->context);
if(command != NfcCommandContinue) break;
const PicopassDeviceData* picopass_data = instance->event_data.req_write_key.data;
const uint8_t* new_key = instance->event_data.req_write_key.key;
bool is_elite_key = instance->event_data.req_write_key.is_elite_key;
const uint8_t* csn = picopass_data->AA1[PICOPASS_CSN_BLOCK_INDEX].data;
const uint8_t* config_block = picopass_data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data;
uint8_t fuses = config_block[7];
const uint8_t* old_key = picopass_data->AA1[PICOPASS_SECURE_KD_BLOCK_INDEX].data;
PicopassBlock new_block = {};
loclass_iclass_calc_div_key(csn, new_key, new_block.data, is_elite_key);
if((fuses & 0x80) == 0x80) {
FURI_LOG_D(TAG, "Plain write for personalized mode key change");
} else {
FURI_LOG_D(TAG, "XOR write for application mode key change");
// XOR when in application mode
for(size_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
new_block.data[i] ^= old_key[i];
}
}
FURI_LOG_D(TAG, "Writing key to %d block", PICOPASS_SECURE_KD_BLOCK_INDEX);
uint8_t data[9] = {};
data[0] = PICOPASS_SECURE_KD_BLOCK_INDEX;
memcpy(&data[1], new_block.data, PICOPASS_BLOCK_LEN);
loclass_doMAC_N(data, sizeof(data), instance->div_key, instance->mac.data);
FURI_LOG_D(
TAG,
"loclass_doMAC_N %d %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x",
PICOPASS_SECURE_KD_BLOCK_INDEX,
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
data[8],
instance->mac.data[0],
instance->mac.data[1],
instance->mac.data[2],
instance->mac.data[3]);
error = picopass_poller_write_block(
instance, PICOPASS_SECURE_KD_BLOCK_INDEX, &new_block, &instance->mac);
if(error != PicopassErrorNone) {
FURI_LOG_E(
TAG, "Failed to write block %d. Error %d", PICOPASS_SECURE_KD_BLOCK_INDEX, error);
instance->state = PicopassPollerStateFail;
break;
}
instance->state = PicopassPollerStateSuccess;
} while(false);
return command;
}
NfcCommand picopass_poller_success_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandContinue;
instance->event.type = PicopassPollerEventTypeSuccess;
command = instance->callback(instance->event, instance->context);
furi_delay_ms(100);
return command;
}
NfcCommand picopass_poller_fail_handler(PicopassPoller* instance) {
NfcCommand command = NfcCommandReset;
instance->event.type = PicopassPollerEventTypeFail;
command = instance->callback(instance->event, instance->context);
picopass_poller_reset(instance);
instance->state = PicopassPollerStateDetect;
return command;
}
static const PicopassPollerStateHandler picopass_poller_state_handler[PicopassPollerStateNum] = {
[PicopassPollerStateRequestMode] = picopass_poller_request_mode_handler,
[PicopassPollerStateDetect] = picopass_poller_detect_handler,
[PicopassPollerStateSelect] = picopass_poller_select_handler,
[PicopassPollerStatePreAuth] = picopass_poller_pre_auth_handler,
[PicopassPollerStateCheckSecurity] = picopass_poller_check_security,
[PicopassPollerStateAuth] = picopass_poller_auth_handler,
[PicopassPollerStateReadBlock] = picopass_poller_read_block_handler,
[PicopassPollerStateWriteBlock] = picopass_poller_write_block_handler,
[PicopassPollerStateWriteKey] = picopass_poller_write_key_handler,
[PicopassPollerStateParseCredential] = picopass_poller_parse_credential_handler,
[PicopassPollerStateParseWiegand] = picopass_poller_parse_wiegand_handler,
[PicopassPollerStateSuccess] = picopass_poller_success_handler,
[PicopassPollerStateFail] = picopass_poller_fail_handler,
};
static NfcCommand picopass_poller_callback(NfcEvent event, void* context) {
furi_assert(context);
PicopassPoller* instance = context;
NfcCommand command = NfcCommandContinue;
if(event.type == NfcEventTypePollerReady) {
command = picopass_poller_state_handler[instance->state](instance);
}
if(instance->session_state == PicopassPollerSessionStateStopRequest) {
command = NfcCommandStop;
}
return command;
}
void picopass_poller_start(
PicopassPoller* instance,
PicopassPollerCallback callback,
void* context) {
furi_assert(instance);
furi_assert(instance->session_state == PicopassPollerSessionStateIdle);
instance->callback = callback;
instance->context = context;
instance->session_state = PicopassPollerSessionStateActive;
nfc_start(instance->nfc, picopass_poller_callback, instance);
}
void picopass_poller_stop(PicopassPoller* instance) {
furi_assert(instance);
instance->session_state = PicopassPollerSessionStateStopRequest;
nfc_stop(instance->nfc);
instance->session_state = PicopassPollerSessionStateIdle;
}
PicopassPoller* picopass_poller_alloc(Nfc* nfc) {
furi_assert(nfc);
PicopassPoller* instance = malloc(sizeof(PicopassPoller));
instance->nfc = nfc;
nfc_config(instance->nfc, NfcModePoller, NfcTechIso15693);
nfc_set_guard_time_us(instance->nfc, 10000);
nfc_set_fdt_poll_fc(instance->nfc, 5000);
nfc_set_fdt_poll_poll_us(instance->nfc, 1000);
instance->event.data = &instance->event_data;
instance->data = malloc(sizeof(PicopassDeviceData));
instance->tx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
instance->rx_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
instance->tmp_buffer = bit_buffer_alloc(PICOPASS_POLLER_BUFFER_SIZE);
return instance;
}
void picopass_poller_free(PicopassPoller* instance) {
furi_assert(instance);
free(instance->data);
bit_buffer_free(instance->tx_buffer);
bit_buffer_free(instance->rx_buffer);
bit_buffer_free(instance->tmp_buffer);
free(instance);
}
const PicopassDeviceData* picopass_poller_get_data(PicopassPoller* instance) {
furi_assert(instance);
return instance->data;
}

View File

@@ -0,0 +1,80 @@
#pragma once
#include <nfc/nfc.h>
#include "picopass_protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
PicopassPollerEventTypeRequestMode,
PicopassPollerEventTypeCardDetected,
PicopassPollerEventTypeCardLost,
PicopassPollerEventTypeRequestKey,
PicopassPollerEventTypeRequestWriteBlock,
PicopassPollerEventTypeRequestWriteKey,
PicopassPollerEventTypeSuccess,
PicopassPollerEventTypeFail,
} PicopassPollerEventType;
typedef enum {
PicopassPollerModeRead,
PicopassPollerModeWrite,
PicopassPollerModeWriteKey,
} PicopassPollerMode;
typedef struct {
PicopassPollerMode mode;
} PicopassPollerEventDataRequestMode;
typedef struct {
uint8_t key[PICOPASS_KEY_LEN];
bool is_key_provided;
bool is_elite_key;
} PicopassPollerEventDataRequestKey;
typedef struct {
bool perform_write;
uint8_t block_num;
const PicopassBlock* block;
} PicopassPollerEventDataRequestWriteBlock;
typedef struct {
const PicopassDeviceData* data;
uint8_t key[PICOPASS_KEY_LEN];
bool is_elite_key;
} PicopassPollerEventDataRequestWriteKey;
typedef union {
PicopassPollerEventDataRequestMode req_mode;
PicopassPollerEventDataRequestKey req_key;
PicopassPollerEventDataRequestWriteBlock req_write;
PicopassPollerEventDataRequestWriteKey req_write_key;
} PicopassPollerEventData;
typedef struct {
PicopassPollerEventType type;
PicopassPollerEventData* data;
} PicopassPollerEvent;
typedef NfcCommand (*PicopassPollerCallback)(PicopassPollerEvent event, void* context);
typedef struct PicopassPoller PicopassPoller;
PicopassPoller* picopass_poller_alloc(Nfc* nfc);
void picopass_poller_free(PicopassPoller* instance);
void picopass_poller_start(
PicopassPoller* instance,
PicopassPollerCallback callback,
void* context);
void picopass_poller_stop(PicopassPoller* instance);
const PicopassDeviceData* picopass_poller_get_data(PicopassPoller* instance);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,217 @@
#include "picopass_poller_i.h"
#include <nfc/helpers/iso14443_crc.h>
#define PICOPASS_POLLER_FWT_FC (100000)
#define TAG "Picopass"
static PicopassError picopass_poller_process_error(NfcError error) {
PicopassError ret = PicopassErrorNone;
switch(error) {
case NfcErrorNone:
ret = PicopassErrorNone;
break;
default:
ret = PicopassErrorTimeout;
break;
}
return ret;
}
static PicopassError picopass_poller_send_frame(
PicopassPoller* instance,
BitBuffer* tx_buffer,
BitBuffer* rx_buffer,
uint32_t fwt_fc) {
PicopassError ret = PicopassErrorNone;
do {
NfcError error = nfc_poller_trx(instance->nfc, tx_buffer, rx_buffer, fwt_fc);
if(error != NfcErrorNone) {
ret = picopass_poller_process_error(error);
break;
}
if(!iso13239_crc_check(Iso13239CrcTypePicopass, rx_buffer)) {
ret = PicopassErrorIncorrectCrc;
break;
}
iso13239_crc_trim(instance->rx_buffer);
} while(false);
return ret;
}
PicopassError picopass_poller_actall(PicopassPoller* instance) {
PicopassError ret = PicopassErrorNone;
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_ACTALL);
NfcError error = nfc_poller_trx(
instance->nfc, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(error != NfcErrorIncompleteFrame) {
ret = picopass_poller_process_error(error);
}
return ret;
}
PicopassError picopass_poller_identify(
PicopassPoller* instance,
PicopassColResSerialNum* col_res_serial_num) {
PicopassError ret = PicopassErrorNone;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_READ_OR_IDENTIFY);
ret = picopass_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(ret != PicopassErrorNone) break;
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassColResSerialNum)) {
ret = PicopassErrorProtocol;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer, col_res_serial_num->data, sizeof(PicopassColResSerialNum));
} while(false);
return ret;
}
PicopassError picopass_poller_select(
PicopassPoller* instance,
PicopassColResSerialNum* col_res_serial_num,
PicopassSerialNum* serial_num) {
PicopassError ret = PicopassErrorNone;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_SELECT);
bit_buffer_append_bytes(
instance->tx_buffer, col_res_serial_num->data, sizeof(PicopassColResSerialNum));
ret = picopass_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(ret != PicopassErrorNone) break;
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassSerialNum)) {
ret = PicopassErrorProtocol;
break;
}
bit_buffer_write_bytes(instance->rx_buffer, serial_num->data, sizeof(PicopassSerialNum));
} while(false);
return ret;
}
PicopassError
picopass_poller_read_block(PicopassPoller* instance, uint8_t block_num, PicopassBlock* block) {
PicopassError ret = PicopassErrorNone;
do {
bit_buffer_reset(instance->tmp_buffer);
bit_buffer_append_byte(instance->tmp_buffer, block_num);
iso13239_crc_append(Iso13239CrcTypePicopass, instance->tmp_buffer);
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_READ_OR_IDENTIFY);
bit_buffer_append(instance->tx_buffer, instance->tmp_buffer);
ret = picopass_poller_send_frame(
instance, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(ret != PicopassErrorNone) break;
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassBlock)) {
ret = PicopassErrorProtocol;
break;
}
bit_buffer_write_bytes(instance->rx_buffer, block->data, sizeof(PicopassBlock));
} while(false);
return ret;
}
PicopassError
picopass_poller_read_check(PicopassPoller* instance, PicopassReadCheckResp* read_check_resp) {
PicopassError ret = PicopassErrorNone;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_READCHECK_KD);
bit_buffer_append_byte(instance->tx_buffer, 0x02);
NfcError error = nfc_poller_trx(
instance->nfc, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(error != NfcErrorNone) {
ret = picopass_poller_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassReadCheckResp)) {
ret = PicopassErrorProtocol;
break;
}
bit_buffer_write_bytes(
instance->rx_buffer, read_check_resp->data, sizeof(PicopassReadCheckResp));
} while(false);
return ret;
}
PicopassError picopass_poller_check(
PicopassPoller* instance,
PicopassMac* mac,
PicopassCheckResp* check_resp) {
PicopassError ret = PicopassErrorNone;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_CHECK);
uint8_t null_arr[4] = {};
bit_buffer_append_bytes(instance->tx_buffer, null_arr, sizeof(null_arr));
bit_buffer_append_bytes(instance->tx_buffer, mac->data, sizeof(PicopassMac));
NfcError error = nfc_poller_trx(
instance->nfc, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(error != NfcErrorNone) {
ret = picopass_poller_process_error(error);
break;
}
if(bit_buffer_get_size_bytes(instance->rx_buffer) != sizeof(PicopassCheckResp)) {
ret = PicopassErrorProtocol;
break;
}
bit_buffer_write_bytes(instance->rx_buffer, check_resp->data, sizeof(PicopassCheckResp));
} while(false);
return ret;
}
PicopassError picopass_poller_write_block(
PicopassPoller* instance,
uint8_t block_num,
const PicopassBlock* block,
const PicopassMac* mac) {
PicopassError ret = PicopassErrorNone;
do {
bit_buffer_reset(instance->tx_buffer);
bit_buffer_append_byte(instance->tx_buffer, RFAL_PICOPASS_CMD_UPDATE);
bit_buffer_append_byte(instance->tx_buffer, block_num);
bit_buffer_append_bytes(instance->tx_buffer, block->data, sizeof(PicopassBlock));
bit_buffer_append_bytes(instance->tx_buffer, mac->data, sizeof(PicopassMac));
NfcError error = nfc_poller_trx(
instance->nfc, instance->tx_buffer, instance->rx_buffer, PICOPASS_POLLER_FWT_FC);
if(error != NfcErrorNone) {
ret = picopass_poller_process_error(error);
break;
}
} while(false);
return ret;
}

View File

@@ -0,0 +1,85 @@
#pragma once
#include "picopass_poller.h"
#include "picopass_protocol.h"
#include <nfc/helpers/iso13239_crc.h>
#define PICOPASS_POLLER_BUFFER_SIZE (255)
#define PICOPASS_CRC_SIZE (2)
typedef enum {
PicopassPollerSessionStateIdle,
PicopassPollerSessionStateActive,
PicopassPollerSessionStateStopRequest,
} PicopassPollerSessionState;
typedef enum {
PicopassPollerStateRequestMode,
PicopassPollerStateDetect,
PicopassPollerStateSelect,
PicopassPollerStatePreAuth,
PicopassPollerStateCheckSecurity,
PicopassPollerStateAuth,
PicopassPollerStateReadBlock,
PicopassPollerStateWriteBlock,
PicopassPollerStateWriteKey,
PicopassPollerStateParseCredential,
PicopassPollerStateParseWiegand,
PicopassPollerStateSuccess,
PicopassPollerStateFail,
PicopassPollerStateNum,
} PicopassPollerState;
struct PicopassPoller {
Nfc* nfc;
PicopassPollerSessionState session_state;
PicopassPollerState state;
PicopassPollerMode mode;
PicopassColResSerialNum col_res_serial_num;
PicopassSerialNum serial_num;
PicopassMac mac;
uint8_t div_key[8];
uint8_t current_block;
uint8_t app_limit;
PicopassDeviceData* data;
BitBuffer* tx_buffer;
BitBuffer* rx_buffer;
BitBuffer* tmp_buffer;
PicopassPollerEvent event;
PicopassPollerEventData event_data;
PicopassPollerCallback callback;
void* context;
};
PicopassError picopass_poller_actall(PicopassPoller* instance);
PicopassError
picopass_poller_identify(PicopassPoller* instance, PicopassColResSerialNum* col_res_serial_num);
PicopassError picopass_poller_select(
PicopassPoller* instance,
PicopassColResSerialNum* col_res_serial_num,
PicopassSerialNum* serial_num);
PicopassError
picopass_poller_read_block(PicopassPoller* instance, uint8_t block_num, PicopassBlock* block);
PicopassError
picopass_poller_read_check(PicopassPoller* instance, PicopassReadCheckResp* read_check_resp);
PicopassError picopass_poller_check(
PicopassPoller* instance,
PicopassMac* mac,
PicopassCheckResp* check_resp);
PicopassError picopass_poller_write_block(
PicopassPoller* instance,
uint8_t block_num,
const PicopassBlock* block,
const PicopassMac* mac);

View File

@@ -0,0 +1,48 @@
#pragma once
#include "../picopass_device.h"
#define PICOPASS_BLOCK_LEN 8
#define PICOPASS_MAX_APP_LIMIT 32
#define PICOPASS_UID_LEN 8
#define PICOPASS_READ_CHECK_RESP_LEN 8
#define PICOPASS_CHECK_RESP_LEN 4
#define PICOPASS_MAC_LEN 4
#define PICOPASS_KEY_LEN 8
#define PICOPASS_FDT_LISTEN_FC (1000)
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
PicopassErrorNone,
PicopassErrorTimeout,
PicopassErrorIncorrectCrc,
PicopassErrorProtocol,
} PicopassError;
typedef struct {
uint8_t data[RFAL_PICOPASS_UID_LEN];
} PicopassColResSerialNum;
typedef struct {
uint8_t data[RFAL_PICOPASS_UID_LEN];
} PicopassSerialNum;
typedef struct {
uint8_t data[PICOPASS_READ_CHECK_RESP_LEN];
} PicopassReadCheckResp;
typedef struct {
uint8_t data[PICOPASS_CHECK_RESP_LEN];
} PicopassCheckResp;
typedef struct {
uint8_t data[PICOPASS_MAC_LEN];
} PicopassMac;
#ifdef __cplusplus
}
#endif

View File

@@ -1,213 +0,0 @@
#include "rfal_picopass.h"
#define RFAL_PICOPASS_TXRX_FLAGS \
(FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_TX_MANUAL | FURI_HAL_NFC_LL_TXRX_FLAGS_AGC_ON | \
FURI_HAL_NFC_LL_TXRX_FLAGS_PAR_RX_REMV | FURI_HAL_NFC_LL_TXRX_FLAGS_CRC_RX_KEEP)
#define TAG "RFAL_PICOPASS"
typedef struct {
uint8_t CMD;
uint8_t CSN[RFAL_PICOPASS_UID_LEN];
} rfalPicoPassSelectReq;
typedef struct {
uint8_t CMD;
uint8_t null[4];
uint8_t mac[4];
} rfalPicoPassCheckReq;
static uint16_t rfalPicoPassUpdateCcitt(uint16_t crcSeed, uint8_t dataByte) {
uint16_t crc = crcSeed;
uint8_t dat = dataByte;
dat ^= (uint8_t)(crc & 0xFFU);
dat ^= (dat << 4);
crc = (crc >> 8) ^ (((uint16_t)dat) << 8) ^ (((uint16_t)dat) << 3) ^ (((uint16_t)dat) >> 4);
return crc;
}
uint16_t rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length) {
uint16_t crc = preloadValue;
uint16_t index;
for(index = 0; index < length; index++) {
crc = rfalPicoPassUpdateCcitt(crc, buf[index]);
}
return crc;
}
FuriHalNfcReturn rfalPicoPassPollerInitialize(void) {
FuriHalNfcReturn ret;
ret = furi_hal_nfc_ll_set_mode(
FuriHalNfcModePollPicopass, FuriHalNfcBitrate26p48, FuriHalNfcBitrate26p48);
if(ret != FuriHalNfcReturnOk) {
return ret;
};
furi_hal_nfc_ll_set_error_handling(FuriHalNfcErrorHandlingNfc);
furi_hal_nfc_ll_set_guard_time(FURI_HAL_NFC_LL_GT_PICOPASS);
furi_hal_nfc_ll_set_fdt_listen(FURI_HAL_NFC_LL_FDT_LISTEN_PICOPASS_POLLER);
furi_hal_nfc_ll_set_fdt_poll(FURI_HAL_NFC_LL_FDT_POLL_PICOPASS_POLLER);
return FuriHalNfcReturnOk;
}
FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void) {
FuriHalNfcReturn ret;
uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_ACTALL};
uint8_t rxBuf[32] = {0};
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
ret = furi_hal_nfc_ll_txrx(txBuf, 1, rxBuf, 32, &recvLen, flags, fwt);
return ret;
}
FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) {
FuriHalNfcReturn ret;
uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_READ_OR_IDENTIFY};
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
ret = furi_hal_nfc_ll_txrx(
txBuf,
sizeof(txBuf),
(uint8_t*)idRes,
sizeof(rfalPicoPassIdentifyRes),
&recvLen,
flags,
fwt);
// printf("identify rx: %d %s\n", recvLen, hex2Str(idRes->CSN, RFAL_PICOPASS_UID_LEN));
return ret;
}
FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes) {
FuriHalNfcReturn ret;
rfalPicoPassSelectReq selReq;
selReq.CMD = RFAL_PICOPASS_CMD_SELECT;
memcpy(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
ret = furi_hal_nfc_ll_txrx(
(uint8_t*)&selReq,
sizeof(rfalPicoPassSelectReq),
(uint8_t*)selRes,
sizeof(rfalPicoPassSelectRes),
&recvLen,
flags,
fwt);
// printf("select rx: %d %s\n", recvLen, hex2Str(selRes->CSN, RFAL_PICOPASS_UID_LEN));
if(ret == FuriHalNfcReturnTimeout) {
return FuriHalNfcReturnOk;
}
return ret;
}
FuriHalNfcReturn rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes) {
FuriHalNfcReturn ret;
uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK_KD, 0x02};
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
ret = furi_hal_nfc_ll_txrx(
txBuf,
sizeof(txBuf),
(uint8_t*)rcRes,
sizeof(rfalPicoPassReadCheckRes),
&recvLen,
flags,
fwt);
// printf("readcheck rx: %d %s\n", recvLen, hex2Str(rcRes->CCNR, 8));
if(ret == FuriHalNfcReturnCrc) {
return FuriHalNfcReturnOk;
}
return ret;
}
FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes) {
FuriHalNfcReturn ret;
rfalPicoPassCheckReq chkReq;
chkReq.CMD = RFAL_PICOPASS_CMD_CHECK;
memcpy(chkReq.mac, mac, 4);
memset(chkReq.null, 0, 4);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
// printf("check tx: %s\n", hex2Str((uint8_t *)&chkReq, sizeof(rfalPicoPassCheckReq)));
ret = furi_hal_nfc_ll_txrx(
(uint8_t*)&chkReq,
sizeof(rfalPicoPassCheckReq),
(uint8_t*)chkRes,
sizeof(rfalPicoPassCheckRes),
&recvLen,
flags,
fwt);
// printf("check rx: %d %s\n", recvLen, hex2Str(chkRes->mac, 4));
if(ret == FuriHalNfcReturnCrc) {
return FuriHalNfcReturnOk;
}
return ret;
}
FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes) {
FuriHalNfcReturn ret;
uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ_OR_IDENTIFY, 0, 0, 0};
txBuf[1] = blockNum;
uint16_t crc = rfalPicoPassCalculateCcitt(0xE012, txBuf + 1, 1);
memcpy(txBuf + 2, &crc, sizeof(uint16_t));
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
ret = furi_hal_nfc_ll_txrx(
txBuf,
sizeof(txBuf),
(uint8_t*)readRes,
sizeof(rfalPicoPassReadBlockRes),
&recvLen,
flags,
fwt);
return ret;
}
FuriHalNfcReturn rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) {
FuriHalNfcReturn ret;
uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_UPDATE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memcpy(txBuf + 2, data, RFAL_PICOPASS_BLOCK_LEN);
memcpy(txBuf + 10, mac, 4);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = furi_hal_nfc_ll_ms2fc(20);
rfalPicoPassReadBlockRes block;
ret = furi_hal_nfc_ll_txrx(
txBuf, sizeof(txBuf), (uint8_t*)&block, sizeof(block), &recvLen, flags, fwt);
if(ret == FuriHalNfcReturnOk) {
// TODO: compare response
}
return ret;
}

View File

@@ -3,7 +3,7 @@
#include <furi_hal_nfc.h>
#define RFAL_PICOPASS_UID_LEN 8
#define RFAL_PICOPASS_BLOCK_LEN 8
#define PICOPASS_BLOCK_LEN 8
enum {
// PicoPass command bytes:
@@ -59,17 +59,6 @@ typedef struct {
} rfalPicoPassCheckRes;
typedef struct {
uint8_t data[RFAL_PICOPASS_BLOCK_LEN];
uint8_t data[PICOPASS_BLOCK_LEN];
uint8_t crc[2];
} rfalPicoPassReadBlockRes;
uint16_t rfalPicoPassCalculateCcitt(uint16_t preloadValue, const uint8_t* buf, uint16_t length);
FuriHalNfcReturn rfalPicoPassPollerInitialize(void);
FuriHalNfcReturn rfalPicoPassPollerCheckPresence(void);
FuriHalNfcReturn rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes);
FuriHalNfcReturn rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes);
FuriHalNfcReturn rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes);
FuriHalNfcReturn rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes);
FuriHalNfcReturn rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes);
FuriHalNfcReturn rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]);

View File

@@ -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(

View File

@@ -25,32 +25,26 @@ void picopass_scene_device_info_on_enter(void* context) {
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget;
uint8_t csn[RFAL_PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) {
uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
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++) {
for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < 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");

View File

@@ -1,172 +1,238 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
#include "../picopass_keys.h"
#define TAG "IclassEliteDictAttack"
#define PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE (10)
typedef enum {
DictAttackStateIdle,
DictAttackStateUserDictInProgress,
DictAttackStateFlipperDictInProgress,
DictAttackStateStandardDictInProgress,
} DictAttackState;
enum {
PicopassSceneEliteDictAttackDictEliteUser,
PicopassSceneEliteDictAttackDictStandart,
PicopassSceneEliteDictAttackDictElite,
};
void picopass_dict_attack_worker_callback(PicopassWorkerEvent event, void* context) {
furi_assert(context);
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
const char* picopass_dict_name[] = {
[PicopassSceneEliteDictAttackDictEliteUser] = "Elite User Dictionary",
[PicopassSceneEliteDictAttackDictStandart] = "Standard System Dictionary",
[PicopassSceneEliteDictAttackDictElite] = "Elite System Dictionary",
};
static bool picopass_elite_dict_attack_change_dict(Picopass* picopass) {
bool success = false;
do {
uint32_t scene_state =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack);
nfc_dict_free(picopass->dict);
picopass->dict = NULL;
if(scene_state == PicopassSceneEliteDictAttackDictElite) break;
if(scene_state == PicopassSceneEliteDictAttackDictEliteUser) {
if(!nfc_dict_check_presence(PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME)) break;
picopass->dict = nfc_dict_alloc(
PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME,
NfcDictModeOpenExisting,
PICOPASS_KEY_LEN);
scene_state = PicopassSceneEliteDictAttackDictStandart;
} else if(scene_state == PicopassSceneEliteDictAttackDictStandart) {
if(!nfc_dict_check_presence(PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME)) break;
picopass->dict = nfc_dict_alloc(
PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME,
NfcDictModeOpenExisting,
PICOPASS_KEY_LEN);
scene_state = PicopassSceneEliteDictAttackDictElite;
}
picopass->dict_attack_ctx.card_detected = true;
picopass->dict_attack_ctx.total_keys = nfc_dict_get_total_keys(picopass->dict);
picopass->dict_attack_ctx.current_key = 0;
picopass->dict_attack_ctx.name = picopass_dict_name[scene_state];
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneEliteDictAttack, scene_state);
success = true;
} while(false);
return success;
}
void picopass_dict_attack_result_callback(void* context) {
NfcCommand picopass_elite_dict_attack_worker_callback(PicopassPollerEvent event, void* context) {
furi_assert(context);
NfcCommand command = NfcCommandContinue;
Picopass* picopass = context;
if(event.type == PicopassPollerEventTypeRequestMode) {
event.data->req_mode.mode = PicopassPollerModeRead;
} else if(event.type == PicopassPollerEventTypeRequestKey) {
uint8_t key[PICOPASS_KEY_LEN] = {};
bool is_key_provided = true;
if(!nfc_dict_get_next_key(picopass->dict, key, PICOPASS_KEY_LEN)) {
if(picopass_elite_dict_attack_change_dict(picopass)) {
is_key_provided = nfc_dict_get_next_key(picopass->dict, key, PICOPASS_KEY_LEN);
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
} else {
is_key_provided = false;
}
}
uint32_t scene_state =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack);
memcpy(event.data->req_key.key, key, PICOPASS_KEY_LEN);
event.data->req_key.is_elite_key =
(scene_state != PicopassSceneEliteDictAttackDictStandart);
event.data->req_key.is_key_provided = is_key_provided;
if(is_key_provided) {
picopass->dict_attack_ctx.current_key++;
if(picopass->dict_attack_ctx.current_key %
PICOPASS_SCENE_DICT_ATTACK_KEYS_BATCH_UPDATE ==
0) {
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
}
}
} else if(event.type == PicopassPollerEventTypeSuccess) {
const PicopassDeviceData* data = picopass_poller_get_data(picopass->poller);
memcpy(&picopass->dev->dev_data, data, sizeof(PicopassDeviceData));
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
} else if(event.type == PicopassPollerEventTypeFail) {
const PicopassDeviceData* data = picopass_poller_get_data(picopass->poller);
memcpy(&picopass->dev->dev_data, data, sizeof(PicopassDeviceData));
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
} else if(event.type == PicopassPollerEventTypeCardLost) {
picopass->dict_attack_ctx.card_detected = false;
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
} else if(event.type == PicopassPollerEventTypeCardDetected) {
picopass->dict_attack_ctx.card_detected = true;
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventDictAttackUpdateView);
}
return command;
}
static void picopass_scene_elite_dict_attack_update_view(Picopass* instance) {
if(instance->dict_attack_ctx.card_detected) {
dict_attack_set_card_detected(instance->dict_attack);
dict_attack_set_header(instance->dict_attack, instance->dict_attack_ctx.name);
dict_attack_set_total_dict_keys(
instance->dict_attack, instance->dict_attack_ctx.total_keys);
dict_attack_set_current_dict_key(
instance->dict_attack, instance->dict_attack_ctx.current_key);
} else {
dict_attack_set_card_removed(instance->dict_attack);
}
}
static void picopass_scene_elite_dict_attack_callback(void* context) {
Picopass* instance = context;
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventDictAttackSkip);
}
static void
picopass_scene_elite_dict_attack_prepare_view(Picopass* picopass, DictAttackState state) {
IclassEliteDictAttackData* dict_attack_data =
&picopass->dev->dev_data.iclass_elite_dict_attack_data;
PicopassWorkerState worker_state = PicopassWorkerStateReady;
IclassEliteDict* dict = NULL;
// Identify scene state
if(state == DictAttackStateIdle) {
if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) {
FURI_LOG_D(TAG, "Starting with user dictionary");
state = DictAttackStateUserDictInProgress;
} else {
FURI_LOG_D(TAG, "Starting with standard dictionary");
state = DictAttackStateStandardDictInProgress;
}
} else if(state == DictAttackStateUserDictInProgress) {
FURI_LOG_D(TAG, "Moving from user dictionary to standard dictionary");
state = DictAttackStateStandardDictInProgress;
} else if(state == DictAttackStateStandardDictInProgress) {
FURI_LOG_D(TAG, "Moving from standard dictionary to elite dictionary");
state = DictAttackStateFlipperDictInProgress;
}
// Setup view
if(state == DictAttackStateUserDictInProgress) {
worker_state = PicopassWorkerStateEliteDictAttack;
dict_attack_set_header(picopass->dict_attack, "Elite User Dictionary");
dict_attack_data->type = IclassEliteDictTypeUser;
dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser);
// If failed to load user dictionary - try the system dictionary
if(!dict) {
FURI_LOG_E(TAG, "User dictionary not found");
state = DictAttackStateStandardDictInProgress;
}
}
if(state == DictAttackStateStandardDictInProgress) {
worker_state = PicopassWorkerStateEliteDictAttack;
dict_attack_set_header(picopass->dict_attack, "Standard System Dictionary");
dict_attack_data->type = IclassStandardDictTypeFlipper;
dict = iclass_elite_dict_alloc(IclassStandardDictTypeFlipper);
if(!dict) {
FURI_LOG_E(TAG, "Flipper standard dictionary not found");
state = DictAttackStateFlipperDictInProgress;
}
}
if(state == DictAttackStateFlipperDictInProgress) {
worker_state = PicopassWorkerStateEliteDictAttack;
dict_attack_set_header(picopass->dict_attack, "Elite System Dictionary");
dict_attack_data->type = IclassEliteDictTypeFlipper;
dict = iclass_elite_dict_alloc(IclassEliteDictTypeFlipper);
if(!dict) {
FURI_LOG_E(TAG, "Flipper Elite dictionary not found");
// Pass through to let the worker handle the failure
}
}
// Free previous dictionary
if(dict_attack_data->dict) {
iclass_elite_dict_free(dict_attack_data->dict);
}
dict_attack_data->dict = dict;
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state);
dict_attack_set_callback(
picopass->dict_attack, picopass_dict_attack_result_callback, picopass);
dict_attack_set_current_sector(picopass->dict_attack, 0);
dict_attack_set_card_detected(picopass->dict_attack);
dict_attack_set_total_dict_keys(
picopass->dict_attack, dict ? iclass_elite_dict_get_total_keys(dict) : 0);
picopass_worker_start(
picopass->worker,
worker_state,
&picopass->dev->dev_data,
picopass_dict_attack_worker_callback,
picopass);
instance->view_dispatcher, PicopassCustomEventDictAttackSkip);
}
void picopass_scene_elite_dict_attack_on_enter(void* context) {
Picopass* picopass = context;
picopass_scene_elite_dict_attack_prepare_view(picopass, DictAttackStateIdle);
dolphin_deed(DolphinDeedNfcRead);
// Setup dict attack context
uint32_t state = PicopassSceneEliteDictAttackDictEliteUser;
bool use_user_dict = nfc_dict_check_presence(PICOPASS_ICLASS_ELITE_DICT_USER_NAME);
if(use_user_dict) {
picopass->dict = nfc_dict_alloc(
PICOPASS_ICLASS_ELITE_DICT_USER_NAME, NfcDictModeOpenExisting, PICOPASS_KEY_LEN);
if(nfc_dict_get_total_keys(picopass->dict) == 0) {
nfc_dict_free(picopass->dict);
use_user_dict = false;
}
}
if(use_user_dict) {
state = PicopassSceneEliteDictAttackDictEliteUser;
} else {
picopass->dict = nfc_dict_alloc(
PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME, NfcDictModeOpenExisting, PICOPASS_KEY_LEN);
state = PicopassSceneEliteDictAttackDictStandart;
}
picopass->dict_attack_ctx.card_detected = true;
picopass->dict_attack_ctx.total_keys = nfc_dict_get_total_keys(picopass->dict);
picopass->dict_attack_ctx.current_key = 0;
picopass->dict_attack_ctx.name = picopass_dict_name[state];
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack, state);
// Setup view
picopass_scene_elite_dict_attack_update_view(picopass);
dict_attack_set_callback(
picopass->dict_attack, picopass_scene_elite_dict_attack_callback, picopass);
// Start worker
picopass->poller = picopass_poller_alloc(picopass->nfc);
picopass_poller_start(picopass->poller, picopass_elite_dict_attack_worker_callback, picopass);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewDictAttack);
picopass_blink_start(picopass);
notification_message(picopass->notifications, &sequence_display_backlight_enforce_on);
}
bool picopass_scene_elite_dict_attack_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
uint32_t state =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneEliteDictAttack);
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassWorkerEventSuccess) {
if(state == DictAttackStateUserDictInProgress ||
state == DictAttackStateStandardDictInProgress) {
picopass_worker_stop(picopass->worker);
picopass_scene_elite_dict_attack_prepare_view(picopass, state);
consumed = true;
if(event.event == PicopassCustomEventPollerSuccess) {
if(memcmp(
picopass->dev->dev_data.pacs.key,
picopass_factory_debit_key,
PICOPASS_BLOCK_LEN) == 0) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess);
} else {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
consumed = true;
}
} else if(event.event == PicopassWorkerEventAborted) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
consumed = true;
} else if(event.event == PicopassWorkerEventCardDetected) {
dict_attack_set_card_detected(picopass->dict_attack);
consumed = true;
} else if(event.event == PicopassWorkerEventNoCardDetected) {
dict_attack_set_card_removed(picopass->dict_attack);
consumed = true;
} else if(event.event == PicopassWorkerEventNewDictKeyBatch) {
dict_attack_inc_current_dict_key(picopass->dict_attack, PICOPASS_DICT_KEY_BATCH_SIZE);
} else if(event.event == PicopassCustomEventDictAttackUpdateView) {
picopass_scene_elite_dict_attack_update_view(picopass);
consumed = true;
} else if(event.event == PicopassCustomEventDictAttackSkip) {
if(state == DictAttackStateUserDictInProgress) {
picopass_worker_stop(picopass->worker);
consumed = true;
} else if(state == DictAttackStateFlipperDictInProgress) {
picopass_worker_stop(picopass->worker);
consumed = true;
} else if(state == DictAttackStateStandardDictInProgress) {
picopass_worker_stop(picopass->worker);
consumed = true;
uint32_t scene_state = scene_manager_get_scene_state(
picopass->scene_manager, PicopassSceneEliteDictAttack);
if(scene_state != PicopassSceneEliteDictAttackDictElite) {
picopass_elite_dict_attack_change_dict(picopass);
picopass_scene_elite_dict_attack_update_view(picopass);
} else {
if(memcmp(
picopass->dev->dev_data.pacs.key,
picopass_factory_debit_key,
PICOPASS_BLOCK_LEN) == 0) {
scene_manager_next_scene(
picopass->scene_manager, PicopassSceneReadFactorySuccess);
} else {
scene_manager_next_scene(
picopass->scene_manager, PicopassSceneReadCardSuccess);
}
}
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
}
return consumed;
}
void picopass_scene_elite_dict_attack_on_exit(void* context) {
Picopass* picopass = context;
IclassEliteDictAttackData* dict_attack_data =
&picopass->dev->dev_data.iclass_elite_dict_attack_data;
// Stop worker
picopass_worker_stop(picopass->worker);
if(dict_attack_data->dict) {
iclass_elite_dict_free(dict_attack_data->dict);
dict_attack_data->dict = NULL;
if(picopass->dict) {
nfc_dict_free(picopass->dict);
picopass->dict = NULL;
}
dict_attack_reset(picopass->dict_attack);
picopass->dict_attack_ctx.current_key = 0;
picopass->dict_attack_ctx.total_keys = 0;
picopass_poller_stop(picopass->poller);
picopass_poller_free(picopass->poller);
// Clear view
popup_reset(picopass->popup);
scene_manager_set_scene_state(
picopass->scene_manager,
PicopassSceneEliteDictAttack,
PicopassSceneEliteDictAttackDictEliteUser);
picopass_blink_stop(picopass);
notification_message(picopass->notifications, &sequence_display_backlight_enforce_auto);
}

View File

@@ -1,10 +1,11 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
void picopass_emulate_worker_callback(PicopassWorkerEvent event, void* context) {
furi_assert(context);
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
NfcCommand picopass_scene_listener_callback(PicopassListenerEvent event, void* context) {
UNUSED(event);
UNUSED(context);
return NfcCommandContinue;
}
void picopass_scene_emulate_on_enter(void* context) {
@@ -17,18 +18,11 @@ void picopass_scene_emulate_on_enter(void* context) {
widget_add_string_element(widget, 89, 32, AlignCenter, AlignTop, FontPrimary, "Emulating");
widget_add_string_element(widget, 89, 42, AlignCenter, AlignTop, FontPrimary, "PicoPass");
// Setup view
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
// Start worker
picopass_worker_start(
picopass->worker,
PicopassWorkerStateEmulate,
&picopass->dev->dev_data,
picopass_emulate_worker_callback,
picopass);
picopass_blink_emulate_start(picopass);
picopass->listener = picopass_listener_alloc(picopass->nfc, &picopass->dev->dev_data);
picopass_listener_start(picopass->listener, picopass_scene_listener_callback, picopass);
}
bool picopass_scene_emulate_on_event(void* context, SceneManagerEvent event) {
@@ -49,9 +43,8 @@ void picopass_scene_emulate_on_exit(void* context) {
Picopass* picopass = context;
picopass_blink_stop(picopass);
// Stop worker
picopass_worker_stop(picopass->worker);
picopass_listener_stop(picopass->listener);
picopass_listener_free(picopass->listener);
// Clear view
widget_reset(picopass->widget);

View File

@@ -1,12 +1,13 @@
#include "../picopass_i.h"
#include <lib/toolbox/name_generator.h>
#include <gui/modules/validators.h>
#include <toolbox/path.h>
void picopass_scene_key_input_text_input_callback(void* context) {
Picopass* picopass = context;
picopass->dev->dev_data.pacs.elite_kdf = true;
memcpy(picopass->dev->dev_data.pacs.key, picopass->byte_input_store, RFAL_PICOPASS_BLOCK_LEN);
memcpy(picopass->write_key_context.key_to_write, picopass->byte_input_store, PICOPASS_KEY_LEN);
picopass->write_key_context.is_elite = true;
view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventByteInputDone);
}
@@ -21,7 +22,7 @@ void picopass_scene_key_input_on_enter(void* context) {
NULL,
picopass,
picopass->byte_input_store,
RFAL_PICOPASS_BLOCK_LEN);
PICOPASS_BLOCK_LEN);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewByteInput);
}

View File

@@ -65,35 +65,43 @@ bool picopass_scene_key_menu_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexWriteStandard) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteStandard);
memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, RFAL_PICOPASS_BLOCK_LEN);
picopass->dev->dev_data.pacs.elite_kdf = false;
memcpy(
picopass->write_key_context.key_to_write, picopass_iclass_key, PICOPASS_KEY_LEN);
picopass->write_key_context.is_elite = false;
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
consumed = true;
} else if(event.event == SubmenuIndexWriteiCE) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCE);
memcpy(picopass->dev->dev_data.pacs.key, picopass_xice_key, RFAL_PICOPASS_BLOCK_LEN);
picopass->dev->dev_data.pacs.elite_kdf = true;
memcpy(picopass->write_key_context.key_to_write, picopass_xice_key, PICOPASS_KEY_LEN);
picopass->write_key_context.is_elite = true;
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
consumed = true;
} else if(event.event == SubmenuIndexWriteiCL) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCL);
memcpy(picopass->dev->dev_data.pacs.key, picopass_xicl_key, RFAL_PICOPASS_BLOCK_LEN);
picopass->dev->dev_data.pacs.elite_kdf = false;
memcpy(picopass->write_key_context.key_to_write, picopass_xicl_key, PICOPASS_KEY_LEN);
picopass->write_key_context.is_elite = false;
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
consumed = true;
} else if(event.event == SubmenuIndexWriteiCS) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteiCS);
memcpy(picopass->dev->dev_data.pacs.key, picopass_xics_key, RFAL_PICOPASS_BLOCK_LEN);
picopass->dev->dev_data.pacs.elite_kdf = false;
memcpy(picopass->write_key_context.key_to_write, picopass_xics_key, PICOPASS_KEY_LEN);
picopass->write_key_context.is_elite = false;
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
consumed = true;
} else if(event.event == SubmenuIndexWriteCustom) {
// If user dictionary, prepopulate with the first key
if(iclass_elite_dict_check_presence(IclassEliteDictTypeUser)) {
IclassEliteDict* dict = iclass_elite_dict_alloc(IclassEliteDictTypeUser);
iclass_elite_dict_get_next_key(dict, picopass->byte_input_store);
iclass_elite_dict_free(dict);
}
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneKeyMenu, SubmenuIndexWriteCustom);
// Key and elite_kdf = true are both set in key_input scene
// Key and elite_kdf = true are both set in key_input scene after the value is input
scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyInput);
consumed = true;
}

View File

@@ -1,80 +1,103 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
void picopass_loclass_worker_callback(PicopassWorkerEvent event, void* context) {
furi_assert(context);
static NfcCommand
picopass_scene_loclass_listener_callback(PicopassListenerEvent event, void* context) {
NfcCommand command = NfcCommandContinue;
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
if(event.type == PicopassListenerEventTypeLoclassGotMac) {
picopass->loclass_context.macs_collected++;
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventLoclassGotMac);
} else if(event.type == PicopassListenerEventTypeLoclassGotStandardKey) {
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventLoclassGotStandardKey);
command = NfcCommandStop;
}
return command;
}
void picopass_loclass_result_callback(void* context) {
static void picopass_loclass_result_callback(void* context) {
furi_assert(context);
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit);
}
void picopass_scene_loclass_on_enter(void* context) {
Picopass* picopass = context;
dolphin_deed(DolphinDeedNfcEmulate);
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneLoclass, 0);
loclass_set_callback(picopass->loclass, picopass_loclass_result_callback, picopass);
// Start worker
picopass_worker_start(
picopass->worker,
PicopassWorkerStateLoclass,
&picopass->dev->dev_data,
picopass_loclass_worker_callback,
picopass);
picopass_blink_emulate_start(picopass);
loclass_set_header(picopass->loclass, "Loclass");
picopass_blink_emulate_start(picopass);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewLoclass);
PicopassDeviceData* data = malloc(sizeof(PicopassDeviceData));
const uint8_t config_block[PICOPASS_BLOCK_LEN] = {
0x12, 0xFF, 0xFF, 0xFF, 0x7F, 0x1F, 0xFF, 0x3C};
memcpy(data->AA1[PICOPASS_CONFIG_BLOCK_INDEX].data, config_block, sizeof(config_block));
const uint8_t epurse[PICOPASS_BLOCK_LEN] = {0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
memcpy(data->AA1[PICOPASS_SECURE_EPURSE_BLOCK_INDEX].data, epurse, sizeof(epurse));
const uint8_t aia[PICOPASS_BLOCK_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
memcpy(data->AA1[PICOPASS_SECURE_AIA_BLOCK_INDEX].data, aia, sizeof(aia));
picopass->listener = picopass_listener_alloc(picopass->nfc, data);
free(data);
if(picopass_listener_set_mode(picopass->listener, PicopassListenerModeLoclass)) {
picopass_listener_start(
picopass->listener, picopass_scene_loclass_listener_callback, picopass);
} else {
loclass_set_num_macs(picopass->loclass, 255);
loclass_set_header(picopass->loclass, "Error Opening Log File");
picopass_listener_free(picopass->listener);
picopass->listener = NULL;
}
}
bool picopass_scene_loclass_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
uint32_t loclass_macs_collected =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneLoclass);
Picopass* picopass = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassWorkerEventLoclassGotMac) {
loclass_macs_collected++;
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneLoclass, loclass_macs_collected);
loclass_set_num_macs(picopass->loclass, loclass_macs_collected);
if(loclass_macs_collected >= LOCLASS_MACS_TO_COLLECT) {
if(event.event == PicopassCustomEventLoclassGotMac) {
notification_message(picopass->notifications, &sequence_single_vibro);
loclass_set_num_macs(picopass->loclass, picopass->loclass_context.macs_collected);
if(picopass->loclass_context.macs_collected >= LOCLASS_MACS_TO_COLLECT) {
notification_message(picopass->notifications, &sequence_double_vibro);
scene_manager_previous_scene(picopass->scene_manager);
}
consumed = true;
} else if(event.event == PicopassWorkerEventLoclassGotStandardKey) {
} else if(event.event == PicopassCustomEventLoclassGotStandardKey) {
loclass_set_header(picopass->loclass, "Loclass (Got Std Key)");
notification_message(picopass->notifications, &sequence_error);
consumed = true;
} else if(event.event == PicopassCustomEventViewExit) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
}
return consumed;
}
void picopass_scene_loclass_on_exit(void* context) {
Picopass* picopass = context;
if(picopass->listener) {
picopass_listener_stop(picopass->listener);
picopass_listener_free(picopass->listener);
}
picopass->loclass_context.macs_collected = 0;
picopass_blink_stop(picopass);
// Stop worker
picopass_worker_stop(picopass->worker);
loclass_reset(picopass->loclass);
// Clear view
widget_reset(picopass->widget);
}
}

View File

@@ -2,10 +2,68 @@
#include <dolphin/dolphin.h>
#include "../picopass_keys.h"
void picopass_read_card_worker_callback(PicopassWorkerEvent event, void* context) {
UNUSED(event);
enum {
PicopassSceneReadCardDictStandart,
PicopassSceneReadCardDictElite,
};
static bool picopass_read_card_change_dict(Picopass* picopass) {
bool success = false;
do {
uint32_t scene_state =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneReadCard);
nfc_dict_free(picopass->dict);
picopass->dict = NULL;
if(scene_state == PicopassSceneReadCardDictElite) break;
if(!nfc_dict_check_presence(PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME)) break;
picopass->dict = nfc_dict_alloc(
PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME, NfcDictModeOpenExisting, PICOPASS_KEY_LEN);
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneReadCard, PicopassSceneReadCardDictElite);
success = true;
} while(false);
return success;
}
NfcCommand picopass_read_card_worker_callback(PicopassPollerEvent event, void* context) {
furi_assert(context);
NfcCommand command = NfcCommandContinue;
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit);
if(event.type == PicopassPollerEventTypeRequestMode) {
event.data->req_mode.mode = PicopassPollerModeRead;
} else if(event.type == PicopassPollerEventTypeRequestKey) {
uint8_t key[PICOPASS_KEY_LEN] = {};
bool is_key_provided = true;
if(!nfc_dict_get_next_key(picopass->dict, key, PICOPASS_KEY_LEN)) {
if(picopass_read_card_change_dict(picopass)) {
is_key_provided = nfc_dict_get_next_key(picopass->dict, key, PICOPASS_KEY_LEN);
} else {
is_key_provided = false;
}
}
uint32_t scene_state =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneReadCard);
memcpy(event.data->req_key.key, key, PICOPASS_KEY_LEN);
event.data->req_key.is_elite_key = (scene_state == PicopassSceneReadCardDictElite);
event.data->req_key.is_key_provided = is_key_provided;
} else if(event.type == PicopassPollerEventTypeSuccess) {
const PicopassDeviceData* data = picopass_poller_get_data(picopass->poller);
memcpy(&picopass->dev->dev_data, data, sizeof(PicopassDeviceData));
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
} else if(event.type == PicopassPollerEventTypeFail) {
const PicopassDeviceData* data = picopass_poller_get_data(picopass->poller);
memcpy(&picopass->dev->dev_data, data, sizeof(PicopassDeviceData));
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
}
return command;
}
void picopass_scene_read_card_on_enter(void* context) {
@@ -17,15 +75,15 @@ void picopass_scene_read_card_on_enter(void* context) {
popup_set_header(popup, "Detecting\npicopass\ncard", 68, 30, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);
picopass->dict = nfc_dict_alloc(
PICOPASS_ICLASS_STANDARD_DICT_FLIPPER_NAME, NfcDictModeOpenExisting, PICOPASS_KEY_LEN);
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneReadCard, PicopassSceneReadCardDictStandart);
// Start worker
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
picopass_worker_start(
picopass->worker,
PicopassWorkerStateDetect,
&picopass->dev->dev_data,
picopass_read_card_worker_callback,
picopass);
picopass->poller = picopass_poller_alloc(picopass->nfc);
picopass_poller_start(picopass->poller, picopass_read_card_worker_callback, picopass);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
picopass_blink_start(picopass);
}
@@ -34,11 +92,11 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventWorkerExit) {
if(event.event == PicopassCustomEventPollerSuccess) {
if(memcmp(
picopass->dev->dev_data.pacs.key,
picopass_factory_debit_key,
RFAL_PICOPASS_BLOCK_LEN) == 0) {
PICOPASS_BLOCK_LEN) == 0) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadFactorySuccess);
} else {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCardSuccess);
@@ -52,10 +110,17 @@ bool picopass_scene_read_card_on_event(void* context, SceneManagerEvent event) {
void picopass_scene_read_card_on_exit(void* context) {
Picopass* picopass = context;
// Stop worker
picopass_worker_stop(picopass->worker);
if(picopass->dict) {
nfc_dict_free(picopass->dict);
picopass->dict = NULL;
}
picopass_poller_stop(picopass->poller);
picopass_poller_free(picopass->poller);
// Clear view
popup_reset(picopass->popup);
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneReadCard, PicopassSceneReadCardDictStandart);
picopass_blink_stop(picopass);
}

View File

@@ -1,5 +1,6 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
#include <picopass_keys.h>
void picopass_scene_read_card_success_widget_callback(
GuiButtonType result,
@@ -31,21 +32,26 @@ void picopass_scene_read_card_success_on_enter(void* context) {
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget;
uint8_t csn[RFAL_PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, RFAL_PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) {
uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
memcpy(csn, AA1[PICOPASS_CSN_BLOCK_INDEX].data, PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(csn_str, "%02X", csn[i]);
}
bool no_key = picopass_is_memset(pacs->key, 0x00, RFAL_PICOPASS_BLOCK_LEN);
// We can't test the pacs->key in case it is intentionally all 0's and we can't test the key block since it is populated with the diversified key before each key test, so we approximate with the PACS config block being blank.
bool no_key = picopass_is_memset(
AA1[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data, 0x00, PICOPASS_BLOCK_LEN);
bool empty = picopass_is_memset(
AA1[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, RFAL_PICOPASS_BLOCK_LEN);
AA1[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data, 0xFF, PICOPASS_BLOCK_LEN);
if(no_key) {
furi_string_cat_printf(wiegand_str, "Read Failed");
bool hid_csn = picopass_device_hid_csn(picopass->dev);
if(pacs->se_enabled) {
furi_string_cat_printf(credential_str, "SE enabled");
} else if(!hid_csn) {
furi_string_cat_printf(credential_str, "Non-HID CSN");
}
widget_add_button_element(
@@ -62,7 +68,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");
@@ -76,18 +82,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++) {
for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < 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");
@@ -95,11 +95,24 @@ void picopass_scene_read_card_success_on_enter(void* context) {
if(pacs->key) {
furi_string_cat_printf(key_str, "Key: ");
uint8_t key[PICOPASS_BLOCK_LEN];
memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN);
uint8_t key[RFAL_PICOPASS_BLOCK_LEN];
memcpy(key, &pacs->key, RFAL_PICOPASS_BLOCK_LEN);
for(uint8_t i = 0; i < RFAL_PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(key_str, "%02X", key[i]);
bool standard_key = true;
// Handle DES key being 56bits with parity in LSB
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
if((key[i] & 0xFE) != (picopass_iclass_key[i] & 0xFE)) {
standard_key = false;
break;
}
}
if(standard_key) {
furi_string_cat_printf(key_str, "Standard");
} else {
for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) {
furi_string_cat_printf(key_str, "%02X", key[i]);
}
}
}

View File

@@ -64,7 +64,9 @@ bool picopass_scene_read_factory_success_on_event(void* context, SceneManagerEve
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
} else if(event.event == GuiButtonTypeCenter) {
memcpy(picopass->dev->dev_data.pacs.key, picopass_iclass_key, RFAL_PICOPASS_BLOCK_LEN);
memcpy(
picopass->write_key_context.key_to_write, picopass_iclass_key, PICOPASS_BLOCK_LEN);
picopass->write_key_context.is_elite = false;
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteKey);
consumed = true;
}

View File

@@ -1,5 +1,5 @@
#include "../picopass_i.h"
#include <toolbox/name_generator.h>
#include <lib/toolbox/name_generator.h>
#include <gui/modules/validators.h>
#include <toolbox/path.h>
@@ -16,7 +16,8 @@ void picopass_scene_save_name_on_enter(void* context) {
TextInput* text_input = picopass->text_input;
bool dev_name_empty = false;
if(!strcmp(picopass->dev->dev_name, "")) {
name_generator_make_auto(picopass->text_store, sizeof(picopass->text_store), "PicoPass");
name_generator_make_auto(
picopass->text_store, sizeof(picopass->text_store), PICOPASS_APP_FILE_PREFIX);
dev_name_empty = true;
} else {
picopass_text_store_set(picopass, picopass->dev->dev_name);

View File

@@ -1,11 +1,11 @@
#include "../picopass_i.h"
enum SubmenuIndex {
SubmenuIndexDelete,
SubmenuIndexInfo,
SubmenuIndexWrite,
SubmenuIndexEmulate,
SubmenuIndexRename,
SubmenuIndexDelete,
};
void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) {

View File

@@ -1,10 +1,42 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
#include "../picopass_keys.h"
void picopass_write_card_worker_callback(PicopassWorkerEvent event, void* context) {
UNUSED(event);
#define PICOPASS_SCENE_WRITE_BLOCK_START 6
#define PICOPASS_SCENE_WRITE_BLOCK_STOP 10
NfcCommand picopass_scene_write_poller_callback(PicopassPollerEvent event, void* context) {
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, event);
NfcCommand command = NfcCommandContinue;
if(event.type == PicopassPollerEventTypeRequestMode) {
event.data->req_mode.mode = PicopassPollerModeWrite;
} else if(event.type == PicopassPollerEventTypeRequestKey) {
memcpy(event.data->req_key.key, picopass_iclass_key, sizeof(picopass_iclass_key));
event.data->req_key.is_elite_key = false;
event.data->req_key.is_key_provided = true;
} else if(event.type == PicopassPollerEventTypeRequestWriteBlock) {
uint8_t block_num =
scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneWriteCard);
if(block_num == PICOPASS_SCENE_WRITE_BLOCK_STOP) {
event.data->req_write.perform_write = false;
} else {
event.data->req_write.block_num = block_num;
event.data->req_write.block = &picopass->dev->dev_data.AA1[block_num];
event.data->req_write.perform_write = true;
block_num++;
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneWriteCard, block_num);
}
} else if(event.type == PicopassPollerEventTypeSuccess) {
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
} else if(event.type == PicopassPollerEventTypeFail) {
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerFail);
}
return command;
}
void picopass_scene_write_card_on_enter(void* context) {
@@ -15,15 +47,14 @@ void picopass_scene_write_card_on_enter(void* context) {
Popup* popup = picopass->popup;
popup_set_header(popup, "Writing\npicopass\ncard", 68, 30, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinSend_97x61);
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneWriteCard, PICOPASS_SCENE_WRITE_BLOCK_START);
// Start worker
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
picopass_worker_start(
picopass->worker,
PicopassWorkerStateWrite,
&picopass->dev->dev_data,
picopass_write_card_worker_callback,
picopass);
picopass->poller = picopass_poller_alloc(picopass->nfc);
picopass_poller_start(picopass->poller, picopass_scene_write_poller_callback, picopass);
picopass_blink_start(picopass);
}
@@ -33,10 +64,10 @@ bool picopass_scene_write_card_on_event(void* context, SceneManagerEvent event)
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassWorkerEventFail) {
if(event.event == PicopassCustomEventPollerFail) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardFailure);
consumed = true;
} else if(event.event == PicopassWorkerEventSuccess) {
} else if(event.event == PicopassCustomEventPollerSuccess) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess);
consumed = true;
}
@@ -48,7 +79,8 @@ void picopass_scene_write_card_on_exit(void* context) {
Picopass* picopass = context;
// Stop worker
picopass_worker_stop(picopass->worker);
picopass_poller_stop(picopass->poller);
picopass_poller_free(picopass->poller);
// Clear view
popup_reset(picopass->popup);

View File

@@ -1,10 +1,32 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
void picopass_write_key_worker_callback(PicopassWorkerEvent event, void* context) {
UNUSED(event);
NfcCommand picopass_scene_write_key_poller_callback(PicopassPollerEvent event, void* context) {
NfcCommand command = NfcCommandContinue;
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventWorkerExit);
if(event.type == PicopassPollerEventTypeRequestMode) {
event.data->req_mode.mode = PicopassPollerModeWriteKey;
} else if(event.type == PicopassPollerEventTypeRequestKey) {
event.data->req_key.is_key_provided = true;
memcpy(event.data->req_key.key, picopass->dev->dev_data.pacs.key, PICOPASS_KEY_LEN);
event.data->req_key.is_elite_key = picopass->dev->dev_data.pacs.elite_kdf;
} else if(event.type == PicopassPollerEventTypeRequestWriteKey) {
event.data->req_write_key.data = &picopass->dev->dev_data;
memcpy(
event.data->req_write_key.key,
picopass->write_key_context.key_to_write,
PICOPASS_KEY_LEN);
event.data->req_write_key.is_elite_key = picopass->write_key_context.is_elite;
} else if(event.type == PicopassPollerEventTypeSuccess) {
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerSuccess);
} else if(event.type == PicopassPollerEventTypeFail) {
view_dispatcher_send_custom_event(
picopass->view_dispatcher, PicopassCustomEventPollerFail);
}
return command;
}
void picopass_scene_write_key_on_enter(void* context) {
@@ -18,14 +40,10 @@ void picopass_scene_write_key_on_enter(void* context) {
// Start worker
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
picopass_worker_start(
picopass->worker,
PicopassWorkerStateWriteKey,
&picopass->dev->dev_data,
picopass_write_key_worker_callback,
picopass);
picopass_blink_start(picopass);
picopass->poller = picopass_poller_alloc(picopass->nfc);
picopass_poller_start(picopass->poller, picopass_scene_write_key_poller_callback, picopass);
}
bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) {
@@ -33,9 +51,12 @@ bool picopass_scene_write_key_on_event(void* context, SceneManagerEvent event) {
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventWorkerExit) {
if(event.event == PicopassCustomEventPollerSuccess) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardSuccess);
consumed = true;
} else if(event.event == PicopassCustomEventPollerFail) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneWriteCardFailure);
consumed = true;
}
}
return consumed;
@@ -45,7 +66,9 @@ void picopass_scene_write_key_on_exit(void* context) {
Picopass* picopass = context;
// Stop worker
picopass_worker_stop(picopass->worker);
picopass_poller_stop(picopass->poller);
picopass_poller_free(picopass->poller);
// Clear view
popup_reset(picopass->popup);

View File

@@ -15,7 +15,6 @@ struct DictAttack {
typedef struct {
DictAttackState state;
MfClassicType type;
FuriString* header;
uint8_t sectors_total;
uint8_t sectors_read;
@@ -123,7 +122,6 @@ void dict_attack_reset(DictAttack* dict_attack) {
DictAttackViewModel * model,
{
model->state = DictAttackStateRead;
model->type = MfClassicType1k;
model->sectors_total = 1;
model->sectors_read = 0;
model->sector_current = 0;
@@ -242,6 +240,15 @@ void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys
true);
}
void dict_attack_set_current_dict_key(DictAttack* dict_attack, uint16_t current_key) {
furi_assert(dict_attack);
with_view_model(
dict_attack->view,
DictAttackViewModel * model,
{ model->dict_keys_current = current_key; },
true);
}
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried) {
furi_assert(dict_attack);
with_view_model(

View File

@@ -3,8 +3,6 @@
#include <gui/view.h>
#include <gui/modules/widget.h>
#include <lib/nfc/protocols/mifare_classic.h>
typedef struct DictAttack DictAttack;
typedef void (*DictAttackCallback)(void* context);
@@ -37,6 +35,8 @@ void dict_attack_inc_keys_found(DictAttack* dict_attack);
void dict_attack_set_total_dict_keys(DictAttack* dict_attack, uint16_t dict_keys_total);
void dict_attack_set_current_dict_key(DictAttack* dict_attack, uint16_t current_key);
void dict_attack_inc_current_dict_key(DictAttack* dict_attack, uint16_t keys_tried);
void dict_attack_set_key_attack(DictAttack* dict_attack, bool is_key_attack, uint8_t sector);

View File

@@ -1,5 +1,6 @@
#include "loclass.h"
#include "../picopass_worker_i.h"
#include <picopass_device.h>
#include <gui/elements.h>
@@ -21,6 +22,10 @@ static void loclass_draw_callback(Canvas* canvas, void* model) {
canvas_set_font(canvas, FontSecondary);
canvas_draw_str_aligned(canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
if(m->num_macs == 255) {
return;
}
float progress = m->num_macs == 0 ? 0 :
(float)(m->num_macs) / (float)(LOCLASS_MACS_TO_COLLECT);