Merge branch 'fz-dev' into dev

This commit is contained in:
MX 2022-10-28 19:43:22 +03:00
commit 4da3f9cf15
No known key found for this signature in database
GPG key ID: 6C4C311DFD4B4AB5
34 changed files with 1334 additions and 440 deletions

View file

@ -24,33 +24,29 @@ jobs:
- name: 'Compile unit tests firmware'
id: compile
continue-on-error: true
run: |
FBT_TOOLCHAIN_PATH=/opt ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1
- name: 'Wait for flipper to finish updating'
id: connect
if: steps.compile.outcome == 'success'
continue-on-error: true
run: |
python3 ./scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}}
- name: 'Format flipper SD card'
id: format
if: steps.connect.outcome == 'success'
continue-on-error: true
run: |
./scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext
- name: 'Copy unit tests to flipper'
- name: 'Copy assets and unit tests data to flipper'
id: copy
if: steps.format.outcome == 'success'
continue-on-error: true
run: |
./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/resources /ext
./scripts/storage.py -p ${{steps.device.outputs.flipper}} send assets/unit_tests /ext/unit_tests
- name: 'Run units and validate results'
if: steps.copy.outcome == 'success'
continue-on-error: true
run: |
python3 ./scripts/testing/units.py ${{steps.device.outputs.flipper}}

View file

@ -36,6 +36,12 @@ ADD_SCENE(nfc, mf_classic_keys_list, MfClassicKeysList)
ADD_SCENE(nfc, mf_classic_keys_delete, MfClassicKeysDelete)
ADD_SCENE(nfc, mf_classic_keys_warn_duplicate, MfClassicKeysWarnDuplicate)
ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
ADD_SCENE(nfc, mf_classic_write, MfClassicWrite)
ADD_SCENE(nfc, mf_classic_write_success, MfClassicWriteSuccess)
ADD_SCENE(nfc, mf_classic_write_fail, MfClassicWriteFail)
ADD_SCENE(nfc, mf_classic_update, MfClassicUpdate)
ADD_SCENE(nfc, mf_classic_update_success, MfClassicUpdateSuccess)
ADD_SCENE(nfc, mf_classic_wrong_card, MfClassicWrongCard)
ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
ADD_SCENE(nfc, emv_menu, EmvMenu)
ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)

View file

@ -28,6 +28,11 @@ void nfc_scene_detect_reader_on_enter(void* context) {
detect_reader_set_callback(nfc->detect_reader, nfc_scene_detect_reader_callback, nfc);
detect_reader_set_nonces_max(nfc->detect_reader, NFC_SCENE_DETECT_READER_PAIR_NONCES_MAX);
NfcDeviceData* dev_data = &nfc->dev->dev_data;
if(dev_data->nfc_data.uid_len) {
detect_reader_set_uid(
nfc->detect_reader, dev_data->nfc_data.uid, dev_data->nfc_data.uid_len);
}
// Store number of collected nonces in scene state
scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDetectReader, 0);

View file

@ -0,0 +1,98 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
enum {
NfcSceneMfClassicUpdateStateCardSearch,
NfcSceneMfClassicUpdateStateCardFound,
};
bool nfc_mf_classic_update_worker_callback(NfcWorkerEvent event, void* context) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
return true;
}
static void nfc_scene_mf_classic_update_setup_view(Nfc* nfc) {
Popup* popup = nfc->popup;
popup_reset(popup);
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicUpdate);
if(state == NfcSceneMfClassicUpdateStateCardSearch) {
popup_set_text(
nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
} else {
popup_set_header(popup, "Updating\nDon't move...", 52, 32, AlignLeft, AlignCenter);
popup_set_icon(popup, 12, 23, &A_Loading_24);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
void nfc_scene_mf_classic_update_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);
nfc_scene_mf_classic_update_setup_view(nfc);
// Setup and start worker
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfClassicUpdate,
&nfc->dev->dev_data,
nfc_mf_classic_update_worker_callback,
nfc);
nfc_blink_emulate_start(nfc);
}
bool nfc_scene_mf_classic_update_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcWorkerEventSuccess) {
nfc_worker_stop(nfc->worker);
if(nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name)) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdateSuccess);
} else {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
}
consumed = true;
} else if(event.event == NfcWorkerEventWrongCard) {
nfc_worker_stop(nfc->worker);
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
consumed = true;
} else if(event.event == NfcWorkerEventCardDetected) {
scene_manager_set_scene_state(
nfc->scene_manager,
NfcSceneMfClassicUpdate,
NfcSceneMfClassicUpdateStateCardFound);
nfc_scene_mf_classic_update_setup_view(nfc);
consumed = true;
} else if(event.event == NfcWorkerEventNoCardDetected) {
scene_manager_set_scene_state(
nfc->scene_manager,
NfcSceneMfClassicUpdate,
NfcSceneMfClassicUpdateStateCardSearch);
nfc_scene_mf_classic_update_setup_view(nfc);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_classic_update_on_exit(void* context) {
Nfc* nfc = context;
nfc_worker_stop(nfc->worker);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicUpdate, NfcSceneMfClassicUpdateStateCardSearch);
// Clear view
popup_reset(nfc->popup);
nfc_blink_stop(nfc);
}

View file

@ -0,0 +1,44 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
void nfc_scene_mf_classic_update_success_popup_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_mf_classic_update_success_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcSave);
notification_message(nfc->notifications, &sequence_success);
Popup* popup = nfc->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Updated!", 11, 20, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_mf_classic_update_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
bool nfc_scene_mf_classic_update_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneFileSelect);
}
}
return consumed;
}
void nfc_scene_mf_classic_update_success_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
popup_reset(nfc->popup);
}

View file

@ -0,0 +1,92 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
enum {
NfcSceneMfClassicWriteStateCardSearch,
NfcSceneMfClassicWriteStateCardFound,
};
bool nfc_mf_classic_write_worker_callback(NfcWorkerEvent event, void* context) {
furi_assert(context);
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, event);
return true;
}
static void nfc_scene_mf_classic_write_setup_view(Nfc* nfc) {
Popup* popup = nfc->popup;
popup_reset(popup);
uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfClassicWrite);
if(state == NfcSceneMfClassicWriteStateCardSearch) {
popup_set_text(
nfc->popup, "Apply the initial\ncard only", 128, 32, AlignRight, AlignCenter);
popup_set_icon(nfc->popup, 0, 8, &I_NFC_manual_60x50);
} else {
popup_set_header(popup, "Writing\nDon't move...", 52, 32, AlignLeft, AlignCenter);
popup_set_icon(popup, 12, 23, &A_Loading_24);
}
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
void nfc_scene_mf_classic_write_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcEmulate);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
nfc_scene_mf_classic_write_setup_view(nfc);
// Setup and start worker
nfc_worker_start(
nfc->worker,
NfcWorkerStateMfClassicWrite,
&nfc->dev->dev_data,
nfc_mf_classic_write_worker_callback,
nfc);
nfc_blink_emulate_start(nfc);
}
bool nfc_scene_mf_classic_write_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcWorkerEventSuccess) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteSuccess);
consumed = true;
} else if(event.event == NfcWorkerEventFail) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWriteFail);
consumed = true;
} else if(event.event == NfcWorkerEventWrongCard) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrongCard);
consumed = true;
} else if(event.event == NfcWorkerEventCardDetected) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardFound);
nfc_scene_mf_classic_write_setup_view(nfc);
consumed = true;
} else if(event.event == NfcWorkerEventNoCardDetected) {
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
nfc_scene_mf_classic_write_setup_view(nfc);
consumed = true;
}
}
return consumed;
}
void nfc_scene_mf_classic_write_on_exit(void* context) {
Nfc* nfc = context;
nfc_worker_stop(nfc->worker);
scene_manager_set_scene_state(
nfc->scene_manager, NfcSceneMfClassicWrite, NfcSceneMfClassicWriteStateCardSearch);
// Clear view
popup_reset(nfc->popup);
nfc_blink_stop(nfc);
}

View file

