mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-15 00:08:11 -07:00
Merge branch 'dev' into ntag-auto-pwd-capture
This commit is contained in:
+19
-19
@@ -2,27 +2,27 @@ Import("env")
|
||||
|
||||
env.Append(
|
||||
LINT_SOURCES=[
|
||||
"lib/app-scened-template",
|
||||
"lib/digital_signal",
|
||||
"lib/drivers",
|
||||
"lib/flipper_format",
|
||||
"lib/infrared",
|
||||
"lib/nfc",
|
||||
"lib/one_wire",
|
||||
"lib/ST25RFAL002",
|
||||
"lib/subghz",
|
||||
"lib/toolbox",
|
||||
"lib/u8g2",
|
||||
"lib/update_util",
|
||||
"lib/print",
|
||||
Dir("app-scened-template"),
|
||||
Dir("digital_signal"),
|
||||
Dir("drivers"),
|
||||
Dir("flipper_format"),
|
||||
Dir("infrared"),
|
||||
Dir("nfc"),
|
||||
Dir("one_wire"),
|
||||
Dir("ST25RFAL002"),
|
||||
Dir("subghz"),
|
||||
Dir("toolbox"),
|
||||
Dir("u8g2"),
|
||||
Dir("update_util"),
|
||||
Dir("print"),
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/one_wire/one_wire_host_timing.h"),
|
||||
File("#/lib/one_wire/one_wire_host.h"),
|
||||
File("#/lib/one_wire/one_wire_slave.h"),
|
||||
File("#/lib/one_wire/one_wire_device.h"),
|
||||
File("#/lib/one_wire/ibutton/ibutton_worker.h"),
|
||||
File("#/lib/one_wire/maxim_crc.h"),
|
||||
File("one_wire/one_wire_host_timing.h"),
|
||||
File("one_wire/one_wire_host.h"),
|
||||
File("one_wire/one_wire_slave.h"),
|
||||
File("one_wire/one_wire_device.h"),
|
||||
File("one_wire/ibutton/ibutton_worker.h"),
|
||||
File("one_wire/maxim_crc.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1496,6 +1496,15 @@ ReturnCode rfalTransceiveBlockingTxRx(
|
||||
uint32_t flags,
|
||||
uint32_t fwt);
|
||||
|
||||
ReturnCode rfalTransceiveBitsBlockingTxRx(
|
||||
uint8_t* txBuf,
|
||||
uint16_t txBufLen,
|
||||
uint8_t* rxBuf,
|
||||
uint16_t rxBufLen,
|
||||
uint16_t* actLen,
|
||||
uint32_t flags,
|
||||
uint32_t fwt);
|
||||
|
||||
ReturnCode rfalTransceiveBitsBlockingTx(
|
||||
uint8_t* txBuf,
|
||||
uint16_t txBufLen,
|
||||
|
||||
@@ -1607,6 +1607,23 @@ ReturnCode rfalTransceiveBlockingTxRx(
|
||||
return ret;
|
||||
}
|
||||
|
||||
ReturnCode rfalTransceiveBitsBlockingTxRx(
|
||||
uint8_t* txBuf,
|
||||
uint16_t txBufLen,
|
||||
uint8_t* rxBuf,
|
||||
uint16_t rxBufLen,
|
||||
uint16_t* actLen,
|
||||
uint32_t flags,
|
||||
uint32_t fwt) {
|
||||
ReturnCode ret;
|
||||
|
||||
EXIT_ON_ERR(
|
||||
ret, rfalTransceiveBitsBlockingTx(txBuf, txBufLen, rxBuf, rxBufLen, actLen, flags, fwt));
|
||||
ret = rfalTransceiveBlockingRx();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*******************************************************************************/
|
||||
static ReturnCode rfalRunTransceiveWorker(void) {
|
||||
if(gRFAL.state == RFAL_STATE_TXRX) {
|
||||
|
||||
@@ -15,7 +15,7 @@ env.Append(
|
||||
],
|
||||
SDK_HEADERS=env.GlobRecursive(
|
||||
"*_ll_*.h",
|
||||
"#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/",
|
||||
Dir("STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/"),
|
||||
exclude="*usb.h",
|
||||
),
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ env.Append(
|
||||
"#/lib/flipper_application",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/flipper_application/flipper_application.h"),
|
||||
File("flipper_application.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ env.Append(
|
||||
"#/lib/flipper_format",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/flipper_format/flipper_format.h"),
|
||||
File("#/lib/flipper_format/flipper_format_i.h"),
|
||||
File("flipper_format.h"),
|
||||
File("flipper_format_i.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -313,7 +313,7 @@ bool flipper_format_stream_write_value_line(Stream* stream, FlipperStreamWriteDa
|
||||
furi_crash("Unknown FF type");
|
||||
}
|
||||
|
||||
if((size_t)(i + 1) < write_data->data_size) {
|
||||
if(((size_t)i + 1) < write_data->data_size) {
|
||||
furi_string_cat(value, " ");
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,11 @@ env.Append(
|
||||
"#/lib/infrared/encoder_decoder",
|
||||
"#/lib/infrared/worker",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("encoder_decoder/infrared.h"),
|
||||
File("worker/infrared_worker.h"),
|
||||
File("worker/infrared_transmit.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -85,8 +85,8 @@ static InfraredStatus infrared_common_decode_bits(InfraredCommonDecoder* decoder
|
||||
if(timings->min_split_time && !level) {
|
||||
if(timing > timings->min_split_time) {
|
||||
/* long low timing - check if we're ready for any of protocol modification */
|
||||
for(size_t i = 0; decoder->protocol->databit_len[i] &&
|
||||
(i < COUNT_OF(decoder->protocol->databit_len));
|
||||
for(size_t i = 0; i < COUNT_OF(decoder->protocol->databit_len) &&
|
||||
decoder->protocol->databit_len[i];
|
||||
++i) {
|
||||
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
|
||||
return InfraredStatusReady;
|
||||
@@ -199,7 +199,7 @@ InfraredMessage* infrared_common_decoder_check_ready(InfraredCommonDecoder* deco
|
||||
bool found_length = false;
|
||||
|
||||
for(size_t i = 0;
|
||||
decoder->protocol->databit_len[i] && (i < COUNT_OF(decoder->protocol->databit_len));
|
||||
i < COUNT_OF(decoder->protocol->databit_len) && decoder->protocol->databit_len[i];
|
||||
++i) {
|
||||
if(decoder->protocol->databit_len[i] == decoder->databit_cnt) {
|
||||
found_length = true;
|
||||
|
||||
@@ -115,3 +115,26 @@ const InfraredCommonProtocolSpec protocol_sirc = {
|
||||
.decode_repeat = NULL,
|
||||
.encode_repeat = infrared_encoder_sirc_encode_repeat,
|
||||
};
|
||||
|
||||
const InfraredCommonProtocolSpec protocol_kaseikyo = {
|
||||
.timings =
|
||||
{
|
||||
.preamble_mark = INFRARED_KASEIKYO_PREAMBLE_MARK,
|
||||
.preamble_space = INFRARED_KASEIKYO_PREAMBLE_SPACE,
|
||||
.bit1_mark = INFRARED_KASEIKYO_BIT1_MARK,
|
||||
.bit1_space = INFRARED_KASEIKYO_BIT1_SPACE,
|
||||
.bit0_mark = INFRARED_KASEIKYO_BIT0_MARK,
|
||||
.bit0_space = INFRARED_KASEIKYO_BIT0_SPACE,
|
||||
.preamble_tolerance = INFRARED_KASEIKYO_PREAMBLE_TOLERANCE,
|
||||
.bit_tolerance = INFRARED_KASEIKYO_BIT_TOLERANCE,
|
||||
.silence_time = INFRARED_KASEIKYO_SILENCE,
|
||||
.min_split_time = INFRARED_KASEIKYO_MIN_SPLIT_TIME,
|
||||
},
|
||||
.databit_len[0] = 48,
|
||||
.no_stop_bit = false,
|
||||
.decode = infrared_common_decode_pdwm,
|
||||
.encode = infrared_common_encode_pdwm,
|
||||
.interpret = infrared_decoder_kaseikyo_interpret,
|
||||
.decode_repeat = NULL,
|
||||
.encode_repeat = NULL,
|
||||
};
|
||||
|
||||
@@ -110,6 +110,20 @@ static const InfraredEncoderDecoder infrared_encoder_decoder[] = {
|
||||
.free = infrared_encoder_sirc_free},
|
||||
.get_protocol_spec = infrared_sirc_get_spec,
|
||||
},
|
||||
{
|
||||
.decoder =
|
||||
{.alloc = infrared_decoder_kaseikyo_alloc,
|
||||
.decode = infrared_decoder_kaseikyo_decode,
|
||||
.reset = infrared_decoder_kaseikyo_reset,
|
||||
.check_ready = infrared_decoder_kaseikyo_check_ready,
|
||||
.free = infrared_decoder_kaseikyo_free},
|
||||
.encoder =
|
||||
{.alloc = infrared_encoder_kaseikyo_alloc,
|
||||
.encode = infrared_encoder_kaseikyo_encode,
|
||||
.reset = infrared_encoder_kaseikyo_reset,
|
||||
.free = infrared_encoder_kaseikyo_free},
|
||||
.get_protocol_spec = infrared_kaseikyo_get_spec,
|
||||
},
|
||||
};
|
||||
|
||||
static int infrared_find_index_by_protocol(InfraredProtocol protocol);
|
||||
|
||||
@@ -31,6 +31,7 @@ typedef enum {
|
||||
InfraredProtocolSIRC,
|
||||
InfraredProtocolSIRC15,
|
||||
InfraredProtocolSIRC20,
|
||||
InfraredProtocolKaseikyo,
|
||||
InfraredProtocolMAX,
|
||||
} InfraredProtocol;
|
||||
|
||||
|
||||
@@ -267,3 +267,54 @@ InfraredStatus infrared_encoder_sirc_encode_repeat(
|
||||
bool* level);
|
||||
|
||||
extern const InfraredCommonProtocolSpec protocol_sirc;
|
||||
|
||||
/***************************************************************************************************
|
||||
* Kaseikyo protocol description
|
||||
* https://github.com/Arduino-IRremote/Arduino-IRremote/blob/master/src/ir_Kaseikyo.hpp
|
||||
****************************************************************************************************
|
||||
* Preamble Preamble Pulse Distance/Width Pause Preamble Preamble
|
||||
* mark space Modulation up to period repeat repeat
|
||||
* mark space
|
||||
*
|
||||
* 3360 1665 48 bit ...130000 3456 1728
|
||||
* __________ _ _ _ _ _ _ _ _ _ _ _ _ _ ___________
|
||||
* ____ __________ _ _ _ __ __ __ _ _ __ __ _ _ ________________ ___________
|
||||
*
|
||||
***************************************************************************************************/
|
||||
|
||||
#define INFRARED_KASEIKYO_UNIT 432
|
||||
#define INFRARED_KASEIKYO_PREAMBLE_MARK (8 * INFRARED_KASEIKYO_UNIT)
|
||||
#define INFRARED_KASEIKYO_PREAMBLE_SPACE (4 * INFRARED_KASEIKYO_UNIT)
|
||||
#define INFRARED_KASEIKYO_BIT1_MARK INFRARED_KASEIKYO_UNIT
|
||||
#define INFRARED_KASEIKYO_BIT1_SPACE (3 * INFRARED_KASEIKYO_UNIT)
|
||||
#define INFRARED_KASEIKYO_BIT0_MARK INFRARED_KASEIKYO_UNIT
|
||||
#define INFRARED_KASEIKYO_BIT0_SPACE INFRARED_KASEIKYO_UNIT
|
||||
#define INFRARED_KASEIKYO_REPEAT_PERIOD 130000
|
||||
#define INFRARED_KASEIKYO_SILENCE INFRARED_KASEIKYO_REPEAT_PERIOD
|
||||
#define INFRARED_KASEIKYO_MIN_SPLIT_TIME INFRARED_KASEIKYO_REPEAT_PAUSE_MIN
|
||||
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MIN 4000
|
||||
#define INFRARED_KASEIKYO_REPEAT_PAUSE_MAX 150000
|
||||
#define INFRARED_KASEIKYO_REPEAT_MARK INFRARED_KASEIKYO_PREAMBLE_MARK
|
||||
#define INFRARED_KASEIKYO_REPEAT_SPACE (INFRARED_KASEIKYO_REPEAT_PERIOD - 56000)
|
||||
#define INFRARED_KASEIKYO_PREAMBLE_TOLERANCE 200 // us
|
||||
#define INFRARED_KASEIKYO_BIT_TOLERANCE 120 // us
|
||||
|
||||
void* infrared_decoder_kaseikyo_alloc(void);
|
||||
void infrared_decoder_kaseikyo_reset(void* decoder);
|
||||
void infrared_decoder_kaseikyo_free(void* decoder);
|
||||
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* decoder);
|
||||
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration);
|
||||
void* infrared_encoder_kaseikyo_alloc(void);
|
||||
InfraredStatus
|
||||
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level);
|
||||
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message);
|
||||
void infrared_encoder_kaseikyo_free(void* encoder_ptr);
|
||||
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder);
|
||||
InfraredStatus infrared_decoder_kaseikyo_decode_repeat(InfraredCommonDecoder* decoder);
|
||||
InfraredStatus infrared_encoder_kaseikyo_encode_repeat(
|
||||
InfraredCommonEncoder* encoder,
|
||||
uint32_t* duration,
|
||||
bool* level);
|
||||
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol);
|
||||
|
||||
extern const InfraredCommonProtocolSpec protocol_kaseikyo;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "infrared.h"
|
||||
#include "infrared_protocol_defs_i.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <furi.h>
|
||||
#include "../infrared_i.h"
|
||||
|
||||
InfraredMessage* infrared_decoder_kaseikyo_check_ready(void* ctx) {
|
||||
return infrared_common_decoder_check_ready(ctx);
|
||||
}
|
||||
|
||||
bool infrared_decoder_kaseikyo_interpret(InfraredCommonDecoder* decoder) {
|
||||
furi_assert(decoder);
|
||||
|
||||
bool result = false;
|
||||
uint16_t vendor_id = ((uint16_t)(decoder->data[1]) << 8) | (uint16_t)decoder->data[0];
|
||||
uint8_t vendor_parity = decoder->data[2] & 0x0f;
|
||||
uint8_t genre1 = decoder->data[2] >> 4;
|
||||
uint8_t genre2 = decoder->data[3] & 0x0f;
|
||||
uint16_t data = (uint16_t)(decoder->data[3] >> 4) | ((uint16_t)(decoder->data[4] & 0x3f) << 4);
|
||||
uint8_t id = decoder->data[4] >> 6;
|
||||
uint8_t parity = decoder->data[5];
|
||||
|
||||
uint8_t vendor_parity_check = decoder->data[0] ^ decoder->data[1];
|
||||
vendor_parity_check = (vendor_parity_check & 0xf) ^ (vendor_parity_check >> 4);
|
||||
uint8_t parity_check = decoder->data[2] ^ decoder->data[3] ^ decoder->data[4];
|
||||
|
||||
if(vendor_parity == vendor_parity_check && parity == parity_check) {
|
||||
decoder->message.command = (uint32_t)data;
|
||||
decoder->message.address = ((uint32_t)id << 24) | ((uint32_t)vendor_id << 8) |
|
||||
((uint32_t)genre1 << 4) | (uint32_t)genre2;
|
||||
decoder->message.protocol = InfraredProtocolKaseikyo;
|
||||
decoder->message.repeat = false;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void* infrared_decoder_kaseikyo_alloc(void) {
|
||||
return infrared_common_decoder_alloc(&protocol_kaseikyo);
|
||||
}
|
||||
|
||||
InfraredMessage* infrared_decoder_kaseikyo_decode(void* decoder, bool level, uint32_t duration) {
|
||||
return infrared_common_decode(decoder, level, duration);
|
||||
}
|
||||
|
||||
void infrared_decoder_kaseikyo_free(void* decoder) {
|
||||
infrared_common_decoder_free(decoder);
|
||||
}
|
||||
|
||||
void infrared_decoder_kaseikyo_reset(void* decoder) {
|
||||
infrared_common_decoder_reset(decoder);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <core/check.h>
|
||||
#include "common/infrared_common_i.h"
|
||||
#include <stdint.h>
|
||||
#include "../infrared_i.h"
|
||||
#include "infrared_protocol_defs_i.h"
|
||||
#include <furi.h>
|
||||
|
||||
void infrared_encoder_kaseikyo_reset(void* encoder_ptr, const InfraredMessage* message) {
|
||||
furi_assert(encoder_ptr);
|
||||
|
||||
InfraredCommonEncoder* encoder = encoder_ptr;
|
||||
infrared_common_encoder_reset(encoder);
|
||||
|
||||
uint32_t address = message->address;
|
||||
uint16_t command = message->command;
|
||||
|
||||
uint8_t id = (address >> 24) & 3;
|
||||
uint16_t vendor_id = (address >> 8) & 0xffff;
|
||||
uint8_t genre1 = (address >> 4) & 0xf;
|
||||
uint8_t genre2 = address & 0xf;
|
||||
|
||||
encoder->data[0] = (uint8_t)(vendor_id & 0xff);
|
||||
encoder->data[1] = (uint8_t)(vendor_id >> 8);
|
||||
uint8_t vendor_parity = encoder->data[0] ^ encoder->data[1];
|
||||
vendor_parity = (vendor_parity & 0xf) ^ (vendor_parity >> 4);
|
||||
encoder->data[2] = (vendor_parity & 0xf) | (genre1 << 4);
|
||||
encoder->data[3] = (genre2 & 0xf) | ((uint8_t)(command & 0xf) << 4);
|
||||
encoder->data[4] = (id << 6) | (uint8_t)(command >> 4);
|
||||
encoder->data[5] = encoder->data[2] ^ encoder->data[3] ^ encoder->data[4];
|
||||
|
||||
encoder->bits_to_encode = encoder->protocol->databit_len[0];
|
||||
}
|
||||
|
||||
void* infrared_encoder_kaseikyo_alloc(void) {
|
||||
return infrared_common_encoder_alloc(&protocol_kaseikyo);
|
||||
}
|
||||
|
||||
void infrared_encoder_kaseikyo_free(void* encoder_ptr) {
|
||||
infrared_common_encoder_free(encoder_ptr);
|
||||
}
|
||||
|
||||
InfraredStatus
|
||||
infrared_encoder_kaseikyo_encode(void* encoder_ptr, uint32_t* duration, bool* level) {
|
||||
return infrared_common_encode(encoder_ptr, duration, level);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "../infrared_i.h"
|
||||
#include "infrared_protocol_defs_i.h"
|
||||
|
||||
static const InfraredProtocolSpecification infrared_kaseikyo_protocol_specification = {
|
||||
.name = "Kaseikyo",
|
||||
.address_length = 26,
|
||||
.command_length = 10,
|
||||
.frequency = INFRARED_COMMON_CARRIER_FREQUENCY,
|
||||
.duty_cycle = INFRARED_COMMON_DUTY_CYCLE,
|
||||
};
|
||||
|
||||
const InfraredProtocolSpecification* infrared_kaseikyo_get_spec(InfraredProtocol protocol) {
|
||||
if(protocol == InfraredProtocolKaseikyo)
|
||||
return &infrared_kaseikyo_protocol_specification;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@@ -2,18 +2,18 @@ Import("env")
|
||||
|
||||
env.Append(
|
||||
LINT_SOURCES=[
|
||||
"lib/lfrfid",
|
||||
Dir("."),
|
||||
],
|
||||
CPPPATH=[
|
||||
"#/lib/lfrfid",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/lfrfid/lfrfid_worker.h"),
|
||||
File("#/lib/lfrfid/lfrfid_raw_worker.h"),
|
||||
File("#/lib/lfrfid/lfrfid_raw_file.h"),
|
||||
File("#/lib/lfrfid/lfrfid_dict_file.h"),
|
||||
File("#/lib/lfrfid/tools/bit_lib.h"),
|
||||
File("#/lib/lfrfid/protocols/lfrfid_protocols.h"),
|
||||
File("lfrfid_worker.h"),
|
||||
File("lfrfid_raw_worker.h"),
|
||||
File("lfrfid_raw_file.h"),
|
||||
File("lfrfid_dict_file.h"),
|
||||
File("tools/bit_lib.h"),
|
||||
File("protocols/lfrfid_protocols.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -140,9 +140,8 @@ size_t lfrfid_worker_dict_get_data_size(LFRFIDWorker* worker, LFRFIDProtocol pro
|
||||
|
||||
static int32_t lfrfid_worker_thread(void* thread_context) {
|
||||
LFRFIDWorker* worker = thread_context;
|
||||
bool running = true;
|
||||
|
||||
while(running) {
|
||||
while(true) {
|
||||
uint32_t flags = furi_thread_flags_wait(LFRFIDEventAll, FuriFlagWaitAny, FuriWaitForever);
|
||||
if(flags != FuriFlagErrorTimeout) {
|
||||
// stop thread
|
||||
|
||||
@@ -7,6 +7,10 @@ env.Append(
|
||||
CPPDEFINES=[
|
||||
("USB_PMASIZE", "0x400"),
|
||||
],
|
||||
SDK_HEADERS=env.GlobRecursive(
|
||||
"*.h",
|
||||
Dir("libusb_stm32/inc"),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@ env.Append(
|
||||
"#/lib/mbedtls",
|
||||
"#/lib/mbedtls/include",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("mbedtls/include/mbedtls/des.h"),
|
||||
File("mbedtls/include/mbedtls/sha1.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ env.Append(
|
||||
"PB_ENABLE_MALLOC",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/micro-ecc/uECC.h"),
|
||||
File("micro-ecc/uECC.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ env.Append(
|
||||
CPPPATH=[
|
||||
"#/lib/nfc",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("nfc_device.h"),
|
||||
],
|
||||
)
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="nfc")
|
||||
|
||||
@@ -201,10 +201,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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+25
-26
@@ -1009,12 +1009,7 @@ static void nfc_device_get_shadow_path(FuriString* orig_path, FuriString* shadow
|
||||
furi_string_cat_printf(shadow_path, "%s", NFC_APP_SHADOW_EXTENSION);
|
||||
}
|
||||
|
||||
static bool nfc_device_save_file(
|
||||
NfcDevice* dev,
|
||||
const char* dev_name,
|
||||
const char* folder,
|
||||
const char* extension,
|
||||
bool use_load_path) {
|
||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
furi_assert(dev);
|
||||
|
||||
bool saved = false;
|
||||
@@ -1024,19 +1019,10 @@ static bool nfc_device_save_file(
|
||||
temp_str = furi_string_alloc();
|
||||
|
||||
do {
|
||||
if(use_load_path && !furi_string_empty(dev->load_path)) {
|
||||
// Get directory name
|
||||
path_extract_dirname(furi_string_get_cstr(dev->load_path), temp_str);
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, furi_string_get_cstr(temp_str))) break;
|
||||
// Make path to file to save
|
||||
furi_string_cat_printf(temp_str, "/%s%s", dev_name, extension);
|
||||
} else {
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
|
||||
// First remove nfc device file if it was saved
|
||||
furi_string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
|
||||
}
|
||||
// Create nfc directory if necessary
|
||||
if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
|
||||
// First remove nfc device file if it was saved
|
||||
furi_string_printf(temp_str, "%s", dev_name);
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break;
|
||||
// Write header
|
||||
@@ -1075,13 +1061,19 @@ static bool nfc_device_save_file(
|
||||
return saved;
|
||||
}
|
||||
|
||||
bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
|
||||
return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION, true);
|
||||
}
|
||||
|
||||
bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
|
||||
bool nfc_device_save_shadow(NfcDevice* dev, const char* path) {
|
||||
dev->shadow_file_exist = true;
|
||||
return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION, true);
|
||||
// Replace extension from .nfc to .shd if necessary
|
||||
FuriString* orig_path = furi_string_alloc();
|
||||
furi_string_set_str(orig_path, path);
|
||||
FuriString* shadow_path = furi_string_alloc();
|
||||
nfc_device_get_shadow_path(orig_path, shadow_path);
|
||||
|
||||
bool file_saved = nfc_device_save(dev, furi_string_get_cstr(shadow_path));
|
||||
furi_string_free(orig_path);
|
||||
furi_string_free(shadow_path);
|
||||
|
||||
return file_saved;
|
||||
}
|
||||
|
||||
static bool nfc_device_load_data(NfcDevice* dev, FuriString* path, bool show_dialog) {
|
||||
@@ -1125,6 +1117,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;
|
||||
@@ -1191,7 +1190,7 @@ bool nfc_file_select(NfcDevice* dev) {
|
||||
};
|
||||
|
||||
bool res =
|
||||
dialog_file_browser_show(dev->dialogs, dev->load_path, nfc_app_folder, &browser_options);
|
||||
dialog_file_browser_show(dev->dialogs, dev->load_path, dev->load_path, &browser_options);
|
||||
|
||||
furi_string_free(nfc_app_folder);
|
||||
if(res) {
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
#include <lib/nfc/protocols/mifare_classic.h>
|
||||
#include <lib/nfc/protocols/mifare_desfire.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NFC_DEV_NAME_MAX_LEN 22
|
||||
#define NFC_READER_DATA_MAX_SIZE 64
|
||||
#define NFC_DICT_KEY_BATCH_SIZE 50
|
||||
@@ -47,9 +51,19 @@ typedef struct {
|
||||
MfClassicDict* dict;
|
||||
} NfcMfClassicDictAttackData;
|
||||
|
||||
typedef enum {
|
||||
NfcReadModeAuto,
|
||||
NfcReadModeMfClassic,
|
||||
NfcReadModeMfUltralight,
|
||||
NfcReadModeMfDesfire,
|
||||
NfcReadModeEMV,
|
||||
NfcReadModeNFCA,
|
||||
} NfcReadMode;
|
||||
|
||||
typedef struct {
|
||||
FuriHalNfcDevData nfc_data;
|
||||
NfcProtocol protocol;
|
||||
NfcReadMode read_mode;
|
||||
union {
|
||||
NfcReaderRequestData reader_data;
|
||||
NfcMfClassicDictAttackData mf_classic_dict_attack_data;
|
||||
@@ -102,3 +116,7 @@ bool nfc_device_delete(NfcDevice* dev, bool use_load_path);
|
||||
bool nfc_device_restore(NfcDevice* dev, bool use_load_path);
|
||||
|
||||
void nfc_device_set_loading_callback(NfcDevice* dev, NfcLoadingCallback callback, void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
+234
-7
@@ -70,12 +70,12 @@ void nfc_worker_start(
|
||||
|
||||
void nfc_worker_stop(NfcWorker* nfc_worker) {
|
||||
furi_assert(nfc_worker);
|
||||
if(nfc_worker->state == NfcWorkerStateBroken || nfc_worker->state == NfcWorkerStateReady) {
|
||||
return;
|
||||
furi_assert(nfc_worker->thread);
|
||||
if(furi_thread_get_state(nfc_worker->thread) != FuriThreadStateStopped) {
|
||||
furi_hal_nfc_stop();
|
||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
|
||||
furi_thread_join(nfc_worker->thread);
|
||||
}
|
||||
furi_hal_nfc_stop();
|
||||
nfc_worker_change_state(nfc_worker, NfcWorkerStateStop);
|
||||
furi_thread_join(nfc_worker->thread);
|
||||
}
|
||||
|
||||
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state) {
|
||||
@@ -90,7 +90,11 @@ int32_t nfc_worker_task(void* context) {
|
||||
furi_hal_nfc_exit_sleep();
|
||||
|
||||
if(nfc_worker->state == NfcWorkerStateRead) {
|
||||
nfc_worker_read(nfc_worker);
|
||||
if(nfc_worker->dev_data->read_mode == NfcReadModeAuto) {
|
||||
nfc_worker_read(nfc_worker);
|
||||
} else {
|
||||
nfc_worker_read_type(nfc_worker);
|
||||
}
|
||||
} else if(nfc_worker->state == NfcWorkerStateUidEmulate) {
|
||||
nfc_worker_emulate_uid(nfc_worker);
|
||||
} else if(nfc_worker->state == NfcWorkerStateEmulateApdu) {
|
||||
@@ -99,6 +103,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) {
|
||||
@@ -390,6 +398,81 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_worker_read_type(NfcWorker* nfc_worker) {
|
||||
furi_assert(nfc_worker);
|
||||
furi_assert(nfc_worker->callback);
|
||||
|
||||
NfcReadMode read_mode = nfc_worker->dev_data->read_mode;
|
||||
nfc_device_data_clear(nfc_worker->dev_data);
|
||||
NfcDeviceData* dev_data = nfc_worker->dev_data;
|
||||
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
NfcWorkerEvent event = 0;
|
||||
bool card_not_detected_notified = false;
|
||||
|
||||
while(nfc_worker->state == NfcWorkerStateRead) {
|
||||
if(furi_hal_nfc_detect(nfc_data, 300)) {
|
||||
FURI_LOG_D(TAG, "Card detected");
|
||||
furi_hal_nfc_sleep();
|
||||
// Process first found device
|
||||
nfc_worker->callback(NfcWorkerEventCardDetected, nfc_worker->context);
|
||||
card_not_detected_notified = false;
|
||||
if(nfc_data->type == FuriHalNfcTypeA) {
|
||||
if(read_mode == NfcReadModeMfClassic) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
nfc_worker->dev_data->mf_classic_data.type = mf_classic_get_classic_type(
|
||||
nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
|
||||
if(nfc_worker_read_mf_classic(nfc_worker, &tx_rx)) {
|
||||
FURI_LOG_D(TAG, "Card read");
|
||||
dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
event = NfcWorkerEventReadMfClassicDone;
|
||||
break;
|
||||
} else {
|
||||
FURI_LOG_D(TAG, "Card read failed");
|
||||
dev_data->protocol = NfcDeviceProtocolMifareClassic;
|
||||
event = NfcWorkerEventReadMfClassicDictAttackRequired;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeMfUltralight) {
|
||||
FURI_LOG_I(TAG, "Mifare Ultralight / NTAG");
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareUl;
|
||||
if(nfc_worker_read_mf_ultralight(nfc_worker, &tx_rx)) {
|
||||
event = NfcWorkerEventReadMfUltralight;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeMfDesfire) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolMifareDesfire;
|
||||
if(nfc_worker_read_mf_desfire(nfc_worker, &tx_rx)) {
|
||||
event = NfcWorkerEventReadMfDesfire;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeEMV) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolEMV;
|
||||
if(nfc_worker_read_bank_card(nfc_worker, &tx_rx)) {
|
||||
event = NfcWorkerEventReadBankCard;
|
||||
break;
|
||||
}
|
||||
} else if(read_mode == NfcReadModeNFCA) {
|
||||
nfc_worker->dev_data->protocol = NfcDeviceProtocolUnknown;
|
||||
event = NfcWorkerEventReadUidNfcA;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(!card_not_detected_notified) {
|
||||
nfc_worker->callback(NfcWorkerEventNoCardDetected, nfc_worker->context);
|
||||
card_not_detected_notified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
furi_hal_nfc_sleep();
|
||||
furi_delay_ms(100);
|
||||
}
|
||||
// Notify caller and exit
|
||||
if(event > NfcWorkerEventReserved) {
|
||||
nfc_worker->callback(event, nfc_worker->context);
|
||||
}
|
||||
}
|
||||
|
||||
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
|
||||
FuriHalNfcTxRxContext tx_rx = {};
|
||||
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
|
||||
@@ -674,6 +757,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);
|
||||
@@ -766,7 +987,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,
|
||||
|
||||
@@ -7,13 +7,14 @@ typedef struct NfcWorker NfcWorker;
|
||||
typedef enum {
|
||||
// Init states
|
||||
NfcWorkerStateNone,
|
||||
NfcWorkerStateBroken,
|
||||
NfcWorkerStateReady,
|
||||
// Main worker states
|
||||
NfcWorkerStateRead,
|
||||
NfcWorkerStateUidEmulate,
|
||||
NfcWorkerStateMfUltralightEmulate,
|
||||
NfcWorkerStateMfClassicEmulate,
|
||||
NfcWorkerStateMfClassicWrite,
|
||||
NfcWorkerStateMfClassicUpdate,
|
||||
NfcWorkerStateReadMfUltralightReadAuth,
|
||||
NfcWorkerStateMfClassicDictAttack,
|
||||
NfcWorkerStateAnalyzeReader,
|
||||
@@ -48,13 +49,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,
|
||||
|
||||
@@ -35,12 +35,18 @@ int32_t nfc_worker_task(void* context);
|
||||
|
||||
void nfc_worker_read(NfcWorker* nfc_worker);
|
||||
|
||||
void nfc_worker_read_type(NfcWorker* nfc_worker);
|
||||
|
||||
void nfc_worker_emulate_uid(NfcWorker* nfc_worker);
|
||||
|
||||
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);
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
@@ -95,7 +82,7 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) {
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t mf_classic_get_total_block_num(MfClassicType type) {
|
||||
uint16_t mf_classic_get_total_block_num(MfClassicType type) {
|
||||
if(type == MfClassicType1k) {
|
||||
return 64;
|
||||
} else if(type == MfClassicType4k) {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -84,12 +98,26 @@ MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t S
|
||||
|
||||
uint8_t mf_classic_get_total_sectors_num(MfClassicType type);
|
||||
|
||||
uint16_t mf_classic_get_total_block_num(MfClassicType type);
|
||||
|
||||
uint8_t mf_classic_get_sector_trailer_block_num_by_sector(uint8_t sector);
|
||||
|
||||
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 +132,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 +177,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);
|
||||
|
||||
@@ -98,7 +98,7 @@ for wrapped_fn in wrapped_fn_list:
|
||||
|
||||
env.Append(
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/print/wrappers.h"),
|
||||
File("wrappers.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -541,7 +541,7 @@ static size_t _etoa(
|
||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
||||
const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
||||
const double z2 = z * z;
|
||||
conv.U = (uint64_t)(exp2 + 1023) << 52U;
|
||||
conv.U = ((uint64_t)exp2 + 1023) << 52U;
|
||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
||||
conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
||||
// correct for rounding errors
|
||||
|
||||
+13
-12
@@ -5,18 +5,19 @@ env.Append(
|
||||
"#/lib/subghz",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/subghz/environment.h"),
|
||||
File("#/lib/subghz/receiver.h"),
|
||||
File("#/lib/subghz/subghz_worker.h"),
|
||||
File("#/lib/subghz/subghz_tx_rx_worker.h"),
|
||||
File("#/lib/subghz/transmitter.h"),
|
||||
File("#/lib/subghz/protocols/raw.h"),
|
||||
File("#/lib/subghz/blocks/const.h"),
|
||||
File("#/lib/subghz/blocks/decoder.h"),
|
||||
File("#/lib/subghz/blocks/encoder.h"),
|
||||
File("#/lib/subghz/blocks/generic.h"),
|
||||
File("#/lib/subghz/blocks/math.h"),
|
||||
File("#/lib/subghz/subghz_setting.h"),
|
||||
File("environment.h"),
|
||||
File("receiver.h"),
|
||||
File("registry.h"),
|
||||
File("subghz_worker.h"),
|
||||
File("subghz_tx_rx_worker.h"),
|
||||
File("transmitter.h"),
|
||||
File("protocols/raw.h"),
|
||||
File("blocks/const.h"),
|
||||
File("blocks/decoder.h"),
|
||||
File("blocks/encoder.h"),
|
||||
File("blocks/generic.h"),
|
||||
File("blocks/math.h"),
|
||||
File("subghz_setting.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -529,6 +529,24 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector(
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2:
|
||||
man = subghz_protocol_keeloq_common_magic_serial_type2_learning(
|
||||
fix, manufacture_code->key);
|
||||
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);
|
||||
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
|
||||
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3:
|
||||
man = subghz_protocol_keeloq_common_magic_serial_type3_learning(
|
||||
fix, manufacture_code->key);
|
||||
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man);
|
||||
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
|
||||
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case KEELOQ_LEARNING_UNKNOWN:
|
||||
// Simple Learning
|
||||
decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
|
||||
|
||||
@@ -94,6 +94,34 @@ inline uint64_t
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {
|
||||
return man | ((uint64_t)data << 40) |
|
||||
return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) |
|
||||
((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);
|
||||
}
|
||||
|
||||
/** Magic_serial_type2 Learning
|
||||
* @param data - btn+serial number (32bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) {
|
||||
uint8_t* p = (uint8_t*)&data;
|
||||
uint8_t* m = (uint8_t*)&man;
|
||||
m[7] = p[0];
|
||||
m[6] = p[1];
|
||||
m[5] = p[2];
|
||||
m[4] = p[3];
|
||||
return man;
|
||||
}
|
||||
|
||||
/** Magic_serial_type3 Learning
|
||||
* @param data - serial number (24bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {
|
||||
return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#define KEELOQ_LEARNING_SECURE 3u
|
||||
#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u
|
||||
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 5u
|
||||
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 6u
|
||||
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 7u
|
||||
|
||||
/**
|
||||
* Simple Learning Encrypt
|
||||
@@ -72,3 +74,19 @@ uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, u
|
||||
*/
|
||||
|
||||
uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man);
|
||||
|
||||
/** Magic_serial_type2 Learning
|
||||
* @param data - btn+serial number (32bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man);
|
||||
|
||||
/** Magic_serial_type3 Learning
|
||||
* @param data - btn+serial number (32bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man);
|
||||
|
||||
@@ -312,7 +312,6 @@ void subghz_protocol_decoder_power_smart_feed(
|
||||
if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) ==
|
||||
POWER_SMART_PACKET_HEADER) {
|
||||
if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) {
|
||||
instance->decoder.decode_data = instance->decoder.decode_data;
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit =
|
||||
subghz_protocol_power_smart_const.min_count_bit_for_found;
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SubGhzEnvironment SubGhzEnvironment;
|
||||
|
||||
typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry;
|
||||
@@ -37,3 +41,7 @@ const SubGhzProtocol* subghz_protocol_registry_get_by_index(
|
||||
* @return Number of protocols
|
||||
*/
|
||||
size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -464,7 +464,7 @@ bool subghz_keystore_raw_encrypted_save(
|
||||
}
|
||||
stream_write_cstring(output_stream, encrypted_line);
|
||||
|
||||
} while(ret > 0 && result);
|
||||
} while(result);
|
||||
|
||||
flipper_format_free(output_flipper_format);
|
||||
|
||||
|
||||
+17
-17
@@ -8,23 +8,23 @@ env.Append(
|
||||
"#/lib/toolbox",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("#/lib/toolbox/manchester_decoder.h"),
|
||||
File("#/lib/toolbox/manchester_encoder.h"),
|
||||
File("#/lib/toolbox/path.h"),
|
||||
File("#/lib/toolbox/random_name.h"),
|
||||
File("#/lib/toolbox/hmac_sha256.h"),
|
||||
File("#/lib/toolbox/crc32_calc.h"),
|
||||
File("#/lib/toolbox/dir_walk.h"),
|
||||
File("#/lib/toolbox/md5.h"),
|
||||
File("#/lib/toolbox/args.h"),
|
||||
File("#/lib/toolbox/saved_struct.h"),
|
||||
File("#/lib/toolbox/version.h"),
|
||||
File("#/lib/toolbox/tar/tar_archive.h"),
|
||||
File("#/lib/toolbox/stream/stream.h"),
|
||||
File("#/lib/toolbox/stream/file_stream.h"),
|
||||
File("#/lib/toolbox/stream/string_stream.h"),
|
||||
File("#/lib/toolbox/stream/buffered_file_stream.h"),
|
||||
File("#/lib/toolbox/protocols/protocol_dict.h"),
|
||||
File("manchester_decoder.h"),
|
||||
File("manchester_encoder.h"),
|
||||
File("path.h"),
|
||||
File("random_name.h"),
|
||||
File("hmac_sha256.h"),
|
||||
File("crc32_calc.h"),
|
||||
File("dir_walk.h"),
|
||||
File("md5.h"),
|
||||
File("args.h"),
|
||||
File("saved_struct.h"),
|
||||
File("version.h"),
|
||||
File("tar/tar_archive.h"),
|
||||
File("stream/stream.h"),
|
||||
File("stream/file_stream.h"),
|
||||
File("stream/string_stream.h"),
|
||||
File("stream/buffered_file_stream.h"),
|
||||
File("protocols/protocol_dict.h"),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ void path_extract_extension(FuriString* path, char* ext, size_t ext_len_max) {
|
||||
size_t dot = furi_string_search_rchar(path, '.');
|
||||
size_t filename_start = furi_string_search_rchar(path, '/');
|
||||
|
||||
if((dot > 0) && (filename_start < dot)) {
|
||||
if((dot != FURI_STRING_FAILURE) && (filename_start < dot)) {
|
||||
strlcpy(ext, &(furi_string_get_cstr(path))[dot], ext_len_max);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,6 @@
|
||||
#include <furi.h>
|
||||
|
||||
void set_random_name(char* name, uint8_t max_name_size) {
|
||||
static bool rand_generator_inited = false;
|
||||
|
||||
if(!rand_generator_inited) {
|
||||
srand(DWT->CYCCNT);
|
||||
rand_generator_inited = true;
|
||||
}
|
||||
const char* prefix[] = {
|
||||
"ancient", "hollow", "strange", "disappeared", "unknown",
|
||||
"unthinkable", "unnamable", "nameless", "my", "concealed",
|
||||
|
||||
Reference in New Issue
Block a user