Skip to content

Commit

Permalink
Merge pull request #17 from bettse/mfc
Browse files Browse the repository at this point in the history
MFC SE
  • Loading branch information
bettse authored Apr 30, 2024
2 parents 498f971 + 70548ac commit 4321676
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 16 deletions.
2 changes: 1 addition & 1 deletion application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ App(
requires=[
"gui", "storage", "nfc",
],
stack_size=7 * 1024,
stack_size=5 * 1024,
order=20,
sources=[
"*.c",
Expand Down
190 changes: 180 additions & 10 deletions sam_api.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#include "sam_api.h"
#include <toolbox/path.h>
#include <bit_lib/bit_lib.h>

#define TAG "SAMAPI"

Expand Down Expand Up @@ -728,6 +729,161 @@ void seader_iso14443a_transmit(
bit_buffer_free(rx_buffer);
}

/* Assumes this is called in the context of the NFC API callback */
#define MF_CLASSIC_FWT_FC (60000)
void seader_mfc_transmit(
Seader* seader,
MfClassicPoller* mfc_poller,
uint8_t* buffer,
size_t len,
uint16_t timeout,
uint8_t format[3]) {
UNUSED(timeout);

furi_assert(seader);
furi_assert(buffer);
furi_assert(mfc_poller);
SeaderWorker* seader_worker = seader->worker;
SeaderUartBridge* seader_uart = seader_worker->uart;

BitBuffer* tx_buffer = bit_buffer_alloc(len);
BitBuffer* rx_buffer = bit_buffer_alloc(SEADER_POLLER_MAX_BUFFER_SIZE);

do {
if(format[0] == 0x00 && format[1] == 0xC0 && format[2] == 0x00) {
bit_buffer_append_bytes(tx_buffer, buffer, len);
MfClassicError error =
mf_classic_poller_send_frame(mfc_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "mf_classic_poller_send_frame error %d", error);
seader_worker->stage = SeaderPollerEventTypeFail;
break;
}
} else if(
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x40) ||
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x24) ||
(format[0] == 0x00 && format[1] == 0x00 && format[2] == 0x44)) {
memset(display, 0, sizeof(display));
for(uint8_t i = 0; i < len; i++) {
snprintf(display + (i * 2), sizeof(display), "%02x", buffer[i]);
}
FURI_LOG_D(TAG, "NFC Send with parity %d: %s", len, display);

// Only handles message up to 8 data bytes
uint8_t tx_parity = 0;
uint8_t len_without_parity = len - 1;

// Don't forget to swap the bits of buffer[8]
for(size_t i = 0; i < len; i++) {
bit_lib_reverse_bits(buffer + i, 0, 8);
}

// Pull out parity bits
for(size_t i = 0; i < len_without_parity; i++) {
bool val = bit_lib_get_bit(buffer + i + 1, i);
bit_lib_set_bit(&tx_parity, i, val);
}

for(size_t i = 0; i < len_without_parity; i++) {
buffer[i] = (buffer[i] << i) | (buffer[i + 1] >> (8 - i));
}
bit_buffer_append_bytes(tx_buffer, buffer, len_without_parity);

for(size_t i = 0; i < len_without_parity; i++) {
bit_lib_reverse_bits(buffer + i, 0, 8);
bit_buffer_set_byte_with_parity(
tx_buffer, i, buffer[i], bit_lib_get_bit(&tx_parity, i));
}

memset(display, 0, sizeof(display));
for(uint8_t i = 0; i < bit_buffer_get_size_bytes(tx_buffer); i++) {
snprintf(
display + (i * 2), sizeof(display), "%02x", bit_buffer_get_byte(tx_buffer, i));
}
FURI_LOG_D(
TAG,
"NFC Send without parity %d: %s [%02x]",
bit_buffer_get_size_bytes(tx_buffer),
display,
tx_parity);

MfClassicError error = mf_classic_poller_send_custom_parity_frame(
mfc_poller, tx_buffer, rx_buffer, MF_CLASSIC_FWT_FC);
if(error != MfClassicErrorNone) {
FURI_LOG_W(TAG, "mf_classic_poller_send_encrypted_frame error %d", error);
seader_worker->stage = SeaderPollerEventTypeFail;
break;
}

size_t length = bit_buffer_get_size_bytes(rx_buffer);
const uint8_t* rx_parity = bit_buffer_get_parity(rx_buffer);

memset(display, 0, sizeof(display));
for(uint8_t i = 0; i < length; i++) {
snprintf(
display + (i * 2), sizeof(display), "%02x", bit_buffer_get_byte(rx_buffer, i));
}
FURI_LOG_D(
TAG, "NFC Response without parity %d: %s [%02x]", length, display, rx_parity[0]);

uint8_t with_parity[SEADER_POLLER_MAX_BUFFER_SIZE];
memset(with_parity, 0, sizeof(with_parity));

for(size_t i = 0; i < length; i++) {
uint8_t b = bit_buffer_get_byte(rx_buffer, i);
bit_lib_reverse_bits(&b, 0, 8);
bit_buffer_set_byte(rx_buffer, i, b);
}

length = length + (length / 8) + 1;

uint8_t parts = 1 + length / 9;
for(size_t p = 0; p < parts; p++) {
uint8_t doffset = p * 9;
uint8_t soffset = p * 8;

for(size_t i = 0; i < 9; i++) {
with_parity[i + doffset] = bit_buffer_get_byte(rx_buffer, i + soffset) >> i;
if(i > 0) {
with_parity[i + doffset] |= bit_buffer_get_byte(rx_buffer, i + soffset - 1)
<< (9 - i);
}

if(i > 0) {
bool val = bit_lib_get_bit(rx_parity, i - 1);
bit_lib_set_bit(with_parity + i, i - 1, val);
}
}
}

for(size_t i = 0; i < length; i++) {
bit_lib_reverse_bits(with_parity + i, 0, 8);
}

bit_buffer_copy_bytes(rx_buffer, with_parity, length);

memset(display, 0, sizeof(display));
for(uint8_t i = 0; i < length; i++) {
snprintf(
display + (i * 2), sizeof(display), "%02x", bit_buffer_get_byte(rx_buffer, i));
}
FURI_LOG_D(
TAG, "NFC Response with parity %d: %s [%02x]", length, display, rx_parity[0]);

} else {
FURI_LOG_W(TAG, "UNHANDLED FORMAT");
}

seader_send_nfc_rx(
seader_uart,
(uint8_t*)bit_buffer_get_data(rx_buffer),
bit_buffer_get_size_bytes(rx_buffer));

} while(false);
bit_buffer_free(tx_buffer);
bit_buffer_free(rx_buffer);
}