@ -0,0 +1,58 @@
#include "../nfc_i.h"
void nfc_scene_mf_classic_write_fail_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mf_classic_write_fail_on_enter(void* context) {
Nfc* nfc = context;
Widget* widget = nfc->widget;
notification_message(nfc->notifications, &sequence_error);
widget_add_icon_element(widget, 72, 17, &I_DolphinCommon_56x48);
widget_add_string_element(
widget, 7, 4, AlignLeft, AlignTop, FontPrimary, "Writing gone wrong!");
widget_add_string_multiline_element(
widget,
7,
17,
AlignLeft,
AlignTop,
FontSecondary,
"Not all sectors\nwere written\ncorrectly.");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Finish", nfc_scene_mf_classic_write_fail_widget_callback, nfc);
// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mf_classic_write_fail_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneFileSelect);
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneSavedMenu);
}
return consumed;
}
void nfc_scene_mf_classic_write_fail_on_exit(void* context) {
Nfc* nfc = context;
widget_reset(nfc->widget);
}

View file

@ -0,0 +1,44 @@
#include "../nfc_i.h"
#include <dolphin/dolphin.h>
void nfc_scene_mf_classic_write_success_popup_callback(void* context) {
Nfc* nfc = context;
view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
}
void nfc_scene_mf_classic_write_success_on_enter(void* context) {
Nfc* nfc = context;
DOLPHIN_DEED(DolphinDeedNfcSave);
notification_message(nfc->notifications, &sequence_success);
Popup* popup = nfc->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Successfully\nwritten", 13, 22, AlignLeft, AlignBottom);
popup_set_timeout(popup, 1500);
popup_set_context(popup, nfc);
popup_set_callback(popup, nfc_scene_mf_classic_write_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewPopup);
}
bool nfc_scene_mf_classic_write_success_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == NfcCustomEventViewExit) {
consumed = scene_manager_search_and_switch_to_previous_scene(
nfc->scene_manager, NfcSceneFileSelect);
}
}
return consumed;
}
void nfc_scene_mf_classic_write_success_on_exit(void* context) {
Nfc* nfc = context;
// Clear view
popup_reset(nfc->popup);
}

View file

@ -0,0 +1,53 @@
#include "../nfc_i.h"
void nfc_scene_mf_classic_wrong_card_widget_callback(
GuiButtonType result,
InputType type,
void* context) {
Nfc* nfc = context;
if(type == InputTypeShort) {
view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
}
}
void nfc_scene_mf_classic_wrong_card_on_enter(void* context) {
Nfc* nfc = context;
Widget* widget = nfc->widget;
notification_message(nfc->notifications, &sequence_error);
widget_add_icon_element(widget, 73, 17, &I_DolphinCommon_56x48);
widget_add_string_element(
widget, 3, 4, AlignLeft, AlignTop, FontPrimary, "This is wrong card");
widget_add_string_multiline_element(
widget,
4,
17,
AlignLeft,
AlignTop,
FontSecondary,
"Data management\nis only possible\nwith initial card");
widget_add_button_element(
widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_classic_wrong_card_widget_callback, nfc);
// Setup and start worker
view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
}
bool nfc_scene_mf_classic_wrong_card_on_event(void* context, SceneManagerEvent event) {
Nfc* nfc = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == GuiButtonTypeLeft) {
consumed = scene_manager_previous_scene(nfc->scene_manager);
}
}
return consumed;
}
void nfc_scene_mf_classic_wrong_card_on_exit(void* context) {
Nfc* nfc = context;
widget_reset(nfc->widget);
}

View file

