Picopass load/info/delete (#1562)

* increase stack size
* rfalPicoPassPollerWriteBlock
* UI for loading picopass
* Move picopass parsing and add delete, delete success

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Eric Betts 2022-08-23 06:19:17 -07:00 committed by GitHub
parent ddd5d5a535
commit f92127c0a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 559 additions and 93 deletions

View File

@ -5,7 +5,7 @@ App(
entry_point="picopass_app",
cdefines=["APP_PICOPASS"],
requires=["storage", "gui"],
stack_size=1 * 1024,
stack_size=4 * 1024,
icon="A_Plugins_14",
order=30,
)

View File

@ -56,6 +56,11 @@ Picopass* picopass_alloc() {
view_dispatcher_add_view(
picopass->view_dispatcher, PicopassViewPopup, popup_get_view(picopass->popup));
// Loading
picopass->loading = loading_alloc();
view_dispatcher_add_view(
picopass->view_dispatcher, PicopassViewLoading, loading_get_view(picopass->loading));
// Text Input
picopass->text_input = text_input_alloc();
view_dispatcher_add_view(
@ -86,6 +91,10 @@ void picopass_free(Picopass* picopass) {
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewPopup);
popup_free(picopass->popup);
// Loading
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewLoading);
loading_free(picopass->loading);
// TextInput
view_dispatcher_remove_view(picopass->view_dispatcher, PicopassViewTextInput);
text_input_free(picopass->text_input);
@ -148,6 +157,20 @@ void picopass_blink_stop(Picopass* picopass) {
notification_message(picopass->notifications, &picopass_sequence_blink_stop);
}
void picopass_show_loading_popup(void* context, bool show) {
Picopass* picopass = context;
TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME);
if(show) {
// Raise timer priority so that animations can play
vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewLoading);
} else {
// Restore default timer priority
vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY);
}
}
int32_t picopass_app(void* p) {
UNUSED(p);
Picopass* picopass = picopass_alloc();

View File

@ -8,6 +8,9 @@
static const char* picopass_file_header = "Flipper Picopass device";
static const uint32_t picopass_file_version = 1;
const uint8_t picopass_iclass_decryptionkey[] =
{0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36};
PicopassDevice* picopass_device_alloc() {
PicopassDevice* picopass_dev = malloc(sizeof(PicopassDevice));
picopass_dev->dev_data.pacs.legacy = false;
@ -15,6 +18,7 @@ PicopassDevice* picopass_device_alloc() {
picopass_dev->dev_data.pacs.pin_length = 0;
picopass_dev->storage = furi_record_open(RECORD_STORAGE);
picopass_dev->dialogs = furi_record_open(RECORD_DIALOGS);
string_init(picopass_dev->load_path);
return picopass_dev;
}
@ -111,7 +115,7 @@ static bool picopass_device_save_file(
} while(0);
if(!saved) {
dialog_message_show_storage_error(dev->dialogs, "Can not save\nkey file");
dialog_message_show_storage_error(dev->dialogs, "Can not save\nfile");
}
string_clear(temp_str);
flipper_format_free(file);
@ -128,11 +132,83 @@ bool picopass_device_save(PicopassDevice* dev, const char* dev_name) {
return false;
}
static bool picopass_device_load_data(PicopassDevice* dev, string_t path, bool show_dialog) {
bool parsed = false;
FlipperFormat* file = flipper_format_file_alloc(dev->storage);
PicopassBlock* AA1 = dev->dev_data.AA1;
PicopassPacs* pacs = &dev->dev_data.pacs;
string_t temp_str;
string_init(temp_str);
bool deprecated_version = false;
if(dev->loading_cb) {
dev->loading_cb(dev->loading_cb_ctx, true);
}
do {
if(!flipper_format_file_open_existing(file, string_get_cstr(path))) break;
// Read and verify file header
uint32_t version = 0;
if(!flipper_format_read_header(file, temp_str, &version)) break;
if(string_cmp_str(temp_str, picopass_file_header) || (version != picopass_file_version)) {
deprecated_version = true;
break;
}
// Parse header blocks
bool block_read = true;
for(size_t i = 0; i < 6; i++) {
string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_hex(
file, string_get_cstr(temp_str), AA1[i].data, PICOPASS_BLOCK_LEN)) {
block_read = false;
break;
}
}
size_t app_limit = AA1[PICOPASS_CONFIG_BLOCK_INDEX].data[0];
for(size_t i = 6; i < app_limit; i++) {
string_printf(temp_str, "Block %d", i);
if(!flipper_format_read_hex(
file, 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;
parsed = true;
} while(false);
if(dev->loading_cb) {
dev->loading_cb(dev->loading_cb_ctx, false);
}
if((!parsed) && (show_dialog)) {
if(deprecated_version) {
dialog_message_show_storage_error(dev->dialogs, "File format deprecated");
} else {
dialog_message_show_storage_error(dev->dialogs, "Can not parse\nfile");
}
}
string_clear(temp_str);
flipper_format_free(file);
return parsed;
}
void picopass_device_clear(PicopassDevice* dev) {
furi_assert(dev);
picopass_device_data_clear(&dev->dev_data);
memset(&dev->dev_data, 0, sizeof(dev->dev_data));
dev->format = PicopassDeviceSaveFormatHF;
string_reset(dev->load_path);
}
void picopass_device_free(PicopassDevice* picopass_dev) {
@ -144,6 +220,36 @@ void picopass_device_free(PicopassDevice* picopass_dev) {
free(picopass_dev);
}
bool picopass_file_select(PicopassDevice* dev) {
furi_assert(dev);
// Input events and views are managed by file_browser
string_t picopass_app_folder;
string_init_set_str(picopass_app_folder, PICOPASS_APP_FOLDER);
bool res = dialog_file_browser_show(
dev->dialogs,
dev->load_path,
picopass_app_folder,
PICOPASS_APP_EXTENSION,
true,
&I_Nfc_10px,
true);
string_clear(picopass_app_folder);
if(res) {
string_t filename;
string_init(filename);
path_extract_filename(dev->load_path, filename, true);
strncpy(dev->dev_name, string_get_cstr(filename), PICOPASS_DEV_NAME_MAX_LEN);
res = picopass_device_load_data(dev, dev->load_path, true);
if(res) {
picopass_device_set_name(dev, dev->dev_name);
}
string_clear(filename);
}
return res;
}
void picopass_device_data_clear(PicopassDeviceData* dev_data) {
for(size_t i = 0; i < PICOPASS_MAX_APP_LIMIT; i++) {
memset(dev_data->AA1[i].data, 0, sizeof(dev_data->AA1[i].data));
@ -152,3 +258,122 @@ void picopass_device_data_clear(PicopassDeviceData* dev_data) {
dev_data->pacs.se_enabled = false;
dev_data->pacs.pin_length = 0;
}
bool picopass_device_delete(PicopassDevice* dev, bool use_load_path) {
furi_assert(dev);
bool deleted = false;
string_t file_path;
string_init(file_path);
do {
// Delete original file
if(use_load_path && !string_empty_p(dev->load_path)) {
string_set(file_path, dev->load_path);
} else {
string_printf(
file_path, "%s/%s%s", PICOPASS_APP_FOLDER, dev->dev_name, PICOPASS_APP_EXTENSION);
}
if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
deleted = true;
} while(0);
if(!deleted) {
dialog_message_show_storage_error(dev->dialogs, "Can not remove file");
}
string_clear(file_path);
return deleted;
}
void picopass_device_set_loading_callback(
PicopassDevice* dev,
PicopassLoadingCallback callback,
void* context) {
furi_assert(dev);
dev->loading_cb = callback;
dev->loading_cb_ctx = context;
}
ReturnCode 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;
mbedtls_des3_init(&ctx);
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;
// Thank you proxmark!
pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0);
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
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;
}
err = picopass_device_decrypt(AA1[8].data, pacs->pin0);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
return err;
}
err = picopass_device_decrypt(AA1[9].data, pacs->pin1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
return err;
}
} else if(pacs->encryption == PicopassDeviceEncryptionNone) {
FURI_LOG_D(TAG, "No Encryption");
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 {
FURI_LOG_D(TAG, "Unknown encryption");
}
return ERR_NONE;
}
ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) {
uint32_t* halves = (uint32_t*)data;
if(halves[0] == 0) {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
record->bitLength = 31 - leading0s;
} else {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));
record->bitLength = 63 - leading0s;
}
FURI_LOG_D(TAG, "bitLength: %d", record->bitLength);
if(record->bitLength == 26) {
uint8_t* v4 = data + 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\n", record->FacilityCode, record->CardNumber);
record->valid = true;
} else {
record->CardNumber = 0;
record->FacilityCode = 0;
record->valid = false;
}
return ERR_NONE;
}

