Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FL-3835] Ultralight C authentication with des key #3720

Merged
merged 30 commits into from
Jul 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2ce1bf6
Update api_symbols.csv
RebornedBrain Jun 10, 2024
f61e773
Ultralight C 3des implementation added
RebornedBrain Jun 13, 2024
e2471f0
Access check for Ultralight cards is now splitted into 2 functions on…
RebornedBrain Jun 13, 2024
b7d3086
Ultralight C authentication command handlers added
RebornedBrain Jun 13, 2024
7d7bd11
Update api_symbols.csv and api_symbols.csv
RebornedBrain Jun 13, 2024
37ab0b5
Length added to ultralight encrypt function
RebornedBrain Jun 17, 2024
fc8abff
New structure for storing 3des key added
RebornedBrain Jun 17, 2024
788df00
Reseting of 3des_key added
RebornedBrain Jun 17, 2024
62e7fc5
des_context init/deinit added to poller
RebornedBrain Jun 17, 2024
f5301c4
New poller step for ultralight c auth added
RebornedBrain Jun 17, 2024
80c57e8
Added ultralight c des key to application
RebornedBrain Jun 17, 2024
bf5ce42
Renamed felica unlock scenes to more generic des auth scenes, because…
RebornedBrain Jun 17, 2024
bd9e197
Show different menus for different ultralight card types
RebornedBrain Jun 17, 2024
876371c
Update api_symbols.csv and api_symbols.csv
RebornedBrain Jun 17, 2024
fd4e3d0
Some macro defines added
RebornedBrain Jun 18, 2024
636c143
Different amount of pages will be now read for ultralight C and others
RebornedBrain Jun 18, 2024
2f0787c
New unit test for ultralight C
RebornedBrain Jun 18, 2024
782799e
Some comments and macro replacements
RebornedBrain Jun 19, 2024
3fbd4f0
Merge branch 'dev'
RebornedBrain Jun 19, 2024
6cb8f3c
Merge branch 'dev' into reborned/unlock_ulc
hedger Jun 21, 2024
3c117c6
New function added to api
RebornedBrain Jun 25, 2024
f1721a1
Now all data read checks mfulC separately
RebornedBrain Jun 25, 2024
009a829
Adjusted listener to handle missing 3des_key properly
RebornedBrain Jun 25, 2024
44d2a0a
Now poller populates 3des_key after reading with auth to card data
RebornedBrain Jun 25, 2024
88578f8
Merge branch 'dev'
RebornedBrain Jun 26, 2024
50ad005
Merge branch 'dev' into reborned/unlock_ulc
gornekich Jul 2, 2024
104d2fa
Merge branch 'dev' into reborned/unlock_ulc
gornekich Jul 2, 2024
4b657a8
Nfc: rename _3des_key to tdes_key
skotopes Jul 2, 2024
a26498f
Bump API Symbols
skotopes Jul 2, 2024
693ab5f
Mute PVS Warnings
skotopes Jul 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions applications/debug/unit_tests/tests/nfc/nfc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
}
Expand Down Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions applications/main/nfc/helpers/mf_ultralight_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down
1 change: 1 addition & 0 deletions applications/main/nfc/helpers/mf_ultralight_auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ typedef enum {
typedef struct {
MfUltralightAuthType type;
MfUltralightAuthPassword password;
MfUltralightC3DesAuthKey tdes_key;
MfUltralightAuthPack pack;
} MfUltralightAuth;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions applications/main/nfc/scenes/nfc_scene_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
Expand All @@ -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;
Expand All @@ -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);
Expand Down
74 changes: 68 additions & 6 deletions lib/nfc/protocols/mf_ultralight/mf_ultralight.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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);
}
Loading