@ -4,6 +4,9 @@
enum SubmenuIndex {
SubmenuIndexEmulate,
SubmenuIndexEditUid,
SubmenuIndexDetectReader,
SubmenuIndexWrite,
SubmenuIndexUpdate,
SubmenuIndexRename,
SubmenuIndexDelete,
SubmenuIndexInfo,
@ -43,6 +46,28 @@ void nfc_scene_saved_menu_on_enter(void* context) {
submenu_add_item(
submenu, "Emulate", SubmenuIndexEmulate, nfc_scene_saved_menu_submenu_callback, nfc);
}
if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) {
if(!mf_classic_is_card_read(&nfc->dev->dev_data.mf_classic_data)) {
submenu_add_item(
submenu,
"Detect reader",
SubmenuIndexDetectReader,
nfc_scene_saved_menu_submenu_callback,
nfc);
}
submenu_add_item(
submenu,
"Write To Initial Card",
SubmenuIndexWrite,
nfc_scene_saved_menu_submenu_callback,
nfc);
submenu_add_item(
submenu,
"Update From Initial Card",
SubmenuIndexUpdate,
nfc_scene_saved_menu_submenu_callback,
nfc);
}
submenu_add_item(
submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
if(nfc->dev->shadow_file_exist) {
@ -80,6 +105,15 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
}
DOLPHIN_DEED(DolphinDeedNfcEmulate);
consumed = true;
} else if(event.event == SubmenuIndexDetectReader) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
consumed = true;
} else if(event.event == SubmenuIndexWrite) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicWrite);
consumed = true;
} else if(event.event == SubmenuIndexUpdate) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicUpdate);
consumed = true;
} else if(event.event == SubmenuIndexRename) {
scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
consumed = true;

View file

@ -53,6 +53,7 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) {
} else if(event.event == SubmenuIndexDetectReader) {
bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK;
if(sd_exist) {
nfc_device_data_clear(&nfc->dev->dev_data);
scene_manager_next_scene(nfc->scene_manager, NfcSceneDetectReader);
DOLPHIN_DEED(DolphinDeedNfcDetectReader);
} else {

View file

@ -2,6 +2,8 @@
#include <assets_icons.h>
#include <gui/elements.h>
#define DETECT_READER_UID_MAX_LEN (10)
struct DetectReader {
View* view;
DetectReaderDoneCallback callback;
@ -12,6 +14,7 @@ typedef struct {
uint16_t nonces;
uint16_t nonces_max;
DetectReaderState state;
FuriString* uid_str;
} DetectReaderViewModel;
static void detect_reader_draw_callback(Canvas* canvas, void* model) {
@ -23,6 +26,10 @@ static void detect_reader_draw_callback(Canvas* canvas, void* model) {
if(m->state == DetectReaderStateStart) {
snprintf(text, sizeof(text), "Touch the reader");
canvas_draw_icon(canvas, 21, 13, &I_Move_flipper_26x39);
if(furi_string_size(m->uid_str)) {
elements_multiline_text_aligned(
canvas, 64, 64, AlignCenter, AlignBottom, furi_string_get_cstr(m->uid_str));
}
} else if(m->state == DetectReaderStateReaderDetected) {
snprintf(text, sizeof(text), "Move the Flipper away");
canvas_draw_icon(canvas, 24, 25, &I_Release_arrow_18x15);
@ -86,12 +93,24 @@ DetectReader* detect_reader_alloc() {
view_set_input_callback(detect_reader->view, detect_reader_input_callback);
view_set_context(detect_reader->view, detect_reader);
with_view_model(
detect_reader->view,
DetectReaderViewModel * model,
{ model->uid_str = furi_string_alloc(); },
false);
return detect_reader;
}
void detect_reader_free(DetectReader* detect_reader) {
furi_assert(detect_reader);
with_view_model(
detect_reader->view,
DetectReaderViewModel * model,
{ furi_string_free(model->uid_str); },
false);
view_free(detect_reader->view);
free(detect_reader);
}
@ -106,6 +125,7 @@ void detect_reader_reset(DetectReader* detect_reader) {
model->nonces = 0;
model->nonces_max = 0;
model->state = DetectReaderStateStart;
furi_string_reset(model->uid_str);
},
false);
}
@ -152,3 +172,19 @@ void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState stat
with_view_model(
detect_reader->view, DetectReaderViewModel * model, { model->state = state; }, true);
}
void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len) {
furi_assert(detect_reader);
furi_assert(uid);
furi_assert(uid_len < DETECT_READER_UID_MAX_LEN);
with_view_model(
detect_reader->view,
DetectReaderViewModel * model,
{
furi_string_set_str(model->uid_str, "UID:");
for(size_t i = 0; i < uid_len; i++) {
furi_string_cat_printf(model->uid_str, " %02X", uid[i]);
}
},
true);
}

View file

@ -32,3 +32,5 @@ void detect_reader_set_nonces_max(DetectReader* detect_reader, uint16_t nonces_m
void detect_reader_set_nonces_collected(DetectReader* detect_reader, uint16_t nonces_collected);
void detect_reader_set_state(DetectReader* detect_reader, DetectReaderState state);
void detect_reader_set_uid(DetectReader* detect_reader, uint8_t* uid, uint8_t uid_len);

View file

@ -178,23 +178,6 @@ sources.extend(
)
)
fwenv.AppendUnique(
LINKFLAGS=[
"-specs=nano.specs",
"-specs=nosys.specs",
"-Wl,--gc-sections",
"-Wl,--undefined=uxTopUsedPriority",
"-Wl,--wrap,_malloc_r",
"-Wl,--wrap,_free_r",
"-Wl,--wrap,_calloc_r",
"-Wl,--wrap,_realloc_r",
"-n",
"-Xlinker",
"-Map=${TARGET}.map",
],
)
# Debug
# print(fwenv.Dump())

View file

@ -620,6 +620,10 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity(
uint16_t in_buff_bits,
uint8_t* out_data,
uint8_t* out_parity) {
if(in_buff_bits < 8) {
out_data[0] = in_buff[0];
return in_buff_bits;
}
if(in_buff_bits % 9 != 0) {
return 0;
}
@ -635,7 +639,7 @@ uint16_t furi_hal_nfc_bitstream_to_data_and_parity(
bit_processed += 9;
curr_byte++;
}
return curr_byte;
return curr_byte * 8;
}
bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
@ -692,8 +696,8 @@ bool furi_hal_nfc_tx_rx(FuriHalNfcTxRxContext* tx_rx, uint16_t timeout_ms) {
if(tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRaw ||
tx_rx->tx_rx_type == FuriHalNfcTxRxTypeRxRaw) {
tx_rx->rx_bits = 8 * furi_hal_nfc_bitstream_to_data_and_parity(
temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity);
tx_rx->rx_bits = furi_hal_nfc_bitstream_to_data_and_parity(
temp_rx_buff, *temp_rx_bits, tx_rx->rx_data, tx_rx->rx_parity);
} else {
memcpy(tx_rx->rx_data, temp_rx_buff, MIN(*temp_rx_bits / 8, FURI_HAL_NFC_DATA_BUFF_SIZE));
tx_rx->rx_bits = *temp_rx_bits;

View file

@ -205,10 +205,17 @@ NfcProtocol
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
furi_assert(instance);
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
return &instance->nfc_data;
}
void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data) {
furi_assert(instance);
furi_assert(nfc_data);
memcpy(&instance->nfc_data, nfc_data, sizeof(FuriHalNfcDevData));
}
static void reader_analyzer_write(
ReaderAnalyzer* instance,
uint8_t* data,

View file

@ -35,6 +35,8 @@ NfcProtocol
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
void reader_analyzer_set_nfc_data(ReaderAnalyzer* instance, FuriHalNfcDevData* nfc_data);
void reader_analyzer_prepare_tx_rx(
ReaderAnalyzer* instance,
FuriHalNfcTxRxContext* tx_rx,

View file

@ -1152,6 +1152,13 @@ static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dia
if(!flipper_format_read_hex(file, "UID", data->uid, data->uid_len)) break;
if(!flipper_format_read_hex(file, "ATQA", data->atqa, 2)) break;
if(!flipper_format_read_hex(file, "SAK", &data->sak, 1)) break;
// Load CUID
uint8_t* cuid_start = data->uid;
if(data->uid_len == 7) {
cuid_start = &data->uid[3];
}
data->cuid = (cuid_start[0] << 24) | (cuid_start[1] << 16) | (cuid_start[2] << 8) |
(cuid_start[3]);
// Parse other data
if(dev->format == NfcDeviceSaveFormatMifareUl) {
if(!nfc_device_load_mifare_ul_data(file, dev)) break;

View file

@ -99,6 +99,10 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_emulate_mf_ultralight(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicEmulate) {
nfc_worker_emulate_mf_classic(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicWrite) {
nfc_worker_write_mf_classic(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicUpdate) {
nfc_worker_update_mf_classic(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
nfc_worker_mf_ultralight_read_auth(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
@ -678,6 +682,144 @@ void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
rfal_platform_spi_release();
}
void nfc_worker_write_mf_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
bool card_found_notified = false;
FuriHalNfcDevData nfc_data = {};
MfClassicData* src_data = &nfc_worker->dev_data->mf_classic_data;
MfClassicData dest_data = *src_data;
while(nfc_worker->state == NfcWorkerStateMfClassicWrite) {
if(furi_hal_nfc_detect(&nfc_data, 200)) {
if(!card_found_notified) {
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
card_found_notified = true;
}
furi_hal_nfc_sleep();
FURI_LOG_I(TAG, "Check low level nfc data");
if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) {
FURI_LOG_E(TAG, "Wrong card");
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
break;
}
FURI_LOG_I(TAG, "Check mf classic type");
MfClassicType type =
mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak);
if(type != nfc_worker->dev_data->mf_classic_data.type) {
FURI_LOG_E(TAG, "Wrong mf classic type");
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
break;
}
// Set blocks not read
mf_classic_set_sector_data_not_read(&dest_data);
FURI_LOG_I(TAG, "Updating card sectors");
uint8_t total_sectors = mf_classic_get_total_sectors_num(type);
bool write_success = true;
for(uint8_t i = 0; i < total_sectors; i++) {
FURI_LOG_I(TAG, "Reading sector %d", i);
mf_classic_read_sector(&tx_rx, &dest_data, i);
bool old_data_read = mf_classic_is_sector_data_read(src_data, i);
bool new_data_read = mf_classic_is_sector_data_read(&dest_data, i);
if(old_data_read != new_data_read) {
FURI_LOG_E(TAG, "Failed to update sector %d", i);
write_success = false;
break;
}
if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break;
if(!mf_classic_write_sector(&tx_rx, &dest_data, src_data, i)) {
FURI_LOG_E(TAG, "Failed to write %d sector", i);
write_success = false;
break;
}
}
if(nfc_worker->state != NfcWorkerStateMfClassicWrite) break;
if(write_success) {
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
break;
} else {
nfc_worker->callback(NfcWorkerEventFail, nfc_worker->context);
break;
}
} else {
if(card_found_notified) {
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
card_found_notified = false;
}
}
furi_delay_ms(300);
}
}
void nfc_worker_update_mf_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
bool card_found_notified = false;
FuriHalNfcDevData nfc_data = {};
MfClassicData* old_data = &nfc_worker->dev_data->mf_classic_data;
MfClassicData new_data = *old_data;
while(nfc_worker->state == NfcWorkerStateMfClassicUpdate) {
if(furi_hal_nfc_detect(&nfc_data, 200)) {
if(!card_found_notified) {
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
card_found_notified = true;
}
furi_hal_nfc_sleep();
FURI_LOG_I(TAG, "Check low level nfc data");
if(memcmp(&nfc_data, &nfc_worker->dev_data->nfc_data, sizeof(FuriHalNfcDevData))) {
FURI_LOG_E(TAG, "Low level nfc data mismatch");
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
break;
}
FURI_LOG_I(TAG, "Check MF classic type");
MfClassicType type =
mf_classic_get_classic_type(nfc_data.atqa[0], nfc_data.atqa[1], nfc_data.sak);
if(type != nfc_worker->dev_data->mf_classic_data.type) {
FURI_LOG_E(TAG, "MF classic type mismatch");
nfc_worker->callback(NfcWorkerEventWrongCard, nfc_worker->context);
break;
}
// Set blocks not read
mf_classic_set_sector_data_not_read(&new_data);
FURI_LOG_I(TAG, "Updating card sectors");
uint8_t total_sectors = mf_classic_get_total_sectors_num(type);
bool update_success = true;
for(uint8_t i = 0; i < total_sectors; i++) {
FURI_LOG_I(TAG, "Reading sector %d", i);
mf_classic_read_sector(&tx_rx, &new_data, i);
bool old_data_read = mf_classic_is_sector_data_read(old_data, i);
bool new_data_read = mf_classic_is_sector_data_read(&new_data, i);
if(old_data_read != new_data_read) {
FURI_LOG_E(TAG, "Failed to update sector %d", i);
update_success = false;
break;
}
if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break;
}
if(nfc_worker->state != NfcWorkerStateMfClassicUpdate) break;
// Check updated data
if(update_success) {
*old_data = new_data;
nfc_worker->callback(NfcWorkerEventSuccess, nfc_worker->context);
break;
}
} else {
if(card_found_notified) {
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
card_found_notified = false;
}
}
furi_delay_ms(300);
}
}
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
furi_assert(nfc_worker);
furi_assert(nfc_worker->callback);
@ -769,7 +911,13 @@ void nfc_worker_analyze_reader(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
ReaderAnalyzer* reader_analyzer = nfc_worker->reader_analyzer;
FuriHalNfcDevData* nfc_data = reader_analyzer_get_nfc_data(reader_analyzer);
FuriHalNfcDevData* nfc_data = NULL;
if(nfc_worker->dev_data->protocol == NfcDeviceProtocolMifareClassic) {
nfc_data = &nfc_worker->dev_data->nfc_data;
reader_analyzer_set_nfc_data(reader_analyzer, nfc_data);
} else {
nfc_data = reader_analyzer_get_nfc_data(reader_analyzer);
}
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
.data = nfc_worker->dev_data->mf_classic_data,

View file

@ -14,6 +14,8 @@ typedef enum {
NfcWorkerStateUidEmulate,
NfcWorkerStateMfUltralightEmulate,
NfcWorkerStateMfClassicEmulate,
NfcWorkerStateMfClassicWrite,
NfcWorkerStateMfClassicUpdate,
NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader,
@ -48,13 +50,16 @@ typedef enum {
NfcWorkerEventNoCardDetected,
NfcWorkerEventWrongCardDetected,
// Mifare Classic events
// Read Mifare Classic events
NfcWorkerEventNoDictFound,
NfcWorkerEventNewSector,
NfcWorkerEventNewDictKeyBatch,
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,
// Write Mifare Classic events
NfcWorkerEventWrongCard,
// Detect Reader events
NfcWorkerEventDetectReaderDetected,
NfcWorkerEventDetectReaderLost,

View file

@ -41,6 +41,10 @@ void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker);
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_write_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_update_mf_classic(NfcWorker* nfc_worker);
void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker);
void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);

View file

@ -73,3 +73,55 @@ uint32_t prng_successor(uint32_t x, uint32_t n) {
return SWAPENDIAN(x);
}
void crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
uint16_t encrypted_data_bits,
uint8_t* decrypted_data) {
furi_assert(crypto);
furi_assert(encrypted_data);
furi_assert(decrypted_data);
if(encrypted_data_bits < 8) {
uint8_t decrypted_byte = 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
decrypted_data[0] = decrypted_byte;
} else {
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
}
}
}
void crypto1_encrypt(
Crypto1* crypto,
uint8_t* keystream,
uint8_t* plain_data,
uint16_t plain_data_bits,
uint8_t* encrypted_data,
uint8_t* encrypted_parity) {
furi_assert(crypto);
furi_assert(plain_data);
furi_assert(encrypted_data);
furi_assert(encrypted_parity);
if(plain_data_bits < 8) {
encrypted_data[0] = 0;
for(size_t i = 0; i < plain_data_bits; i++) {
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
}
} else {
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
plain_data[i];
encrypted_parity[i / 8] |=
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
<< (7 - (i & 0x0007)));
}
}
}

View file

@ -21,3 +21,17 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted);
uint32_t crypto1_filter(uint32_t in);
uint32_t prng_successor(uint32_t x, uint32_t n);
void crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
uint16_t encrypted_data_bits,
uint8_t* decrypted_data);
void crypto1_encrypt(
Crypto1* crypto,
uint8_t* keystream,
uint8_t* plain_data,
uint16_t plain_data_bits,
uint8_t* encrypted_data,
uint8_t* encrypted_parity);