View File

@ -7,6 +7,10 @@
#include <rfal_picopass.h>
#include <mbedtls/des.h>
#include <loclass/optimized_ikeys.h>
#include <loclass/optimized_cipher.h>
#define PICOPASS_DEV_NAME_MAX_LEN 22
#define PICOPASS_READER_DATA_MAX_SIZE 64
#define PICOPASS_BLOCK_LEN 8
@ -20,6 +24,8 @@
#define PICOPASS_APP_EXTENSION ".picopass"
#define PICOPASS_APP_SHADOW_EXTENSION ".pas"
typedef void (*PicopassLoadingCallback)(void* context, bool state);
typedef enum {
PicopassDeviceEncryptionUnknown = 0,
PicopassDeviceEncryptionNone = 0x14,
@ -67,6 +73,9 @@ typedef struct {
char dev_name[PICOPASS_DEV_NAME_MAX_LEN + 1];
string_t load_path;
PicopassDeviceSaveFormat format;
PicopassLoadingCallback loading_cb;
void* loading_cb_ctx;
} PicopassDevice;
PicopassDevice* picopass_device_alloc();
@ -77,6 +86,18 @@ void picopass_device_set_name(PicopassDevice* dev, const char* name);
bool picopass_device_save(PicopassDevice* dev, const char* dev_name);
bool picopass_file_select(PicopassDevice* dev);
void picopass_device_data_clear(PicopassDeviceData* dev_data);
void picopass_device_clear(PicopassDevice* dev);
bool picopass_device_delete(PicopassDevice* dev, bool use_load_path);
void picopass_device_set_loading_callback(
PicopassDevice* dev,
PicopassLoadingCallback callback,
void* context);
ReturnCode picopass_device_parse_credential(PicopassBlock* AA1, PicopassPacs* pacs);
ReturnCode picopass_device_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record);

