diff --git a/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc new file mode 100644 index 00000000000..8c125865c35 --- /dev/null +++ b/applications/debug/unit_tests/resources/unit_tests/nfc/Ultralight_C.nfc @@ -0,0 +1,71 @@ +Filetype: Flipper NFC device +Version: 4 +# Device type can be ISO14443-3A, ISO14443-3B, ISO14443-4A, ISO14443-4B, ISO15693-3, FeliCa, NTAG/Ultralight, Mifare Classic, Mifare DESFire, SLIX, ST25TB +Device type: NTAG/Ultralight +# UID is common for all formats +UID: 04 BA FF CA 4D 5D 80 +# ISO14443-3A specific data +ATQA: 00 44 +SAK: 00 +# NTAG/Ultralight specific data +Data format version: 2 +NTAG/Ultralight type: Mifare Ultralight C +Signature: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +Mifare version: 00 00 00 00 00 00 00 00 +Counter 0: 0 +Tearing 0: 00 +Counter 1: 0 +Tearing 1: 00 +Counter 2: 0 +Tearing 2: 00 +Pages total: 48 +Pages read: 48 +Page 0: 04 BA FF C9 +Page 1: CA 4D 5D 80 +Page 2: 5A 48 00 00 +Page 3: E1 10 12 00 +Page 4: 01 03 A0 0C +Page 5: 34 03 00 FE +Page 6: 00 00 00 00 +Page 7: 00 00 00 00 +Page 8: 00 00 00 00 +Page 9: 00 00 00 00 +Page 10: 00 00 BE AF +Page 11: 00 00 00 00 +Page 12: 00 00 00 00 +Page 13: 00 00 00 00 +Page 14: 00 00 00 00 +Page 15: 00 00 00 00 +Page 16: 00 00 00 00 +Page 17: 00 00 00 00 +Page 18: 00 00 00 00 +Page 19: 00 00 00 00 +Page 20: 00 00 00 00 +Page 21: 00 00 00 00 +Page 22: 00 00 00 00 +Page 23: 00 00 00 00 +Page 24: 00 00 00 00 +Page 25: 00 00 00 00 +Page 26: 00 00 00 00 +Page 27: 00 00 00 00 +Page 28: 00 00 00 00 +Page 29: 00 00 00 00 +Page 30: 00 00 00 00 +Page 31: 00 00 00 00 +Page 32: 00 00 00 00 +Page 33: 00 00 00 00 +Page 34: 00 00 00 00 +Page 35: 00 00 00 00 +Page 36: 00 00 00 00 +Page 37: 00 00 00 00 +Page 38: 00 00 00 00 +Page 39: 00 00 00 00 +Page 40: 00 00 00 00 +Page 41: 00 00 00 00 +Page 42: 05 00 00 00 +Page 43: 00 00 00 00 +Page 44: 00 00 00 00 +Page 45: 00 00 00 00 +Page 46: 00 00 00 00 +Page 47: 00 00 00 00 +Failed authentication attempts: 0 diff --git a/applications/debug/unit_tests/tests/nfc/nfc_test.c b/applications/debug/unit_tests/tests/nfc/nfc_test.c index 1e440707643..5f172d0b5ce 100644 --- a/applications/debug/unit_tests/tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/tests/nfc/nfc_test.c @@ -286,6 +286,10 @@ MU_TEST(mf_ultralight_21_reader) { mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_21.nfc")); } +MU_TEST(mf_ultralight_c_reader) { + mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ultralight_C.nfc")); +} + MU_TEST(ntag_215_reader) { mf_ultralight_reader_test(EXT_PATH("unit_tests/nfc/Ntag215.nfc")); } @@ -828,6 +832,7 @@ MU_TEST_SUITE(nfc) { MU_RUN_TEST(ntag_215_reader); MU_RUN_TEST(ntag_216_reader); MU_RUN_TEST(ntag_213_locked_reader); + MU_RUN_TEST(mf_ultralight_c_reader); MU_RUN_TEST(mf_ultralight_write); diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.c b/applications/main/nfc/helpers/mf_ultralight_auth.c index d954c1f7e91..e97649cb3ed 100644 --- a/applications/main/nfc/helpers/mf_ultralight_auth.c +++ b/applications/main/nfc/helpers/mf_ultralight_auth.c @@ -20,6 +20,7 @@ void mf_ultralight_auth_reset(MfUltralightAuth* instance) { instance->type = MfUltralightAuthTypeNone; memset(&instance->password, 0, sizeof(MfUltralightAuthPassword)); + memset(&instance->tdes_key, 0, sizeof(MfUltralightC3DesAuthKey)); memset(&instance->pack, 0, sizeof(MfUltralightAuthPack)); } diff --git a/applications/main/nfc/helpers/mf_ultralight_auth.h b/applications/main/nfc/helpers/mf_ultralight_auth.h index 51267b3eab3..c9e80765fb5 100644 --- a/applications/main/nfc/helpers/mf_ultralight_auth.h +++ b/applications/main/nfc/helpers/mf_ultralight_auth.h @@ -17,6 +17,7 @@ typedef enum { typedef struct { MfUltralightAuthType type; MfUltralightAuthPassword password; + MfUltralightC3DesAuthKey tdes_key; MfUltralightAuthPack pack; } MfUltralightAuth; diff --git a/applications/main/nfc/helpers/protocol_support/felica/felica.c b/applications/main/nfc/helpers/protocol_support/felica/felica.c index ceb18065369..561cd4d2e91 100644 --- a/applications/main/nfc/helpers/protocol_support/felica/felica.c +++ b/applications/main/nfc/helpers/protocol_support/felica/felica.c @@ -106,7 +106,7 @@ static void nfc_scene_read_success_on_enter_felica(NfcApp* instance) { FuriString* temp_str = furi_string_alloc(); - if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn)) { + if(!scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDesAuthUnlockWarn)) { furi_string_cat_printf( temp_str, "\e#%s\n", nfc_device_get_name(device, NfcDeviceNameTypeFull)); nfc_render_felica_info(data, NfcProtocolFormatTypeShort, temp_str); @@ -163,7 +163,7 @@ static void nfc_scene_read_menu_on_enter_felica(NfcApp* instance) { static bool nfc_scene_read_menu_on_event_felica(NfcApp* instance, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexUnlock) { - scene_manager_next_scene(instance->scene_manager, NfcSceneFelicaKeyInput); + scene_manager_next_scene(instance->scene_manager, NfcSceneDesAuthKeyInput); return true; } } diff --git a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c index b55ba130e76..3adf2a1f5c3 100644 --- a/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c +++ b/applications/main/nfc/helpers/protocol_support/mf_ultralight/mf_ultralight.c @@ -150,6 +150,7 @@ static NfcCommand } if(!mf_ultralight_event->data->auth_context.skip_auth) { mf_ultralight_event->data->auth_context.password = instance->mf_ul_auth->password; + mf_ultralight_event->data->auth_context.tdes_key = instance->mf_ul_auth->tdes_key; } } else if(mf_ultralight_event->type == MfUltralightPollerEventTypeAuthSuccess) { instance->mf_ul_auth->pack = mf_ultralight_event->data->auth_context.pack; @@ -243,7 +244,13 @@ static bool nfc_scene_read_and_saved_menu_on_event_mf_ultralight( if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexUnlock) { - scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightUnlockMenu); + const MfUltralightData* data = + nfc_device_get_data(instance->nfc_device, NfcProtocolMfUltralight); + + uint32_t next_scene = (data->type == MfUltralightTypeMfulC) ? + NfcSceneDesAuthKeyInput : + NfcSceneMfUltralightUnlockMenu; + scene_manager_next_scene(instance->scene_manager, next_scene); consumed = true; } else if(event.event == SubmenuIndexWrite) { scene_manager_next_scene(instance->scene_manager, NfcSceneMfUltralightWrite); diff --git a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c index f1d504d2486..598cf7a7e1b 100644 --- a/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c +++ b/applications/main/nfc/helpers/protocol_support/nfc_protocol_support_unlock_helper.c @@ -22,7 +22,7 @@ void nfc_unlock_helper_setup_from_state(NfcApp* instance) { bool unlocking = scene_manager_has_previous_scene( instance->scene_manager, NfcSceneMfUltralightUnlockWarn) || - scene_manager_has_previous_scene(instance->scene_manager, NfcSceneFelicaUnlockWarn); + scene_manager_has_previous_scene(instance->scene_manager, NfcSceneDesAuthUnlockWarn); uint32_t state = unlocking ? NfcSceneReadMenuStateCardSearch : NfcSceneReadMenuStateCardFound; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index 3017d16a406..83c8ffeed3e 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -33,8 +33,8 @@ ADD_SCENE(nfc, mf_ultralight_unlock_menu, MfUltralightUnlockMenu) ADD_SCENE(nfc, mf_ultralight_unlock_warn, MfUltralightUnlockWarn) ADD_SCENE(nfc, mf_ultralight_key_input, MfUltralightKeyInput) ADD_SCENE(nfc, mf_ultralight_capture_pass, MfUltralightCapturePass) -ADD_SCENE(nfc, felica_key_input, FelicaKeyInput) -ADD_SCENE(nfc, felica_unlock_warn, FelicaUnlockWarn) +ADD_SCENE(nfc, des_auth_key_input, DesAuthKeyInput) +ADD_SCENE(nfc, des_auth_unlock_warn, DesAuthUnlockWarn) ADD_SCENE(nfc, mf_desfire_more_info, MfDesfireMoreInfo) ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp) diff --git a/applications/main/nfc/scenes/nfc_scene_felica_key_input.c b/applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c similarity index 52% rename from applications/main/nfc/scenes/nfc_scene_felica_key_input.c rename to applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c index b04f12dae85..f3a6ecb328e 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_key_input.c +++ b/applications/main/nfc/scenes/nfc_scene_des_auth_key_input.c @@ -1,43 +1,54 @@ #include "../nfc_app_i.h" -void nfc_scene_felica_key_input_byte_input_callback(void* context) { +void nfc_scene_des_auth_key_input_byte_input_callback(void* context) { NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventByteInputDone); } -void nfc_scene_felica_key_input_on_enter(void* context) { +void nfc_scene_des_auth_key_input_on_enter(void* context) { NfcApp* nfc = context; // Setup view + NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device); + uint8_t* key = (protocol == NfcProtocolFelica) ? nfc->felica_auth->card_key.data : + nfc->mf_ul_auth->tdes_key.data; + ByteInput* byte_input = nfc->byte_input; byte_input_set_header_text(byte_input, "Enter key in hex"); byte_input_set_result_callback( byte_input, - nfc_scene_felica_key_input_byte_input_callback, + nfc_scene_des_auth_key_input_byte_input_callback, NULL, nfc, - nfc->felica_auth->card_key.data, + key, FELICA_DATA_BLOCK_SIZE); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewByteInput); } -bool nfc_scene_felica_key_input_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_des_auth_key_input_on_event(void* context, SceneManagerEvent event) { NfcApp* nfc = context; UNUSED(event); bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - nfc->felica_auth->skip_auth = false; - scene_manager_next_scene(nfc->scene_manager, NfcSceneFelicaUnlockWarn); + NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device); + + if(protocol == NfcProtocolFelica) { + nfc->felica_auth->skip_auth = false; + } else { + nfc->mf_ul_auth->type = MfUltralightAuthTypeManual; + } + + scene_manager_next_scene(nfc->scene_manager, NfcSceneDesAuthUnlockWarn); consumed = true; } } return consumed; } -void nfc_scene_felica_key_input_on_exit(void* context) { +void nfc_scene_des_auth_key_input_on_exit(void* context) { NfcApp* nfc = context; // Clear view diff --git a/applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c b/applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c similarity index 68% rename from applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c rename to applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c index 15b61dfa510..80c735c9ca1 100644 --- a/applications/main/nfc/scenes/nfc_scene_felica_unlock_warn.c +++ b/applications/main/nfc/scenes/nfc_scene_des_auth_unlock_warn.c @@ -1,25 +1,30 @@ #include "../nfc_app_i.h" -void nfc_scene_felica_unlock_warn_dialog_callback(DialogExResult result, void* context) { +void nfc_scene_des_auth_unlock_warn_dialog_callback(DialogExResult result, void* context) { NfcApp* nfc = context; view_dispatcher_send_custom_event(nfc->view_dispatcher, result); } -void nfc_scene_felica_unlock_warn_on_enter(void* context) { +void nfc_scene_des_auth_unlock_warn_on_enter(void* context) { NfcApp* nfc = context; const char* message = "Risky Action!"; DialogEx* dialog_ex = nfc->dialog_ex; dialog_ex_set_context(dialog_ex, nfc); - dialog_ex_set_result_callback(dialog_ex, nfc_scene_felica_unlock_warn_dialog_callback); + dialog_ex_set_result_callback(dialog_ex, nfc_scene_des_auth_unlock_warn_dialog_callback); dialog_ex_set_header(dialog_ex, message, 64, 0, AlignCenter, AlignTop); FuriString* str = furi_string_alloc(); furi_string_cat_printf(str, "Unlock with key: "); + + NfcProtocol protocol = nfc_device_get_protocol(nfc->nfc_device); + uint8_t* key = (protocol == NfcProtocolFelica) ? nfc->felica_auth->card_key.data : + nfc->mf_ul_auth->tdes_key.data; + for(uint8_t i = 0; i < FELICA_DATA_BLOCK_SIZE; i++) - furi_string_cat_printf(str, "%02X ", nfc->felica_auth->card_key.data[i]); + furi_string_cat_printf(str, "%02X ", key[i]); furi_string_cat_printf(str, "?"); nfc_text_store_set(nfc, furi_string_get_cstr(str)); @@ -33,7 +38,7 @@ void nfc_scene_felica_unlock_warn_on_enter(void* context) { view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx); } -bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent event) { +bool nfc_scene_des_auth_unlock_warn_on_event(void* context, SceneManagerEvent event) { NfcApp* nfc = context; UNUSED(event); bool consumed = false; @@ -51,7 +56,7 @@ bool nfc_scene_felica_unlock_warn_on_event(void* context, SceneManagerEvent even return consumed; } -void nfc_scene_felica_unlock_warn_on_exit(void* context) { +void nfc_scene_des_auth_unlock_warn_on_exit(void* context) { NfcApp* nfc = context; dialog_ex_reset(nfc->dialog_ex); diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c index 1f3481c1786..b10ac40b65b 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.c @@ -624,15 +624,19 @@ bool mf_ultralight_is_all_data_read(const MfUltralightData* data) { furi_check(data); bool all_read = false; - if(data->pages_read == data->pages_total || - (data->type == MfUltralightTypeMfulC && data->pages_read == data->pages_total - 4)) { - // Having read all the pages doesn't mean that we've got everything. - // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, - // so a default read on an auth-supported NTAG is never complete. + + if(data->pages_read == data->pages_total) { uint32_t feature_set = mf_ultralight_get_feature_support_set(data->type); - if(!mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportPasswordAuth)) { + if((data->type == MfUltralightTypeMfulC) && + mf_ultralight_support_feature(feature_set, MfUltralightFeatureSupportAuthenticate)) { + all_read = true; + } else if(!mf_ultralight_support_feature( + feature_set, MfUltralightFeatureSupportPasswordAuth)) { all_read = true; } else { + // Having read all the pages doesn't mean that we've got everything. + // By default PWD is 0xFFFFFFFF, but if read back it is always 0x00000000, + // so a default read on an auth-supported NTAG is never complete. MfUltralightConfigPages* config = NULL; if(mf_ultralight_get_config_page(data, &config)) { uint32_t pass = bit_lib_bytes_to_num_be( @@ -669,3 +673,61 @@ bool mf_ultralight_is_counter_configured(const MfUltralightData* data) { return configured; } + +void mf_ultralight_3des_shift_data(uint8_t* const data) { + furi_check(data); + + uint8_t buf = data[0]; + for(uint8_t i = 1; i < MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE; i++) { + data[i - 1] = data[i]; + } + data[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE - 1] = buf; +} + +bool mf_ultralight_3des_key_valid(const MfUltralightData* data) { + furi_check(data); + furi_check(data->type == MfUltralightTypeMfulC); + + return !(data->pages_read == data->pages_total - 4); +} + +const uint8_t* mf_ultralight_3des_get_key(const MfUltralightData* data) { + furi_check(data); + furi_check(data->type == MfUltralightTypeMfulC); + + return data->page[44].data; +} + +void mf_ultralight_3des_encrypt( + mbedtls_des3_context* ctx, + const uint8_t* ck, + const uint8_t* iv, + const uint8_t* input, + const uint8_t length, + uint8_t* out) { + furi_check(ctx); + furi_check(ck); + furi_check(iv); + furi_check(input); + furi_check(out); + + mbedtls_des3_set2key_enc(ctx, ck); + mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_ENCRYPT, length, (uint8_t*)iv, input, out); +} + +void mf_ultralight_3des_decrypt( + mbedtls_des3_context* ctx, + const uint8_t* ck, + const uint8_t* iv, + const uint8_t* input, + const uint8_t length, + uint8_t* out) { + furi_check(ctx); + furi_check(ck); + furi_check(iv); + furi_check(input); + furi_check(out); + + mbedtls_des3_set2key_dec(ctx, ck); + mbedtls_des3_crypt_cbc(ctx, MBEDTLS_DES_DECRYPT, length, (uint8_t*)iv, input, out); +} \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h index 582e9e0e260..f748f681376 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -37,7 +38,15 @@ extern "C" { #define MF_ULTRALIGHT_TEARING_FLAG_NUM (3) #define MF_ULTRALIGHT_AUTH_PASSWORD_SIZE (4) #define MF_ULTRALIGHT_AUTH_PACK_SIZE (2) -#define MF_ULTRALIGHT_AUTH_RESPONSE_SIZE (9) + +#define MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE (9) +#define MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE (16) +#define MF_ULTRALIGHT_C_AUTH_DATA_SIZE (MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE) +#define MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE (8) +#define MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE (8) +#define MF_ULTRALIGHT_C_AUTH_RND_A_BLOCK_OFFSET (0) +#define MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET (8) +#define MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE (MF_ULTRALIGHT_C_AUTH_DATA_SIZE + 1) typedef enum { MfUltralightErrorNone, @@ -119,6 +128,10 @@ typedef struct { uint8_t data[MF_ULTRALIGHT_AUTH_PASSWORD_SIZE]; } MfUltralightAuthPassword; +typedef struct { + uint8_t data[MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE]; +} MfUltralightC3DesAuthKey; + typedef struct { uint8_t data[MF_ULTRALIGHT_AUTH_PACK_SIZE]; } MfUltralightAuthPack; @@ -226,6 +239,28 @@ bool mf_ultralight_detect_protocol(const Iso14443_3aData* iso14443_3a_data); bool mf_ultralight_is_counter_configured(const MfUltralightData* data); +void mf_ultralight_3des_shift_data(uint8_t* const arr); + +bool mf_ultralight_3des_key_valid(const MfUltralightData* data); + +const uint8_t* mf_ultralight_3des_get_key(const MfUltralightData* data); + +void mf_ultralight_3des_encrypt( + mbedtls_des3_context* ctx, + const uint8_t* ck, + const uint8_t* iv, + const uint8_t* input, + const uint8_t length, + uint8_t* out); + +void mf_ultralight_3des_decrypt( + mbedtls_des3_context* ctx, + const uint8_t* ck, + const uint8_t* iv, + const uint8_t* input, + const uint8_t length, + uint8_t* out); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c index 5bef2a354c9..b5980b3882c 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener.c @@ -4,16 +4,12 @@ #include #include +#include #define TAG "MfUltralightListener" #define MF_ULTRALIGHT_LISTENER_MAX_TX_BUFF_SIZE (256) -typedef enum { - MfUltralightListenerAccessTypeRead, - MfUltralightListenerAccessTypeWrite, -} MfUltralightListenerAccessType; - typedef struct { uint8_t cmd; size_t cmd_len_bits; @@ -24,31 +20,15 @@ static bool mf_ultralight_listener_check_access( MfUltralightListener* instance, uint16_t start_page, MfUltralightListenerAccessType access_type) { - bool access_success = false; - bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite); - - do { - if(!mf_ultralight_support_feature( - instance->features, MfUltralightFeatureSupportPasswordAuth)) { - access_success = true; - break; - } - if(instance->auth_state != MfUltralightListenerAuthStateSuccess) { - if((instance->config->auth0 <= start_page) && - (instance->config->access.prot || is_write_op)) { - break; - } - } - if(instance->config->access.cfglck && is_write_op) { - uint16_t config_page_start = instance->data->pages_total - 4; - if((start_page == config_page_start) || (start_page == config_page_start + 1)) { - break; - } - } - - access_success = true; - } while(false); - + bool access_success = true; + + if(mf_ultralight_support_feature(instance->features, MfUltralightFeatureSupportAuthenticate)) { + access_success = mf_ultralight_c_check_access( + instance->data, start_page, access_type, instance->auth_state); + } else if(mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportPasswordAuth)) { + access_success = mf_ultralight_common_check_access(instance, start_page, access_type); + } return access_success; } @@ -565,6 +545,82 @@ static MfUltralightCommand return command; } +static MfUltralightCommand + mf_ultralight_c_authenticate_handler_p2(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + FURI_LOG_T(TAG, "CMD_ULC_AUTH_2"); + UNUSED(instance); + do { + if(bit_buffer_get_byte(buffer, 0) != 0xAF || + bit_buffer_get_size_bytes(buffer) != MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE || + !mf_ultralight_3des_key_valid(instance->data)) + break; + + const uint8_t* data = bit_buffer_get_data(buffer) + 1; + const uint8_t* iv = data + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET; + + uint8_t out[MF_ULTRALIGHT_C_AUTH_DATA_SIZE] = {0}; + + const uint8_t* ck = mf_ultralight_3des_get_key(instance->data); + mf_ultralight_3des_decrypt( + &instance->des_context, ck, instance->encB, data, sizeof(out), out); + + uint8_t* rndA = out; + const uint8_t* decoded_shifted_rndB = out + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET; + + mf_ultralight_3des_shift_data(rndA); + mf_ultralight_3des_shift_data(instance->rndB); + if(memcmp(decoded_shifted_rndB, instance->rndB, sizeof(instance->rndB)) == 0) { + instance->auth_state = MfUltralightListenerAuthStateSuccess; + } + + mf_ultralight_3des_encrypt( + &instance->des_context, ck, iv, rndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE, rndA); + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, 0x00); + bit_buffer_append_bytes(instance->tx_buffer, rndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE); + + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + + command = MfUltralightCommandProcessed; + } while(false); + return command; +} + +static MfUltralightCommand + mf_ultralight_c_authenticate_handler_p1(MfUltralightListener* instance, BitBuffer* buffer) { + MfUltralightCommand command = MfUltralightCommandNotProcessedNAK; + FURI_LOG_T(TAG, "CMD_ULC_AUTH_1"); + do { + if(!mf_ultralight_support_feature( + instance->features, MfUltralightFeatureSupportAuthenticate) && + bit_buffer_get_byte(buffer, 1) == 0x00) + break; + + bit_buffer_reset(instance->tx_buffer); + bit_buffer_append_byte(instance->tx_buffer, 0xAF); + + furi_hal_random_fill_buf(instance->rndB, sizeof(instance->rndB)); + + const uint8_t iv[MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE] = {0}; + const uint8_t* ck = mf_ultralight_3des_get_key(instance->data); + + mf_ultralight_3des_encrypt( + &instance->des_context, ck, iv, instance->rndB, sizeof(instance->rndB), instance->encB); + + bit_buffer_append_bytes(instance->tx_buffer, instance->encB, sizeof(instance->encB)); + + iso14443_3a_listener_send_standard_frame( + instance->iso14443_3a_listener, instance->tx_buffer); + command = MfUltralightCommandProcessed; + mf_ultralight_composite_command_set_next( + instance, mf_ultralight_c_authenticate_handler_p2); + } while(false); + return command; +} + static const MfUltralightListenerCmdHandler mf_ultralight_command[] = { { .cmd = MF_ULTRALIGHT_CMD_READ_PAGE, @@ -631,7 +687,11 @@ static const MfUltralightListenerCmdHandler mf_ultralight_command[] = { .cmd_len_bits = 21 * 8, .callback = mf_ultralight_listener_vcsl_handler, }, -}; + { + .cmd = MF_ULTRALIGHT_CMD_AUTH, + .cmd_len_bits = 2 * 8, + .callback = mf_ultralight_c_authenticate_handler_p1, + }}; static void mf_ultralight_listener_prepare_emulation(MfUltralightListener* instance) { MfUltralightData* data = instance->data; @@ -695,6 +755,7 @@ MfUltralightListener* mf_ultralight_listener_alloc( instance->generic_event.protocol = NfcProtocolMfUltralight; instance->generic_event.instance = instance; instance->generic_event.event_data = &instance->mfu_event; + mbedtls_des3_init(&instance->des_context); return instance; } @@ -706,6 +767,7 @@ void mf_ultralight_listener_free(MfUltralightListener* instance) { bit_buffer_free(instance->tx_buffer); furi_string_free(instance->mirror.ascii_mirror_data); + mbedtls_des3_free(&instance->des_context); free(instance); } diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c index 64647492de1..3a9b893aa5d 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.c @@ -576,4 +576,61 @@ bool mf_ultralight_auth_check_password( const MfUltralightAuthPassword* config_pass, const MfUltralightAuthPassword* auth_pass) { return memcmp(config_pass->data, auth_pass->data, sizeof(MfUltralightAuthPassword)) == 0; +} + +bool mf_ultralight_common_check_access( + const MfUltralightListener* instance, + const uint16_t start_page, + const MfUltralightListenerAccessType access_type) { + bool access_success = false; + bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite); + + do { + if(instance->auth_state != MfUltralightListenerAuthStateSuccess) { + if((instance->config->auth0 <= start_page) && + (instance->config->access.prot || is_write_op)) { + break; + } + } + + if(instance->config->access.cfglck && is_write_op) { + uint16_t config_page_start = instance->data->pages_total - 4; + if((start_page == config_page_start) || (start_page == config_page_start + 1)) { + break; + } + } + + access_success = true; + } while(false); + + return access_success; +} + +bool mf_ultralight_c_check_access( + const MfUltralightData* data, + const uint16_t start_page, + const MfUltralightListenerAccessType access_type, + const MfUltralightListenerAuthState auth_state) { + bool access_success = false; + bool is_write_op = (access_type == MfUltralightListenerAccessTypeWrite); + + do { + if(start_page >= 44) break; + + const uint8_t auth0 = data->page[42].data[0]; + const uint8_t auth1 = data->page[43].data[0] & 0x01; + + if(auth0 < 0x03 || auth0 >= 0x30 || auth_state == MfUltralightListenerAuthStateSuccess) { + access_success = true; + break; + } + + if((auth0 <= start_page) && ((auth1 == 0) || (auth1 == 1 || is_write_op))) { //-V560 + break; + } + + access_success = true; + } while(false); + + return access_success; } \ No newline at end of file diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h index ba448d0879c..c43658eddbb 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_listener_i.h @@ -13,6 +13,11 @@ typedef enum { MfUltralightListenerAuthStateSuccess, } MfUltralightListenerAuthState; +typedef enum { + MfUltralightListenerAccessTypeRead, + MfUltralightListenerAccessTypeWrite, +} MfUltralightListenerAccessType; + typedef enum { MfUltralightCommandNotFound, MfUltralightCommandProcessed, @@ -63,6 +68,9 @@ struct MfUltralightListener { bool single_counter_increased; MfUltralightMirrorMode mirror; MfUltralightListenerCompositeCommandContext composite_cmd; + mbedtls_des3_context des_context; + uint8_t rndB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE]; + uint8_t encB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE]; void* context; }; @@ -118,6 +126,17 @@ bool mf_ultralight_auth_limit_check_and_update(MfUltralightListener* instance, b bool mf_ultralight_auth_check_password( const MfUltralightAuthPassword* config_pass, const MfUltralightAuthPassword* auth_pass); + +bool mf_ultralight_common_check_access( + const MfUltralightListener* instance, + const uint16_t start_page, + const MfUltralightListenerAccessType access_type); + +bool mf_ultralight_c_check_access( + const MfUltralightData* data, + const uint16_t start_page, + const MfUltralightListenerAccessType access_type, + const MfUltralightListenerAuthState auth_state); #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c index 52b445859e7..0e5ff001162 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.c @@ -3,6 +3,7 @@ #include #include +#include #define TAG "MfUltralightPoller" @@ -180,7 +181,7 @@ MfUltralightPoller* mf_ultralight_poller_alloc(Iso14443_3aPoller* iso14443_3a_po instance->general_event.protocol = NfcProtocolMfUltralight; instance->general_event.event_data = &instance->mfu_event; instance->general_event.instance = instance; - + mbedtls_des3_init(&instance->des_context); return instance; } @@ -193,6 +194,7 @@ void mf_ultralight_poller_free(MfUltralightPoller* instance) { bit_buffer_free(instance->tx_buffer); bit_buffer_free(instance->rx_buffer); mf_ultralight_free(instance->data); + mbedtls_des3_free(&instance->des_context); free(instance); } @@ -258,7 +260,7 @@ static NfcCommand mf_ultralight_poller_handler_read_version(MfUltralightPoller* } static NfcCommand mf_ultralight_poller_handler_check_ultralight_c(MfUltralightPoller* instance) { - instance->error = mf_ultralight_poller_authenticate(instance); + instance->error = mf_ultralight_poller_authentication_test(instance); if(instance->error == MfUltralightErrorNone) { FURI_LOG_D(TAG, "Ultralight C detected"); instance->data->type = MfUltralightTypeMfulC; @@ -315,6 +317,10 @@ static NfcCommand mf_ultralight_poller_handler_read_signature(MfUltralightPoller } } else { FURI_LOG_D(TAG, "Skip reading signature"); + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportAuthenticate)) { + next_state = MfUltralightPollerStateAuthMfulC; + } } instance->state = next_state; @@ -436,6 +442,50 @@ static NfcCommand mf_ultralight_poller_handler_auth(MfUltralightPoller* instance return command; } +static NfcCommand mf_ultralight_poller_handler_auth_ultralight_c(MfUltralightPoller* instance) { + NfcCommand command = NfcCommandContinue; + FURI_LOG_D(TAG, "MfulC auth"); + if(mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportAuthenticate)) { + instance->mfu_event.type = MfUltralightPollerEventTypeAuthRequest; + + command = instance->callback(instance->general_event, instance->context); + if(!instance->mfu_event.data->auth_context.skip_auth) { + FURI_LOG_D(TAG, "Trying to authenticate with 3des key"); + instance->auth_context.tdes_key = instance->mfu_event.data->auth_context.tdes_key; + do { + uint8_t output[MF_ULTRALIGHT_C_AUTH_DATA_SIZE]; + uint8_t RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; + furi_hal_random_fill_buf(RndA, sizeof(RndA)); + instance->error = mf_ultralight_poller_authenticate_start(instance, RndA, output); + if(instance->error != MfUltralightErrorNone) break; + + uint8_t decoded_shifted_RndA[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; + const uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET; + instance->error = mf_ultralight_poller_authenticate_end( + instance, RndB, output, decoded_shifted_RndA); + if(instance->error != MfUltralightErrorNone) break; + + mf_ultralight_3des_shift_data(RndA); + instance->auth_context.auth_success = + (memcmp(RndA, decoded_shifted_RndA, sizeof(decoded_shifted_RndA)) == 0); + + if(instance->auth_context.auth_success) { + FURI_LOG_D(TAG, "Auth success"); + } + } while(false); + + if(instance->error != MfUltralightErrorNone || !instance->auth_context.auth_success) { + FURI_LOG_D(TAG, "Auth failed"); + iso14443_3a_poller_halt(instance->iso14443_3a_poller); + } + } + } + instance->state = MfUltralightPollerStateReadPages; + + return command; +} + static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* instance) { MfUltralightPageReadCommandData data = {}; uint16_t start_page = instance->pages_read; @@ -455,8 +505,9 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in instance->error = mf_ultralight_poller_read_page(instance, start_page, &data); } + const uint8_t read_cnt = instance->data->type == MfUltralightTypeMfulC ? 1 : 4; if(instance->error == MfUltralightErrorNone) { - for(size_t i = 0; i < 4; i++) { + for(size_t i = 0; i < read_cnt; i++) { if(start_page + i < instance->pages_total) { FURI_LOG_D(TAG, "Read page %d success", start_page + i); instance->data->page[start_page + i] = data.page[i]; @@ -468,11 +519,16 @@ static NfcCommand mf_ultralight_poller_handler_read_pages(MfUltralightPoller* in instance->state = MfUltralightPollerStateReadCounters; } } else { - FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read); - if(instance->pages_read) { - instance->state = MfUltralightPollerStateReadCounters; + if(instance->data->type == MfUltralightTypeMfulC && + !mf_ultralight_3des_key_valid(instance->data)) { + instance->state = MfUltralightPollerStateCheckMfulCAuthStatus; } else { - instance->state = MfUltralightPollerStateReadFailed; + FURI_LOG_D(TAG, "Read page %d failed", instance->pages_read); + if(instance->pages_read) { + instance->state = MfUltralightPollerStateReadCounters; + } else { + instance->state = MfUltralightPollerStateReadFailed; + } } } @@ -524,6 +580,31 @@ static NfcCommand mf_ultralight_poller_handler_try_default_pass(MfUltralightPoll return NfcCommandContinue; } +static NfcCommand + mf_ultralight_poller_handler_check_mfuc_auth_status(MfUltralightPoller* instance) { + instance->state = MfUltralightPollerStateReadSuccess; + + do { + if(!mf_ultralight_support_feature( + instance->feature_set, MfUltralightFeatureSupportAuthenticate)) + break; + + if(!instance->auth_context.auth_success) { + FURI_LOG_D(TAG, "Skip 3des key populating"); + break; + } + + memcpy( + &instance->data->page[44], + instance->auth_context.tdes_key.data, + MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE); + instance->data->pages_read = instance->pages_total; + instance->pages_read = instance->pages_total; + } while(false); + + return NfcCommandContinue; +} + static NfcCommand mf_ultralight_poller_handler_read_fail(MfUltralightPoller* instance) { FURI_LOG_D(TAG, "Read Failed"); iso14443_3a_poller_halt(instance->iso14443_3a_poller); @@ -663,6 +744,9 @@ static const MfUltralightPollerReadHandler mf_ultralight_poller_handler_read_tearing_flags, [MfUltralightPollerStateAuth] = mf_ultralight_poller_handler_auth, [MfUltralightPollerStateTryDefaultPass] = mf_ultralight_poller_handler_try_default_pass, + [MfUltralightPollerStateCheckMfulCAuthStatus] = + mf_ultralight_poller_handler_check_mfuc_auth_status, + [MfUltralightPollerStateAuthMfulC] = mf_ultralight_poller_handler_auth_ultralight_c, [MfUltralightPollerStateReadPages] = mf_ultralight_poller_handler_read_pages, [MfUltralightPollerStateReadFailed] = mf_ultralight_poller_handler_read_fail, [MfUltralightPollerStateReadSuccess] = mf_ultralight_poller_handler_read_success, diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h index 2343be089bf..e5001732446 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller.h @@ -42,6 +42,7 @@ typedef enum { */ typedef struct { MfUltralightAuthPassword password; /**< Password to be used for authentication. */ + MfUltralightC3DesAuthKey tdes_key; MfUltralightAuthPack pack; /**< Pack received on successfull authentication. */ bool auth_success; /**< Set to true if authentication succeeded, false otherwise. */ bool skip_auth; /**< Set to true if authentication should be skipped, false otherwise. */ @@ -85,12 +86,33 @@ MfUltralightError mf_ultralight_poller_auth_pwd( * * Must ONLY be used inside the callback function. * - * This function now is used only to identify Mf Ultralight C cards. + * This function is used to start authentication process for Ultralight C cards. * * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] RndA Randomly generated block which is required for authentication process. + * @param[out] output Authentication encryption result. * @return MfUltralightErrorNone if card supports authentication command, an error code on otherwise. */ -MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance); +MfUltralightError mf_ultralight_poller_authenticate_start( + MfUltralightPoller* instance, + const uint8_t* RndA, + uint8_t* output); + +/** + * @brief End authentication procedure + * + * This function is used to end authentication process for Ultralight C cards. + * + * @param[in, out] instance pointer to the instance to be used in the transaction. + * @param[in] RndB Block received from the card (card generates it randomly) which is required for authentication process. + * @param[in] request Contains data of RndA + RndB', where RndB' is decoded and shifted RndB received from the card on previous step. + * @param[out] response Must return RndA' which an encrypted shifted RndA value received from the card and decrypted by this function. +*/ +MfUltralightError mf_ultralight_poller_authenticate_end( + MfUltralightPoller* instance, + const uint8_t* RndB, + const uint8_t* request, + uint8_t* response); /** * @brief Read page from card. diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c index cc3433f7318..141ab6c46d0 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.c @@ -62,11 +62,17 @@ MfUltralightError mf_ultralight_poller_auth_pwd( return ret; } -MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance) { +static MfUltralightError mf_ultralight_poller_send_authenticate_cmd( + MfUltralightPoller* instance, + const uint8_t* cmd, + const uint8_t length, + const bool initial_cmd, + uint8_t* response) { furi_check(instance); + furi_check(cmd); + furi_check(response); - uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00}; - bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd)); + bit_buffer_copy_bytes(instance->tx_buffer, cmd, length); MfUltralightError ret = MfUltralightErrorNone; Iso14443_3aError error = Iso14443_3aErrorNone; @@ -80,12 +86,104 @@ MfUltralightError mf_ultralight_poller_authenticate(MfUltralightPoller* instance ret = mf_ultralight_process_error(error); break; } - if((bit_buffer_get_size_bytes(instance->rx_buffer) != MF_ULTRALIGHT_AUTH_RESPONSE_SIZE) && - (bit_buffer_get_byte(instance->rx_buffer, 0) != 0xAF)) { + + const uint8_t expected_response_code = initial_cmd ? 0xAF : 0x00; + if((bit_buffer_get_byte(instance->rx_buffer, 0) != expected_response_code) || + (bit_buffer_get_size_bytes(instance->rx_buffer) != + MF_ULTRALIGHT_C_AUTH_RESPONSE_SIZE)) { ret = MfUltralightErrorAuth; break; } - //Save encrypted PICC random number RndB here if needed + + memcpy( + response, + bit_buffer_get_data(instance->rx_buffer) + 1, + MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE); + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_authentication_test(MfUltralightPoller* instance) { + furi_check(instance); + + uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00}; + uint8_t dummy[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE]; + return mf_ultralight_poller_send_authenticate_cmd( + instance, auth_cmd, sizeof(auth_cmd), true, dummy); +} + +MfUltralightError mf_ultralight_poller_authenticate_start( + MfUltralightPoller* instance, + const uint8_t* RndA, + uint8_t* output) { + furi_check(instance); + furi_check(RndA); + furi_check(output); + + MfUltralightError ret = MfUltralightErrorNone; + do { + uint8_t encRndB[MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE] = {0}; + uint8_t auth_cmd[2] = {MF_ULTRALIGHT_CMD_AUTH, 0x00}; + ret = mf_ultralight_poller_send_authenticate_cmd( + instance, auth_cmd, sizeof(auth_cmd), true, encRndB /* instance->encRndB */); + + if(ret != MfUltralightErrorNone) break; + + uint8_t iv[MF_ULTRALIGHT_C_AUTH_IV_BLOCK_SIZE] = {0}; + uint8_t* RndB = output + MF_ULTRALIGHT_C_AUTH_RND_B_BLOCK_OFFSET; + mf_ultralight_3des_decrypt( + &instance->des_context, + instance->mfu_event.data->auth_context.tdes_key.data, + iv, + encRndB, + sizeof(encRndB), + RndB); + mf_ultralight_3des_shift_data(RndB); + + memcpy(output, RndA, MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE); + + mf_ultralight_3des_encrypt( + &instance->des_context, + instance->mfu_event.data->auth_context.tdes_key.data, + encRndB, + output, + MF_ULTRALIGHT_C_AUTH_DATA_SIZE, + output); + + } while(false); + + return ret; +} + +MfUltralightError mf_ultralight_poller_authenticate_end( + MfUltralightPoller* instance, + const uint8_t* RndB, + const uint8_t* request, + uint8_t* response) { + furi_check(instance); + furi_check(RndB); + furi_check(request); + furi_check(response); + + uint8_t auth_cmd[MF_ULTRALIGHT_C_ENCRYPTED_PACK_SIZE] = {0xAF}; //-V1009 + memcpy(&auth_cmd[1], request, MF_ULTRALIGHT_C_AUTH_DATA_SIZE); + bit_buffer_copy_bytes(instance->tx_buffer, auth_cmd, sizeof(auth_cmd)); + + MfUltralightError ret = MfUltralightErrorNone; + do { + ret = mf_ultralight_poller_send_authenticate_cmd( + instance, auth_cmd, sizeof(auth_cmd), false, response); + + if(ret != MfUltralightErrorNone) break; + + mf_ultralight_3des_decrypt( + &instance->des_context, + instance->mfu_event.data->auth_context.tdes_key.data, + RndB, + bit_buffer_get_data(instance->rx_buffer) + 1, + MF_ULTRALIGHT_C_AUTH_RND_BLOCK_SIZE, + response); } while(false); return ret; diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h index 3f8645fe73a..1f395ddd200 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_i.h @@ -58,8 +58,10 @@ typedef enum { MfUltralightPollerStateReadCounters, MfUltralightPollerStateReadTearingFlags, MfUltralightPollerStateAuth, + MfUltralightPollerStateAuthMfulC, MfUltralightPollerStateReadPages, MfUltralightPollerStateTryDefaultPass, + MfUltralightPollerStateCheckMfulCAuthStatus, MfUltralightPollerStateReadFailed, MfUltralightPollerStateReadSuccess, MfUltralightPollerStateRequestWriteData, @@ -87,6 +89,7 @@ struct MfUltralightPoller { uint8_t tearing_flag_total; uint16_t current_page; MfUltralightError error; + mbedtls_des3_context des_context; NfcGenericEvent general_event; MfUltralightPollerEvent mfu_event; @@ -110,6 +113,8 @@ bool mf_ultralight_poller_ntag_i2c_addr_lin_to_tag( uint8_t* tag, uint8_t* pages_left); +MfUltralightError mf_ultralight_poller_authentication_test(MfUltralightPoller* instance); + #ifdef __cplusplus } #endif diff --git a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c index 157e2bdef7b..9958dc50de5 100644 --- a/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c +++ b/lib/nfc/protocols/mf_ultralight/mf_ultralight_poller_sync.c @@ -251,6 +251,12 @@ static NfcCommand mf_ultralight_poller_read_callback(NfcGenericEvent event, void command = NfcCommandStop; } else if(mfu_event->type == MfUltralightPollerEventTypeAuthRequest) { mfu_event->data->auth_context.skip_auth = true; + if(mf_ultralight_support_feature( + mfu_poller->feature_set, MfUltralightFeatureSupportAuthenticate)) { + mfu_event->data->auth_context.skip_auth = false; + memset( + mfu_poller->auth_context.tdes_key.data, 0x00, MF_ULTRALIGHT_C_AUTH_DES_KEY_SIZE); + } } if(command == NfcCommandStop) { diff --git a/targets/f18/api_symbols.csv b/targets/f18/api_symbols.csv index 48c1ab0ab64..23e098a91de 100644 --- a/targets/f18/api_symbols.csv +++ b/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,67.2,, +Version,+,68.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, Header,+,applications/services/cli/cli.h,, diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d7782ee91bf..6cccce1e6dc 100644 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,67.2,, +Version,+,68.0,, Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/bt/bt_service/bt_keys_storage.h,, @@ -2574,6 +2574,11 @@ Function,+,mf_plus_reset,void,MfPlusData* Function,+,mf_plus_save,_Bool,"const MfPlusData*, FlipperFormat*" Function,+,mf_plus_set_uid,_Bool,"MfPlusData*, const uint8_t*, size_t" Function,+,mf_plus_verify,_Bool,"MfPlusData*, const FuriString*" +Function,+,mf_ultralight_3des_decrypt,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*" +Function,+,mf_ultralight_3des_encrypt,void,"mbedtls_des3_context*, const uint8_t*, const uint8_t*, const uint8_t*, const uint8_t, uint8_t*" +Function,+,mf_ultralight_3des_get_key,const uint8_t*,const MfUltralightData* +Function,+,mf_ultralight_3des_key_valid,_Bool,const MfUltralightData* +Function,+,mf_ultralight_3des_shift_data,void,uint8_t* Function,+,mf_ultralight_alloc,MfUltralightData*, Function,+,mf_ultralight_copy,void,"MfUltralightData*, const MfUltralightData*" Function,+,mf_ultralight_detect_protocol,_Bool,const Iso14443_3aData* @@ -2594,7 +2599,8 @@ Function,+,mf_ultralight_is_equal,_Bool,"const MfUltralightData*, const MfUltral Function,+,mf_ultralight_is_page_pwd_or_pack,_Bool,"MfUltralightType, uint16_t" Function,+,mf_ultralight_load,_Bool,"MfUltralightData*, FlipperFormat*, uint32_t" Function,+,mf_ultralight_poller_auth_pwd,MfUltralightError,"MfUltralightPoller*, MfUltralightPollerAuthContext*" -Function,+,mf_ultralight_poller_authenticate,MfUltralightError,MfUltralightPoller* +Function,+,mf_ultralight_poller_authenticate_end,MfUltralightError,"MfUltralightPoller*, const uint8_t*, const uint8_t*, uint8_t*" +Function,+,mf_ultralight_poller_authenticate_start,MfUltralightError,"MfUltralightPoller*, const uint8_t*, uint8_t*" Function,+,mf_ultralight_poller_read_counter,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightCounter*" Function,+,mf_ultralight_poller_read_page,MfUltralightError,"MfUltralightPoller*, uint8_t, MfUltralightPageReadCommandData*" Function,+,mf_ultralight_poller_read_page_from_sector,MfUltralightError,"MfUltralightPoller*, uint8_t, uint8_t, MfUltralightPageReadCommandData*"