View file

@ -9,21 +9,8 @@
#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U)
#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U)
#define MF_CLASSIC_READ_SECT_CMD (0x30)
typedef enum {
MfClassicActionDataRead,
MfClassicActionDataWrite,
MfClassicActionDataInc,
MfClassicActionDataDec,
MfClassicActionKeyARead,
MfClassicActionKeyAWrite,
MfClassicActionKeyBRead,
MfClassicActionKeyBWrite,
MfClassicActionACRead,
MfClassicActionACWrite,
} MfClassicAction;
#define MF_CLASSIC_READ_BLOCK_CMD (0x30)
#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0)
const char* mf_classic_get_type_str(MfClassicType type) {
if(type == MfClassicType1k) {
@ -122,6 +109,24 @@ void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassic
FURI_BIT_SET(data->block_read_mask[block_num / 32], block_num % 32);
}
bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num) {
furi_assert(data);
uint8_t first_block = mf_classic_get_first_block_num_of_sector(sector_num);
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sector_num);
bool data_read = true;
for(size_t i = first_block; i < first_block + total_blocks; i++) {
data_read &= mf_classic_is_block_read(data, i);
}
return data_read;
}
void mf_classic_set_sector_data_not_read(MfClassicData* data) {
furi_assert(data);
memset(data->block_read_mask, 0, sizeof(data->block_read_mask));
}
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type) {
furi_assert(data);
@ -190,6 +195,9 @@ void mf_classic_get_read_sectors_and_keys(
uint8_t* sectors_read,
uint8_t* keys_found) {
furi_assert(data);
furi_assert(sectors_read);
furi_assert(keys_found);
*sectors_read = 0;
*keys_found = 0;
uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
@ -225,12 +233,12 @@ bool mf_classic_is_card_read(MfClassicData* data) {
return card_read;
}
static bool mf_classic_is_allowed_access_sector_trailer(
MfClassicEmulator* emulator,
bool mf_classic_is_allowed_access_sector_trailer(
MfClassicData* data,
uint8_t block_num,
MfClassicKey key,
MfClassicAction action) {
uint8_t* sector_trailer = emulator->data.block[block_num].value;
uint8_t* sector_trailer = data->block[block_num].value;
uint8_t AC = ((sector_trailer[7] >> 5) & 0x04) | ((sector_trailer[8] >> 2) & 0x02) |
((sector_trailer[8] >> 7) & 0x01);
switch(action) {
@ -266,13 +274,13 @@ static bool mf_classic_is_allowed_access_sector_trailer(
return true;
}
static bool mf_classic_is_allowed_access_data_block(
MfClassicEmulator* emulator,
bool mf_classic_is_allowed_access_data_block(
MfClassicData* data,
uint8_t block_num,
MfClassicKey key,
MfClassicAction action) {
uint8_t* sector_trailer =
emulator->data.block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
data->block[mf_classic_get_sector_trailer_num_by_block(block_num)].value;
uint8_t sector_block;
if(block_num <= 128) {
@ -336,9 +344,10 @@ static bool mf_classic_is_allowed_access(
MfClassicKey key,
MfClassicAction action) {
if(mf_classic_is_sector_trailer(block_num)) {
return mf_classic_is_allowed_access_sector_trailer(emulator, block_num, key, action);
return mf_classic_is_allowed_access_sector_trailer(
&emulator->data, block_num, key, action);
} else {
return mf_classic_is_allowed_access_data_block(emulator, block_num, key, action);
return mf_classic_is_allowed_access_data_block(&emulator->data, block_num, key, action);
}
}
@ -514,25 +523,17 @@ bool mf_classic_read_block(
furi_assert(block);
bool read_block_success = false;
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_SECT_CMD, block_num, 0x00, 0x00};
uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00};
nfca_append_crc16(plain_cmd, 2);
memset(tx_rx->tx_data, 0, sizeof(tx_rx->tx_data));
memset(tx_rx->tx_parity, 0, sizeof(tx_rx->tx_parity));
for(uint8_t i = 0; i < 4; i++) {
tx_rx->tx_data[i] = crypto1_byte(crypto, 0x00, 0) ^ plain_cmd[i];
tx_rx->tx_parity[0] |=
((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_cmd[i])) & 0x01) << (7 - i);
}
crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 4 * 9;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 8 * (MF_CLASSIC_BLOCK_SIZE + 2)) {
uint8_t block_received[MF_CLASSIC_BLOCK_SIZE + 2];
for(uint8_t i = 0; i < MF_CLASSIC_BLOCK_SIZE + 2; i++) {
block_received[i] = crypto1_byte(crypto, 0, 0) ^ tx_rx->rx_data[i];
}
crypto1_decrypt(crypto, tx_rx->rx_data, tx_rx->rx_bits, block_received);
uint16_t crc_calc = nfca_get_crc16(block_received, MF_CLASSIC_BLOCK_SIZE);
uint16_t crc_received = (block_received[MF_CLASSIC_BLOCK_SIZE + 1] << 8) |
block_received[MF_CLASSIC_BLOCK_SIZE];
@ -754,49 +755,6 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data
return sectors_read;
}
void mf_crypto1_decrypt(
Crypto1* crypto,
uint8_t* encrypted_data,
uint16_t encrypted_data_bits,
uint8_t* decrypted_data) {
if(encrypted_data_bits < 8) {
uint8_t decrypted_byte = 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 0)) << 0;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 1)) << 1;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 2)) << 2;
decrypted_byte |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(encrypted_data[0], 3)) << 3;
decrypted_data[0] = decrypted_byte;
} else {
for(size_t i = 0; i < encrypted_data_bits / 8; i++) {
decrypted_data[i] = crypto1_byte(crypto, 0, 0) ^ encrypted_data[i];
}
}
}
void mf_crypto1_encrypt(
Crypto1* crypto,
uint8_t* keystream,
uint8_t* plain_data,
uint16_t plain_data_bits,
uint8_t* encrypted_data,
uint8_t* encrypted_parity) {
if(plain_data_bits < 8) {
encrypted_data[0] = 0;
for(size_t i = 0; i < plain_data_bits; i++) {
encrypted_data[0] |= (crypto1_bit(crypto, 0, 0) ^ FURI_BIT(plain_data[0], i)) << i;
}
} else {
memset(encrypted_parity, 0, plain_data_bits / 8 + 1);
for(uint8_t i = 0; i < plain_data_bits / 8; i++) {
encrypted_data[i] = crypto1_byte(crypto, keystream ? keystream[i] : 0, 0) ^
plain_data[i];
encrypted_parity[i / 8] |=
(((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(plain_data[i])) & 0x01)
<< (7 - (i & 0x0007)));
}
}
}
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(emulator);
furi_assert(tx_rx);
@ -819,7 +777,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
tx_rx->rx_bits);
break;
}
mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
}
if(plain_data[0] == 0x50 && plain_data[1] == 0x00) {
@ -857,7 +815,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
tx_rx->tx_bits = sizeof(nt) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
} else {
mf_crypto1_encrypt(
crypto1_encrypt(
&emulator->crypto,
nt_keystream,
nt,
@ -904,7 +862,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
uint32_t ans = prng_successor(nonce, 96);
uint8_t responce[4] = {};
nfc_util_num2bytes(ans, 4, responce);
mf_crypto1_encrypt(
crypto1_encrypt(
&emulator->crypto,
NULL,
responce,
@ -938,7 +896,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
// Send NACK
uint8_t nack = 0x04;
if(is_encrypted) {
mf_crypto1_encrypt(
crypto1_encrypt(
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
} else {
tx_rx->tx_data[0] = nack;
@ -951,7 +909,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
}
nfca_append_crc16(block_data, 16);
mf_crypto1_encrypt(
crypto1_encrypt(
&emulator->crypto,
NULL,
block_data,
@ -967,14 +925,14 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
}
// Send ACK
uint8_t ack = 0x0A;
mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
tx_rx->tx_bits = 4;
if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break;
if(tx_rx->rx_bits != 18 * 8) break;
mf_crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data);
uint8_t block_data[16] = {};
memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE);
if(mf_classic_is_sector_trailer(block)) {
@ -1002,7 +960,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
}
// Send ACK
ack = 0x0A;
mf_crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent;
tx_rx->tx_bits = 4;
} else {
@ -1015,8 +973,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
// Send NACK
uint8_t nack = 0x04;
if(is_encrypted) {
mf_crypto1_encrypt(
&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity);
} else {
tx_rx->tx_data[0] = nack;
}
@ -1027,3 +984,143 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_
return true;
}
bool mf_classic_write_block(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key) {
furi_assert(tx_rx);
furi_assert(src_block);
Crypto1 crypto = {};
uint8_t plain_data[18] = {};
uint8_t resp = 0;
bool write_success = false;
do {
furi_hal_nfc_sleep();
if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto)) {
FURI_LOG_D(TAG, "Auth fail");
break;
}
// Send write command
plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD;
plain_data[1] = block_num;
nfca_append_crc16(plain_data, 2);
crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 4 * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp);
break;
}
} else {
FURI_LOG_D(TAG, "Not ACK received");
break;
}
} else {
FURI_LOG_D(TAG, "Failed to send write cmd");
break;
}
// Send data
memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE);
nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE);
crypto1_encrypt(
&crypto,
NULL,
plain_data,
(MF_CLASSIC_BLOCK_SIZE + 2) * 8,
tx_rx->tx_data,
tx_rx->tx_parity);
tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
if(furi_hal_nfc_tx_rx(tx_rx, 50)) {
if(tx_rx->rx_bits == 4) {
crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp);
if(resp != 0x0A) {
FURI_LOG_D(TAG, "NACK received on sending data");
break;
}
} else {
FURI_LOG_D(TAG, "Not ACK received");
break;
}
} else {
FURI_LOG_D(TAG, "Failed to send data");
break;
}
write_success = true;
// Send Halt
plain_data[0] = 0x50;
plain_data[1] = 0x00;
nfca_append_crc16(plain_data, 2);
crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity);
tx_rx->tx_bits = 2 * 8;
tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw;
// No response is expected
furi_hal_nfc_tx_rx(tx_rx, 50);
} while(false);
return write_success;
}
bool mf_classic_write_sector(
FuriHalNfcTxRxContext* tx_rx,
MfClassicData* dest_data,
MfClassicData* src_data,
uint8_t sec_num) {
furi_assert(tx_rx);
furi_assert(dest_data);
furi_assert(src_data);
uint8_t first_block = mf_classic_get_first_block_num_of_sector(sec_num);
uint8_t total_blocks = mf_classic_get_blocks_num_in_sector(sec_num);
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(dest_data, sec_num);
bool key_a_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyA);
bool key_b_found = mf_classic_is_key_found(dest_data, sec_num, MfClassicKeyB);
bool write_success = true;
for(size_t i = first_block; i < first_block + total_blocks; i++) {
// Compare blocks
if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE)) {
bool key_a_write_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyA, MfClassicActionDataWrite);
bool key_b_write_allowed = mf_classic_is_allowed_access_data_block(
dest_data, i, MfClassicKeyB, MfClassicActionDataWrite);
if(key_a_found && key_a_write_allowed) {
FURI_LOG_I(TAG, "Writing block %d with key A", i);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) {
FURI_LOG_E(TAG, "Failed to write block %d", i);
write_success = false;
break;
}
} else if(key_b_found && key_b_write_allowed) {
FURI_LOG_I(TAG, "Writing block %d with key A", i);
uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6);
if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) {
FURI_LOG_E(TAG, "Failed to write block %d", i);
write_success = false;
break;
}
} else {
FURI_LOG_E(TAG, "Failed to find key with write access");
write_success = false;
break;
}
} else {
FURI_LOG_D(TAG, "Blocks %d are equal", i);
}
}
return write_success;
}