void seader_parse_nfc_command_transmit(
Seader* seader,
NFCSend_t* nfcSend,
Expand Down Expand Up @@ -757,13 +913,23 @@ void seader_parse_nfc_command_transmit(
seader_iso15693_transmit(
seader, spc->picopass_poller, nfcSend->data.buf, nfcSend->data.size);
} else if(frameProtocol == FrameProtocol_nfc) {
seader_iso14443a_transmit(
seader,
spc->iso14443_4a_poller,
nfcSend->data.buf,
nfcSend->data.size,
(uint16_t)timeOut,
nfcSend->format->buf);
if(spc->iso14443_4a_poller) {
seader_iso14443a_transmit(
seader,
spc->iso14443_4a_poller,
nfcSend->data.buf,
nfcSend->data.size,
(uint16_t)timeOut,
nfcSend->format->buf);
} else if(spc->mfc_poller) {
seader_mfc_transmit(
seader,
spc->mfc_poller,
nfcSend->data.buf,
nfcSend->data.size,
(uint16_t)timeOut,
nfcSend->format->buf);
}
} else {
FURI_LOG_W(TAG, "unknown frame protocol %lx", frameProtocol);
}
Expand Down Expand Up @@ -911,18 +1077,22 @@ NfcCommand seader_worker_card_detect(
OCTET_STRING_t atqa_string = {.buf = atqa, .size = 2};
uint8_t protocol_bytes[] = {0x00, 0x00};

if(sak == 0 && atqa == NULL) {
if(sak == 0 && atqa == NULL) { // picopass
protocol_bytes[1] = FrameProtocol_iclass;
OCTET_STRING_fromBuf(
&cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));
memcpy(credential->diversifier, uid, uid_len);
credential->diversifier_len = uid_len;
credential->isDesfire = false;
} else {
} else if(atqa == 0) { // MFC
protocol_bytes[1] = FrameProtocol_nfc;
OCTET_STRING_fromBuf(
&cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));
cardDetails->sak = &sak_string;
} else { // type 4
protocol_bytes[1] = FrameProtocol_nfc;
OCTET_STRING_fromBuf(
&cardDetails->protocol, (const char*)protocol_bytes, sizeof(protocol_bytes));

cardDetails->sak = &sak_string;
cardDetails->atqa = &atqa_string;
credential->isDesfire = seader_mf_df_check_card_type(atqa[0], atqa[1], sak);
Expand Down
1 change: 1 addition & 0 deletions scenes/seader_scene_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ ADD_SCENE(seader, credential_info, CredentialInfo)
ADD_SCENE(seader, sam_info, SamInfo)
ADD_SCENE(seader, virtual_credential, VirtualCredential)
ADD_SCENE(seader, formats, Formats)
ADD_SCENE(seader, read_mfc, ReadMfc)
65 changes: 65 additions & 0 deletions scenes/seader_scene_read_mfc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "../seader_i.h"
#include <dolphin/dolphin.h>

#define TAG "SceneReadNfc"

void seader_scene_read_mfc_on_enter(void* context) {
Seader* seader = context;
dolphin_deed(DolphinDeedNfcRead);

// Setup view
Popup* popup = seader->popup;
popup_set_header(popup, "Detecting\nMFC\ncard", 68, 30, AlignLeft, AlignTop);
popup_set_icon(popup, 0, 3, &I_RFIDDolphinReceive_97x61);

// Start worker
view_dispatcher_switch_to_view(seader->view_dispatcher, SeaderViewPopup);

seader->poller = nfc_poller_alloc(seader->nfc, NfcProtocolMfClassic);

seader->worker->stage = SeaderPollerEventTypeCardDetect;
seader_credential_clear(seader->credential);
seader->credential->type = SeaderCredentialTypeMifareClassic;

FURI_LOG_W(TAG, "Start poller");
nfc_poller_start(seader->poller, seader_worker_poller_callback_mfc, seader);

seader_blink_start(seader);
}

bool seader_scene_read_mfc_on_event(void* context, SceneManagerEvent event) {
Seader* seader = context;
bool consumed = false;

if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SeaderCustomEventWorkerExit) {
seader->credential->type = SeaderCredentialTypeMifareClassic;
scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
consumed = true;
} else if(event.event == SeaderCustomEventPollerSuccess) {
seader->credential->type = SeaderCredentialTypeMifareClassic;
scene_manager_next_scene(seader->scene_manager, SeaderSceneReadCardSuccess);
consumed = true;
}
} else if(event.type == SceneManagerEventTypeBack) {
scene_manager_search_and_switch_to_previous_scene(
seader->scene_manager, SeaderSceneSamPresent);
consumed = true;
}

return consumed;
}