View File

@ -14,6 +14,7 @@
#include <gui/modules/submenu.h>
#include <gui/modules/popup.h>
#include <gui/modules/loading.h>
#include <gui/modules/text_input.h>
#include <gui/modules/widget.h>
@ -55,6 +56,7 @@ struct Picopass {
// Common Views
Submenu* submenu;
Popup* popup;
Loading* loading;
TextInput* text_input;
Widget* widget;
};
@ -62,6 +64,7 @@ struct Picopass {
typedef enum {
PicopassViewMenu,
PicopassViewPopup,
PicopassViewLoading,
PicopassViewTextInput,
PicopassViewWidget,
} PicopassView;
@ -75,3 +78,5 @@ void picopass_text_store_clear(Picopass* picopass);
void picopass_blink_start(Picopass* picopass);
void picopass_blink_stop(Picopass* picopass);
void picopass_show_loading_popup(void* context, bool show);

View File

@ -1,23 +1,9 @@
#include "picopass_worker_i.h"
#include <furi_hal.h>
#include <stdlib.h>
#include <st25r3916.h>
#include <rfal_analogConfig.h>
#include <rfal_rf.h>
#include <rfal_nfc.h>
#include <mbedtls/des.h>
#include <loclass/optimized_ikeys.h>
#include <loclass/optimized_cipher.h>
#include <platform.h>
#define TAG "PicopassWorker"
const uint8_t picopass_iclass_key[] = {0xaf, 0xa7, 0x85, 0xa7, 0xda, 0xb3, 0x33, 0x78};
const uint8_t picopass_iclass_decryptionkey[] =
{0xb4, 0x21, 0x2c, 0xca, 0xb7, 0xed, 0x21, 0x0f, 0x7b, 0x93, 0xd4, 0x59, 0x39, 0xc7, 0xdd, 0x36};
const uint8_t picopass_factory_key[] = {0x76, 0x65, 0x54, 0x43, 0x32, 0x21, 0x10, 0x00};
static void picopass_worker_enable_field() {
st25r3916TxRxOn();
@ -31,44 +17,6 @@ static ReturnCode picopass_worker_disable_field(ReturnCode rc) {
return rc;
}
static ReturnCode picopass_worker_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;
mbedtls_des3_init(&ctx);
mbedtls_des3_set2key_dec(&ctx, key);
mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data);
mbedtls_des3_free(&ctx);
return ERR_NONE;
}
static ReturnCode picopass_worker_parse_wiegand(uint8_t* data, PicopassWiegandRecord* record) {
uint32_t* halves = (uint32_t*)data;
if(halves[0] == 0) {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1]));
record->bitLength = 31 - leading0s;
} else {
uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0]));
record->bitLength = 63 - leading0s;
}
FURI_LOG_D(TAG, "bitLength: %d", record->bitLength);
if(record->bitLength == 26) {
uint8_t* v4 = data + 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\n", record->FacilityCode, record->CardNumber);
record->valid = true;
} else {
record->CardNumber = 0;
record->FacilityCode = 0;
record->valid = false;
}
return ERR_NONE;
}
/***************************** Picopass Worker API *******************************/
PicopassWorker* picopass_worker_alloc() {
@ -272,47 +220,16 @@ void picopass_worker_detect(PicopassWorker* picopass_worker) {
FURI_LOG_E(TAG, "picopass_read_card error %d", err);
}
// Thank you proxmark!
pacs->legacy = (memcmp(AA1[5].data, "\xff\xff\xff\xff\xff\xff\xff\xff", 8) == 0);
pacs->se_enabled = (memcmp(AA1[5].data, "\xff\xff\xff\x00\x06\xff\xff\xff", 8) == 0);
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_worker_decrypt(AA1[7].data, pacs->credential);
err = picopass_device_parse_credential(AA1, pacs);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
break;
FURI_LOG_E(TAG, "picopass_device_parse_credential error %d", err);
}
err = picopass_worker_decrypt(AA1[8].data, pacs->pin0);
err = picopass_device_parse_wiegand(pacs->credential, &pacs->record);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
break;
FURI_LOG_E(TAG, "picopass_device_parse_wiegand error %d", err);
}
err = picopass_worker_decrypt(AA1[9].data, pacs->pin1);
if(err != ERR_NONE) {
FURI_LOG_E(TAG, "decrypt error %d", err);
break;
}
} else if(pacs->encryption == PicopassDeviceEncryptionNone) {
FURI_LOG_D(TAG, "No Encryption");
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 {
FURI_LOG_D(TAG, "Unknown encryption");
break;
}
picopass_worker_parse_wiegand(pacs->credential, &pacs->record);
// Notify caller and exit
if(picopass_worker->callback) {
picopass_worker->callback(PicopassWorkerEventSuccess, picopass_worker->context);

View File

@ -6,6 +6,15 @@
#include <furi.h>
#include <lib/toolbox/stream/file_stream.h>
#include <furi_hal.h>
#include <stdlib.h>
#include <st25r3916.h>
#include <rfal_analogConfig.h>
#include <rfal_rf.h>
#include <platform.h>
struct PicopassWorker {
FuriThread* thread;
Storage* storage;

View File

@ -5,3 +5,7 @@ ADD_SCENE(picopass, card_menu, CardMenu)
ADD_SCENE(picopass, save_name, SaveName)
ADD_SCENE(picopass, save_success, SaveSuccess)
ADD_SCENE(picopass, saved_menu, SavedMenu)
ADD_SCENE(picopass, file_select, FileSelect)
ADD_SCENE(picopass, device_info, DeviceInfo)
ADD_SCENE(picopass, delete, Delete)
ADD_SCENE(picopass, delete_success, DeleteSuccess)

View File

@ -0,0 +1,58 @@
#include "../picopass_i.h"
void picopass_scene_delete_widget_callback(GuiButtonType result, InputType type, void* context) {
Picopass* picopass = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(picopass->view_dispatcher, result);
}
}
void picopass_scene_delete_on_enter(void* context) {
Picopass* picopass = context;
// Setup Custom Widget view
char temp_str[64];
snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", picopass->dev->dev_name);
widget_add_text_box_element(
picopass->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false);
widget_add_button_element(
picopass->widget,
GuiButtonTypeLeft,
"Back",
picopass_scene_delete_widget_callback,
picopass);
widget_add_button_element(
picopass->widget,
GuiButtonTypeRight,
"Delete",
picopass_scene_delete_widget_callback,
picopass);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
}
bool picopass_scene_delete_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
return scene_manager_previous_scene(picopass->scene_manager);
} else if(event.event == GuiButtonTypeRight) {
if(picopass_device_delete(picopass->dev, true)) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeleteSuccess);
} else {
scene_manager_search_and_switch_to_previous_scene(
picopass->scene_manager, PicopassSceneStart);
}
consumed = true;
}
}
return consumed;
}
void picopass_scene_delete_on_exit(void* context) {
Picopass* picopass = context;
widget_reset(picopass->widget);
}