View file

@ -27,6 +27,20 @@ typedef enum {
MfClassicKeyB,
} MfClassicKey;
typedef enum {
MfClassicActionDataRead,
MfClassicActionDataWrite,
MfClassicActionDataInc,
MfClassicActionDataDec,
MfClassicActionKeyARead,
MfClassicActionKeyAWrite,
MfClassicActionKeyBRead,
MfClassicActionKeyBWrite,
MfClassicActionACRead,
MfClassicActionACWrite,
} MfClassicAction;
typedef struct {
uint8_t value[MF_CLASSIC_BLOCK_SIZE];
} MfClassicBlock;
@ -90,6 +104,18 @@ bool mf_classic_is_sector_trailer(uint8_t block);
uint8_t mf_classic_get_sector_by_block(uint8_t block);
bool mf_classic_is_allowed_access_sector_trailer(
MfClassicData* data,
uint8_t block_num,
MfClassicKey key,
MfClassicAction action);
bool mf_classic_is_allowed_access_data_block(
MfClassicData* data,
uint8_t block_num,
MfClassicKey key,
MfClassicAction action);
bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type);
void mf_classic_set_key_found(
@ -104,6 +130,10 @@ bool mf_classic_is_block_read(MfClassicData* data, uint8_t block_num);
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
bool mf_classic_is_sector_data_read(MfClassicData* data, uint8_t sector_num);
void mf_classic_set_sector_data_not_read(MfClassicData* data);
bool mf_classic_is_sector_read(MfClassicData* data, uint8_t sector_num);
bool mf_classic_is_card_read(MfClassicData* data);
@ -145,3 +175,16 @@ uint8_t mf_classic_read_card(
uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data);
bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx);
bool mf_classic_write_block(
FuriHalNfcTxRxContext* tx_rx,
MfClassicBlock* src_block,
uint8_t block_num,
MfClassicKey key_type,
uint64_t key);
bool mf_classic_write_sector(
FuriHalNfcTxRxContext* tx_rx,
MfClassicData* dest_data,
MfClassicData* src_data,
uint8_t sec_num);