void seader_scene_read_mfc_on_exit(void* context) {
Seader* seader = context;

if(seader->poller) {
nfc_poller_stop(seader->poller);
nfc_poller_free(seader->poller);
}

// Clear view
popup_reset(seader->popup);

seader_blink_stop(seader);
}
12 changes: 12 additions & 0 deletions scenes/seader_scene_sam_present.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
enum SubmenuIndex {
SubmenuIndexReadPicopass,
SubmenuIndexRead14a,
SubmenuIndexReadMfc,
SubmenuIndexSaved,
SubmenuIndexSamInfo,
SubmenuIndexFwVersion,
Expand Down Expand Up @@ -33,6 +34,12 @@ void seader_scene_sam_present_on_update(void* context) {
SubmenuIndexRead14a,
seader_scene_sam_present_submenu_callback,
seader);
submenu_add_item(
submenu,
"Read MFC",
SubmenuIndexReadMfc,
seader_scene_sam_present_submenu_callback,
seader);
submenu_add_item(
submenu, "Saved", SubmenuIndexSaved, seader_scene_sam_present_submenu_callback, seader);

Expand Down Expand Up @@ -75,6 +82,11 @@ bool seader_scene_sam_present_on_event(void* context, SceneManagerEvent event) {
seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexRead14a);
scene_manager_next_scene(seader->scene_manager, SeaderSceneRead14a);
consumed = true;
} else if(event.event == SubmenuIndexReadMfc) {
scene_manager_set_scene_state(
seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexReadMfc);
scene_manager_next_scene(seader->scene_manager, SeaderSceneReadMfc);
consumed = true;
} else if(event.event == SubmenuIndexSamInfo) {
scene_manager_set_scene_state(
seader->scene_manager, SeaderSceneSamPresent, SubmenuIndexSamInfo);
Expand Down
2 changes: 1 addition & 1 deletion seader_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <furi.h>
#include <furi_hal.h>

#define SEADER_UART_RX_BUF_SIZE (256)
#define SEADER_UART_RX_BUF_SIZE (128)

typedef struct {
uint8_t uart_ch;
Expand Down
4 changes: 1 addition & 3 deletions seader_i.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@
#include <input/input.h>

#include <lib/nfc/nfc.h>
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a.h>

#include <nfc/nfc_poller.h>

#include <nfc/nfc_device.h>
#include <nfc/helpers/nfc_data_generator.h>

Expand Down Expand Up @@ -124,6 +121,7 @@ struct Seader {

struct SeaderPollerContainer {
Iso14443_4aPoller* iso14443_4a_poller;
MfClassicPoller* mfc_poller;
PicopassPoller* picopass_poller;
};

Expand Down
Loading

0 comments on commit 4321676

Please sign in to comment.