View File

@ -0,0 +1,40 @@
#include "../picopass_i.h"
void picopass_scene_delete_success_popup_callback(void* context) {
Picopass* picopass = context;
view_dispatcher_send_custom_event(picopass->view_dispatcher, PicopassCustomEventViewExit);
}
void picopass_scene_delete_success_on_enter(void* context) {
Picopass* picopass = context;
// Setup view
Popup* popup = picopass->popup;
popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, picopass);
popup_set_callback(popup, picopass_scene_delete_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewPopup);
}
bool picopass_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PicopassCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
picopass->scene_manager, PicopassSceneStart);
}
}
return consumed;
}
void picopass_scene_delete_success_on_exit(void* context) {
Picopass* picopass = context;
// Clear view
popup_reset(picopass->popup);
}

View File

@ -0,0 +1,82 @@
#include "../picopass_i.h"
#include <dolphin/dolphin.h>
void picopass_scene_device_info_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
Picopass* picopass = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(picopass->view_dispatcher, result);
}
}
void picopass_scene_device_info_on_enter(void* context) {
Picopass* picopass = context;
string_t credential_str;
string_t wiegand_str;
string_init(credential_str);
string_init(wiegand_str);
DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
// Setup view
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
Widget* widget = picopass->widget;
size_t bytesLength = 1 + pacs->record.bitLength / 8;
string_set_str(credential_str, "");
for(uint8_t i = PICOPASS_BLOCK_LEN - bytesLength; i < PICOPASS_BLOCK_LEN; i++) {
string_cat_printf(credential_str, " %02X", pacs->credential[i]);
}
if(pacs->record.valid) {
string_cat_printf(
wiegand_str, "FC: %u CN: %u", pacs->record.FacilityCode, pacs->record.CardNumber);
} else {
string_cat_printf(wiegand_str, "%d bits", pacs->record.bitLength);
}
widget_add_string_element(
widget, 64, 12, AlignCenter, AlignCenter, FontPrimary, string_get_cstr(wiegand_str));
widget_add_string_element(
widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(credential_str));
string_clear(credential_str);
string_clear(wiegand_str);
widget_add_button_element(
picopass->widget,
GuiButtonTypeLeft,
"Back",
picopass_scene_device_info_widget_callback,
picopass);
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
}
bool picopass_scene_device_info_on_event(void* context, SceneManagerEvent event) {
Picopass* picopass = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(picopass->scene_manager);
} else if(event.event == PicopassCustomEventViewExit) {
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
consumed = true;
}
return consumed;
}
void picopass_scene_device_info_on_exit(void* context) {
Picopass* picopass = context;
// Clear views
widget_reset(picopass->widget);
}