View file

@ -0,0 +1,44 @@
from typing import Set, ClassVar
from dataclasses import dataclass, field
@dataclass(frozen=True)
class ApiEntryFunction:
name: str
returns: str
params: str
csv_type: ClassVar[str] = "Function"
def dictify(self):
return dict(name=self.name, type=self.returns, params=self.params)
@dataclass(frozen=True)
class ApiEntryVariable:
name: str
var_type: str
csv_type: ClassVar[str] = "Variable"
def dictify(self):
return dict(name=self.name, type=self.var_type, params=None)
@dataclass(frozen=True)
class ApiHeader:
name: str
csv_type: ClassVar[str] = "Header"
def dictify(self):
return dict(name=self.name, type=None, params=None)
@dataclass
class ApiEntries:
# These are sets, to avoid creating duplicates when we have multiple
# declarations with same signature
functions: Set[ApiEntryFunction] = field(default_factory=set)
variables: Set[ApiEntryVariable] = field(default_factory=set)
headers: Set[ApiHeader] = field(default_factory=set)

View file

@ -4,284 +4,18 @@ import csv
import operator
from enum import Enum, auto
from typing import List, Set, ClassVar, Any
from dataclasses import dataclass, field
from typing import Set, ClassVar, Any
from dataclasses import dataclass
from ansi.color import fg
from cxxheaderparser.parser import CxxParser
# 'Fixing' complaints about typedefs
CxxParser._fundamentals.discard("wchar_t")
from cxxheaderparser.types import (
EnumDecl,
Field,
ForwardDecl,
FriendDecl,
Function,
Method,
Typedef,
UsingAlias,
UsingDecl,
Variable,
Pointer,
Type,
PQName,
NameSpecifier,
FundamentalSpecifier,
Parameter,
Array,
Value,
Token,
FunctionType,
from . import (
ApiEntries,
ApiEntryFunction,
ApiEntryVariable,
ApiHeader,
)
from cxxheaderparser.parserstate import (
State,
EmptyBlockState,
ClassBlockState,
ExternBlockState,
NamespaceBlockState,
)
@dataclass(frozen=True)
class ApiEntryFunction:
name: str
returns: str
params: str
csv_type: ClassVar[str] = "Function"
def dictify(self):
return dict(name=self.name, type=self.returns, params=self.params)
@dataclass(frozen=True)
class ApiEntryVariable:
name: str
var_type: str
csv_type: ClassVar[str] = "Variable"
def dictify(self):
return dict(name=self.name, type=self.var_type, params=None)
@dataclass(frozen=True)
class ApiHeader:
name: str
csv_type: ClassVar[str] = "Header"
def dictify(self):
return dict(name=self.name, type=None, params=None)
@dataclass
class ApiEntries:
# These are sets, to avoid creating duplicates when we have multiple
# declarations with same signature
functions: Set[ApiEntryFunction] = field(default_factory=set)
variables: Set[ApiEntryVariable] = field(default_factory=set)
headers: Set[ApiHeader] = field(default_factory=set)
class SymbolManager:
def __init__(self):
self.api = ApiEntries()
self.name_hashes = set()
# Calculate hash of name and raise exception if it already is in the set
def _name_check(self, name: str):
name_hash = gnu_sym_hash(name)
if name_hash in self.name_hashes:
raise Exception(f"Hash collision on {name}")
self.name_hashes.add(name_hash)
def add_function(self, function_def: ApiEntryFunction):
if function_def in self.api.functions:
return
self._name_check(function_def.name)
self.api.functions.add(function_def)
def add_variable(self, variable_def: ApiEntryVariable):
if variable_def in self.api.variables:
return
self._name_check(variable_def.name)
self.api.variables.add(variable_def)
def add_header(self, header: str):
self.api.headers.add(ApiHeader(header))
def gnu_sym_hash(name: str):
h = 0x1505
for c in name:
h = (h << 5) + h + ord(c)
return str(hex(h))[-8:]
class SdkCollector:
def __init__(self):
self.symbol_manager = SymbolManager()
def add_header_to_sdk(self, header: str):
self.symbol_manager.add_header(header)
def process_source_file_for_sdk(self, file_path: str):
visitor = SdkCxxVisitor(self.symbol_manager)
with open(file_path, "rt") as f:
content = f.read()
parser = CxxParser(file_path, content, visitor, None)
parser.parse()
def get_api(self):
return self.symbol_manager.api
def stringify_array_dimension(size_descr):
if not size_descr:
return ""
return stringify_descr(size_descr)
def stringify_array_descr(type_descr):
assert isinstance(type_descr, Array)
return (
stringify_descr(type_descr.array_of),
stringify_array_dimension(type_descr.size),
)
def stringify_descr(type_descr):
if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)):
return type_descr.name
elif isinstance(type_descr, PQName):
return "::".join(map(stringify_descr, type_descr.segments))
elif isinstance(type_descr, Pointer):
# Hack
if isinstance(type_descr.ptr_to, FunctionType):
return stringify_descr(type_descr.ptr_to)
return f"{stringify_descr(type_descr.ptr_to)}*"
elif isinstance(type_descr, Type):
return (
f"{'const ' if type_descr.const else ''}"
f"{'volatile ' if type_descr.volatile else ''}"
f"{stringify_descr(type_descr.typename)}"
)
elif isinstance(type_descr, Parameter):
return stringify_descr(type_descr.type)
elif isinstance(type_descr, Array):
# Hack for 2d arrays
if isinstance(type_descr.array_of, Array):
argtype, dimension = stringify_array_descr(type_descr.array_of)
return (
f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]"
)
return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]"
elif isinstance(type_descr, Value):
return " ".join(map(stringify_descr, type_descr.tokens))
elif isinstance(type_descr, FunctionType):
return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})"
elif isinstance(type_descr, Token):
return type_descr.value
elif type_descr is None:
return ""
else:
raise Exception("unsupported type_descr: %s" % type_descr)
class SdkCxxVisitor:
def __init__(self, symbol_manager: SymbolManager):
self.api = symbol_manager
def on_variable(self, state: State, v: Variable) -> None:
if not v.extern:
return
self.api.add_variable(
ApiEntryVariable(
stringify_descr(v.name),
stringify_descr(v.type),
)
)
def on_function(self, state: State, fn: Function) -> None:
if fn.inline or fn.has_body:
return
self.api.add_function(
ApiEntryFunction(
stringify_descr(fn.name),
stringify_descr(fn.return_type),
", ".join(map(stringify_descr, fn.parameters))
+ (", ..." if fn.vararg else ""),
)
)
def on_define(self, state: State, content: str) -> None:
pass
def on_pragma(self, state: State, content: str) -> None:
pass
def on_include(self, state: State, filename: str) -> None:
pass
def on_empty_block_start(self, state: EmptyBlockState) -> None:
pass
def on_empty_block_end(self, state: EmptyBlockState) -> None:
pass
def on_extern_block_start(self, state: ExternBlockState) -> None:
pass
def on_extern_block_end(self, state: ExternBlockState) -> None:
pass
def on_namespace_start(self, state: NamespaceBlockState) -> None:
pass
def on_namespace_end(self, state: NamespaceBlockState) -> None:
pass
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
pass
def on_typedef(self, state: State, typedef: Typedef) -> None:
pass
def on_using_namespace(self, state: State, namespace: List[str]) -> None:
pass
def on_using_alias(self, state: State, using: UsingAlias) -> None:
pass
def on_using_declaration(self, state: State, using: UsingDecl) -> None:
pass
def on_enum(self, state: State, enum: EnumDecl) -> None:
pass
def on_class_start(self, state: ClassBlockState) -> None:
pass
def on_class_field(self, state: State, f: Field) -> None:
pass
def on_class_method(self, state: ClassBlockState, method: Method) -> None:
pass
def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None:
pass
def on_class_end(self, state: ClassBlockState) -> None:
pass
@dataclass(frozen=True)
class SdkVersion:

