Skip to content

Commit

Permalink
upd picopass
Browse files Browse the repository at this point in the history
  • Loading branch information
xMasterX committed May 1, 2024
1 parent ccfe2de commit ad8745d
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 1 deletion.
12 changes: 12 additions & 0 deletions base_pack/picopass/application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ App(
apptype=FlipperAppType.EXTERNAL,
targets=["f7"],
entry_point="picopass_app",
sources=[
"*.c", "!plugin/*.c",
],
requires=[
"storage",
"gui",
Expand All @@ -23,3 +26,12 @@ App(
fap_icon_assets="icons",
fap_file_assets="files",
)

App(
appid="plugin_wiegand",
apptype=FlipperAppType.PLUGIN,
entry_point="plugin_wiegand_ep",
requires=["picopass"],
sources=["plugin/wiegand.c"],
fal_embedded=True,
)
23 changes: 23 additions & 0 deletions base_pack/picopass/picopass.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,27 @@ Picopass* picopass_alloc() {
view_dispatcher_add_view(
picopass->view_dispatcher, PicopassViewLoclass, loclass_get_view(picopass->loclass));

picopass->plugin_manager =
plugin_manager_alloc(PLUGIN_APP_ID, PLUGIN_API_VERSION, firmware_api_interface);

picopass->plugin_wiegand = NULL;
if(plugin_manager_load_all(picopass->plugin_manager, APP_DATA_PATH("plugins")) !=
PluginManagerErrorNone) {
FURI_LOG_E(TAG, "Failed to load all libs");
} else {
uint32_t plugin_count = plugin_manager_get_count(picopass->plugin_manager);
FURI_LOG_I(TAG, "Loaded %lu plugin(s)", plugin_count);

for(uint32_t i = 0; i < plugin_count; i++) {
const PluginWiegand* plugin = plugin_manager_get_ep(picopass->plugin_manager, i);
FURI_LOG_I(TAG, "plugin name: %s", plugin->name);
if(strcmp(plugin->name, "Plugin Wiegand") == 0) {
// Have to cast to drop "const" qualifier
picopass->plugin_wiegand = (PluginWiegand*)plugin;
}
}
}

return picopass;
}

Expand Down Expand Up @@ -158,6 +179,8 @@ void picopass_free(Picopass* picopass) {
furi_record_close(RECORD_NOTIFICATION);
picopass->notifications = NULL;

plugin_manager_free(picopass->plugin_manager);

free(picopass);
}

Expand Down
8 changes: 8 additions & 0 deletions base_pack/picopass/picopass_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
#include "protocol/picopass_poller.h"
#include "protocol/picopass_listener.h"

#include "plugin/interface.h"
#include <flipper_application/flipper_application.h>
#include <flipper_application/plugins/plugin_manager.h>
#include <loader/firmware_api/firmware_api.h>

#define PICOPASS_TEXT_STORE_SIZE 129

#define PICOPASS_ICLASS_ELITE_DICT_FLIPPER_NAME APP_ASSETS_PATH("iclass_elite_dict.txt")
Expand Down Expand Up @@ -107,6 +112,9 @@ struct Picopass {
DictAttack* dict_attack;
Loclass* loclass;

PluginManager* plugin_manager;
PluginWiegand* plugin_wiegand;

PicopassDictAttackContext dict_attack_ctx;
PicopassWriteKeyContext write_key_context;
PicopassLoclassContext loclass_context;
Expand Down
14 changes: 14 additions & 0 deletions base_pack/picopass/plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Flipper zero wiegand plugin

Add as git submodule: `git submodule add https://gitlab.com/bettse/flipper-wiegand-plugin.git plugin`

Add to your `application.fam`
```
App(
appid="plugin_wiegand",
apptype=FlipperAppType.PLUGIN,
entry_point="plugin_wiegand_ep",
requires=["seader"],
sources=["plugin/wiegand.c"],
)
```
20 changes: 20 additions & 0 deletions base_pack/picopass/plugin/interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @file plugin_interface.h
* @brief Example plugin interface.
*
* Common interface between a plugin and host application
*/
#pragma once

#include <stdint.h>
#include <stddef.h>
#include <furi.h>

#define PLUGIN_APP_ID "plugin_wiegand"
#define PLUGIN_API_VERSION 1

typedef struct {
const char* name;
int (*count)(uint8_t, uint64_t);
void (*description)(uint8_t, uint64_t, size_t, FuriString*);
} PluginWiegand;
152 changes: 152 additions & 0 deletions base_pack/picopass/plugin/wiegand.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@

#include "interface.h"

#include <lib/bit_lib/bit_lib.h>
#include <flipper_application/flipper_application.h>

/*
* Huge thanks to the proxmark codebase:
* https://github.com/RfidResearchGroup/proxmark3/blob/master/client/src/wiegand_formats.c
*/

// Structure for packed wiegand messages
// Always align lowest value (last transmitted) bit to ordinal position 0 (lowest valued bit bottom)
typedef struct {
uint8_t Length; // Number of encoded bits in wiegand message (excluding headers and preamble)
uint32_t Top; // Bits in x<<64 positions
uint32_t Mid; // Bits in x<<32 positions
uint32_t Bot; // Lowest ordinal positions
} wiegand_message_t;

static inline uint8_t oddparity32(uint32_t x) {
return bit_lib_test_parity_32(x, BitLibParityOdd);
}

static inline uint8_t evenparity32(uint32_t x) {
return bit_lib_test_parity_32(x, BitLibParityEven);
}

static int wiegand_C1k35s_parse(uint8_t bit_length, uint64_t bits, FuriString* description) {
if(bit_length != 35) {
return 0;
}

wiegand_message_t value;
value.Mid = bits >> 32;
value.Bot = bits;
wiegand_message_t* packed = &value;

uint32_t cn = (packed->Bot >> 1) & 0x000FFFFF;
uint32_t fc = ((packed->Mid & 1) << 11) | ((packed->Bot >> 21));
bool valid = (evenparity32((packed->Mid & 0x1) ^ (packed->Bot & 0xB6DB6DB6)) ==
((packed->Mid >> 1) & 1)) &&
(oddparity32((packed->Mid & 0x3) ^ (packed->Bot & 0x6DB6DB6C)) ==
((packed->Bot >> 0) & 1)) &&
(oddparity32((packed->Mid & 0x3) ^ (packed->Bot & 0xFFFFFFFF)) ==
((packed->Mid >> 2) & 1));

if(valid) {
furi_string_cat_printf(description, "C1k35s\nFC: %ld CN: %ld\n", fc, cn);
return 1;
}

return 0;
}

static int wiegand_h10301_parse(uint8_t bit_length, uint64_t bits, FuriString* description) {
if(bit_length != 26) {
return 0;
}

//E XXXX XXXX XXXX
//XXXX XXXX XXXX O
uint32_t eBitMask = 0x02000000;
uint32_t oBitMask = 0x00000001;
uint32_t eParityMask = 0x01FFE000;
uint32_t oParityMask = 0x00001FFE;
uint8_t eBit = (eBitMask & bits) >> 25;
uint8_t oBit = (oBitMask & bits) >> 0;

bool eParity = bit_lib_test_parity_32((bits & eParityMask) >> 13, BitLibParityEven) ==
(eBit == 1);
bool oParity = bit_lib_test_parity_32((bits & oParityMask) >> 1, BitLibParityOdd) ==
(oBit == 1);

FURI_LOG_D(
PLUGIN_APP_ID,
"eBit: %d, oBit: %d, eParity: %d, oParity: %d",
eBit,
oBit,
eParity,
oParity);

if(eParity && oParity) {
uint32_t cnMask = 0x1FFFE;
uint16_t cn = ((bits & cnMask) >> 1);

uint32_t fcMask = 0x1FE0000;
uint16_t fc = ((bits & fcMask) >> 17);

furi_string_cat_printf(description, "H10301\nFC: %d CN: %d\n", fc, cn);
return 1;
}

return 0;
}

static int wiegand_format_count(uint8_t bit_length, uint64_t bits) {
UNUSED(bit_length);
UNUSED(bits);
int count = 0;
FuriString* ignore = furi_string_alloc();

count += wiegand_h10301_parse(bit_length, bits, ignore);
count += wiegand_C1k35s_parse(bit_length, bits, ignore);

furi_string_free(ignore);

FURI_LOG_I(PLUGIN_APP_ID, "count: %i", count);
return count;
}

static void wiegand_format_description(
uint8_t bit_length,
uint64_t bits,
size_t index,
FuriString* description) {
FURI_LOG_I(PLUGIN_APP_ID, "description %d", index);
UNUSED(bit_length);
UNUSED(bits);

size_t i = 0;

i += wiegand_h10301_parse(bit_length, bits, description);
if(i - 1 == index) {
return;
}
i += wiegand_C1k35s_parse(bit_length, bits, description);
if(i - 1 == index) {
return;
}

furi_string_cat_printf(description, "[%i] <name> FC: CN:", index);
}

/* Actual implementation of app<>plugin interface */
static const PluginWiegand plugin_wiegand = {
.name = "Plugin Wiegand",
.count = &wiegand_format_count,
.description = &wiegand_format_description,
};

/* Plugin descriptor to comply with basic plugin specification */
static const FlipperAppPluginDescriptor plugin_wiegand_descriptor = {
.appid = PLUGIN_APP_ID,
.ep_api_version = PLUGIN_API_VERSION,
.entry_point = &plugin_wiegand,
};

/* Plugin entry point - must return a pointer to const descriptor */
const FlipperAppPluginDescriptor* plugin_wiegand_ep(void) {
return &plugin_wiegand_descriptor;
}
24 changes: 24 additions & 0 deletions base_pack/picopass/scenes/picopass_scene_card_menu.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ enum SubmenuIndex {
SubmenuIndexSave,
SubmenuIndexSaveAsLF,
SubmenuIndexSaveAsSeader,
SubmenuIndexParse,
SubmenuIndexChangeKey,
SubmenuIndexWrite,
SubmenuIndexEmulate,
Expand All @@ -22,6 +23,7 @@ void picopass_scene_card_menu_on_enter(void* context) {
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
PicopassBlock* card_data = picopass->dev->dev_data.card_data;
PicopassDeviceAuthMethod auth = picopass->dev->dev_data.auth;
PluginWiegand* plugin = picopass->plugin_wiegand;

bool SE = card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].valid &&
0x30 == card_data[PICOPASS_ICLASS_PACS_CFG_BLOCK_INDEX].data[0];
Expand Down Expand Up @@ -59,6 +61,23 @@ void picopass_scene_card_menu_on_enter(void* context) {
SubmenuIndexSaveAsLF,
picopass_scene_card_menu_submenu_callback,
picopass);

if(plugin) {
// Convert from byte array to uint64_t
uint64_t credential = 0;
memcpy(&credential, pacs->credential, sizeof(uint64_t));
credential = __builtin_bswap64(credential);

size_t format_count = plugin->count(pacs->bitLength, credential);
if(format_count > 0) {
submenu_add_item(
submenu,
"Parse",
SubmenuIndexParse,
picopass_scene_card_menu_submenu_callback,
picopass);
}
}
}

if(auth == PicopassDeviceAuthMethodNone || auth == PicopassDeviceAuthMethodKey) {
Expand Down Expand Up @@ -131,6 +150,11 @@ bool picopass_scene_card_menu_on_event(void* context, SceneManagerEvent event) {
picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexChangeKey);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneKeyMenu);
consumed = true;
} else if(event.event == SubmenuIndexParse) {
scene_manager_set_scene_state(
picopass->scene_manager, PicopassSceneCardMenu, SubmenuIndexParse);
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFormats);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
Expand Down
1 change: 1 addition & 0 deletions base_pack/picopass/scenes/picopass_scene_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ ADD_SCENE(picopass, loclass, Loclass)
ADD_SCENE(picopass, key_input, KeyInput)
ADD_SCENE(picopass, nr_mac_saved, NrMacSaved)
ADD_SCENE(picopass, more_info, MoreInfo)
ADD_SCENE(picopass, formats, Formats)
23 changes: 22 additions & 1 deletion base_pack/picopass/scenes/picopass_scene_device_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void picopass_scene_device_info_on_enter(void* context) {
// Setup view
PicopassBlock* card_data = picopass->dev->dev_data.card_data;
PicopassPacs* pacs = &picopass->dev->dev_data.pacs;
PluginWiegand* plugin = picopass->plugin_wiegand;
Widget* widget = picopass->widget;

uint8_t csn[PICOPASS_BLOCK_LEN] = {0};
Expand Down Expand Up @@ -77,10 +78,27 @@ void picopass_scene_device_info_on_enter(void* context) {
"Back",
picopass_scene_device_info_widget_callback,
picopass);

if(plugin) {
// Convert from byte array to uint64_t
uint64_t credential = 0;
memcpy(&credential, pacs->credential, sizeof(uint64_t));
credential = __builtin_bswap64(credential);

size_t format_count = plugin->count(pacs->bitLength, credential);
if(format_count > 0) {
widget_add_button_element(
picopass->widget,
GuiButtonTypeCenter,
"Parse",
picopass_scene_device_info_widget_callback,
picopass);
}
}
widget_add_button_element(
picopass->widget,
GuiButtonTypeRight,
"More",
"Raw",
picopass_scene_device_info_widget_callback,
picopass);

Expand All @@ -97,6 +115,9 @@ bool picopass_scene_device_info_on_event(void* context, SceneManagerEvent event)
} else if(event.event == GuiButtonTypeRight) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneMoreInfo);
consumed = true;
} else if(event.event == GuiButtonTypeCenter) {
scene_manager_next_scene(picopass->scene_manager, PicopassSceneFormats);
consumed = true;
} else if(event.event == PicopassCustomEventViewExit) {
view_dispatcher_switch_to_view(picopass->view_dispatcher, PicopassViewWidget);
consumed = true;
Expand Down
Loading

0 comments on commit ad8745d

Please sign in to comment.