View File

@ -0,0 +1,25 @@
#include "../picopass_i.h"
#include "picopass/picopass_device.h"
void picopass_scene_file_select_on_enter(void* context) {
Picopass* picopass = context;
// Process file_select return
picopass_device_set_loading_callback(picopass->dev, picopass_show_loading_popup, picopass);
if(picopass_file_select(picopass->dev)) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneSavedMenu);
} else {
scene_manager_search_and_switch_to_previous_scene(
picopass->scene_manager, PicopassSceneStart);
}
picopass_device_set_loading_callback(picopass->dev, NULL, picopass);
}
bool picopass_scene_file_select_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
UNUSED(event);
return false;
}
void picopass_scene_file_select_on_exit(void* context) {
UNUSED(context);
}

View File

@ -1,5 +1,11 @@
#include "../picopass_i.h"
enum SubmenuIndex {
SubmenuIndexDelete,
SubmenuIndexInfo,
SubmenuIndexWrite,
};
void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
Picopass* picopass = context;
@ -8,6 +14,16 @@ void picopass_scene_saved_menu_submenu_callback(void* context, uint32_t index) {
void picopass_scene_saved_menu_on_enter(void* context) {
Picopass* picopass = context;
Submenu* submenu = picopass->submenu;
submenu_add_item(
submenu,
"Delete",
SubmenuIndexDelete,
picopass_scene_saved_menu_submenu_callback,
picopass);
submenu_add_item(
submenu, "Info", SubmenuIndexInfo, picopass_scene_saved_menu_submenu_callback, picopass);
submenu_set_selected_item(
picopass->submenu,
@ -23,6 +39,14 @@ bool picopass_scene_saved_menu_on_event(void* context, SceneManagerEvent event)
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneSavedMenu, event.event);
if(event.event == SubmenuIndexDelete) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneDelete);
consumed = true;
} else if(event.event == SubmenuIndexInfo) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneDeviceInfo);
consumed = true;
}
}
return consumed;