View file

@ -0,0 +1,238 @@
from typing import List
from cxxheaderparser.parser import CxxParser
from . import (
ApiEntries,
ApiEntryFunction,
ApiEntryVariable,
ApiHeader,
)
# 'Fixing' complaints about typedefs
CxxParser._fundamentals.discard("wchar_t")
from cxxheaderparser.types import (
EnumDecl,
Field,
ForwardDecl,
FriendDecl,
Function,
Method,
Typedef,
UsingAlias,
UsingDecl,
Variable,
Pointer,
Type,
PQName,
NameSpecifier,
FundamentalSpecifier,
Parameter,
Array,
Value,
Token,
FunctionType,
)
from cxxheaderparser.parserstate import (
State,
EmptyBlockState,
ClassBlockState,
ExternBlockState,
NamespaceBlockState,
)
class SymbolManager:
def __init__(self):
self.api = ApiEntries()
self.name_hashes = set()
# Calculate hash of name and raise exception if it already is in the set
def _name_check(self, name: str):
name_hash = gnu_sym_hash(name)
if name_hash in self.name_hashes:
raise Exception(f"Hash collision on {name}")
self.name_hashes.add(name_hash)
def add_function(self, function_def: ApiEntryFunction):
if function_def in self.api.functions:
return
self._name_check(function_def.name)
self.api.functions.add(function_def)
def add_variable(self, variable_def: ApiEntryVariable):
if variable_def in self.api.variables:
return
self._name_check(variable_def.name)
self.api.variables.add(variable_def)
def add_header(self, header: str):
self.api.headers.add(ApiHeader(header))
def gnu_sym_hash(name: str):
h = 0x1505
for c in name:
h = (h << 5) + h + ord(c)
return str(hex(h))[-8:]
class SdkCollector:
def __init__(self):
self.symbol_manager = SymbolManager()
def add_header_to_sdk(self, header: str):
self.symbol_manager.add_header(header)
def process_source_file_for_sdk(self, file_path: str):
visitor = SdkCxxVisitor(self.symbol_manager)
with open(file_path, "rt") as f:
content = f.read()
parser = CxxParser(file_path, content, visitor, None)
parser.parse()
def get_api(self):
return self.symbol_manager.api
def stringify_array_dimension(size_descr):
if not size_descr:
return ""
return stringify_descr(size_descr)
def stringify_array_descr(type_descr):
assert isinstance(type_descr, Array)
return (
stringify_descr(type_descr.array_of),
stringify_array_dimension(type_descr.size),
)
def stringify_descr(type_descr):
if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)):
return type_descr.name
elif isinstance(type_descr, PQName):
return "::".join(map(stringify_descr, type_descr.segments))
elif isinstance(type_descr, Pointer):
# Hack
if isinstance(type_descr.ptr_to, FunctionType):
return stringify_descr(type_descr.ptr_to)
return f"{stringify_descr(type_descr.ptr_to)}*"
elif isinstance(type_descr, Type):
return (
f"{'const ' if type_descr.const else ''}"
f"{'volatile ' if type_descr.volatile else ''}"
f"{stringify_descr(type_descr.typename)}"
)
elif isinstance(type_descr, Parameter):
return stringify_descr(type_descr.type)
elif isinstance(type_descr, Array):
# Hack for 2d arrays
if isinstance(type_descr.array_of, Array):
argtype, dimension = stringify_array_descr(type_descr.array_of)
return (
f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]"
)
return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]"
elif isinstance(type_descr, Value):
return " ".join(map(stringify_descr, type_descr.tokens))
elif isinstance(type_descr, FunctionType):
return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})"
elif isinstance(type_descr, Token):
return type_descr.value
elif type_descr is None:
return ""
else:
raise Exception("unsupported type_descr: %s" % type_descr)
class SdkCxxVisitor:
def __init__(self, symbol_manager: SymbolManager):
self.api = symbol_manager
def on_variable(self, state: State, v: Variable) -> None:
if not v.extern:
return
self.api.add_variable(
ApiEntryVariable(
stringify_descr(v.name),
stringify_descr(v.type),
)
)
def on_function(self, state: State, fn: Function) -> None:
if fn.inline or fn.has_body:
return
self.api.add_function(
ApiEntryFunction(
stringify_descr(fn.name),
stringify_descr(fn.return_type),
", ".join(map(stringify_descr, fn.parameters))
+ (", ..." if fn.vararg else ""),
)
)
def on_define(self, state: State, content: str) -> None:
pass
def on_pragma(self, state: State, content: str) -> None:
pass
def on_include(self, state: State, filename: str) -> None:
pass
def on_empty_block_start(self, state: EmptyBlockState) -> None:
pass
def on_empty_block_end(self, state: EmptyBlockState) -> None:
pass
def on_extern_block_start(self, state: ExternBlockState) -> None:
pass
def on_extern_block_end(self, state: ExternBlockState) -> None:
pass
def on_namespace_start(self, state: NamespaceBlockState) -> None:
pass
def on_namespace_end(self, state: NamespaceBlockState) -> None:
pass
def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
pass
def on_typedef(self, state: State, typedef: Typedef) -> None:
pass
def on_using_namespace(self, state: State, namespace: List[str]) -> None:
pass
def on_using_alias(self, state: State, using: UsingAlias) -> None:
pass
def on_using_declaration(self, state: State, using: UsingDecl) -> None:
pass
def on_enum(self, state: State, enum: EnumDecl) -> None:
pass
def on_class_start(self, state: ClassBlockState) -> None:
pass
def on_class_field(self, state: State, f: Field) -> None:
pass
def on_class_method(self, state: ClassBlockState, method: Method) -> None:
pass
def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None:
pass
def on_class_end(self, state: ClassBlockState) -> None:
pass

View file

@ -8,7 +8,7 @@ import os
import pathlib
from fbt.elfmanifest import assemble_manifest_data
from fbt.appmanifest import FlipperApplication, FlipperManifestException
from fbt.sdk import SdkCache
from fbt.sdk.cache import SdkCache
import itertools
from ansi.color import fg

View file