View File

@ -17,6 +17,8 @@ void picopass_scene_start_on_enter(void* context) {
Submenu* submenu = picopass->submenu;
submenu_add_item(
submenu, "Read Card", SubmenuIndexRead, picopass_scene_start_submenu_callback, picopass);
submenu_add_item(
submenu, "Saved", SubmenuIndexSaved, picopass_scene_start_submenu_callback, picopass);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(picopass->scene_manager, PicopassSceneStart));
@ -32,6 +34,9 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) {
if(event.event == SubmenuIndexRead) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard);
consumed = true;
} else if(event.event == SubmenuIndexSaved) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect);
consumed = true;
}
scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event);
}

View File

@ -26,6 +26,7 @@ enum {
RFAL_PICOPASS_CMD_READCHECK = 0x88,
RFAL_PICOPASS_CMD_CHECK = 0x05,
RFAL_PICOPASS_CMD_READ = 0x0C,
RFAL_PICOPASS_CMD_WRITE = 0x0C,
};
typedef struct {
@ -58,5 +59,6 @@ ReturnCode rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes)
ReturnCode rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes);
ReturnCode rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes);
ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes);
ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]);
#endif /* RFAL_PICOPASS_H */

View File

@ -158,3 +158,29 @@ ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRe
fwt);
return ret;
}
ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) {
ReturnCode ret;
uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_WRITE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memcpy(txBuf + 2, data, RFAL_PICOPASS_MAX_BLOCK_LEN);
memcpy(txBuf + 10, mac, 4);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
rfalPicoPassReadBlockRes readRes;
ret = rfalTransceiveBlockingTxRx(
txBuf,
sizeof(txBuf),
(uint8_t*)&readRes,
sizeof(rfalPicoPassReadBlockRes),
&recvLen,
flags,
fwt);
// TODO: compare response
return ret;
}