@ -4,7 +4,7 @@ from SCons.Action import Action
from SCons.Errors import UserError
# from SCons.Scanner import C
from SCons.Script import Mkdir, Copy, Delete, Entry
from SCons.Script import Entry
from SCons.Util import LogicalLines
import os.path
@ -12,7 +12,8 @@ import posixpath
import pathlib
import json
from fbt.sdk import SdkCollector, SdkCache
from fbt.sdk.collector import SdkCollector
from fbt.sdk.cache import SdkCache
def ProcessSdkDepends(env, filename):
@ -49,15 +50,19 @@ def prebuild_sdk_create_origin_file(target, source, env):
class SdkMeta:
def __init__(self, env):
def __init__(self, env, tree_builder: "SdkTreeBuilder"):
self.env = env
self.treebuilder = tree_builder
def save_to(self, json_manifest_path: str):
meta_contents = {
"sdk_symbols": self.env["SDK_DEFINITION"].name,
"sdk_symbols": self.treebuilder.build_sdk_file_path(
self.env["SDK_DEFINITION"].path
),
"cc_args": self._wrap_scons_vars("$CCFLAGS $_CCCOMCOM"),
"cpp_args": self._wrap_scons_vars("$CXXFLAGS $CCFLAGS $_CCCOMCOM"),
"linker_args": self._wrap_scons_vars("$LINKFLAGS"),
"linker_script": self.env.subst("${LINKER_SCRIPT_PATH}"),
}
with open(json_manifest_path, "wt") as f:
json.dump(meta_contents, f, indent=4)
@ -68,6 +73,8 @@ class SdkMeta:
class SdkTreeBuilder:
SDK_DIR_SUBST = "SDK_ROOT_DIR"
def __init__(self, env, target, source) -> None:
self.env = env
self.target = target
@ -88,6 +95,8 @@ class SdkTreeBuilder:
self.header_depends = list(
filter(lambda fname: fname.endswith(".h"), depends.split()),
)
self.header_depends.append(self.env.subst("${LINKER_SCRIPT_PATH}"))
self.header_depends.append(self.env.subst("${SDK_DEFINITION}"))
self.header_dirs = sorted(
set(map(os.path.normpath, map(os.path.dirname, self.header_depends)))
)
@ -102,17 +111,33 @@ class SdkTreeBuilder:
)
sdk_dirs = ", ".join(f"'{dir}'" for dir in self.header_dirs)
for dir in full_fw_paths:
if dir in sdk_dirs:
filtered_paths.append(
posixpath.normpath(posixpath.join(self.target_sdk_dir_name, dir))
)
filtered_paths.extend(
map(
self.build_sdk_file_path,
filter(lambda path: path in sdk_dirs, full_fw_paths),
)
)
sdk_env = self.env.Clone()
sdk_env.Replace(CPPPATH=filtered_paths)
meta = SdkMeta(sdk_env)
sdk_env.Replace(
CPPPATH=filtered_paths,
LINKER_SCRIPT=self.env.subst("${APP_LINKER_SCRIPT}"),
ORIG_LINKER_SCRIPT_PATH=self.env["LINKER_SCRIPT_PATH"],
LINKER_SCRIPT_PATH=self.build_sdk_file_path("${ORIG_LINKER_SCRIPT_PATH}"),
)
meta = SdkMeta(sdk_env, self)
meta.save_to(self.target[0].path)
def build_sdk_file_path(self, orig_path: str) -> str:
return posixpath.normpath(
posixpath.join(
self.SDK_DIR_SUBST,
self.target_sdk_dir_name,
orig_path,
)
).replace("\\", "/")
def emitter(self, target, source, env):
target_folder = target[0]
target = [target_folder.File("sdk.opts")]
@ -128,8 +153,6 @@ class SdkTreeBuilder:
for sdkdir in dirs_to_create:
os.makedirs(sdkdir, exist_ok=True)
shutil.copy2(self.env["SDK_DEFINITION"].path, self.sdk_root_dir.path)
for header in self.header_depends:
shutil.copy2(header, self.sdk_deploy_dir.File(header).path)

View file

@ -48,48 +48,52 @@ class Main(App):
)
self.parser_copy.set_defaults(func=self.copy)
def get_project_filename(self, project, filetype):
def get_project_file_name(self, project: ProjectDir, filetype: str) -> str:
# Temporary fix
project_name = project.project
if project_name == "firmware":
if filetype == "zip":
project_name = "sdk"
elif filetype != "elf":
project_name = "full"
if project_name == "firmware" and filetype != "elf":
project_name = "full"
return f"{self.DIST_FILE_PREFIX}{self.target}-{project_name}-{self.args.suffix}.{filetype}"
return self.get_dist_file_name(project_name, filetype)
def get_dist_filepath(self, filename):
def get_dist_file_name(self, dist_artifact_type: str, filetype: str) -> str:
return f"{self.DIST_FILE_PREFIX}{self.target}-{dist_artifact_type}-{self.args.suffix}.{filetype}"
def get_dist_file_path(self, filename: str) -> str:
return join(self.output_dir_path, filename)
def copy_single_project(self, project):
def copy_single_project(self, project: ProjectDir) -> None:
obj_directory = join("build", project.dir)
for filetype in ("elf", "bin", "dfu", "json"):
if exists(src_file := join(obj_directory, f"{project.project}.{filetype}")):
shutil.copyfile(
src_file,
self.get_dist_filepath(
self.get_project_filename(project, filetype)
self.get_dist_file_path(
self.get_project_file_name(project, filetype)
),
)
if exists(sdk_folder := join(obj_directory, "sdk")):
with zipfile.ZipFile(
self.get_dist_filepath(self.get_project_filename(project, "zip")),
"w",
zipfile.ZIP_DEFLATED,
) as zf:
for root, dirs, files in walk(sdk_folder):
for file in files:
zf.write(
join(root, file),
relpath(
join(root, file),
sdk_folder,
),
)
for foldertype in ("sdk", "lib"):
if exists(sdk_folder := join(obj_directory, foldertype)):
self.package_zip(foldertype, sdk_folder)
def copy(self):
def package_zip(self, foldertype, sdk_folder):
with zipfile.ZipFile(
self.get_dist_file_path(self.get_dist_file_name(foldertype, "zip")),
"w",
zipfile.ZIP_DEFLATED,
) as zf:
for root, _, files in walk(sdk_folder):
for file in files:
zf.write(
join(root, file),
relpath(
join(root, file),
sdk_folder,
),
)
def copy(self) -> int:
self.projects = dict(
map(
lambda pd: (pd.project, pd),
@ -144,12 +148,12 @@ class Main(App):
"-t",
self.target,
"--dfu",
self.get_dist_filepath(
self.get_project_filename(self.projects["firmware"], "dfu")
self.get_dist_file_path(
self.get_project_file_name(self.projects["firmware"], "dfu")
),
"--stage",
self.get_dist_filepath(
self.get_project_filename(self.projects["updater"], "bin")
self.get_dist_file_path(
self.get_project_file_name(self.projects["updater"], "bin")
),
]
if self.args.resources:

View file

@ -23,12 +23,12 @@ if (!(Test-Path -LiteralPath "$repo_root\toolchain")) {
New-Item "$repo_root\toolchain" -ItemType Directory
}
Write-Host -NoNewline "Unziping Windows toolchain.."
Write-Host -NoNewline "Extracting Windows toolchain.."
Add-Type -Assembly "System.IO.Compression.Filesystem"
[System.IO.Compression.ZipFile]::ExtractToDirectory("$toolchain_zip", "$repo_root\")
[System.IO.Compression.ZipFile]::ExtractToDirectory("$repo_root\$toolchain_zip", "$repo_root\")
Move-Item -Path "$repo_root\$toolchain_dir" -Destination "$repo_root\toolchain\x86_64-windows"
Write-Host "done!"
Write-Host -NoNewline "Clearing temporary files.."
Write-Host -NoNewline "Cleaning up temporary files.."
Remove-Item -LiteralPath "$repo_root\$toolchain_zip" -Force
Write-Host "done!"

View file

@ -21,7 +21,7 @@ appenv = ENV.Clone(
)
appenv.Replace(
LINKER_SCRIPT="application_ext",
LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"),
)
appenv.AppendUnique(

View file

@ -32,12 +32,27 @@ else:
],
)
ENV.Append(
ENV.AppendUnique(
LINKFLAGS=[
"-Tfirmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld",
"-specs=nano.specs",
"-specs=nosys.specs",
"-Wl,--gc-sections",
"-Wl,--undefined=uxTopUsedPriority",
"-Wl,--wrap,_malloc_r",
"-Wl,--wrap,_free_r",
"-Wl,--wrap,_calloc_r",
"-Wl,--wrap,_realloc_r",
"-n",
"-Xlinker",
"-Map=${TARGET}.map",
"-T${LINKER_SCRIPT_PATH}",
],
)
ENV.SetDefault(
LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld",
)
if ENV["FIRMWARE_BUILD_CFG"] == "updater":
ENV.Append(
IMAGE_BASE_ADDRESS="0x20000000",
@ -47,4 +62,5 @@ else:
ENV.Append(
IMAGE_BASE_ADDRESS="0x8000000",
LINKER_SCRIPT="stm32wb55xx_flash",
APP_LINKER_SCRIPT="application_ext",
)