Merge branch 'dev' into mrtd

This commit is contained in:
Chris van Marle
2022-09-28 09:24:34 +02:00
1113 changed files with 17715 additions and 5800 deletions
+11 -3
View File
@@ -15,7 +15,15 @@ env.Append(
"lib/u8g2",
"lib/update_util",
"lib/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"),
],
)
env.Append(
@@ -24,7 +32,7 @@ env.Append(
"#/lib", # TODO: remove!
"#/lib/mlib",
# Ugly hack
"${BUILD_DIR}/assets/compiled",
Dir("../assets/compiled"),
],
CPPDEFINES=[
'"M_MEMORY_FULL(x)=abort()"',
@@ -76,8 +84,8 @@ libs = env.BuildModules(
"nfc",
"appframe",
"misc",
"loclass",
"lfrfid",
"flipper_application",
],
)
-64
View File
@@ -1,64 +0,0 @@
#ifndef RFAL_PICOPASS_H
#define RFAL_PICOPASS_H
/*
******************************************************************************
* INCLUDES
******************************************************************************
*/
#include "platform.h"
#include "rfal_rf.h"
#include "rfal_crc.h"
#include "st_errno.h"
#define RFAL_PICOPASS_UID_LEN 8
#define RFAL_PICOPASS_MAX_BLOCK_LEN 8
#define RFAL_PICOPASS_TXRX_FLAGS \
(RFAL_TXRX_FLAGS_CRC_TX_MANUAL | RFAL_TXRX_FLAGS_AGC_ON | RFAL_TXRX_FLAGS_PAR_RX_REMV | \
RFAL_TXRX_FLAGS_CRC_RX_KEEP)
enum {
RFAL_PICOPASS_CMD_ACTALL = 0x0A,
RFAL_PICOPASS_CMD_IDENTIFY = 0x0C,
RFAL_PICOPASS_CMD_SELECT = 0x81,
RFAL_PICOPASS_CMD_READCHECK = 0x88,
RFAL_PICOPASS_CMD_CHECK = 0x05,
RFAL_PICOPASS_CMD_READ = 0x0C,
RFAL_PICOPASS_CMD_WRITE = 0x87,
};
typedef struct {
uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Anti-collision CSN
uint8_t crc[2];
} rfalPicoPassIdentifyRes;
typedef struct {
uint8_t CSN[RFAL_PICOPASS_UID_LEN]; // Real CSN
uint8_t crc[2];
} rfalPicoPassSelectRes;
typedef struct {
uint8_t CCNR[8];
} rfalPicoPassReadCheckRes;
typedef struct {
uint8_t mac[4];
} rfalPicoPassCheckRes;
typedef struct {
uint8_t data[RFAL_PICOPASS_MAX_BLOCK_LEN];
uint8_t crc[2];
} rfalPicoPassReadBlockRes;
ReturnCode rfalPicoPassPollerInitialize(void);
ReturnCode rfalPicoPassPollerCheckPresence(void);
ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes);
ReturnCode rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes);
ReturnCode rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes);
ReturnCode rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes);
ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes);
ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]);
#endif /* RFAL_PICOPASS_H */
-184
View File
@@ -1,184 +0,0 @@
#include "rfal_picopass.h"
#include "utils.h"
#define TAG "RFAL_PICOPASS"
typedef struct {
uint8_t CMD;
uint8_t CSN[RFAL_PICOPASS_UID_LEN];
} rfalPicoPassSelectReq;
typedef struct {
uint8_t CMD;
uint8_t null[4];
uint8_t mac[4];
} rfalPicoPassCheckReq;
ReturnCode rfalPicoPassPollerInitialize(void) {
ReturnCode ret;
EXIT_ON_ERR(ret, rfalSetMode(RFAL_MODE_POLL_PICOPASS, RFAL_BR_26p48, RFAL_BR_26p48));
rfalSetErrorHandling(RFAL_ERRORHANDLING_NFC);
rfalSetGT(RFAL_GT_PICOPASS);
rfalSetFDTListen(RFAL_FDT_LISTEN_PICOPASS_POLLER);
rfalSetFDTPoll(RFAL_FDT_POLL_PICOPASS_POLLER);
return ERR_NONE;
}
ReturnCode rfalPicoPassPollerCheckPresence(void) {
ReturnCode ret;
uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_ACTALL};
uint8_t rxBuf[32] = {0};
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
ret = rfalTransceiveBlockingTxRx(txBuf, 1, rxBuf, 32, &recvLen, flags, fwt);
return ret;
}
ReturnCode rfalPicoPassPollerIdentify(rfalPicoPassIdentifyRes* idRes) {
ReturnCode ret;
uint8_t txBuf[1] = {RFAL_PICOPASS_CMD_IDENTIFY};
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
ret = rfalTransceiveBlockingTxRx(
txBuf,
sizeof(txBuf),
(uint8_t*)idRes,
sizeof(rfalPicoPassIdentifyRes),
&recvLen,
flags,
fwt);
// printf("identify rx: %d %s\n", recvLen, hex2Str(idRes->CSN, RFAL_PICOPASS_UID_LEN));
return ret;
}
ReturnCode rfalPicoPassPollerSelect(uint8_t* csn, rfalPicoPassSelectRes* selRes) {
ReturnCode ret;
rfalPicoPassSelectReq selReq;
selReq.CMD = RFAL_PICOPASS_CMD_SELECT;
ST_MEMCPY(selReq.CSN, csn, RFAL_PICOPASS_UID_LEN);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
ret = rfalTransceiveBlockingTxRx(
(uint8_t*)&selReq,
sizeof(rfalPicoPassSelectReq),
(uint8_t*)selRes,
sizeof(rfalPicoPassSelectRes),
&recvLen,
flags,
fwt);
// printf("select rx: %d %s\n", recvLen, hex2Str(selRes->CSN, RFAL_PICOPASS_UID_LEN));
if(ret == ERR_TIMEOUT) {
return ERR_NONE;
}
return ret;
}
ReturnCode rfalPicoPassPollerReadCheck(rfalPicoPassReadCheckRes* rcRes) {
ReturnCode ret;
uint8_t txBuf[2] = {RFAL_PICOPASS_CMD_READCHECK, 0x02};
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
ret = rfalTransceiveBlockingTxRx(
txBuf,
sizeof(txBuf),
(uint8_t*)rcRes,
sizeof(rfalPicoPassReadCheckRes),
&recvLen,
flags,
fwt);
// printf("readcheck rx: %d %s\n", recvLen, hex2Str(rcRes->CCNR, 8));
if(ret == ERR_CRC) {
return ERR_NONE;
}
return ret;
}
ReturnCode rfalPicoPassPollerCheck(uint8_t* mac, rfalPicoPassCheckRes* chkRes) {
ReturnCode ret;
rfalPicoPassCheckReq chkReq;
chkReq.CMD = RFAL_PICOPASS_CMD_CHECK;
ST_MEMCPY(chkReq.mac, mac, 4);
ST_MEMSET(chkReq.null, 0, 4);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
// printf("check tx: %s\n", hex2Str((uint8_t *)&chkReq, sizeof(rfalPicoPassCheckReq)));
ret = rfalTransceiveBlockingTxRx(
(uint8_t*)&chkReq,
sizeof(rfalPicoPassCheckReq),
(uint8_t*)chkRes,
sizeof(rfalPicoPassCheckRes),
&recvLen,
flags,
fwt);
// printf("check rx: %d %s\n", recvLen, hex2Str(chkRes->mac, 4));
if(ret == ERR_CRC) {
return ERR_NONE;
}
return ret;
}
ReturnCode rfalPicoPassPollerReadBlock(uint8_t blockNum, rfalPicoPassReadBlockRes* readRes) {
ReturnCode ret;
uint8_t txBuf[4] = {RFAL_PICOPASS_CMD_READ, 0, 0, 0};
txBuf[1] = blockNum;
uint16_t crc = rfalCrcCalculateCcitt(0xE012, txBuf + 1, 1);
memcpy(txBuf + 2, &crc, sizeof(uint16_t));
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
ret = rfalTransceiveBlockingTxRx(
txBuf,
sizeof(txBuf),
(uint8_t*)readRes,
sizeof(rfalPicoPassReadBlockRes),
&recvLen,
flags,
fwt);
return ret;
}
ReturnCode rfalPicoPassPollerWriteBlock(uint8_t blockNum, uint8_t data[8], uint8_t mac[4]) {
ReturnCode ret;
uint8_t txBuf[14] = {RFAL_PICOPASS_CMD_WRITE, blockNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
memcpy(txBuf + 2, data, RFAL_PICOPASS_MAX_BLOCK_LEN);
memcpy(txBuf + 10, mac, 4);
uint16_t recvLen = 0;
uint32_t flags = RFAL_PICOPASS_TXRX_FLAGS;
uint32_t fwt = rfalConvMsTo1fc(20);
rfalPicoPassReadBlockRes block;
ret = rfalTransceiveBlockingTxRx(
txBuf, sizeof(txBuf), (uint8_t*)&block, sizeof(block), &recvLen, flags, fwt);
if(ret == ERR_NONE) {
// TODO: compare response
}
return ret;
}
+5
View File
@@ -13,6 +13,11 @@ env.Append(
"USE_FULL_ASSERT",
"USE_FULL_LL_DRIVER",
],
SDK_HEADERS=env.GlobRecursive(
"*_ll_*.h",
"#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/",
exclude="*usb.h",
),
)
if env["RAM_EXEC"]:
+2 -1
View File
@@ -1,4 +1,5 @@
template <typename TApp> class GenericScene {
template <typename TApp>
class GenericScene {
public:
virtual void on_enter(TApp* app, bool need_restore) = 0;
virtual bool on_event(TApp* app, typename TApp::Event* event) = 0;
@@ -6,7 +6,8 @@
*
* @tparam TRecordClass record class
*/
template <typename TRecordClass> class RecordController {
template <typename TRecordClass>
class RecordController {
public:
/**
* @brief Construct a new Record Controller object for record with record name
+2 -1
View File
@@ -11,7 +11,8 @@
* @tparam TScene generic scene class
* @tparam TApp application class
*/
template <typename TScene, typename TApp> class SceneController {
template <typename TScene, typename TApp>
class SceneController {
public:
/**
* @brief Add scene to scene container
+14 -7
View File
@@ -33,12 +33,14 @@ namespace ext {
/**
* Dummy type for tag-dispatching.
*/
template <typename T> struct tag_type {};
template <typename T>
struct tag_type {};
/**
* A value of tag_type<T>.
*/
template <typename T> constexpr tag_type<T> tag{};
template <typename T>
constexpr tag_type<T> tag{};
/**
* A type_index implementation without RTTI.
@@ -47,7 +49,8 @@ struct type_index {
/**
* Creates a type_index object for the specified type.
*/
template <typename T> type_index(tag_type<T>) noexcept : hash_code_{index<T>} {
template <typename T>
type_index(tag_type<T>) noexcept : hash_code_{index<T>} {
}
/**
@@ -61,7 +64,8 @@ private:
/**
* Unique integral index associated to template type argument.
*/
template <typename T> static std::size_t const index;
template <typename T>
static std::size_t const index;
/**
* Global counter for generating index values.
@@ -75,14 +79,16 @@ private:
std::size_t hash_code_;
};
template <typename> std::size_t const type_index::index = type_index::counter()++;
template <typename>
std::size_t const type_index::index = type_index::counter()++;
/**
* Creates a type_index object for the specified type.
*
* Equivalent to `ext::type_index{ext::tag<T>}`.
*/
template <typename T> type_index make_type_index() noexcept {
template <typename T>
type_index make_type_index() noexcept {
return tag<T>;
}
@@ -111,7 +117,8 @@ inline bool operator>=(type_index const& a, type_index const& b) noexcept {
}
}
template <> struct std::hash<ext::type_index> {
template <>
struct std::hash<ext::type_index> {
using argument_type = ext::type_index;
using result_type = std::size_t;
+8 -4
View File
@@ -12,7 +12,8 @@
* @tparam TApp application class
* @tparam TViewModules variadic list of ViewModules
*/
template <typename TApp, typename... TViewModules> class ViewController {
template <typename TApp, typename... TViewModules>
class ViewController {
public:
ViewController() {
event_queue = furi_message_queue_alloc(10, sizeof(typename TApp::Event));
@@ -44,7 +45,8 @@ public:
* @tparam T Concrete ViewModule class
* @return T* ViewModule pointer
*/
template <typename T> T* get() {
template <typename T>
T* get() {
uint32_t view_index = ext::make_type_index<T>().hash_code();
furi_check(holder.count(view_index) != 0);
return static_cast<T*>(holder[view_index]);
@@ -56,7 +58,8 @@ public:
* @tparam T Concrete ViewModule class
* @return T* ViewModule pointer
*/
template <typename T> operator T*() {
template <typename T>
operator T*() {
uint32_t view_index = ext::make_type_index<T>().hash_code();
furi_check(holder.count(view_index) != 0);
return static_cast<T*>(holder[view_index]);
@@ -68,7 +71,8 @@ public:
* @tparam T Concrete ViewModule class
* @return T* ViewModule pointer
*/
template <typename T> void switch_to() {
template <typename T>
void switch_to() {
uint32_t view_index = ext::make_type_index<T>().hash_code();
furi_check(holder.count(view_index) != 0);
view_dispatcher_switch_to_view(view_dispatcher, view_index);
Submodule lib/cxxheaderparser added at ba4222560f
+8
View File
@@ -6,6 +6,10 @@
#include <furi_hal_gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
bool start_level;
uint32_t edge_cnt;
@@ -29,3 +33,7 @@ uint32_t digital_signal_get_edges_cnt(DigitalSignal* signal);
uint32_t digital_signal_get_edge(DigitalSignal* signal, uint32_t edge_num);
void digital_signal_send(DigitalSignal* signal, const GpioPin* gpio);
#ifdef __cplusplus
}
#endif
+11 -3
View File
@@ -1,5 +1,4 @@
#include "bq25896.h"
#include "bq25896_reg.h"
#include <stddef.h>
@@ -81,7 +80,7 @@ void bq25896_poweroff(FuriHalI2cBusHandle* handle) {
handle, BQ25896_ADDRESS, 0x09, *(uint8_t*)&bq25896_regs.r09, BQ25896_I2C_TIMEOUT);
}
bool bq25896_is_charging(FuriHalI2cBusHandle* handle) {
ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle) {
furi_hal_i2c_read_mem(
handle,
BQ25896_ADDRESS,
@@ -91,7 +90,16 @@ bool bq25896_is_charging(FuriHalI2cBusHandle* handle) {
BQ25896_I2C_TIMEOUT);
furi_hal_i2c_read_reg_8(
handle, BQ25896_ADDRESS, 0x0B, (uint8_t*)&bq25896_regs.r0B, BQ25896_I2C_TIMEOUT);
return bq25896_regs.r0B.CHRG_STAT != ChrgStatNo;
return bq25896_regs.r0B.CHRG_STAT;
}
bool bq25896_is_charging(FuriHalI2cBusHandle* handle) {
// Include precharge, fast charging, and charging termination done as "charging"
return bq25896_get_charge_status(handle) != ChrgStatNo;
}
bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle) {
return bq25896_get_charge_status(handle) == ChrgStatDone;
}
void bq25896_enable_charging(FuriHalI2cBusHandle* handle) {
+8
View File
@@ -1,5 +1,7 @@
#pragma once
#include "bq25896_reg.h"
#include <stdbool.h>
#include <stdint.h>
#include <furi_hal_i2c.h>
@@ -10,9 +12,15 @@ void bq25896_init(FuriHalI2cBusHandle* handle);
/** Send device into shipping mode */
void bq25896_poweroff(FuriHalI2cBusHandle* handle);
/** Get charging status */
ChrgStat bq25896_get_charge_status(FuriHalI2cBusHandle* handle);
/** Is currently charging */
bool bq25896_is_charging(FuriHalI2cBusHandle* handle);
/** Is charging completed while connected to charger */
bool bq25896_is_charging_done(FuriHalI2cBusHandle* handle);
/** Enable charging */
void bq25896_enable_charging(FuriHalI2cBusHandle* handle);
+20
View File
@@ -0,0 +1,20 @@
Import("env")
env.Append(
CPPPATH=[
"#/lib/flipper_application",
],
SDK_HEADERS=[
File("#/lib/flipper_application/flipper_application.h"),
],
)
libenv = env.Clone(FW_LIB_NAME="flipper_application")
libenv.ApplyLibFlags()
sources = libenv.GlobRecursive("*.c")
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
Return("lib")
@@ -0,0 +1,21 @@
#include "application_manifest.h"
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) {
if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) ||
(manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) {
return false;
}
return true;
}
bool flipper_application_manifest_is_compatible(
const FlipperApplicationManifest* manifest,
const ElfApiInterface* api_interface) {
if(manifest->base.api_version.major != api_interface->api_version_major /* ||
manifest->base.api_version.minor > app->api_interface->api_version_minor */) {
return false;
}
return true;
}
@@ -0,0 +1,70 @@
/**
* @file application_manifest.h
* Flipper application manifest
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "elf/elf_api_interface.h"
#ifdef __cplusplus
extern "C" {
#endif
#define FAP_MANIFEST_MAGIC 0x52474448
#define FAP_MANIFEST_SUPPORTED_VERSION 1
#define FAP_MANIFEST_MAX_APP_NAME_LENGTH 32
#define FAP_MANIFEST_MAX_ICON_SIZE 32 // TODO: reduce size?
#pragma pack(push, 1)
typedef struct {
uint32_t manifest_magic;
uint32_t manifest_version;
union {
struct {
uint16_t minor;
uint16_t major;
};
uint32_t version;
} api_version;
uint16_t hardware_target_id;
} FlipperApplicationManifestBase;
typedef struct {
FlipperApplicationManifestBase base;
uint16_t stack_size;
uint32_t app_version;
char name[FAP_MANIFEST_MAX_APP_NAME_LENGTH];
char has_icon;
char icon[FAP_MANIFEST_MAX_ICON_SIZE];
} FlipperApplicationManifestV1;
typedef FlipperApplicationManifestV1 FlipperApplicationManifest;
#pragma pack(pop)
/**
* @brief Check if manifest is valid
*
* @param manifest
* @return bool
*/
bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest);
/**
* @brief Check if manifest is compatible with current ELF API interface
*
* @param manifest
* @param api_interface
* @return bool
*/
bool flipper_application_manifest_is_compatible(
const FlipperApplicationManifest* manifest,
const ElfApiInterface* api_interface);
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
#pragma once
#include <elf.h>
#include <stdbool.h>
#define ELF_INVALID_ADDRESS 0xFFFFFFFF
typedef struct {
uint16_t api_version_major;
uint16_t api_version_minor;
bool (*resolver_callback)(const char* name, Elf32_Addr* address);
} ElfApiInterface;
+835
View File
@@ -0,0 +1,835 @@
#include <elf.h>
#include "elf_file.h"
#include "elf_file_i.h"
#include "elf_api_interface.h"
#define TAG "elf"
#define ELF_NAME_BUFFER_LEN 32
#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr))
#define IS_FLAGS_SET(v, m) ((v & m) == m)
#define RESOLVER_THREAD_YIELD_STEP 30
// #define ELF_DEBUG_LOG 1
#ifndef ELF_DEBUG_LOG
#undef FURI_LOG_D
#define FURI_LOG_D(...)
#endif
#define TRAMPOLINE_CODE_SIZE 6
/**
ldr r12, [pc, #2]
bx r12
*/
const uint8_t trampoline_code_little_endian[TRAMPOLINE_CODE_SIZE] =
{0xdf, 0xf8, 0x02, 0xc0, 0x60, 0x47};
typedef struct {
uint8_t code[TRAMPOLINE_CODE_SIZE];
uint32_t addr;
} __attribute__((packed)) JMPTrampoline;
/**************************************************************************************************/
/********************************************* Caches *********************************************/
/**************************************************************************************************/
static bool address_cache_get(AddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {
Elf32_Addr* addr = AddressCache_get(cache, symEntry);
if(addr) {
*symAddr = *addr;
return true;
} else {
return false;
}
}
static void address_cache_put(AddressCache_t cache, int symEntry, Elf32_Addr symAddr) {
AddressCache_set_at(cache, symEntry, symAddr);
}
/**************************************************************************************************/
/********************************************** ELF ***********************************************/
/**************************************************************************************************/
static ELFSection* elf_file_get_section(ELFFile* elf, const char* name) {
return ELFSectionDict_get(elf->sections, name);
}
static void elf_file_put_section(ELFFile* elf, const char* name, ELFSection* section) {
ELFSectionDict_set_at(elf->sections, strdup(name), *section);
}
static bool elf_read_string_from_offset(ELFFile* elf, off_t offset, string_t name) {
bool result = false;
off_t old = storage_file_tell(elf->fd);
do {
if(!storage_file_seek(elf->fd, offset, true)) break;
char buffer[ELF_NAME_BUFFER_LEN + 1];
buffer[ELF_NAME_BUFFER_LEN] = 0;
while(true) {
uint16_t read = storage_file_read(elf->fd, buffer, ELF_NAME_BUFFER_LEN);
string_cat_str(name, buffer);
if(strlen(buffer) < ELF_NAME_BUFFER_LEN) {
result = true;
break;
}
if(storage_file_get_error(elf->fd) != FSE_OK || read == 0) break;
}
} while(false);
storage_file_seek(elf->fd, old, true);
return result;
}
static bool elf_read_section_name(ELFFile* elf, off_t offset, string_t name) {
return elf_read_string_from_offset(elf, elf->section_table_strings + offset, name);
}
static bool elf_read_symbol_name(ELFFile* elf, off_t offset, string_t name) {
return elf_read_string_from_offset(elf, elf->symbol_table_strings + offset, name);
}
static bool elf_read_section_header(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header) {
off_t offset = SECTION_OFFSET(elf, section_idx);
return storage_file_seek(elf->fd, offset, true) &&
storage_file_read(elf->fd, section_header, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);
}
static bool
elf_read_section(ELFFile* elf, size_t section_idx, Elf32_Shdr* section_header, string_t name) {
if(!elf_read_section_header(elf, section_idx, section_header)) {
return false;
}
if(section_header->sh_name && !elf_read_section_name(elf, section_header->sh_name, name)) {
return false;
}
return true;
}
static bool elf_read_symbol(ELFFile* elf, int n, Elf32_Sym* sym, string_t name) {
bool success = false;
off_t old = storage_file_tell(elf->fd);
off_t pos = elf->symbol_table + n * sizeof(Elf32_Sym);
if(storage_file_seek(elf->fd, pos, true) &&
storage_file_read(elf->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {
if(sym->st_name)
success = elf_read_symbol_name(elf, sym->st_name, name);
else {
Elf32_Shdr shdr;
success = elf_read_section(elf, sym->st_shndx, &shdr, name);
}
}
storage_file_seek(elf->fd, old, true);
return success;
}
static ELFSection* elf_section_of(ELFFile* elf, int index) {
ELFSectionDict_it_t it;
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
if(itref->value.sec_idx == index) {
return &itref->value;
}
}
return NULL;
}
static Elf32_Addr elf_address_of(ELFFile* elf, Elf32_Sym* sym, const char* sName) {
if(sym->st_shndx == SHN_UNDEF) {
Elf32_Addr addr = 0;
if(elf->api_interface->resolver_callback(sName, &addr)) {
return addr;
}
} else {
ELFSection* symSec = elf_section_of(elf, sym->st_shndx);
if(symSec) {
return ((Elf32_Addr)symSec->data) + sym->st_value;
}
}
FURI_LOG_D(TAG, " Can not find address for symbol %s", sName);
return ELF_INVALID_ADDRESS;
}
__attribute__((unused)) static const char* elf_reloc_type_to_str(int symt) {
#define STRCASE(name) \
case name: \
return #name;
switch(symt) {
STRCASE(R_ARM_NONE)
STRCASE(R_ARM_TARGET1)
STRCASE(R_ARM_ABS32)
STRCASE(R_ARM_THM_PC22)
STRCASE(R_ARM_THM_JUMP24)
default:
return "R_<unknow>";
}
#undef STRCASE
}
static JMPTrampoline* elf_create_trampoline(Elf32_Addr addr) {
JMPTrampoline* trampoline = malloc(sizeof(JMPTrampoline));
memcpy(trampoline->code, trampoline_code_little_endian, TRAMPOLINE_CODE_SIZE);
trampoline->addr = addr;
return trampoline;
}
static void elf_relocate_jmp_call(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
int offset, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
int to_thumb, is_call, blx_bit = 1 << 12;
/* Get initial offset */
hi = ((uint16_t*)relAddr)[0];
lo = ((uint16_t*)relAddr)[1];
s = (hi >> 10) & 1;
j1 = (lo >> 13) & 1;
j2 = (lo >> 11) & 1;
i1 = (j1 ^ s) ^ 1;
i2 = (j2 ^ s) ^ 1;
imm10 = hi & 0x3ff;
imm11 = lo & 0x7ff;
offset = (s << 24) | (i1 << 23) | (i2 << 22) | (imm10 << 12) | (imm11 << 1);
if(offset & 0x01000000) offset -= 0x02000000;
to_thumb = symAddr & 1;
is_call = (type == R_ARM_THM_PC22);
/* Store offset */
int offset_copy = offset;
/* Compute final offset */
offset += symAddr - relAddr;
if(!to_thumb && is_call) {
blx_bit = 0; /* bl -> blx */
offset = (offset + 3) & -4; /* Compute offset from aligned PC */
}
/* Check that relocation is possible
* offset must not be out of range
* if target is to be entered in arm mode:
- bit 1 must not set
- instruction must be a call (bl) or a jump to PLT */
if(!to_thumb || offset >= 0x1000000 || offset < -0x1000000) {
if(to_thumb || (symAddr & 2) || (!is_call)) {
FURI_LOG_D(
TAG,
"can't relocate value at %x, %s, doing trampoline",
relAddr,
elf_reloc_type_to_str(type));
Elf32_Addr addr;
if(!address_cache_get(elf->trampoline_cache, symAddr, &addr)) {
addr = (Elf32_Addr)elf_create_trampoline(symAddr);
address_cache_put(elf->trampoline_cache, symAddr, addr);
}
offset = offset_copy;
offset += (int)addr - relAddr;
if(!to_thumb && is_call) {
blx_bit = 0; /* bl -> blx */
offset = (offset + 3) & -4; /* Compute offset from aligned PC */
}
}
}
/* Compute and store final offset */
s = (offset >> 24) & 1;
i1 = (offset >> 23) & 1;
i2 = (offset >> 22) & 1;
j1 = s ^ (i1 ^ 1);
j2 = s ^ (i2 ^ 1);
imm10 = (offset >> 12) & 0x3ff;
imm11 = (offset >> 1) & 0x7ff;
(*(uint16_t*)relAddr) = (uint16_t)((hi & 0xf800) | (s << 10) | imm10);
(*(uint16_t*)(relAddr + 2)) =
(uint16_t)((lo & 0xc000) | (j1 << 13) | blx_bit | (j2 << 11) | imm11);
}
static void elf_relocate_mov(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
uint16_t upper_insn = ((uint16_t*)relAddr)[0];
uint16_t lower_insn = ((uint16_t*)relAddr)[1];
/* MOV*<C> <Rd>,#<imm16>
*
* i = upper[10]
* imm4 = upper[3:0]
* imm3 = lower[14:12]
* imm8 = lower[7:0]
*
* imm16 = imm4:i:imm3:imm8
*/
uint32_t i = (upper_insn >> 10) & 1; /* upper[10] */
uint32_t imm4 = upper_insn & 0x000F; /* upper[3:0] */
uint32_t imm3 = (lower_insn >> 12) & 0x7; /* lower[14:12] */
uint32_t imm8 = lower_insn & 0x00FF; /* lower[7:0] */
int32_t addend = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; /* imm16 */
uint32_t addr = (symAddr + addend);
if (type == R_ARM_THM_MOVT_ABS) {
addr >>= 16; /* upper 16 bits */
} else {
addr &= 0x0000FFFF; /* lower 16 bits */
}
/* Re-encode */
((uint16_t*)relAddr)[0] = (upper_insn & 0xFBF0)
| (((addr >> 11) & 1) << 10) /* i */
| ((addr >> 12) & 0x000F); /* imm4 */
((uint16_t*)relAddr)[1] = (lower_insn & 0x8F00)
| (((addr >> 8) & 0x7) << 12) /* imm3 */
| (addr & 0x00FF); /* imm8 */
}
static bool elf_relocate_symbol(ELFFile* elf, Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
switch(type) {
case R_ARM_TARGET1:
case R_ARM_ABS32:
*((uint32_t*)relAddr) += symAddr;
FURI_LOG_D(TAG, " R_ARM_ABS32 relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
case R_ARM_THM_PC22:
case R_ARM_THM_JUMP24:
elf_relocate_jmp_call(elf, relAddr, type, symAddr);
FURI_LOG_D(
TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
case R_ARM_THM_MOVW_ABS_NC:
case R_ARM_THM_MOVT_ABS:
elf_relocate_mov(relAddr, type, symAddr);
FURI_LOG_D(TAG, " R_ARM_THM_MOVW_ABS_NC/MOVT_ABS relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
default:
FURI_LOG_E(TAG, " Undefined relocation %d", type);
return false;
}
return true;
}
static bool elf_relocate(ELFFile* elf, Elf32_Shdr* h, ELFSection* s) {
if(s->data) {
Elf32_Rel rel;
size_t relEntries = h->sh_size / sizeof(rel);
size_t relCount;
(void)storage_file_seek(elf->fd, h->sh_offset, true);
FURI_LOG_D(TAG, " Offset Info Type Name");
int relocate_result = true;
string_t symbol_name;
string_init(symbol_name);
for(relCount = 0; relCount < relEntries; relCount++) {
if(relCount % RESOLVER_THREAD_YIELD_STEP == 0) {
FURI_LOG_D(TAG, " reloc YIELD");
furi_delay_tick(1);
}
if(storage_file_read(elf->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
FURI_LOG_E(TAG, " reloc read fail");
string_clear(symbol_name);
return false;
}
Elf32_Addr symAddr;
int symEntry = ELF32_R_SYM(rel.r_info);
int relType = ELF32_R_TYPE(rel.r_info);
Elf32_Addr relAddr = ((Elf32_Addr)s->data) + rel.r_offset;
if(!address_cache_get(elf->relocation_cache, symEntry, &symAddr)) {
Elf32_Sym sym;
string_reset(symbol_name);
if(!elf_read_symbol(elf, symEntry, &sym, symbol_name)) {
FURI_LOG_E(TAG, " symbol read fail");
string_clear(symbol_name);
return false;
}
FURI_LOG_D(
TAG,
" %08X %08X %-16s %s",
(unsigned int)rel.r_offset,
(unsigned int)rel.r_info,
elf_reloc_type_to_str(relType),
string_get_cstr(symbol_name));
symAddr = elf_address_of(elf, &sym, string_get_cstr(symbol_name));
address_cache_put(elf->relocation_cache, symEntry, symAddr);
}
if(symAddr != ELF_INVALID_ADDRESS) {
FURI_LOG_D(
TAG,
" symAddr=%08X relAddr=%08X",
(unsigned int)symAddr,
(unsigned int)relAddr);
if(!elf_relocate_symbol(elf, relAddr, relType, symAddr)) {
relocate_result = false;
}
} else {
FURI_LOG_E(TAG, " No symbol address of %s", string_get_cstr(symbol_name));
relocate_result = false;
}
}
string_clear(symbol_name);
return relocate_result;
} else {
FURI_LOG_D(TAG, "Section not loaded");
}
return false;
}
/**************************************************************************************************/
/********************************************* MISC ***********************************************/
/**************************************************************************************************/
static bool cstr_prefix(const char* prefix, const char* string) {
return strncmp(prefix, string, strlen(prefix)) == 0;
}
/**************************************************************************************************/
/************************************ Internal FAP interfaces *************************************/
/**************************************************************************************************/
typedef enum {
SectionTypeERROR = 0,
SectionTypeUnused = 1 << 0,
SectionTypeData = 1 << 1,
SectionTypeRelData = 1 << 2,
SectionTypeSymTab = 1 << 3,
SectionTypeStrTab = 1 << 4,
SectionTypeManifest = 1 << 5,
SectionTypeDebugLink = 1 << 6,
SectionTypeValid = SectionTypeSymTab | SectionTypeStrTab | SectionTypeManifest,
} SectionType;
static bool elf_load_metadata(
ELFFile* elf,
Elf32_Shdr* section_header,
FlipperApplicationManifest* manifest) {
if(section_header->sh_size < sizeof(FlipperApplicationManifest)) {
return false;
}
if(manifest == NULL) {
return true;
}
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
storage_file_read(elf->fd, manifest, section_header->sh_size) ==
section_header->sh_size;
}
static bool elf_load_debug_link(ELFFile* elf, Elf32_Shdr* section_header) {
elf->debug_link_info.debug_link_size = section_header->sh_size;
elf->debug_link_info.debug_link = malloc(section_header->sh_size);
return storage_file_seek(elf->fd, section_header->sh_offset, true) &&
storage_file_read(elf->fd, elf->debug_link_info.debug_link, section_header->sh_size) ==
section_header->sh_size;
}
static SectionType elf_preload_section(
ELFFile* elf,
size_t section_idx,
Elf32_Shdr* section_header,
string_t name_string,
FlipperApplicationManifest* manifest) {
const char* name = string_get_cstr(name_string);
const struct {
const char* prefix;
SectionType type;
} lookup_sections[] = {
{".text", SectionTypeData},
{".rodata", SectionTypeData},
{".data", SectionTypeData},
{".bss", SectionTypeData},
{".preinit_array", SectionTypeData},
{".init_array", SectionTypeData},
{".fini_array", SectionTypeData},
{".rel.text", SectionTypeRelData},
{".rel.rodata", SectionTypeRelData},
{".rel.data", SectionTypeRelData},
{".rel.preinit_array", SectionTypeRelData},
{".rel.init_array", SectionTypeRelData},
{".rel.fini_array", SectionTypeRelData},
};
for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) {
if(cstr_prefix(lookup_sections[i].prefix, name)) {
FURI_LOG_D(TAG, "Found section %s", lookup_sections[i].prefix);
if(lookup_sections[i].type == SectionTypeRelData) {
name = name + strlen(".rel");
}
ELFSection* section_p = elf_file_get_section(elf, name);
if(!section_p) {
ELFSection section = {
.data = NULL,
.sec_idx = 0,
.rel_sec_idx = 0,
.size = 0,
};
elf_file_put_section(elf, name, &section);
section_p = elf_file_get_section(elf, name);
}
if(lookup_sections[i].type == SectionTypeRelData) {
section_p->rel_sec_idx = section_idx;
} else {
section_p->sec_idx = section_idx;
}
return lookup_sections[i].type;
}
}
if(strcmp(name, ".symtab") == 0) {
FURI_LOG_D(TAG, "Found .symtab section");
elf->symbol_table = section_header->sh_offset;
elf->symbol_count = section_header->sh_size / sizeof(Elf32_Sym);
return SectionTypeSymTab;
} else if(strcmp(name, ".strtab") == 0) {
FURI_LOG_D(TAG, "Found .strtab section");
elf->symbol_table_strings = section_header->sh_offset;
return SectionTypeStrTab;
} else if(strcmp(name, ".fapmeta") == 0) {
FURI_LOG_D(TAG, "Found .fapmeta section");
if(elf_load_metadata(elf, section_header, manifest)) {
return SectionTypeManifest;
} else {
return SectionTypeERROR;
}
} else if(strcmp(name, ".gnu_debuglink") == 0) {
FURI_LOG_D(TAG, "Found .gnu_debuglink section");
if(elf_load_debug_link(elf, section_header)) {
return SectionTypeDebugLink;
} else {
return SectionTypeERROR;
}
}
return SectionTypeUnused;
}
static bool elf_load_section_data(ELFFile* elf, ELFSection* section) {
Elf32_Shdr section_header;
if(section->sec_idx == 0) {
FURI_LOG_D(TAG, "Section is not present");
return true;
}
if(!elf_read_section_header(elf, section->sec_idx, &section_header)) {
return false;
}
if(section_header.sh_size == 0) {
FURI_LOG_D(TAG, "No data for section");
return true;
}
section->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign);
section->size = section_header.sh_size;
if(section_header.sh_type == SHT_NOBITS) {
/* section is empty (.bss?) */
/* no need to memset - allocator already did that */
return true;
}
if((!storage_file_seek(elf->fd, section_header.sh_offset, true)) ||
(storage_file_read(elf->fd, section->data, section_header.sh_size) !=
section_header.sh_size)) {
FURI_LOG_E(TAG, " seek/read fail");
return false;
}
FURI_LOG_D(TAG, "0x%X", section->data);
return true;
}
static bool elf_relocate_section(ELFFile* elf, ELFSection* section) {
Elf32_Shdr section_header;
if(section->rel_sec_idx) {
FURI_LOG_D(TAG, "Relocating section");
if(elf_read_section_header(elf, section->rel_sec_idx, &section_header))
return elf_relocate(elf, &section_header, section);
else {
FURI_LOG_E(TAG, "Error reading section header");
return false;
}
} else {
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
}
return true;
}
static void elf_file_call_section_list(ELFFile* elf, const char* name, bool reverse_order) {
ELFSection* section = elf_file_get_section(elf, name);
if(section && section->size) {
const uint32_t* start = section->data;
const uint32_t* end = section->data + section->size;
if(reverse_order) {
while(end > start) {
end--;
((void (*)(void))(*end))();
}
} else {
while(start < end) {
((void (*)(void))(*start))();
start++;
}
}
}
}
/**************************************************************************************************/
/********************************************* Public *********************************************/
/**************************************************************************************************/
ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface) {
ELFFile* elf = malloc(sizeof(ELFFile));
elf->fd = storage_file_alloc(storage);
elf->api_interface = api_interface;
ELFSectionDict_init(elf->sections);
AddressCache_init(elf->trampoline_cache);
return elf;
}
void elf_file_free(ELFFile* elf) {
// free sections data
{
ELFSectionDict_it_t it;
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) {
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
if(itref->value.data) {
aligned_free(itref->value.data);
}
free((void*)itref->key);
}
ELFSectionDict_clear(elf->sections);
}
// free trampoline data
{
AddressCache_it_t it;
for(AddressCache_it(it, elf->trampoline_cache); !AddressCache_end_p(it);
AddressCache_next(it)) {
const AddressCache_itref_t* itref = AddressCache_cref(it);
free((void*)itref->value);
}
AddressCache_clear(elf->trampoline_cache);
}
if(elf->debug_link_info.debug_link) {
free(elf->debug_link_info.debug_link);
}
storage_file_free(elf->fd);
free(elf);
}
bool elf_file_open(ELFFile* elf, const char* path) {
Elf32_Ehdr h;
Elf32_Shdr sH;
if(!storage_file_open(elf->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||
!storage_file_seek(elf->fd, 0, true) ||
storage_file_read(elf->fd, &h, sizeof(h)) != sizeof(h) ||
!storage_file_seek(elf->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||
storage_file_read(elf->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {
return false;
}
elf->entry = h.e_entry;
elf->sections_count = h.e_shnum;
elf->section_table = h.e_shoff;
elf->section_table_strings = sH.sh_offset;
return true;
}
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest) {
bool result = false;
string_t name;
string_init(name);
FURI_LOG_D(TAG, "Looking for manifest section");
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
string_reset(name);
if(!elf_read_section(elf, section_idx, &section_header, name)) {
break;
}
if(string_cmp(name, ".fapmeta") == 0) {
if(elf_load_metadata(elf, &section_header, manifest)) {
FURI_LOG_D(TAG, "Load manifest done");
result = true;
break;
} else {
break;
}
}
}
string_clear(name);
return result;
}
bool elf_file_load_section_table(ELFFile* elf, FlipperApplicationManifest* manifest) {
SectionType loaded_sections = SectionTypeERROR;
string_t name;
string_init(name);
FURI_LOG_D(TAG, "Scan ELF indexs...");
for(size_t section_idx = 1; section_idx < elf->sections_count; section_idx++) {
Elf32_Shdr section_header;
string_reset(name);
if(!elf_read_section(elf, section_idx, &section_header, name)) {
loaded_sections = SectionTypeERROR;
break;
}
FURI_LOG_D(TAG, "Preloading data for section #%d %s", section_idx, string_get_cstr(name));
SectionType section_type =
elf_preload_section(elf, section_idx, &section_header, name, manifest);
loaded_sections |= section_type;
if(section_type == SectionTypeERROR) {
loaded_sections = SectionTypeERROR;
break;
}
}
string_clear(name);
FURI_LOG_D(TAG, "Load symbols done");
return IS_FLAGS_SET(loaded_sections, SectionTypeValid);
}
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf) {
ELFFileLoadStatus status = ELFFileLoadStatusSuccess;
ELFSectionDict_it_t it;
AddressCache_init(elf->relocation_cache);
size_t start = furi_get_tick();
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
FURI_LOG_D(TAG, "Loading section '%s'", itref->key);
if(!elf_load_section_data(elf, &itref->value)) {
FURI_LOG_E(TAG, "Error loading section '%s'", itref->key);
status = ELFFileLoadStatusUnspecifiedError;
}
}
if(status == ELFFileLoadStatusSuccess) {
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it);
ELFSectionDict_next(it)) {
ELFSectionDict_itref_t* itref = ELFSectionDict_ref(it);
FURI_LOG_D(TAG, "Relocating section '%s'", itref->key);
if(!elf_relocate_section(elf, &itref->value)) {
FURI_LOG_E(TAG, "Error relocating section '%s'", itref->key);
status = ELFFileLoadStatusMissingImports;
}
}
}
/* Fixing up entry point */
if(status == ELFFileLoadStatusSuccess) {
ELFSection* text_section = elf_file_get_section(elf, ".text");
if(text_section == NULL) {
FURI_LOG_E(TAG, "No .text section found");
status = ELFFileLoadStatusUnspecifiedError;
} else {
elf->entry += (uint32_t)text_section->data;
}
}
FURI_LOG_D(TAG, "Relocation cache size: %u", AddressCache_size(elf->relocation_cache));
FURI_LOG_D(TAG, "Trampoline cache size: %u", AddressCache_size(elf->trampoline_cache));
AddressCache_clear(elf->relocation_cache);
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
return status;
}
void elf_file_pre_run(ELFFile* elf) {
elf_file_call_section_list(elf, ".preinit_array", false);
elf_file_call_section_list(elf, ".init_array", false);
}
int32_t elf_file_run(ELFFile* elf, void* args) {
int32_t result;
result = ((int32_t(*)(void*))elf->entry)(args);
return result;
}
void elf_file_post_run(ELFFile* elf) {
elf_file_call_section_list(elf, ".fini_array", true);
}
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file) {
return elf_file->api_interface;
}
void elf_file_init_debug_info(ELFFile* elf, ELFDebugInfo* debug_info) {
// set entry
debug_info->entry = elf->entry;
// copy debug info
memcpy(&debug_info->debug_link_info, &elf->debug_link_info, sizeof(ELFDebugLinkInfo));
// init mmap
debug_info->mmap_entry_count = ELFSectionDict_size(elf->sections);
debug_info->mmap_entries = malloc(sizeof(ELFMemoryMapEntry) * debug_info->mmap_entry_count);
uint32_t mmap_entry_idx = 0;
ELFSectionDict_it_t it;
for(ELFSectionDict_it(it, elf->sections); !ELFSectionDict_end_p(it); ELFSectionDict_next(it)) {
const ELFSectionDict_itref_t* itref = ELFSectionDict_cref(it);
const void* data_ptr = itref->value.data;
if(data_ptr) {
debug_info->mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
debug_info->mmap_entries[mmap_entry_idx].name = itref->key;
mmap_entry_idx++;
}
}
}
void elf_file_clear_debug_info(ELFDebugInfo* debug_info) {
// clear debug info
memset(&debug_info->debug_link_info, 0, sizeof(ELFDebugLinkInfo));
// clear mmap
if(debug_info->mmap_entries) {
free(debug_info->mmap_entries);
debug_info->mmap_entries = NULL;
}
debug_info->mmap_entry_count = 0;
}
+127
View File
@@ -0,0 +1,127 @@
/**
* @file elf_file.h
* ELF file loader
*/
#pragma once
#include <storage/storage.h>
#include "../application_manifest.h"
#include "elf_api_interface.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct ELFFile ELFFile;
typedef struct {
const char* name;
uint32_t address;
} ELFMemoryMapEntry;
typedef struct {
uint32_t debug_link_size;
uint8_t* debug_link;
} ELFDebugLinkInfo;
typedef struct {
uint32_t mmap_entry_count;
ELFMemoryMapEntry* mmap_entries;
ELFDebugLinkInfo debug_link_info;
off_t entry;
} ELFDebugInfo;
typedef enum {
ELFFileLoadStatusSuccess = 0,
ELFFileLoadStatusUnspecifiedError,
ELFFileLoadStatusNoFreeMemory,
ELFFileLoadStatusMissingImports,
} ELFFileLoadStatus;
/**
* @brief Allocate ELFFile instance
* @param storage
* @param api_interface
* @return ELFFile*
*/
ELFFile* elf_file_alloc(Storage* storage, const ElfApiInterface* api_interface);
/**
* @brief Free ELFFile instance
* @param elf_file
*/
void elf_file_free(ELFFile* elf_file);
/**
* @brief Open ELF file
* @param elf_file
* @param path
* @return bool
*/
bool elf_file_open(ELFFile* elf_file, const char* path);
/**
* @brief Load ELF file manifest
* @param elf
* @param manifest
* @return bool
*/
bool elf_file_load_manifest(ELFFile* elf, FlipperApplicationManifest* manifest);
/**
* @brief Load ELF file section table (load stage #1)
* @param elf_file
* @param manifest
* @return bool
*/
bool elf_file_load_section_table(ELFFile* elf_file, FlipperApplicationManifest* manifest);
/**
* @brief Load and relocate ELF file sections (load stage #2)
* @param elf_file
* @return ELFFileLoadStatus
*/
ELFFileLoadStatus elf_file_load_sections(ELFFile* elf_file);
/**
* @brief Execute ELF file pre-run stage, call static constructors for example (load stage #3)
* @param elf
*/
void elf_file_pre_run(ELFFile* elf);
/**
* @brief Run ELF file (load stage #4)
* @param elf_file
* @param args
* @return int32_t
*/
int32_t elf_file_run(ELFFile* elf_file, void* args);
/**
* @brief Execute ELF file post-run stage, call static destructors for example (load stage #5)
* @param elf
*/
void elf_file_post_run(ELFFile* elf);
/**
* @brief Get ELF file API interface
* @param elf_file
* @return const ElfApiInterface*
*/
const ElfApiInterface* elf_file_get_api_interface(ELFFile* elf_file);
/**
* @brief Get ELF file debug info
* @param elf_file
* @param debug_info
*/
void elf_file_init_debug_info(ELFFile* elf_file, ELFDebugInfo* debug_info);
/**
* @brief Clear ELF file debug info generated by elf_file_init_debug_info
* @param debug_info
*/
void elf_file_clear_debug_info(ELFDebugInfo* debug_info);
#ifdef __cplusplus
}
#endif
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include "elf_file.h"
#include <m-dict.h>
#ifdef __cplusplus
extern "C" {
#endif
DICT_DEF2(AddressCache, int, M_DEFAULT_OPLIST, Elf32_Addr, M_DEFAULT_OPLIST)
/**
* Callable elf entry type
*/
typedef int32_t(entry_t)(void*);
typedef struct {
void* data;
uint16_t sec_idx;
uint16_t rel_sec_idx;
Elf32_Word size;
} ELFSection;
DICT_DEF2(ELFSectionDict, const char*, M_CSTR_OPLIST, ELFSection, M_POD_OPLIST)
struct ELFFile {
size_t sections_count;
off_t section_table;
off_t section_table_strings;
size_t symbol_count;
off_t symbol_table;
off_t symbol_table_strings;
off_t entry;
ELFSectionDict_t sections;
AddressCache_t relocation_cache;
AddressCache_t trampoline_cache;
File* fd;
const ElfApiInterface* api_interface;
ELFDebugLinkInfo debug_link_info;
};
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,144 @@
#include "flipper_application.h"
#include "elf/elf_file.h"
#define TAG "fapp"
struct FlipperApplication {
ELFDebugInfo state;
FlipperApplicationManifest manifest;
ELFFile* elf;
FuriThread* thread;
};
/* For debugger access to app state */
FlipperApplication* last_loaded_app = NULL;
FlipperApplication*
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface) {
FlipperApplication* app = malloc(sizeof(FlipperApplication));
app->elf = elf_file_alloc(storage, api_interface);
app->thread = NULL;
return app;
}
void flipper_application_free(FlipperApplication* app) {
furi_assert(app);
if(app->thread) {
furi_thread_join(app->thread);
furi_thread_free(app->thread);
}
last_loaded_app = NULL;
elf_file_clear_debug_info(&app->state);
elf_file_free(app->elf);
free(app);
}
static FlipperApplicationPreloadStatus
flipper_application_validate_manifest(FlipperApplication* app) {
if(!flipper_application_manifest_is_valid(&app->manifest)) {
return FlipperApplicationPreloadStatusInvalidManifest;
}
if(!flipper_application_manifest_is_compatible(
&app->manifest, elf_file_get_api_interface(app->elf))) {
return FlipperApplicationPreloadStatusApiMismatch;
}
return FlipperApplicationPreloadStatusSuccess;
}
/* Parse headers, load manifest */
FlipperApplicationPreloadStatus
flipper_application_preload_manifest(FlipperApplication* app, const char* path) {
if(!elf_file_open(app->elf, path) || !elf_file_load_manifest(app->elf, &app->manifest)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
return flipper_application_validate_manifest(app);
}
/* Parse headers, load full file */
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path) {
if(!elf_file_open(app->elf, path) || !elf_file_load_section_table(app->elf, &app->manifest)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
return flipper_application_validate_manifest(app);
}
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
return &app->manifest;
}
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
last_loaded_app = app;
ELFFileLoadStatus status = elf_file_load_sections(app->elf);
switch(status) {
case ELFFileLoadStatusSuccess:
elf_file_init_debug_info(app->elf, &app->state);
return FlipperApplicationLoadStatusSuccess;
case ELFFileLoadStatusNoFreeMemory:
return FlipperApplicationLoadStatusNoFreeMemory;
case ELFFileLoadStatusMissingImports:
return FlipperApplicationLoadStatusMissingImports;
default:
return FlipperApplicationLoadStatusUnspecifiedError;
}
}
static int32_t flipper_application_thread(void* context) {
elf_file_pre_run(last_loaded_app->elf);
int32_t result = elf_file_run(last_loaded_app->elf, context);
elf_file_post_run(last_loaded_app->elf);
return result;
}
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args) {
furi_check(app->thread == NULL);
const FlipperApplicationManifest* manifest = flipper_application_get_manifest(app);
furi_check(manifest->stack_size > 0);
app->thread = furi_thread_alloc();
furi_thread_set_stack_size(app->thread, manifest->stack_size);
furi_thread_set_name(app->thread, manifest->name);
furi_thread_set_callback(app->thread, flipper_application_thread);
furi_thread_set_context(app->thread, args);
return app->thread;
}
static const char* preload_status_strings[] = {
[FlipperApplicationPreloadStatusSuccess] = "Success",
[FlipperApplicationPreloadStatusUnspecifiedError] = "Unknown error",
[FlipperApplicationPreloadStatusInvalidFile] = "Invalid file",
[FlipperApplicationPreloadStatusInvalidManifest] = "Invalid file manifest",
[FlipperApplicationPreloadStatusApiMismatch] = "API version mismatch",
[FlipperApplicationPreloadStatusTargetMismatch] = "Hardware target mismatch",
};
static const char* load_status_strings[] = {
[FlipperApplicationLoadStatusSuccess] = "Success",
[FlipperApplicationLoadStatusUnspecifiedError] = "Unknown error",
[FlipperApplicationLoadStatusNoFreeMemory] = "Out of memory",
[FlipperApplicationLoadStatusMissingImports] = "Found unsatisfied imports",
};
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status) {
if(status >= COUNT_OF(preload_status_strings) || preload_status_strings[status] == NULL) {
return "Unknown error";
}
return preload_status_strings[status];
}
const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status) {
if(status >= COUNT_OF(load_status_strings) || load_status_strings[status] == NULL) {
return "Unknown error";
}
return load_status_strings[status];
}
@@ -0,0 +1,120 @@
/**
* @file flipper_application.h
* Flipper application
*/
#pragma once
#include "application_manifest.h"
#include "elf/elf_api_interface.h"
#include <furi.h>
#include <storage/storage.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FlipperApplicationPreloadStatusSuccess = 0,
FlipperApplicationPreloadStatusUnspecifiedError,
FlipperApplicationPreloadStatusInvalidFile,
FlipperApplicationPreloadStatusInvalidManifest,
FlipperApplicationPreloadStatusApiMismatch,
FlipperApplicationPreloadStatusTargetMismatch,
} FlipperApplicationPreloadStatus;
typedef enum {
FlipperApplicationLoadStatusSuccess = 0,
FlipperApplicationLoadStatusUnspecifiedError,
FlipperApplicationLoadStatusNoFreeMemory,
FlipperApplicationLoadStatusMissingImports,
} FlipperApplicationLoadStatus;
/**
* @brief Get text description of preload status
* @param status Status code
* @return String pointer to description
*/
const char* flipper_application_preload_status_to_string(FlipperApplicationPreloadStatus status);
/**
* @brief Get text description of load status
* @param status Status code
* @return String pointer to description
*/
const char* flipper_application_load_status_to_string(FlipperApplicationLoadStatus status);
typedef struct FlipperApplication FlipperApplication;
typedef struct {
const char* name;
uint32_t address;
} FlipperApplicationMemoryMapEntry;
typedef struct {
uint32_t mmap_entry_count;
FlipperApplicationMemoryMapEntry* mmap_entries;
uint32_t debug_link_size;
uint8_t* debug_link;
} FlipperApplicationState;
/**
* @brief Initialize FlipperApplication object
* @param storage Storage instance
* @param api_interface ELF API interface to use for pre-loading and symbol resolving
* @return Application instance
*/
FlipperApplication*
flipper_application_alloc(Storage* storage, const ElfApiInterface* api_interface);
/**
* @brief Destroy FlipperApplication object
* @param app Application pointer
*/
void flipper_application_free(FlipperApplication* app);
/**
* @brief Validate elf file and load application metadata
* @param app Application pointer
* @return Preload result code
*/
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path);
/**
* @brief Validate elf file and load application manifest
* @param app Application pointer
* @return Preload result code
*/
FlipperApplicationPreloadStatus
flipper_application_preload_manifest(FlipperApplication* app, const char* path);
/**
* @brief Get pointer to application manifest for preloaded application
* @param app Application pointer
* @return Pointer to application manifest
*/
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app);
/**
* @brief Load sections and process relocations for already pre-loaded application
* @param app Application pointer
* @return Load result code
*/
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app);
/**
* @brief Create application thread at entry point address, using app name and
* stack size from metadata. Returned thread isn't started yet.
* Can be only called once for application instance.
* @param app Applicaiton pointer
* @param args Object to pass to app's entry point
* @return Created thread
*/
FuriThread* flipper_application_spawn(FlipperApplication* app, void* args);
#ifdef __cplusplus
}
#endif
+4
View File
@@ -4,6 +4,10 @@ env.Append(
CPPPATH=[
"#/lib/flipper_format",
],
SDK_HEADERS=[
File("#/lib/flipper_format/flipper_format.h"),
File("#/lib/flipper_format/flipper_format_i.h"),
],
)
+8
View File
@@ -7,6 +7,14 @@ env.Append(
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"),
],
)
libenv = env.Clone(FW_LIB_NAME="lfrfid")
+4
View File
@@ -205,6 +205,10 @@ bool protocol_awid_write_data(ProtocolAwid* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);
protocol_awid_decode(protocol->encoded_data, protocol->data);
protocol_awid_encode(protocol->data, (uint8_t*)protocol->encoded_data);
if(request->write_type == LFRFIDWriteTypeT5577) {
+8
View File
@@ -248,6 +248,14 @@ bool protocol_em4100_write_data(ProtocolEM4100* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_em4100_encoder_start(protocol);
em4100_decode(
(uint8_t*)&protocol->encoded_data,
sizeof(EM4100DecodedData),
protocol->data,
EM4100_DECODED_DATA_SIZE);
protocol_em4100_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
+4
View File
@@ -178,6 +178,10 @@ bool protocol_fdx_a_write_data(ProtocolFDXA* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_fdx_a_encoder_start(protocol);
protocol_fdx_a_decode(protocol->encoded_data, protocol->data);
protocol_fdx_a_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
+4
View File
@@ -334,6 +334,10 @@ bool protocol_fdx_b_write_data(ProtocolFDXB* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_fdx_b_encoder_start(protocol);
protocol_fdx_b_decode(protocol);
protocol_fdx_b_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
@@ -249,6 +249,10 @@ bool protocol_gallagher_write_data(ProtocolGallagher* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_gallagher_encoder_start(protocol);
protocol_gallagher_decode(protocol);
protocol_gallagher_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
+4
View File
@@ -337,6 +337,10 @@ bool protocol_h10301_write_data(ProtocolH10301* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_h10301_encoder_start(protocol);
protocol_h10301_decode(protocol->encoded_data, protocol->data);
protocol_h10301_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
@@ -171,6 +171,10 @@ bool protocol_hid_ex_generic_write_data(ProtocolHIDEx* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_hid_ex_generic_encoder_start(protocol);
protocol_hid_ex_generic_decode(protocol->encoded_data, protocol->data);
protocol_hid_ex_generic_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
@@ -203,6 +203,10 @@ bool protocol_hid_generic_write_data(ProtocolHID* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_hid_generic_encoder_start(protocol);
protocol_hid_generic_decode(protocol->encoded_data, protocol->data);
protocol_hid_generic_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
@@ -259,6 +259,10 @@ bool protocol_io_prox_xsf_write_data(ProtocolIOProxXSF* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);
protocol_io_prox_xsf_decode(protocol->encoded_data, protocol->data);
protocol_io_prox_xsf_encode(protocol->data, protocol->encoded_data);
if(request->write_type == LFRFIDWriteTypeT5577) {
@@ -169,6 +169,10 @@ bool protocol_jablotron_write_data(ProtocolJablotron* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_jablotron_encoder_start(protocol);
protocol_jablotron_decode(protocol);
protocol_jablotron_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
@@ -182,6 +182,10 @@ bool protocol_pac_stanley_write_data(ProtocolPACStanley* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_pac_stanley_encoder_start(protocol);
protocol_pac_stanley_decode(protocol);
protocol_pac_stanley_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
+4
View File
@@ -162,6 +162,10 @@ bool protocol_paradox_write_data(ProtocolParadox* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data);
protocol_paradox_decode(protocol->encoded_data, protocol->data);
protocol_paradox_encode(protocol->data, (uint8_t*)protocol->encoded_data);
if(request->write_type == LFRFIDWriteTypeT5577) {
+4
View File
@@ -219,6 +219,10 @@ bool protocol_pyramid_write_data(ProtocolPyramid* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_pyramid_encode(protocol);
protocol_pyramid_decode(protocol);
protocol_pyramid_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
+4
View File
@@ -157,6 +157,10 @@ bool protocol_viking_write_data(ProtocolViking* protocol, void* data) {
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
bool result = false;
// Correct protocol data by redecoding
protocol_viking_encoder_start(protocol);
protocol_viking_decode(protocol);
protocol_viking_encoder_start(protocol);
if(request->write_type == LFRFIDWriteTypeT5577) {
+56 -41
View File
@@ -5,21 +5,26 @@
#ifdef FURI_NDEBUG
#define LFS_NO_ASSERT
#define LFS_ASSERT(x)
#else
#else
#define LFS_ASSERT furi_assert
#endif
#define LFS_TAG "Lfs"
#ifdef FURI_LFS_DEBUG
#define LFS_TRACE(...) FURI_LOG_T(LFS_TAG, __VA_ARGS__);
#define LFS_DEBUG(...) FURI_LOG_D(LFS_TAG, __VA_ARGS__);
#else
#define LFS_TRACE(...)
#define LFS_DEBUG(...)
#endif // FURI_LFS_DEBUG
#define LFS_WARN(...) FURI_LOG_W(LFS_TAG, __VA_ARGS__);
#define LFS_ERROR(...) FURI_LOG_E(LFS_TAG, __VA_ARGS__);
// Because crc
#undef LFS_CONFIG
@@ -35,16 +40,13 @@
#ifndef LFS_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS_NO_DEBUG) || \
!defined(LFS_NO_WARN) || \
!defined(LFS_NO_ERROR) || \
defined(LFS_YES_TRACE)
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR) || \
defined(LFS_YES_TRACE)
#include <stdio.h>
#endif
#ifdef __cplusplus
extern "C"
{
extern "C" {
#endif
// Builtin functions, these may be replaced by more efficient
@@ -66,21 +68,29 @@ static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
}
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
return lfs_aligndown(a + alignment-1, alignment);
return lfs_aligndown(a + alignment - 1, alignment);
}
// Find the smallest power of 2 greater than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1);
return 32 - __builtin_clz(a - 1);
#else
uint32_t r = 0;
uint32_t s;
a -= 1;
s = (a > 0xffff) << 4; a >>= s; r |= s;
s = (a > 0xff ) << 3; a >>= s; r |= s;
s = (a > 0xf ) << 2; a >>= s; r |= s;
s = (a > 0x3 ) << 1; a >>= s; r |= s;
s = (a > 0xffff) << 4;
a >>= s;
r |= s;
s = (a > 0xff) << 3;
a >>= s;
r |= s;
s = (a > 0xf) << 2;
a >>= s;
r |= s;
s = (a > 0x3) << 1;
a >>= s;
r |= s;
return (r | (a >> 1)) + 1;
#endif
}
@@ -114,20 +124,23 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) {
// Convert between 32-bit little-endian and native order
static inline uint32_t lfs_fromle32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
#if !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \
BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \
__BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \
__BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return __builtin_bswap32(a);
#else
return (((uint8_t*)&a)[0] << 0) |
(((uint8_t*)&a)[1] << 8) |
(((uint8_t*)&a)[2] << 16) |
return (((uint8_t*)&a)[0] << 0) | (((uint8_t*)&a)[1] << 8) | (((uint8_t*)&a)[2] << 16) |
(((uint8_t*)&a)[3] << 24);
#endif
}
@@ -138,21 +151,24 @@ static inline uint32_t lfs_tole32(uint32_t a) {
// Convert between 32-bit big-endian and native order
static inline uint32_t lfs_frombe32(uint32_t a) {
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
#if !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_LITTLE_ENDIAN) && \
BYTE_ORDER == ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_LITTLE_ENDIAN) && \
__BYTE_ORDER == __ORDER_LITTLE_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return __builtin_bswap32(a);
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
#elif !defined(LFS_NO_INTRINSICS) && \
((defined(BYTE_ORDER) && defined(ORDER_BIG_ENDIAN) && BYTE_ORDER == ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER) && defined(__ORDER_BIG_ENDIAN) && \
__BYTE_ORDER == __ORDER_BIG_ENDIAN) || \
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
return a;
#else
return (((uint8_t*)&a)[0] << 24) |
(((uint8_t*)&a)[1] << 16) |
(((uint8_t*)&a)[2] << 8) |
(((uint8_t*)&a)[3] << 0);
return (((uint8_t*)&a)[0] << 24) | (((uint8_t*)&a)[1] << 16) | (((uint8_t*)&a)[2] << 8) |
(((uint8_t*)&a)[3] << 0);
#endif
}
@@ -161,11 +177,11 @@ static inline uint32_t lfs_tobe32(uint32_t a) {
}
// Calculate CRC-32 with polynomial = 0x04c11db7
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
uint32_t lfs_crc(uint32_t crc, const void* buffer, size_t size);
// Allocate memory, only used if buffers are not provided to littlefs
// Note, memory must be 64-bit aligned
static inline void *lfs_malloc(size_t size) {
static inline void* lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
return malloc(size);
#else
@@ -175,7 +191,7 @@ static inline void *lfs_malloc(size_t size) {
}
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs_free(void *p) {
static inline void lfs_free(void* p) {
#ifndef LFS_NO_MALLOC
free(p);
#else
@@ -183,7 +199,6 @@ static inline void lfs_free(void *p) {
#endif
}
#ifdef __cplusplus
} /* extern "C" */
#endif
-17
View File
@@ -1,17 +0,0 @@
Import("env")
env.Append(
CPPPATH=[
"#/lib/loclass",
],
)
libenv = env.Clone(FW_LIB_NAME="loclass")
libenv.ApplyLibFlags()
sources = Glob("loclass/*.c", source=True)
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
libenv.Install("${LIB_DIST_DIR}", lib)
Return("lib")
-313
View File
@@ -1,313 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
/*
This file contains an optimized version of the MAC-calculation algorithm. Some measurements on
a std laptop showed it runs in about 1/3 of the time:
Std: 0.428962
Opt: 0.151609
Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can
be easily dropped into a code base.
The optimizations have been performed in the following steps:
* Parameters passed by reference instead of by value.
* Iteration instead of recursion, un-nesting recursive loops into for-loops.
* Handling of bytes instead of individual bits, for less shuffling and masking
* Less creation of "objects", structs, and instead reuse of alloc:ed memory
* Inlining some functions via #define:s
As a consequence, this implementation is less generic. Also, I haven't bothered documenting this.
For a thorough documentation, check out the MAC-calculation within cipher.c instead.
-- MHS 2015
**/
/**
The runtime of opt_doTagMAC_2() with the MHS optimized version was 403 microseconds on Proxmark3.
This was still to slow for some newer readers which didn't want to wait that long.
Further optimizations to speedup the MAC calculations:
* Optimized opt_Tt logic
* Look up table for opt_select
* Removing many unnecessary bit maskings (& 0x1)
* updating state in place instead of alternating use of a second state structure
* remove the necessity to reverse bits of input and output bytes
opt_doTagMAC_2() now completes in 270 microseconds.
-- piwi 2019
**/
/**
add the possibility to do iCLASS on device only
-- iceman 2020
**/
#include "optimized_cipher.h"
#include "optimized_elite.h"
#include "optimized_ikeys.h"
#include "optimized_cipherutils.h"
static const uint8_t loclass_opt_select_LUT[256] = {
00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04,
01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04,
06, 05, 04, 07, 04, 05, 06, 07, 06, 05, 05, 06, 04, 05, 07, 06,
07, 04, 05, 06, 04, 05, 06, 07, 07, 04, 04, 07, 04, 05, 07, 06,
06, 05, 04, 07, 04, 05, 06, 07, 02, 01, 01, 02, 00, 01, 03, 02,
03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06,
00, 03, 02, 01, 02, 03, 00, 01, 00, 03, 03, 00, 02, 03, 01, 00,
05, 06, 07, 04, 06, 07, 04, 05, 05, 06, 06, 05, 06, 07, 05, 04,
02, 01, 00, 03, 00, 01, 02, 03, 06, 05, 05, 06, 04, 05, 07, 06,
03, 00, 01, 02, 00, 01, 02, 03, 07, 04, 04, 07, 04, 05, 07, 06,
02, 01, 00, 03, 00, 01, 02, 03, 02, 01, 01, 02, 00, 01, 03, 02,
03, 00, 01, 02, 00, 01, 02, 03, 03, 00, 00, 03, 00, 01, 03, 02,
04, 07, 06, 05, 06, 07, 04, 05, 00, 03, 03, 00, 02, 03, 01, 00,
01, 02, 03, 00, 02, 03, 00, 01, 05, 06, 06, 05, 06, 07, 05, 04,
04, 07, 06, 05, 06, 07, 04, 05, 04, 07, 07, 04, 06, 07, 05, 04,
01, 02, 03, 00, 02, 03, 00, 01, 01, 02, 02, 01, 02, 03, 01, 00
};
/********************** the table above has been generated with this code: ********
#include "util.h"
static void init_opt_select_LUT(void) {
for (int r = 0; r < 256; r++) {
uint8_t r_ls2 = r << 2;
uint8_t r_and_ls2 = r & r_ls2;
uint8_t r_or_ls2 = r | r_ls2;
uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3);
uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r;
uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r;
loclass_opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1);
}
print_result("", loclass_opt_select_LUT, 256);
}
***********************************************************************************/
#define loclass_opt__select(x,y,r) (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ( (r | r << 2) >> 3)))\
|(2 & (((r | r << 2) >> 6) ^ ( (r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1)))\
|(1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x))
static void loclass_opt_successor(const uint8_t *k, LoclassState_t *s, uint8_t y) {
uint16_t Tt = s->t & 0xc533;
Tt = Tt ^ (Tt >> 1);
Tt = Tt ^ (Tt >> 4);
Tt = Tt ^ (Tt >> 10);
Tt = Tt ^ (Tt >> 8);
s->t = (s->t >> 1);
s->t |= (Tt ^ (s->r >> 7) ^ (s->r >> 3)) << 15;
uint8_t opt_B = s->b;
opt_B ^= s->b >> 6;
opt_B ^= s->b >> 5;
opt_B ^= s->b >> 4;
s->b = s->b >> 1;
s->b |= (opt_B ^ s->r) << 7;
uint8_t opt_select = loclass_opt_select_LUT[s->r] & 0x04;
opt_select |= (loclass_opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02;
opt_select |= (loclass_opt_select_LUT[s->r] ^ Tt) & 0x01;
uint8_t r = s->r;
s->r = (k[opt_select] ^ s->b) + s->l ;
s->l = s->r + r;
}
static void loclass_opt_suc(const uint8_t *k, LoclassState_t *s, const uint8_t *in, uint8_t length, bool add32Zeroes) {
for (int i = 0; i < length; i++) {
uint8_t head;
head = in[i];
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
head >>= 1;
loclass_opt_successor(k, s, head);
}
//For tag MAC, an additional 32 zeroes
if (add32Zeroes) {
for (int i = 0; i < 16; i++) {
loclass_opt_successor(k, s, 0);
loclass_opt_successor(k, s, 0);
}
}
}
static void loclass_opt_output(const uint8_t *k, LoclassState_t *s, uint8_t *buffer) {
for (uint8_t times = 0; times < 4; times++) {
uint8_t bout = 0;
bout |= (s->r & 0x4) >> 2;
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4) >> 1;
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4);
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4) << 1;
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4) << 2;
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4) << 3;
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4) << 4;
loclass_opt_successor(k, s, 0);
bout |= (s->r & 0x4) << 5;
loclass_opt_successor(k, s, 0);
buffer[times] = bout;
}
}
static void loclass_opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) {
LoclassState_t _init = {
((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l
((k[0] ^ 0x4c) + 0x21) & 0xFF,// r
0x4c, // b
0xE012 // t
};
loclass_opt_suc(k, &_init, input, 12, false);
loclass_opt_output(k, &_init, out);
}
static void loclass_opt_MAC_N(uint8_t *k, uint8_t *input, uint8_t in_size, uint8_t *out) {
LoclassState_t _init = {
((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l
((k[0] ^ 0x4c) + 0x21) & 0xFF,// r
0x4c, // b
0xE012 // t
};
loclass_opt_suc(k, &_init, input, in_size, false);
loclass_opt_output(k, &_init, out);
}
void loclass_opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) {
uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0};
loclass_opt_MAC(div_key_p, cc_nr_p, dest);
memcpy(mac, dest, 4);
}
void loclass_opt_doReaderMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) {
loclass_opt_suc(div_key_p, &_init, nr, 4, false);
loclass_opt_output(div_key_p, &_init, mac);
}
void loclass_doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]) {
uint8_t dest [] = {0, 0, 0, 0, 0, 0, 0, 0};
loclass_opt_MAC_N(div_key_p, in_p, in_size, dest);
memcpy(mac, dest, 4);
}
void loclass_opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) {
LoclassState_t _init = {
((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l
((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r
0x4c, // b
0xE012 // t
};
loclass_opt_suc(div_key_p, &_init, cc_p, 12, true);
loclass_opt_output(div_key_p, &_init, mac);
}
/**
* The tag MAC can be divided (both can, but no point in dividing the reader mac) into
* two functions, since the first 8 bytes are known, we can pre-calculate the state
* reached after feeding CC to the cipher.
* @param cc_p
* @param div_key_p
* @return the cipher state
*/
LoclassState_t loclass_opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) {
LoclassState_t _init = {
((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l
((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r
0x4c, // b
0xE012 // t
};
loclass_opt_suc(div_key_p, &_init, cc_p, 8, false);
return _init;
}
/**
* The second part of the tag MAC calculation, since the CC is already calculated into the state,
* this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag
* MAC response.
* @param _init - precalculated cipher state
* @param nr - the reader challenge
* @param mac - where to store the MAC
* @param div_key_p - the key to use
*/
void loclass_opt_doTagMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) {
loclass_opt_suc(div_key_p, &_init, nr, 4, true);
loclass_opt_output(div_key_p, &_init, mac);
}
void loclass_iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite) {
if (elite) {
uint8_t keytable[128] = {0};
uint8_t key_index[8] = {0};
uint8_t key_sel[8] = { 0 };
uint8_t key_sel_p[8] = { 0 };
loclass_hash2(key, keytable);
loclass_hash1(csn, key_index);
for (uint8_t i = 0; i < 8 ; i++)
key_sel[i] = keytable[key_index[i]];
//Permute from iclass format to standard format
loclass_permutekey_rev(key_sel, key_sel_p);
loclass_diversifyKey(csn, key_sel_p, div_key);
} else {
loclass_diversifyKey(csn, key, div_key);
}
}
-90
View File
@@ -1,90 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// More recently from https://github.com/RfidResearchGroup/proxmark3
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
#ifndef OPTIMIZED_CIPHER_H
#define OPTIMIZED_CIPHER_H
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
/**
* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2
* consisting of the following four components:
* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ;
* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ;
* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 .
* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 .
**/
typedef struct {
uint8_t l;
uint8_t r;
uint8_t b;
uint16_t t;
} LoclassState_t;
/** The reader MAC is MAC(key, CC * NR )
**/
void loclass_opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]);
void loclass_opt_doReaderMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p);
/**
* The tag MAC is MAC(key, CC * NR * 32x0))
*/
void loclass_opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]);
/**
* The tag MAC can be divided (both can, but no point in dividing the reader mac) into
* two functions, since the first 8 bytes are known, we can pre-calculate the state
* reached after feeding CC to the cipher.
* @param cc_p
* @param div_key_p
* @return the cipher state
*/
LoclassState_t loclass_opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p);
/**
* The second part of the tag MAC calculation, since the CC is already calculated into the state,
* this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag
* MAC response.
* @param _init - precalculated cipher state
* @param nr - the reader challenge
* @param mac - where to store the MAC
* @param div_key_p - the key to use
*/
void loclass_opt_doTagMAC_2(LoclassState_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p);
void loclass_doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]);
void loclass_iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite);
#endif // OPTIMIZED_CIPHER_H
-137
View File
@@ -1,137 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
#include "optimized_cipherutils.h"
#include <stdint.h>
/**
*
* @brief Return and remove the first bit (x0) in the stream : <x0 x1 x2 x3 ... xn >
* @param stream
* @return
*/
bool loclass_headBit(LoclassBitstreamIn_t *stream) {
int bytepos = stream->position >> 3; // divide by 8
int bitpos = (stream->position++) & 7; // mask out 00000111
return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1;
}
/**
* @brief Return and remove the last bit (xn) in the stream: <x0 x1 x2 ... xn>
* @param stream
* @return
*/
bool loclass_tailBit(LoclassBitstreamIn_t *stream) {
int bitpos = stream->numbits - 1 - (stream->position++);
int bytepos = bitpos >> 3;
bitpos &= 7;
return (*(stream->buffer + bytepos) >> (7 - bitpos)) & 1;
}
/**
* @brief Pushes bit onto the stream
* @param stream
* @param bit
*/
void loclass_pushBit(LoclassBitstreamOut_t *stream, bool bit) {
int bytepos = stream->position >> 3; // divide by 8
int bitpos = stream->position & 7;
*(stream->buffer + bytepos) |= (bit) << (7 - bitpos);
stream->position++;
stream->numbits++;
}
/**
* @brief Pushes the lower six bits onto the stream
* as b0 b1 b2 b3 b4 b5 b6
* @param stream
* @param bits
*/
void loclass_push6bits(LoclassBitstreamOut_t *stream, uint8_t bits) {
loclass_pushBit(stream, bits & 0x20);
loclass_pushBit(stream, bits & 0x10);
loclass_pushBit(stream, bits & 0x08);
loclass_pushBit(stream, bits & 0x04);
loclass_pushBit(stream, bits & 0x02);
loclass_pushBit(stream, bits & 0x01);
}
/**
* @brief loclass_bitsLeft
* @param stream
* @return number of bits left in stream
*/
int loclass_bitsLeft(LoclassBitstreamIn_t *stream) {
return stream->numbits - stream->position;
}
/**
* @brief numBits
* @param stream
* @return Number of bits stored in stream
*/
void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest) {
while (len--) {
dest[len] = (uint8_t) n;
n >>= 8;
}
}
uint64_t loclass_x_bytes_to_num(uint8_t *src, size_t len) {
uint64_t num = 0;
while (len--) {
num = (num << 8) | (*src);
src++;
}
return num;
}
uint8_t loclass_reversebytes(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
void loclass_reverse_arraybytes(uint8_t *arr, size_t len) {
uint8_t i;
for (i = 0; i < len ; i++) {
arr[i] = loclass_reversebytes(arr[i]);
}
}
void loclass_reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) {
uint8_t i;
for (i = 0; i < len ; i++) {
dest[i] = loclass_reversebytes(arr[i]);
}
}
-64
View File
@@ -1,64 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// More recently from https://github.com/RfidResearchGroup/proxmark3
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
#ifndef CIPHERUTILS_H
#define CIPHERUTILS_H
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct {
uint8_t *buffer;
uint8_t numbits;
uint8_t position;
} LoclassBitstreamIn_t;
typedef struct {
uint8_t *buffer;
uint8_t numbits;
uint8_t position;
} LoclassBitstreamOut_t;
bool loclass_headBit(LoclassBitstreamIn_t *stream);
bool loclass_tailBit(LoclassBitstreamIn_t *stream);
void loclass_pushBit(LoclassBitstreamOut_t *stream, bool bit);
int loclass_bitsLeft(LoclassBitstreamIn_t *stream);
void loclass_push6bits(LoclassBitstreamOut_t *stream, uint8_t bits);
void loclass_x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest);
uint64_t loclass_x_bytes_to_num(uint8_t *src, size_t len);
uint8_t loclass_reversebytes(uint8_t b);
void loclass_reverse_arraybytes(uint8_t *arr, size_t len);
void loclass_reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len);
#endif // CIPHERUTILS_H
-234
View File
@@ -1,234 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
#include "optimized_elite.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <mbedtls/des.h>
#include "optimized_ikeys.h"
/**
* @brief Permutes a key from standard NIST format to Iclass specific format
* from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220
*
* If you loclass_permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below.
*
* 1 0 1 1 1 1 1 1 bf
* 0 0 0 0 0 0 0 1 01
* 0 0 1 0 1 1 0 1 2d
* 0 0 1 0 1 0 1 0 2a
* 1 1 1 1 1 0 0 1 f9
* 0 1 0 0 0 1 0 0 44
* 1 0 0 0 1 1 0 1 8d
* 0 1 1 0 1 1 0 0 6c
*
* 8 0 b 8 b a 9 e
* a d 9 8 b 7 0 a
*
* @param key
* @param dest
*/
void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]) {
int i;
for (i = 0 ; i < 8 ; i++) {
dest[i] = (((key[7] & (0x80 >> i)) >> (7 - i)) << 7) |
(((key[6] & (0x80 >> i)) >> (7 - i)) << 6) |
(((key[5] & (0x80 >> i)) >> (7 - i)) << 5) |
(((key[4] & (0x80 >> i)) >> (7 - i)) << 4) |
(((key[3] & (0x80 >> i)) >> (7 - i)) << 3) |
(((key[2] & (0x80 >> i)) >> (7 - i)) << 2) |
(((key[1] & (0x80 >> i)) >> (7 - i)) << 1) |
(((key[0] & (0x80 >> i)) >> (7 - i)) << 0);
}
}
/**
* Permutes a key from iclass specific format to NIST format
* @brief loclass_permutekey_rev
* @param key
* @param dest
*/
void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]) {
int i;
for (i = 0 ; i < 8 ; i++) {
dest[7 - i] = (((key[0] & (0x80 >> i)) >> (7 - i)) << 7) |
(((key[1] & (0x80 >> i)) >> (7 - i)) << 6) |
(((key[2] & (0x80 >> i)) >> (7 - i)) << 5) |
(((key[3] & (0x80 >> i)) >> (7 - i)) << 4) |
(((key[4] & (0x80 >> i)) >> (7 - i)) << 3) |
(((key[5] & (0x80 >> i)) >> (7 - i)) << 2) |
(((key[6] & (0x80 >> i)) >> (7 - i)) << 1) |
(((key[7] & (0x80 >> i)) >> (7 - i)) << 0);
}
}
/**
* Helper function for loclass_hash1
* @brief loclass_rr
* @param val
* @return
*/
static uint8_t loclass_rr(uint8_t val) {
return val >> 1 | ((val & 1) << 7);
}
/**
* Helper function for loclass_hash1
* @brief rl
* @param val
* @return
*/
static uint8_t loclass_rl(uint8_t val) {
return val << 1 | ((val & 0x80) >> 7);
}
/**
* Helper function for loclass_hash1
* @brief loclass_swap
* @param val
* @return
*/
static uint8_t loclass_swap(uint8_t val) {
return ((val >> 4) & 0xFF) | ((val & 0xFF) << 4);
}
/**
* Hash1 takes CSN as input, and determines what bytes in the keytable will be used
* when constructing the K_sel.
* @param csn the CSN used
* @param k output
*/
void loclass_hash1(const uint8_t csn[], uint8_t k[]) {
k[0] = csn[0] ^ csn[1] ^ csn[2] ^ csn[3] ^ csn[4] ^ csn[5] ^ csn[6] ^ csn[7];
k[1] = csn[0] + csn[1] + csn[2] + csn[3] + csn[4] + csn[5] + csn[6] + csn[7];
k[2] = loclass_rr(loclass_swap(csn[2] + k[1]));
k[3] = loclass_rl(loclass_swap(csn[3] + k[0]));
k[4] = ~loclass_rr(csn[4] + k[2]) + 1;
k[5] = ~loclass_rl(csn[5] + k[3]) + 1;
k[6] = loclass_rr(csn[6] + (k[4] ^ 0x3c));
k[7] = loclass_rl(csn[7] + (k[5] ^ 0xc3));
k[7] &= 0x7F;
k[6] &= 0x7F;
k[5] &= 0x7F;
k[4] &= 0x7F;
k[3] &= 0x7F;
k[2] &= 0x7F;
k[1] &= 0x7F;
k[0] &= 0x7F;
}
/**
Definition 14. Define the rotate key function loclass_rk : (F 82 ) 8 × N → (F 82 ) 8 as
loclass_rk(x [0] . . . x [7] , 0) = x [0] . . . x [7]
loclass_rk(x [0] . . . x [7] , n + 1) = loclass_rk(loclass_rl(x [0] ) . . . loclass_rl(x [7] ), n)
**/
static void loclass_rk(uint8_t *key, uint8_t n, uint8_t *outp_key) {
memcpy(outp_key, key, 8);
uint8_t j;
while (n-- > 0) {
for (j = 0; j < 8 ; j++)
outp_key[j] = loclass_rl(outp_key[j]);
}
return;
}
static mbedtls_des_context loclass_ctx_enc;
static mbedtls_des_context loclass_ctx_dec;
static void loclass_desdecrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) {
uint8_t key_std_format[8] = {0};
loclass_permutekey_rev(iclass_key, key_std_format);
mbedtls_des_setkey_dec(&loclass_ctx_dec, key_std_format);
mbedtls_des_crypt_ecb(&loclass_ctx_dec, input, output);
}
static void loclass_desencrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) {
uint8_t key_std_format[8] = {0};
loclass_permutekey_rev(iclass_key, key_std_format);
mbedtls_des_setkey_enc(&loclass_ctx_enc, key_std_format);
mbedtls_des_crypt_ecb(&loclass_ctx_enc, input, output);
}
/**
* @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select.
* @param key unpermuted custom key
* @param loclass_hash1 loclass_hash1
* @param key_sel output key_sel=h[loclass_hash1[i]]
*/
void hash2(uint8_t *key64, uint8_t *outp_keytable) {
/**
*Expected:
* High Security Key Table
00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1
10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21
20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2
30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C
40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6
50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42
60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95
70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB
**** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/
uint8_t key64_negated[8] = {0};
uint8_t z[8][8] = {{0}, {0}};
uint8_t temp_output[8] = {0};
//calculate complement of key
int i;
for (i = 0; i < 8; i++)
key64_negated[i] = ~key64[i];
// Once again, key is on iclass-format
loclass_desencrypt_iclass(key64, key64_negated, z[0]);
uint8_t y[8][8] = {{0}, {0}};
// y[0]=DES_dec(z[0],~key)
// Once again, key is on iclass-format
loclass_desdecrypt_iclass(z[0], key64_negated, y[0]);
for (i = 1; i < 8; i++) {
loclass_rk(key64, i, temp_output);
loclass_desdecrypt_iclass(temp_output, z[i - 1], z[i]);
loclass_desencrypt_iclass(temp_output, y[i - 1], y[i]);
}
if (outp_keytable != NULL) {
for (i = 0 ; i < 8 ; i++) {
memcpy(outp_keytable + i * 16, y[i], 8);
memcpy(outp_keytable + 8 + i * 16, z[i], 8);
}
}
}
-58
View File
@@ -1,58 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// More recently from https://github.com/RfidResearchGroup/proxmark3
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
#ifndef ELITE_CRACK_H
#define ELITE_CRACK_H
#include <stdint.h>
#include <stdlib.h>
void loclass_permutekey(const uint8_t key[8], uint8_t dest[8]);
/**
* Permutes a key from iclass specific format to NIST format
* @brief loclass_permutekey_rev
* @param key
* @param dest
*/
void loclass_permutekey_rev(const uint8_t key[8], uint8_t dest[8]);
/**
* Hash1 takes CSN as input, and determines what bytes in the keytable will be used
* when constructing the K_sel.
* @param csn the CSN used
* @param k output
*/
void loclass_hash1(const uint8_t *csn, uint8_t *k);
void loclass_hash2(uint8_t *key64, uint8_t *outp_keytable);
#endif
-321
View File
@@ -1,321 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
/**
From "Dismantling iclass":
This section describes in detail the built-in key diversification algorithm of iClass.
Besides the obvious purpose of deriving a card key from a master key, this
algorithm intends to circumvent weaknesses in the cipher by preventing the
usage of certain weak keys. In order to compute a diversified key, the iClass
reader first encrypts the card identity id with the master key K, using single
DES. The resulting ciphertext is then input to a function called loclass_hash0 which
outputs the diversified key k.
k = loclass_hash0(DES enc (id, K))
Here the DES encryption of id with master key K outputs a cryptogram c
of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8
which is used as input to the loclass_hash0 function. This function introduces some
obfuscation by performing a number of permutations, complement and modulo
operations, see Figure 2.5. Besides that, it checks for and removes patterns like
similar key bytes, which could produce a strong bias in the cipher. Finally, the
output of loclass_hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 .
**/
#include "optimized_ikeys.h"
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <mbedtls/des.h>
#include "optimized_cipherutils.h"
static const uint8_t loclass_pi[35] = {
0x0F, 0x17, 0x1B, 0x1D, 0x1E, 0x27, 0x2B, 0x2D,
0x2E, 0x33, 0x35, 0x39, 0x36, 0x3A, 0x3C, 0x47,
0x4B, 0x4D, 0x4E, 0x53, 0x55, 0x56, 0x59, 0x5A,
0x5C, 0x63, 0x65, 0x66, 0x69, 0x6A, 0x6C, 0x71,
0x72, 0x74, 0x78
};
/**
* @brief The key diversification algorithm uses 6-bit bytes.
* This implementation uses 64 bit uint to pack seven of them into one
* variable. When they are there, they are placed as follows:
* XXXX XXXX N0 .... N7, occupying the last 48 bits.
*
* This function picks out one from such a collection
* @param all
* @param n bitnumber
* @return
*/
static uint8_t loclass_getSixBitByte(uint64_t c, int n) {
return (c >> (42 - 6 * n)) & 0x3F;
}
/**
* @brief Puts back a six-bit 'byte' into a uint64_t.
* @param c buffer
* @param z the value to place there
* @param n bitnumber.
*/
static void loclass_pushbackSixBitByte(uint64_t *c, uint8_t z, int n) {
//0x XXXX YYYY ZZZZ ZZZZ ZZZZ
// ^z0 ^z7
//z0: 1111 1100 0000 0000
uint64_t masked = z & 0x3F;
uint64_t eraser = 0x3F;
masked <<= 42 - 6 * n;
eraser <<= 42 - 6 * n;
//masked <<= 6*n;
//eraser <<= 6*n;
eraser = ~eraser;
(*c) &= eraser;
(*c) |= masked;
}
/**
* @brief Swaps the z-values.
* If the input value has format XYZ0Z1...Z7, the output will have the format
* XYZ7Z6...Z0 instead
* @param c
* @return
*/
static uint64_t loclass_swapZvalues(uint64_t c) {
uint64_t newz = 0;
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 0), 7);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 1), 6);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 2), 5);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 3), 4);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 4), 3);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 5), 2);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 6), 1);
loclass_pushbackSixBitByte(&newz, loclass_getSixBitByte(c, 7), 0);
newz |= (c & 0xFFFF000000000000);
return newz;
}
/**
* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3
*/
static uint64_t loclass_ck(int i, int j, uint64_t z) {
if (i == 1 && j == -1) {
// loclass_ck(1, 1, z [0] . . . z [3] ) = z [0] . . . z [3]
return z;
} else if (j == -1) {
// loclass_ck(i, 1, z [0] . . . z [3] ) = loclass_ck(i 1, i 2, z [0] . . . z [3] )
return loclass_ck(i - 1, i - 2, z);
}
if (loclass_getSixBitByte(z, i) == loclass_getSixBitByte(z, j)) {
//loclass_ck(i, j 1, z [0] . . . z [i] ← j . . . z [3] )
uint64_t newz = 0;
int c;
for (c = 0; c < 4; c++) {
uint8_t val = loclass_getSixBitByte(z, c);
if (c == i)
loclass_pushbackSixBitByte(&newz, j, c);
else
loclass_pushbackSixBitByte(&newz, val, c);
}
return loclass_ck(i, j - 1, newz);
} else {
return loclass_ck(i, j - 1, z);
}
}
/**
Definition 8.
Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as
check(z [0] . . . z [7] ) = loclass_ck(3, 2, z [0] . . . z [3] ) · loclass_ck(3, 2, z [4] . . . z [7] )
where loclass_ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as
loclass_ck(1, 1, z [0] . . . z [3] ) = z [0] . . . z [3]
loclass_ck(i, 1, z [0] . . . z [3] ) = loclass_ck(i 1, i 2, z [0] . . . z [3] )
loclass_ck(i, j, z [0] . . . z [3] ) =
loclass_ck(i, j 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ;
loclass_ck(i, j 1, z [0] . . . z [3] ), otherwise
otherwise.
**/
static uint64_t loclass_check(uint64_t z) {
//These 64 bits are divided as c = x, y, z [0] , . . . , z [7]
// loclass_ck(3, 2, z [0] . . . z [3] )
uint64_t ck1 = loclass_ck(3, 2, z);
// loclass_ck(3, 2, z [4] . . . z [7] )
uint64_t ck2 = loclass_ck(3, 2, z << 24);
//The loclass_ck function will place the values
// in the middle of z.
ck1 &= 0x00000000FFFFFF000000;
ck2 &= 0x00000000FFFFFF000000;
return ck1 | ck2 >> 24;
}
static void loclass_permute(LoclassBitstreamIn_t *p_in, uint64_t z, int l, int r, LoclassBitstreamOut_t *out) {
if (loclass_bitsLeft(p_in) == 0)
return;
bool pn = loclass_tailBit(p_in);
if (pn) { // pn = 1
uint8_t zl = loclass_getSixBitByte(z, l);
loclass_push6bits(out, zl + 1);
loclass_permute(p_in, z, l + 1, r, out);
} else { // otherwise
uint8_t zr = loclass_getSixBitByte(z, r);
loclass_push6bits(out, zr);
loclass_permute(p_in, z, l, r + 1, out);
}
}
/**
* @brief
*Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as
* loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where
* z'[i] = (z[i] mod (63-i)) + i i = 0...3
* z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3
* ẑ = check(z');
* @param c
* @param k this is where the diversified key is put (should be 8 bytes)
* @return
*/
void loclass_hash0(uint64_t c, uint8_t k[8]) {
c = loclass_swapZvalues(c);
//These 64 bits are divided as c = x, y, z [0] , . . . , z [7]
// x = 8 bits
// y = 8 bits
// z0-z7 6 bits each : 48 bits
uint8_t x = (c & 0xFF00000000000000) >> 56;
uint8_t y = (c & 0x00FF000000000000) >> 48;
uint64_t zP = 0;
for (int n = 0; n < 4 ; n++) {
uint8_t zn = loclass_getSixBitByte(c, n);
uint8_t zn4 = loclass_getSixBitByte(c, n + 4);
uint8_t _zn = (zn % (63 - n)) + n;
uint8_t _zn4 = (zn4 % (64 - n)) + n;
loclass_pushbackSixBitByte(&zP, _zn, n);
loclass_pushbackSixBitByte(&zP, _zn4, n + 4);
}
uint64_t zCaret = loclass_check(zP);
uint8_t p = loclass_pi[x % 35];
if (x & 1) //Check if x7 is 1
p = ~p;
LoclassBitstreamIn_t p_in = { &p, 8, 0 };
uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0};
LoclassBitstreamOut_t out = {outbuffer, 0, 0};
loclass_permute(&p_in, zCaret, 0, 4, &out); //returns 48 bits? or 6 8-bytes
//Out is now a buffer containing six-bit bytes, should be 48 bits
// if all went well
//Shift z-values down onto the lower segment
uint64_t zTilde = loclass_x_bytes_to_num(outbuffer, sizeof(outbuffer));
zTilde >>= 16;
for (int i = 0; i < 8; i++) {
// the key on index i is first a bit from y
// then six bits from z,
// then a bit from p
// Init with zeroes
k[i] = 0;
// First, place yi leftmost in k
//k[i] |= (y << i) & 0x80 ;
// First, place y(7-i) leftmost in k
k[i] |= (y << (7 - i)) & 0x80 ;
uint8_t zTilde_i = loclass_getSixBitByte(zTilde, i);
// zTildeI is now on the form 00XXXXXX
// with one leftshift, it'll be
// 0XXXXXX0
// So after leftshift, we can OR it into k
// However, when doing complement, we need to
// again MASK 0XXXXXX0 (0x7E)
zTilde_i <<= 1;
//Finally, add bit from p or p-mod
//Shift bit i into rightmost location (mask only after complement)
uint8_t p_i = p >> i & 0x1;
if (k[i]) { // yi = 1
k[i] |= ~zTilde_i & 0x7E;
k[i] |= p_i & 1;
k[i] += 1;
} else { // otherwise
k[i] |= zTilde_i & 0x7E;
k[i] |= (~p_i) & 1;
}
}
}
/**
* @brief Performs Elite-class key diversification
* @param csn
* @param key
* @param div_key
*/
void loclass_diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key) {
mbedtls_des_context loclass_ctx_enc;
// Prepare the DES key
mbedtls_des_setkey_enc(&loclass_ctx_enc, key);
uint8_t crypted_csn[8] = {0};
// Calculate DES(CSN, KEY)
mbedtls_des_crypt_ecb(&loclass_ctx_enc, csn, crypted_csn);
//Calculate HASH0(DES))
uint64_t c_csn = loclass_x_bytes_to_num(crypted_csn, sizeof(crypted_csn));
loclass_hash0(c_csn, div_key);
}
-66
View File
@@ -1,66 +0,0 @@
//-----------------------------------------------------------------------------
// Borrowed initially from https://github.com/holiman/loclass
// More recently from https://github.com/RfidResearchGroup/proxmark3
// Copyright (C) 2014 Martin Holst Swende
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// See LICENSE.txt for the text of the license.
//-----------------------------------------------------------------------------
// WARNING
//
// THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY.
//
// USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL
// PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL,
// AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES.
//
// THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS.
//-----------------------------------------------------------------------------
// It is a reconstruction of the cipher engine used in iClass, and RFID techology.
//
// The implementation is based on the work performed by
// Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and
// Milosch Meriac in the paper "Dismantling IClass".
//-----------------------------------------------------------------------------
#ifndef IKEYS_H
#define IKEYS_H
#include <inttypes.h>
/**
* @brief
*Definition 11. Let the function loclass_hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as
* loclass_hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where
* z'[i] = (z[i] mod (63-i)) + i i = 0...3
* z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3
* ẑ = check(z');
* @param c
* @param k this is where the diversified key is put (should be 8 bytes)
* @return
*/
void loclass_hash0(uint64_t c, uint8_t k[8]);
/**
* @brief Performs Elite-class key diversification
* @param csn
* @param key
* @param div_key
*/
void loclass_diversifyKey(uint8_t *csn, const uint8_t *key, uint8_t *div_key);
/**
* @brief Permutes a key from standard NIST format to Iclass specific format
* @param key
* @param dest
*/
#endif // IKEYS_H
+8
View File
@@ -11,6 +11,14 @@ env.Append(
libenv = env.Clone(FW_LIB_NAME="mbedtls")
libenv.ApplyLibFlags()
libenv.AppendUnique(
CCFLAGS=[
# Required for lib to be linkable with .faps
"-mword-relocations",
"-mlong-calls",
],
)
sources = [
"mbedtls/library/des.c",
"mbedtls/library/sha1.c",
+3
View File
@@ -12,6 +12,9 @@ env.Append(
CPPDEFINES=[
"PB_ENABLE_MALLOC",
],
SDK_HEADERS=[
File("#/lib/micro-ecc/uECC.h"),
],
)
+209 -34
View File
@@ -5,6 +5,7 @@
#define MF_CLASSIC_DICT_FLIPPER_PATH EXT_PATH("nfc/assets/mf_classic_dict.nfc")
#define MF_CLASSIC_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
#define MF_CLASSIC_DICT_UNIT_TEST_PATH EXT_PATH("unit_tests/mf_classic_dict.nfc")
#define TAG "MfClassicDict"
@@ -23,6 +24,9 @@ bool mf_classic_dict_check_presence(MfClassicDictType dict_type) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_FLIPPER_PATH, NULL) == FSE_OK;
} else if(dict_type == MfClassicDictTypeUser) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_USER_PATH, NULL) == FSE_OK;
} else if(dict_type == MfClassicDictTypeUnitTest) {
dict_present = storage_common_stat(storage, MF_CLASSIC_DICT_UNIT_TEST_PATH, NULL) ==
FSE_OK;
}
furi_record_close(RECORD_STORAGE);
@@ -50,6 +54,15 @@ MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type) {
buffered_file_stream_close(dict->stream);
break;
}
} else if(dict_type == MfClassicDictTypeUnitTest) {
if(!buffered_file_stream_open(
dict->stream,
MF_CLASSIC_DICT_UNIT_TEST_PATH,
FSAM_READ_WRITE,
FSOM_CREATE_ALWAYS)) {
buffered_file_stream_close(dict->stream);
break;
}
}
// Read total amount of keys
@@ -86,38 +99,30 @@ void mf_classic_dict_free(MfClassicDict* dict) {
free(dict);
}
static void mf_classic_dict_int_to_str(uint8_t* key_int, string_t key_str) {
string_reset(key_str);
for(size_t i = 0; i < 6; i++) {
string_cat_printf(key_str, "%02X", key_int[i]);
}
}
static void mf_classic_dict_str_to_int(string_t key_str, uint64_t* key_int) {
uint8_t key_byte_tmp;
*key_int = 0ULL;
for(uint8_t i = 0; i < 12; i += 2) {
args_char_to_hex(
string_get_char(key_str, i), string_get_char(key_str, i + 1), &key_byte_tmp);
*key_int |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2);
}
}
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict) {
furi_assert(dict);
return dict->total_keys;
}
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
uint8_t key_byte_tmp = 0;
string_t next_line;
string_init(next_line);
bool key_read = false;
*key = 0ULL;
while(!key_read) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
for(uint8_t i = 0; i < 12; i += 2) {
args_char_to_hex(
string_get_char(next_line, i), string_get_char(next_line, i + 1), &key_byte_tmp);
*key |= (uint64_t)key_byte_tmp << 8 * (5 - i / 2);
}
key_read = true;
}
string_clear(next_line);
return key_read;
}
bool mf_classic_dict_rewind(MfClassicDict* dict) {
furi_assert(dict);
furi_assert(dict->stream);
@@ -125,24 +130,194 @@ bool mf_classic_dict_rewind(MfClassicDict* dict) {
return stream_rewind(dict->stream);
}
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) {
bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key) {
furi_assert(dict);
furi_assert(dict->stream);
string_t key_str;
string_init(key_str);
for(size_t i = 0; i < 6; i++) {
string_cat_printf(key_str, "%02X", key[i]);
bool key_read = false;
string_reset(key);
while(!key_read) {
if(!stream_read_line(dict->stream, key)) break;
if(string_get_char(key, 0) == '#') continue;
if(string_size(key) != NFC_MF_CLASSIC_KEY_LEN) continue;
string_left(key, 12);
key_read = true;
}
string_cat_printf(key_str, "\n");
return key_read;
}
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
string_t temp_key;
string_init(temp_key);
bool key_read = mf_classic_dict_get_next_key_str(dict, temp_key);
if(key_read) {
mf_classic_dict_str_to_int(temp_key, key);
}
string_clear(temp_key);
return key_read;
}
bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key) {
furi_assert(dict);
furi_assert(dict->stream);
string_t next_line;
string_init(next_line);
bool key_found = false;
stream_rewind(dict->stream);
while(!key_found) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
string_left(next_line, 12);
if(!string_equal_p(key, next_line)) continue;
key_found = true;
}
string_clear(next_line);
return key_found;
}
bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key) {
string_t temp_key;
string_init(temp_key);
mf_classic_dict_int_to_str(key, temp_key);
bool key_found = mf_classic_dict_is_key_present_str(dict, temp_key);
string_clear(temp_key);
return key_found;
}
bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key) {
furi_assert(dict);
furi_assert(dict->stream);
string_cat_printf(key, "\n");
bool key_added = false;
do {
if(!stream_seek(dict->stream, 0, StreamOffsetFromEnd)) break;
if(!stream_insert_string(dict->stream, key_str)) break;
if(!stream_insert_string(dict->stream, key)) break;
dict->total_keys++;
key_added = true;
} while(false);
string_clear(key_str);
string_left(key, 12);
return key_added;
}
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key) {
furi_assert(dict);
furi_assert(dict->stream);
string_t temp_key;
string_init(temp_key);
mf_classic_dict_int_to_str(key, temp_key);
bool key_added = mf_classic_dict_add_key_str(dict, temp_key);
string_clear(temp_key);
return key_added;
}
bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target) {
furi_assert(dict);
furi_assert(dict->stream);
string_t next_line;
uint32_t index = 0;
string_init(next_line);
string_reset(key);
bool key_found = false;
while(!key_found) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
if(index++ != target) continue;
string_set_n(key, next_line, 0, 12);
key_found = true;
}
string_clear(next_line);
return key_found;
}
bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target) {
furi_assert(dict);
furi_assert(dict->stream);
string_t temp_key;
string_init(temp_key);
bool key_found = mf_classic_dict_get_key_at_index_str(dict, temp_key, target);
if(key_found) {
mf_classic_dict_str_to_int(temp_key, key);
}
string_clear(temp_key);
return key_found;
}
bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target) {
furi_assert(dict);
furi_assert(dict->stream);
string_t next_line;
string_init(next_line);
bool key_found = false;
uint32_t index = 0;
stream_rewind(dict->stream);
while(!key_found) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
string_left(next_line, 12);
if(!string_equal_p(key, next_line)) continue;
key_found = true;
*target = index;
}
string_clear(next_line);
return key_found;
}
bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target) {
furi_assert(dict);
furi_assert(dict->stream);
string_t temp_key;
string_init(temp_key);
mf_classic_dict_int_to_str(key, temp_key);
bool key_found = mf_classic_dict_find_index_str(dict, temp_key, target);
string_clear(temp_key);
return key_found;
}
bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target) {
furi_assert(dict);
furi_assert(dict->stream);
string_t next_line;
string_init(next_line);
uint32_t index = 0;
bool key_removed = false;
while(!key_removed) {
if(!stream_read_line(dict->stream, next_line)) break;
if(string_get_char(next_line, 0) == '#') continue;
if(string_size(next_line) != NFC_MF_CLASSIC_KEY_LEN) continue;
if(index++ != target) continue;
stream_seek(dict->stream, -NFC_MF_CLASSIC_KEY_LEN, StreamOffsetFromCurrent);
if(!stream_delete(dict->stream, NFC_MF_CLASSIC_KEY_LEN)) break;
dict->total_keys--;
key_removed = true;
}
string_clear(next_line);
return key_removed;
}
+72 -1
View File
@@ -9,20 +9,91 @@
typedef enum {
MfClassicDictTypeUser,
MfClassicDictTypeFlipper,
MfClassicDictTypeUnitTest,
} MfClassicDictType;
typedef struct MfClassicDict MfClassicDict;
bool mf_classic_dict_check_presence(MfClassicDictType dict_type);
/** Allocate MfClassicDict instance
*
* @param[in] dict_type The dictionary type
*
* @return MfClassicDict instance
*/
MfClassicDict* mf_classic_dict_alloc(MfClassicDictType dict_type);
/** Free MfClassicDict instance
*
* @param dict MfClassicDict instance
*/
void mf_classic_dict_free(MfClassicDict* dict);
/** Get total keys count
*
* @param dict MfClassicDict instance
*
* @return total keys count
*/
uint32_t mf_classic_dict_get_total_keys(MfClassicDict* dict);
/** Rewind to the beginning
*
* @param dict MfClassicDict instance
*
* @return true on success
*/
bool mf_classic_dict_rewind(MfClassicDict* dict);
bool mf_classic_dict_is_key_present(MfClassicDict* dict, uint8_t* key);
bool mf_classic_dict_is_key_present_str(MfClassicDict* dict, string_t key);
bool mf_classic_dict_get_next_key(MfClassicDict* dict, uint64_t* key);
bool mf_classic_dict_rewind(MfClassicDict* dict);
bool mf_classic_dict_get_next_key_str(MfClassicDict* dict, string_t key);
/** Get key at target offset as uint64_t
*
* @param dict MfClassicDict instance
* @param[out] key Pointer to the uint64_t key
* @param[in] target Target offset from current position
*
* @return true on success
*/
bool mf_classic_dict_get_key_at_index(MfClassicDict* dict, uint64_t* key, uint32_t target);
/** Get key at target offset as string_t
*
* @param dict MfClassicDict instance
* @param[out] key Found key destination buffer
* @param[in] target Target offset from current position
*
* @return true on success
*/
bool mf_classic_dict_get_key_at_index_str(MfClassicDict* dict, string_t key, uint32_t target);
bool mf_classic_dict_add_key(MfClassicDict* dict, uint8_t* key);
/** Add string representation of the key
*
* @param dict MfClassicDict instance
* @param[in] key String representation of the key
*
* @return true on success
*/
bool mf_classic_dict_add_key_str(MfClassicDict* dict, string_t key);
bool mf_classic_dict_find_index(MfClassicDict* dict, uint8_t* key, uint32_t* target);
bool mf_classic_dict_find_index_str(MfClassicDict* dict, string_t key, uint32_t* target);
/** Delete key at target offset
*
* @param dict MfClassicDict instance
* @param[in] target Target offset from current position
*
* @return true on success
*/
bool mf_classic_dict_delete_index(MfClassicDict* dict, uint32_t target);
+230
View File
@@ -0,0 +1,230 @@
#include "mfkey32.h"
#include <furi/furi.h>
#include <storage/storage.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#include <m-array.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/nfc_util.h>
#define TAG "Mfkey32"
#define MFKEY32_LOGS_PATH EXT_PATH("nfc/.mfkey32.log")
typedef enum {
Mfkey32StateIdle,
Mfkey32StateAuthReceived,
Mfkey32StateAuthNtSent,
Mfkey32StateAuthArNrReceived,
} Mfkey32State;
typedef struct {
uint32_t cuid;
uint8_t sector;
MfClassicKey key;
uint32_t nt0;
uint32_t nr0;
uint32_t ar0;
uint32_t nt1;
uint32_t nr1;
uint32_t ar1;
} Mfkey32Params;
ARRAY_DEF(Mfkey32Params, Mfkey32Params, M_POD_OPLIST);
typedef struct {
uint8_t sector;
MfClassicKey key;
uint32_t nt;
uint32_t nr;
uint32_t ar;
} Mfkey32Nonce;
struct Mfkey32 {
Mfkey32State state;
Stream* file_stream;
Mfkey32Params_t params_arr;
Mfkey32Nonce nonce;
uint32_t cuid;
Mfkey32ParseDataCallback callback;
void* context;
};
Mfkey32* mfkey32_alloc(uint32_t cuid) {
Mfkey32* instance = malloc(sizeof(Mfkey32));
instance->cuid = cuid;
instance->state = Mfkey32StateIdle;
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->file_stream = buffered_file_stream_alloc(storage);
if(!buffered_file_stream_open(
instance->file_stream, MFKEY32_LOGS_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) {
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
free(instance);
instance = NULL;
} else {
Mfkey32Params_init(instance->params_arr);
}
furi_record_close(RECORD_STORAGE);
return instance;
}
void mfkey32_free(Mfkey32* instance) {
furi_assert(instance != NULL);
Mfkey32Params_clear(instance->params_arr);
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
free(instance);
}
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
static bool mfkey32_write_params(Mfkey32* instance, Mfkey32Params* params) {
string_t str;
string_init_printf(
str,
"Sector %d key %c cuid %08x nt0 %08x nr0 %08x ar0 %08x nt1 %08x nr1 %08x ar1 %08x\n",
params->sector,
params->key == MfClassicKeyA ? 'A' : 'B',
params->cuid,
params->nt0,
params->nr0,
params->ar0,
params->nt1,
params->nr1,
params->ar1);
bool write_success = stream_write_string(instance->file_stream, str);
string_clear(str);
return write_success;
}
static void mfkey32_add_params(Mfkey32* instance) {
Mfkey32Nonce* nonce = &instance->nonce;
bool nonce_added = false;
// Search if we partially collected params
if(Mfkey32Params_size(instance->params_arr)) {
Mfkey32Params_it_t it;
for(Mfkey32Params_it(it, instance->params_arr); !Mfkey32Params_end_p(it);
Mfkey32Params_next(it)) {
Mfkey32Params* params = Mfkey32Params_ref(it);
if((params->sector == nonce->sector) && (params->key == nonce->key)) {
params->nt1 = nonce->nt;
params->nr1 = nonce->nr;
params->ar1 = nonce->ar;
nonce_added = true;
FURI_LOG_I(
TAG,
"Params for sector %d key %c collected",
params->sector,
params->key == MfClassicKeyA ? 'A' : 'B');
// Write on sd card
if(mfkey32_write_params(instance, params)) {
Mfkey32Params_remove(instance->params_arr, it);
if(instance->callback) {
instance->callback(Mfkey32EventParamCollected, instance->context);
}
}
}
}
}
if(!nonce_added) {
Mfkey32Params params = {
.sector = nonce->sector,
.key = nonce->key,
.cuid = instance->cuid,
.nt0 = nonce->nt,
.nr0 = nonce->nr,
.ar0 = nonce->ar,
};
Mfkey32Params_push_back(instance->params_arr, params);
}
}
void mfkey32_process_data(
Mfkey32* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
furi_assert(instance);
furi_assert(data);
Mfkey32Nonce* nonce = &instance->nonce;
uint16_t data_len = len;
if((data_len > 3) && !crc_dropped) {
data_len -= 2;
}
bool data_processed = false;
if(instance->state == Mfkey32StateIdle) {
if(reader_to_tag) {
if((data[0] == 0x60) || (data[0] == 0x61)) {
nonce->key = data[0] == 0x60 ? MfClassicKeyA : MfClassicKeyB;
nonce->sector = mf_classic_get_sector_by_block(data[1]);
instance->state = Mfkey32StateAuthReceived;
data_processed = true;
}
}
} else if(instance->state == Mfkey32StateAuthReceived) {
if(!reader_to_tag) {
if(len == 4) {
nonce->nt = nfc_util_bytes2num(data, 4);
instance->state = Mfkey32StateAuthNtSent;
data_processed = true;
}
}
} else if(instance->state == Mfkey32StateAuthNtSent) {
if(reader_to_tag) {
if(len == 8) {
nonce->nr = nfc_util_bytes2num(data, 4);
nonce->ar = nfc_util_bytes2num(&data[4], 4);
mfkey32_add_params(instance);
instance->state = Mfkey32StateIdle;
}
}
}
if(!data_processed) {
instance->state = Mfkey32StateIdle;
}
}
uint16_t mfkey32_get_auth_sectors(string_t data_str) {
furi_assert(data_str);
uint16_t nonces_num = 0;
Storage* storage = furi_record_open(RECORD_STORAGE);
Stream* file_stream = buffered_file_stream_alloc(storage);
string_t temp_str;
string_init(temp_str);
do {
if(!buffered_file_stream_open(
file_stream, MFKEY32_LOGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING))
break;
while(true) {
if(!stream_read_line(file_stream, temp_str)) break;
size_t uid_pos = string_search_str(temp_str, "cuid");
string_left(temp_str, uid_pos);
string_push_back(temp_str, '\n');
string_cat(data_str, temp_str);
nonces_num++;
}
} while(false);
buffered_file_stream_close(file_stream);
stream_free(file_stream);
string_clear(temp_str);
return nonces_num;
}
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include <lib/nfc/protocols/mifare_classic.h>
#include <m-string.h>
typedef struct Mfkey32 Mfkey32;
typedef enum {
Mfkey32EventParamCollected,
} Mfkey32Event;
typedef void (*Mfkey32ParseDataCallback)(Mfkey32Event event, void* context);
Mfkey32* mfkey32_alloc(uint32_t cuid);
void mfkey32_free(Mfkey32* instance);
void mfkey32_process_data(
Mfkey32* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped);
void mfkey32_set_callback(Mfkey32* instance, Mfkey32ParseDataCallback callback, void* context);
uint16_t mfkey32_get_auth_sectors(string_t string);
+72
View File
@@ -0,0 +1,72 @@
#include "nfc_debug_log.h"
#include <m-string.h>
#include <storage/storage.h>
#include <stream/buffered_file_stream.h>
#define TAG "NfcDebugLog"
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.txt")
struct NfcDebugLog {
Stream* file_stream;
string_t data_str;
};
NfcDebugLog* nfc_debug_log_alloc() {
NfcDebugLog* instance = malloc(sizeof(NfcDebugLog));
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->file_stream = buffered_file_stream_alloc(storage);
if(!buffered_file_stream_open(
instance->file_stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
instance->file_stream = NULL;
}
if(!instance->file_stream) {
free(instance);
instance = NULL;
} else {
string_init(instance->data_str);
}
furi_record_close(RECORD_STORAGE);
return instance;
}
void nfc_debug_log_free(NfcDebugLog* instance) {
furi_assert(instance);
furi_assert(instance->file_stream);
furi_assert(instance->data_str);
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
string_clear(instance->data_str);
free(instance);
}
void nfc_debug_log_process_data(
NfcDebugLog* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
furi_assert(instance);
furi_assert(instance->file_stream);
furi_assert(instance->data_str);
furi_assert(data);
UNUSED(crc_dropped);
string_printf(instance->data_str, "%lu %c:", furi_get_tick(), reader_to_tag ? 'R' : 'T');
uint16_t data_len = len;
for(size_t i = 0; i < data_len; i++) {
string_cat_printf(instance->data_str, " %02x", data[i]);
}
string_push_back(instance->data_str, '\n');
stream_write_string(instance->file_stream, instance->data_str);
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
typedef struct NfcDebugLog NfcDebugLog;
NfcDebugLog* nfc_debug_log_alloc();
void nfc_debug_log_free(NfcDebugLog* instance);
void nfc_debug_log_process_data(
NfcDebugLog* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped);
+83 -119
View File
@@ -1,7 +1,9 @@
#include "nfc_debug_pcap.h"
#include <storage/storage.h>
#include <stream/buffered_file_stream.h>
#include <furi_hal_nfc.h>
#include <furi_hal_rtc.h>
#include <stream_buffer.h>
#define TAG "NfcDebugPcap"
@@ -16,48 +18,94 @@
#define DATA_PCD_TO_PICC_CRC_DROPPED 0xFA
#define NFC_DEBUG_PCAP_FILENAME EXT_PATH("nfc/debug.pcap")
#define NFC_DEBUG_PCAP_BUFFER_SIZE 64
struct NfcDebugPcapWorker {
bool alive;
Storage* storage;
File* file;
StreamBufferHandle_t stream;
FuriThread* thread;
struct NfcDebugPcap {
Stream* file_stream;
};
static File* nfc_debug_pcap_open(Storage* storage) {
File* file = storage_file_alloc(storage);
if(!storage_file_open(file, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
storage_file_free(file);
return NULL;
}
if(!storage_file_tell(file)) {
struct {
uint32_t magic;
uint16_t major, minor;
uint32_t reserved[2];
uint32_t snaplen;
uint32_t link_type;
} __attribute__((__packed__)) pcap_hdr = {
.magic = PCAP_MAGIC,
.major = PCAP_MAJOR,
.minor = PCAP_MINOR,
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
.link_type = DLT_ISO_14443,
};
if(storage_file_write(file, &pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
FURI_LOG_E(TAG, "Failed to write pcap header");
static Stream* nfc_debug_pcap_open(Storage* storage) {
Stream* stream = NULL;
stream = buffered_file_stream_alloc(storage);
if(!buffered_file_stream_open(stream, NFC_DEBUG_PCAP_FILENAME, FSAM_WRITE, FSOM_OPEN_APPEND)) {
buffered_file_stream_close(stream);
stream_free(stream);
stream = NULL;
} else {
if(!stream_tell(stream)) {
struct {
uint32_t magic;
uint16_t major, minor;
uint32_t reserved[2];
uint32_t snaplen;
uint32_t link_type;
} __attribute__((__packed__)) pcap_hdr = {
.magic = PCAP_MAGIC,
.major = PCAP_MAJOR,
.minor = PCAP_MINOR,
.snaplen = FURI_HAL_NFC_DATA_BUFF_SIZE,
.link_type = DLT_ISO_14443,
};
if(stream_write(stream, (uint8_t*)&pcap_hdr, sizeof(pcap_hdr)) != sizeof(pcap_hdr)) {
FURI_LOG_E(TAG, "Failed to write pcap header");
buffered_file_stream_close(stream);
stream_free(stream);
stream = NULL;
}
}
}
return file;
return stream;
}
static void
nfc_debug_pcap_write(NfcDebugPcapWorker* instance, uint8_t event, uint8_t* data, uint16_t len) {
NfcDebugPcap* nfc_debug_pcap_alloc() {
NfcDebugPcap* instance = malloc(sizeof(NfcDebugPcap));
Storage* storage = furi_record_open(RECORD_STORAGE);
instance->file_stream = nfc_debug_pcap_open(storage);
if(!instance->file_stream) {
free(instance);
instance = NULL;
}
furi_record_close(RECORD_STORAGE);
return instance;
}
void nfc_debug_pcap_free(NfcDebugPcap* instance) {
furi_assert(instance);
furi_assert(instance->file_stream);
buffered_file_stream_close(instance->file_stream);
stream_free(instance->file_stream);
free(instance);
}
void nfc_debug_pcap_process_data(
NfcDebugPcap* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
furi_assert(instance);
furi_assert(data);
FuriHalRtcDateTime datetime;
furi_hal_rtc_get_datetime(&datetime);
uint8_t event = 0;
if(reader_to_tag) {
if(crc_dropped) {
event = DATA_PCD_TO_PICC_CRC_DROPPED;
} else {
event = DATA_PCD_TO_PICC;
}
} else {
if(crc_dropped) {
event = DATA_PICC_TO_PCD_CRC_DROPPED;
} else {
event = DATA_PICC_TO_PCD;
}
}
struct {
// https://wiki.wireshark.org/Development/LibpcapFileFormat#record-packet-header
uint32_t ts_sec;
@@ -77,90 +125,6 @@ static void
.event = event,
.len = len << 8 | len >> 8,
};
xStreamBufferSend(instance->stream, &pkt_hdr, sizeof(pkt_hdr), FuriWaitForever);
xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
}
static void
nfc_debug_pcap_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t event = crc_dropped ? DATA_PCD_TO_PICC_CRC_DROPPED : DATA_PCD_TO_PICC;
nfc_debug_pcap_write(instance, event, data, bits / 8);
}
static void
nfc_debug_pcap_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t event = crc_dropped ? DATA_PICC_TO_PCD_CRC_DROPPED : DATA_PICC_TO_PCD;
nfc_debug_pcap_write(instance, event, data, bits / 8);
}
int32_t nfc_debug_pcap_thread(void* context) {
NfcDebugPcapWorker* instance = context;
uint8_t buffer[NFC_DEBUG_PCAP_BUFFER_SIZE];
while(instance->alive) {
size_t ret =
xStreamBufferReceive(instance->stream, buffer, NFC_DEBUG_PCAP_BUFFER_SIZE, 50);
if(storage_file_write(instance->file, buffer, ret) != ret) {
FURI_LOG_E(TAG, "Failed to write pcap data");
}
}
return 0;
}
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage) {
NfcDebugPcapWorker* instance = malloc(sizeof(NfcDebugPcapWorker));
instance->alive = true;
instance->storage = storage;
instance->file = nfc_debug_pcap_open(storage);
instance->stream = xStreamBufferCreate(4096, 1);
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "PcapWorker");
furi_thread_set_stack_size(instance->thread, 1024);
furi_thread_set_callback(instance->thread, nfc_debug_pcap_thread);
furi_thread_set_context(instance->thread, instance);
furi_thread_start(instance->thread);
return instance;
}
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance) {
furi_assert(instance);
instance->alive = false;
furi_thread_join(instance->thread);
furi_thread_free(instance->thread);
vStreamBufferDelete(instance->stream);
if(instance->file) storage_file_free(instance->file);
instance->storage = NULL;
free(instance);
}
void nfc_debug_pcap_prepare_tx_rx(
NfcDebugPcapWorker* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc) {
if(!instance || !instance->file) return;
if(is_picc) {
tx_rx->sniff_tx = nfc_debug_pcap_write_rx;
tx_rx->sniff_rx = nfc_debug_pcap_write_tx;
} else {
tx_rx->sniff_tx = nfc_debug_pcap_write_tx;
tx_rx->sniff_rx = nfc_debug_pcap_write_rx;
}
tx_rx->sniff_context = instance;
stream_write(instance->file_stream, (uint8_t*)&pkt_hdr, sizeof(pkt_hdr));
stream_write(instance->file_stream, data, len);
}
+11 -15
View File
@@ -1,21 +1,17 @@
#pragma once
#include <furi_hal_nfc.h>
#include <storage/storage.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct NfcDebugPcapWorker NfcDebugPcapWorker;
typedef struct NfcDebugPcap NfcDebugPcap;
NfcDebugPcapWorker* nfc_debug_pcap_alloc(Storage* storage);
NfcDebugPcap* nfc_debug_pcap_alloc();
void nfc_debug_pcap_free(NfcDebugPcapWorker* instance);
void nfc_debug_pcap_free(NfcDebugPcap* instance);
/** Prepare tx/rx context for debug pcap logging, if enabled.
*
* @param instance NfcDebugPcapWorker* instance, can be NULL
* @param tx_rx TX/RX context to log
* @param is_picc if true, record Flipper as PICC, else PCD.
*/
void nfc_debug_pcap_prepare_tx_rx(
NfcDebugPcapWorker* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc);
void nfc_debug_pcap_process_data(
NfcDebugPcap* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped);
+261
View File
@@ -0,0 +1,261 @@
#include "reader_analyzer.h"
#include <stream_buffer.h>
#include <lib/nfc/protocols/nfc_util.h>
#include <lib/nfc/protocols/mifare_classic.h>
#include <m-array.h>
#include "mfkey32.h"
#include "nfc_debug_pcap.h"
#include "nfc_debug_log.h"
#define TAG "ReaderAnalyzer"
#define READER_ANALYZER_MAX_BUFF_SIZE (1024)
typedef struct {
bool reader_to_tag;
bool crc_dropped;
uint16_t len;
} ReaderAnalyzerHeader;
typedef enum {
ReaderAnalyzerNfcDataMfClassic,
} ReaderAnalyzerNfcData;
struct ReaderAnalyzer {
FuriHalNfcDevData nfc_data;
bool alive;
StreamBufferHandle_t stream;
FuriThread* thread;
ReaderAnalyzerParseDataCallback callback;
void* context;
ReaderAnalyzerMode mode;
Mfkey32* mfkey32;
NfcDebugLog* debug_log;
NfcDebugPcap* pcap;
};
const FuriHalNfcDevData reader_analyzer_nfc_data[] = {
[ReaderAnalyzerNfcDataMfClassic] =
{.sak = 0x08,
.atqa = {0x44, 0x00},
.interface = FuriHalNfcInterfaceRf,
.type = FuriHalNfcTypeA,
.uid_len = 7,
.uid = {0x04, 0x77, 0x70, 0x2A, 0x23, 0x4F, 0x80},
.cuid = 0x2A234F80},
};
void reader_analyzer_parse(ReaderAnalyzer* instance, uint8_t* buffer, size_t size) {
if(size < sizeof(ReaderAnalyzerHeader)) return;
size_t bytes_i = 0;
while(bytes_i < size) {
ReaderAnalyzerHeader* header = (ReaderAnalyzerHeader*)&buffer[bytes_i];
uint16_t len = header->len;
if(bytes_i + len > size) break;
bytes_i += sizeof(ReaderAnalyzerHeader);
if(instance->mfkey32) {
mfkey32_process_data(
instance->mfkey32,
&buffer[bytes_i],
len,
header->reader_to_tag,
header->crc_dropped);
}
if(instance->pcap) {
nfc_debug_pcap_process_data(
instance->pcap, &buffer[bytes_i], len, header->reader_to_tag, header->crc_dropped);
}
if(instance->debug_log) {
nfc_debug_log_process_data(
instance->debug_log,
&buffer[bytes_i],
len,
header->reader_to_tag,
header->crc_dropped);
}
bytes_i += len;
}
}
int32_t reader_analyzer_thread(void* context) {
ReaderAnalyzer* reader_analyzer = context;
uint8_t buffer[READER_ANALYZER_MAX_BUFF_SIZE] = {};
while(reader_analyzer->alive || !xStreamBufferIsEmpty(reader_analyzer->stream)) {
size_t ret = xStreamBufferReceive(
reader_analyzer->stream, buffer, READER_ANALYZER_MAX_BUFF_SIZE, 50);
if(ret) {
reader_analyzer_parse(reader_analyzer, buffer, ret);
}
}
return 0;
}
ReaderAnalyzer* reader_analyzer_alloc() {
ReaderAnalyzer* instance = malloc(sizeof(ReaderAnalyzer));
instance->nfc_data = reader_analyzer_nfc_data[ReaderAnalyzerNfcDataMfClassic];
instance->alive = false;
instance->stream =
xStreamBufferCreate(READER_ANALYZER_MAX_BUFF_SIZE, sizeof(ReaderAnalyzerHeader));
instance->thread = furi_thread_alloc();
furi_thread_set_name(instance->thread, "ReaderAnalyzerWorker");
furi_thread_set_stack_size(instance->thread, 2048);
furi_thread_set_callback(instance->thread, reader_analyzer_thread);
furi_thread_set_context(instance->thread, instance);
furi_thread_set_priority(instance->thread, FuriThreadPriorityLow);
return instance;
}
static void reader_analyzer_mfkey_callback(Mfkey32Event event, void* context) {
furi_assert(context);
ReaderAnalyzer* instance = context;
if(event == Mfkey32EventParamCollected) {
if(instance->callback) {
instance->callback(ReaderAnalyzerEventMfkeyCollected, instance->context);
}
}
}
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode) {
furi_assert(instance);
xStreamBufferReset(instance->stream);
if(mode & ReaderAnalyzerModeDebugLog) {
instance->debug_log = nfc_debug_log_alloc();
}
if(mode & ReaderAnalyzerModeMfkey) {
instance->mfkey32 = mfkey32_alloc(instance->nfc_data.cuid);
if(instance->mfkey32) {
mfkey32_set_callback(instance->mfkey32, reader_analyzer_mfkey_callback, instance);
}
}
if(mode & ReaderAnalyzerModeDebugPcap) {
instance->pcap = nfc_debug_pcap_alloc();
}
instance->alive = true;
furi_thread_start(instance->thread);
}
void reader_analyzer_stop(ReaderAnalyzer* instance) {
furi_assert(instance);
instance->alive = false;
furi_thread_join(instance->thread);
if(instance->debug_log) {
nfc_debug_log_free(instance->debug_log);
instance->debug_log = NULL;
}
if(instance->mfkey32) {
mfkey32_free(instance->mfkey32);
instance->mfkey32 = NULL;
}
if(instance->pcap) {
nfc_debug_pcap_free(instance->pcap);
}
}
void reader_analyzer_free(ReaderAnalyzer* instance) {
furi_assert(instance);
reader_analyzer_stop(instance);
furi_thread_free(instance->thread);
vStreamBufferDelete(instance->stream);
free(instance);
}
void reader_analyzer_set_callback(
ReaderAnalyzer* instance,
ReaderAnalyzerParseDataCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
NfcProtocol
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len) {
furi_assert(instance);
furi_assert(buff_rx);
UNUSED(len);
NfcProtocol protocol = NfcDeviceProtocolUnknown;
if((buff_rx[0] == 0x60) || (buff_rx[0] == 0x61)) {
protocol = NfcDeviceProtocolMifareClassic;
}
return protocol;
}
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance) {
furi_assert(instance);
return &instance->nfc_data;
}
static void reader_analyzer_write(
ReaderAnalyzer* instance,
uint8_t* data,
uint16_t len,
bool reader_to_tag,
bool crc_dropped) {
ReaderAnalyzerHeader header = {
.reader_to_tag = reader_to_tag, .crc_dropped = crc_dropped, .len = len};
size_t data_sent = 0;
data_sent = xStreamBufferSend(
instance->stream, &header, sizeof(ReaderAnalyzerHeader), FuriWaitForever);
if(data_sent != sizeof(ReaderAnalyzerHeader)) {
FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, sizeof(ReaderAnalyzerHeader));
}
data_sent = xStreamBufferSend(instance->stream, data, len, FuriWaitForever);
if(data_sent != len) {
FURI_LOG_W(TAG, "Sent %d out of %d bytes", data_sent, len);
}
}
static void
reader_analyzer_write_rx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
UNUSED(crc_dropped);
ReaderAnalyzer* reader_analyzer = context;
uint16_t bytes = bits < 8 ? 1 : bits / 8;
reader_analyzer_write(reader_analyzer, data, bytes, false, crc_dropped);
}
static void
reader_analyzer_write_tx(uint8_t* data, uint16_t bits, bool crc_dropped, void* context) {
UNUSED(crc_dropped);
ReaderAnalyzer* reader_analyzer = context;
uint16_t bytes = bits < 8 ? 1 : bits / 8;
reader_analyzer_write(reader_analyzer, data, bytes, true, crc_dropped);
}
void reader_analyzer_prepare_tx_rx(
ReaderAnalyzer* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc) {
furi_assert(instance);
furi_assert(tx_rx);
if(is_picc) {
tx_rx->sniff_tx = reader_analyzer_write_rx;
tx_rx->sniff_rx = reader_analyzer_write_tx;
} else {
tx_rx->sniff_rx = reader_analyzer_write_rx;
tx_rx->sniff_tx = reader_analyzer_write_tx;
}
tx_rx->sniff_context = instance;
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <stdint.h>
#include <lib/nfc/nfc_device.h>
typedef enum {
ReaderAnalyzerModeDebugLog = 0x01,
ReaderAnalyzerModeMfkey = 0x02,
ReaderAnalyzerModeDebugPcap = 0x04,
} ReaderAnalyzerMode;
typedef enum {
ReaderAnalyzerEventMfkeyCollected,
} ReaderAnalyzerEvent;
typedef struct ReaderAnalyzer ReaderAnalyzer;
typedef void (*ReaderAnalyzerParseDataCallback)(ReaderAnalyzerEvent event, void* context);
ReaderAnalyzer* reader_analyzer_alloc();
void reader_analyzer_free(ReaderAnalyzer* instance);
void reader_analyzer_set_callback(
ReaderAnalyzer* instance,
ReaderAnalyzerParseDataCallback callback,
void* context);
void reader_analyzer_start(ReaderAnalyzer* instance, ReaderAnalyzerMode mode);
void reader_analyzer_stop(ReaderAnalyzer* instance);
NfcProtocol
reader_analyzer_guess_protocol(ReaderAnalyzer* instance, uint8_t* buff_rx, uint16_t len);
FuriHalNfcDevData* reader_analyzer_get_nfc_data(ReaderAnalyzer* instance);
void reader_analyzer_prepare_tx_rx(
ReaderAnalyzer* instance,
FuriHalNfcTxRxContext* tx_rx,
bool is_picc);
+14 -2
View File
@@ -195,6 +195,7 @@ bool nfc_device_load_mifare_ul_data(FlipperFormat* file, NfcDevice* dev) {
}
data->data_size = pages_total * 4;
data->data_read = pages_read * 4;
if(data->data_size > MF_UL_MAX_DUMP_SIZE || data->data_read > MF_UL_MAX_DUMP_SIZE) break;
bool pages_parsed = true;
for(uint16_t i = 0; i < pages_total; i++) {
string_printf(temp_str, "Page %d", i);
@@ -1181,8 +1182,19 @@ bool nfc_file_select(NfcDevice* dev) {
// Input events and views are managed by file_browser
string_t nfc_app_folder;
string_init_set_str(nfc_app_folder, NFC_APP_FOLDER);
bool res = dialog_file_browser_show(
dev->dialogs, dev->load_path, nfc_app_folder, NFC_APP_EXTENSION, true, &I_Nfc_10px, true);
const DialogsFileBrowserOptions browser_options = {
.extension = NFC_APP_EXTENSION,
.skip_assets = true,
.icon = &I_Nfc_10px,
.hide_ext = true,
.item_loader_callback = NULL,
.item_loader_context = NULL,
};
bool res =
dialog_file_browser_show(dev->dialogs, dev->load_path, nfc_app_folder, &browser_options);
string_clear(nfc_app_folder);
if(res) {
string_t filename;
+134 -17
View File
@@ -28,9 +28,7 @@ NfcWorker* nfc_worker_alloc() {
}
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
nfc_worker->debug_pcap_worker = nfc_debug_pcap_alloc(nfc_worker->storage);
}
nfc_worker->reader_analyzer = reader_analyzer_alloc(nfc_worker->storage);
return nfc_worker;
}
@@ -42,7 +40,7 @@ void nfc_worker_free(NfcWorker* nfc_worker) {
furi_record_close(RECORD_STORAGE);
if(nfc_worker->debug_pcap_worker) nfc_debug_pcap_free(nfc_worker->debug_pcap_worker);
reader_analyzer_free(nfc_worker->reader_analyzer);
free(nfc_worker);
}
@@ -105,6 +103,8 @@ int32_t nfc_worker_task(void* context) {
nfc_worker_mf_ultralight_read_auth(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateMfClassicDictAttack) {
nfc_worker_mf_classic_dict_attack(nfc_worker);
} else if(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
nfc_worker_analyze_reader(nfc_worker);
}
furi_hal_nfc_sleep();
nfc_worker_change_state(nfc_worker, NfcWorkerStateReady);
@@ -117,9 +117,31 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
MfUltralightReader reader = {};
MfUltralightData data = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Read card
// Try to read supported card
FURI_LOG_I(TAG, "Trying to read a supported card ...");
for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) {
if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareUl) {
if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) {
if(nfc_supported_card[i].read(nfc_worker, tx_rx)) {
read_success = true;
nfc_supported_card[i].parse(nfc_worker->dev_data);
break;
}
} else {
furi_hal_nfc_sleep();
}
}
}
if(read_success) break;
furi_hal_nfc_sleep();
// Otherwise, try to read as usual
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 200)) break;
if(!mf_ul_read_card(tx_rx, &reader, &data)) break;
// Copy data
@@ -127,6 +149,10 @@ static bool nfc_worker_read_mf_ultralight(NfcWorker* nfc_worker, FuriHalNfcTxRxC
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -134,17 +160,24 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
furi_assert(nfc_worker->callback);
bool read_success = false;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Try to read supported card
FURI_LOG_I(TAG, "Try read supported card ...");
FURI_LOG_I(TAG, "Trying to read a supported card ...");
for(size_t i = 0; i < NfcSupportedCardTypeEnd; i++) {
if(nfc_supported_card[i].protocol == NfcDeviceProtocolMifareClassic) {
if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) {
if(nfc_supported_card[i].read(nfc_worker, tx_rx)) {
read_success = true;
nfc_supported_card[i].parse(nfc_worker->dev_data);
break;
}
} else {
furi_hal_nfc_sleep();
}
}
}
@@ -162,6 +195,9 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
}
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -169,13 +205,21 @@ static bool nfc_worker_read_mf_desfire(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
bool read_success = false;
MifareDesfireData* data = &nfc_worker->dev_data->mf_df_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
if(!mf_df_read_card(tx_rx, data)) break;
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -184,7 +228,11 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
EmvApplication emv_app = {};
EmvData* result = &nfc_worker->dev_data->emv_data;
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, tx_rx, false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, tx_rx, false);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
do {
// Read card
if(!furi_hal_nfc_detect(&nfc_worker->dev_data->nfc_data, 300)) break;
@@ -211,6 +259,10 @@ static bool nfc_worker_read_bank_card(NfcWorker* nfc_worker, FuriHalNfcTxRxConte
read_success = true;
} while(false);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
return read_success;
}
@@ -383,18 +435,14 @@ void nfc_worker_read(NfcWorker* nfc_worker) {
void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* data = &nfc_worker->dev_data->nfc_data;
NfcReaderRequestData* reader_data = &nfc_worker->dev_data->reader_data;
// TODO add support for RATS
// Now remove bit 6 in SAK to support ISO-14443A-3 emulation
// Need to save ATS to support ISO-14443A-4 emulation
uint8_t sak = data->sak;
FURI_BIT_CLEAR(sak, 5);
while(nfc_worker->state == NfcWorkerStateUidEmulate) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, sak, true, 100)) {
if(furi_hal_nfc_listen(data->uid, data->uid_len, data->atqa, data->sak, false, 100)) {
if(furi_hal_nfc_tx_rx(&tx_rx, 100)) {
reader_data->size = tx_rx.rx_bits / 8;
if(reader_data->size > 0) {
@@ -412,7 +460,6 @@ void nfc_worker_emulate_uid(NfcWorker* nfc_worker) {
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData params = {
.uid = {0xCF, 0x72, 0xd4, 0x40},
.uid_len = 4,
@@ -421,6 +468,11 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
.type = FuriHalNfcTypeA,
};
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
while(nfc_worker->state == NfcWorkerStateEmulateApdu) {
if(furi_hal_nfc_listen(params.uid, params.uid_len, params.atqa, params.sak, false, 300)) {
FURI_LOG_D(TAG, "POS terminal detected");
@@ -433,6 +485,10 @@ void nfc_worker_emulate_apdu(NfcWorker* nfc_worker) {
furi_hal_nfc_sleep();
furi_delay_ms(20);
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
void nfc_worker_emulate_mf_ultralight(NfcWorker* nfc_worker) {
@@ -547,7 +603,6 @@ void nfc_worker_mf_classic_dict_attack(NfcWorker* nfc_worker) {
void nfc_worker_emulate_mf_classic(NfcWorker* nfc_worker) {
FuriHalNfcTxRxContext tx_rx = {};
nfc_debug_pcap_prepare_tx_rx(nfc_worker->debug_pcap_worker, &tx_rx, true);
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
@@ -588,6 +643,11 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
MfUltralightReader reader = {};
mf_ul_reset(data);
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_prepare_tx_rx(nfc_worker->reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeDebugLog);
}
uint32_t key = 0;
uint16_t pack = 0;
while(nfc_worker->state == NfcWorkerStateReadMfUltralightReadAuth) {
@@ -640,4 +700,61 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker) {
furi_delay_ms(10);
}
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
reader_analyzer_stop(nfc_worker->reader_analyzer);
}
}
static void nfc_worker_reader_analyzer_callback(ReaderAnalyzerEvent event, void* context) {
furi_assert(context);
NfcWorker* nfc_worker = context;
if(event == ReaderAnalyzerEventMfkeyCollected) {
if(nfc_worker->callback) {
nfc_worker->callback(NfcWorkerEventDetectReaderMfkeyCollected, nfc_worker->context);
}
}
}
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);
MfClassicEmulator emulator = {
.cuid = nfc_util_bytes2num(&nfc_data->uid[nfc_data->uid_len - 4], 4),
.data = nfc_worker->dev_data->mf_classic_data,
.data_changed = false,
};
NfcaSignal* nfca_signal = nfca_signal_alloc();
tx_rx.nfca_signal = nfca_signal;
reader_analyzer_prepare_tx_rx(reader_analyzer, &tx_rx, true);
reader_analyzer_start(nfc_worker->reader_analyzer, ReaderAnalyzerModeMfkey);
reader_analyzer_set_callback(reader_analyzer, nfc_worker_reader_analyzer_callback, nfc_worker);
rfal_platform_spi_acquire();
FURI_LOG_D(TAG, "Start reader analyzer");
while(nfc_worker->state == NfcWorkerStateAnalyzeReader) {
furi_hal_nfc_stop_cmd();
furi_delay_ms(5);
furi_hal_nfc_listen_start(nfc_data);
if(furi_hal_nfc_listen_rx(&tx_rx, 300)) {
NfcProtocol protocol =
reader_analyzer_guess_protocol(reader_analyzer, tx_rx.rx_data, tx_rx.rx_bits / 8);
if(protocol == NfcDeviceProtocolMifareClassic) {
mf_classic_emulator(&emulator, &tx_rx);
}
} else {
FURI_LOG_D(TAG, "No data from reader");
continue;
}
}
rfal_platform_spi_release();
reader_analyzer_stop(nfc_worker->reader_analyzer);
nfca_signal_free(nfca_signal);
}
+5
View File
@@ -16,6 +16,7 @@ typedef enum {
NfcWorkerStateMfClassicEmulate,
NfcWorkerStateReadMfUltralightReadAuth,
NfcWorkerStateMfClassicDictAttack,
NfcWorkerStateAnalyzeReader,
// Debug
NfcWorkerStateEmulateApdu,
NfcWorkerStateField,
@@ -55,8 +56,12 @@ typedef enum {
NfcWorkerEventFoundKeyA,
NfcWorkerEventFoundKeyB,
// Detect Reader events
NfcWorkerEventDetectReaderMfkeyCollected,
// Mifare Ultralight events
NfcWorkerEventMfUltralightPassKey,
} NfcWorkerEvent;
typedef bool (*NfcWorkerCallback)(NfcWorkerEvent event, void* context);
+4 -3
View File
@@ -13,8 +13,7 @@
#include <lib/nfc/protocols/mifare_classic.h>
#include <lib/nfc/protocols/mifare_desfire.h>
#include <lib/nfc/protocols/nfca.h>
#include "helpers/nfc_debug_pcap.h"
#include <lib/nfc/helpers/reader_analyzer.h>
struct NfcWorker {
FuriThread* thread;
@@ -28,7 +27,7 @@ struct NfcWorker {
NfcWorkerState state;
NfcDebugPcapWorker* debug_pcap_worker;
ReaderAnalyzer* reader_analyzer;
};
void nfc_worker_change_state(NfcWorker* nfc_worker, NfcWorkerState state);
@@ -50,3 +49,5 @@ void nfc_worker_mf_ultralight_read_auth(NfcWorker* nfc_worker);
void nfc_worker_mf_ul_auth_attack(NfcWorker* nfc_worker);
void nfc_worker_emulate_apdu(NfcWorker* nfc_worker);
void nfc_worker_analyze_reader(NfcWorker* nfc_worker);
+113
View File
@@ -0,0 +1,113 @@
#include "nfc_supported_card.h"
#include "all_in_one.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
#define ALL_IN_ONE_LAYOUT_UNKNOWN 0
#define ALL_IN_ONE_LAYOUT_A 1
#define ALL_IN_ONE_LAYOUT_D 2
#define ALL_IN_ONE_LAYOUT_E2 3
#define ALL_IN_ONE_LAYOUT_E3 4
#define ALL_IN_ONE_LAYOUT_E5 5
#define ALL_IN_ONE_LAYOUT_2 6
uint8_t all_in_one_get_layout(NfcDeviceData* dev_data) {
// I absolutely hate what's about to happen here.
// Switch on the second half of the third byte of page 5
FURI_LOG_I("all_in_one", "Layout byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 2]);
FURI_LOG_I(
"all_in_one", "Layout half-byte: %02x", dev_data->mf_ul_data.data[(4 * 5) + 3] & 0x0F);
switch(dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F) {
// If it is A, the layout type is a type A layout
case 0x0A:
return ALL_IN_ONE_LAYOUT_A;
case 0x0D:
return ALL_IN_ONE_LAYOUT_D;
case 0x02:
return ALL_IN_ONE_LAYOUT_2;
default:
FURI_LOG_I(
"all_in_one",
"Unknown layout type: %d",
dev_data->mf_ul_data.data[(4 * 5) + 2] & 0x0F);
return ALL_IN_ONE_LAYOUT_UNKNOWN;
}
}
bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
UNUSED(nfc_worker);
// If this is a all_in_one pass, first 2 bytes of page 4 are 0x45 0xD9
MfUltralightReader reader = {};
MfUltralightData data = {};
if(!mf_ul_read_card(tx_rx, &reader, &data)) {
return false;
} else {
if(data.data[4 * 4] == 0x45 && data.data[4 * 4 + 1] == 0xD9) {
FURI_LOG_I("all_in_one", "Pass verified");
return true;
}
}
return false;
}
bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
MfUltralightReader reader = {};
MfUltralightData data = {};
if(!mf_ul_read_card(tx_rx, &reader, &data)) {
return false;
} else {
memcpy(&nfc_worker->dev_data->mf_ul_data, &data, sizeof(data));
FURI_LOG_I("all_in_one", "Card read");
return true;
}
}
bool all_in_one_parser_parse(NfcDeviceData* dev_data) {
if(dev_data->mf_ul_data.data[4 * 4] != 0x45 || dev_data->mf_ul_data.data[4 * 4 + 1] != 0xD9) {
FURI_LOG_I("all_in_one", "Pass not verified");
return false;
}
// If the layout is a then the ride count is stored in the first byte of page 8
uint8_t ride_count = 0;
uint32_t serial = 0;
if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_A) {
ride_count = dev_data->mf_ul_data.data[4 * 8];
} else if(all_in_one_get_layout(dev_data) == ALL_IN_ONE_LAYOUT_D) {
// If the layout is D, the ride count is stored in the second byte of page 9
ride_count = dev_data->mf_ul_data.data[4 * 9 + 1];
// I hate this with a burning passion.
// The number starts at the second half of the third byte on page 4, and is 32 bits long
// So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
// B8 17 A2 A4 BD becomes 81 7A 2A 4B
serial = (dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 |
dev_data->mf_ul_data.data[4 * 4 + 3] << 20 |
dev_data->mf_ul_data.data[4 * 4 + 4] << 12 |
dev_data->mf_ul_data.data[4 * 4 + 5] << 4 |
(dev_data->mf_ul_data.data[4 * 4 + 6] >> 4);
} else {
FURI_LOG_I("all_in_one", "Unknown layout: %d", all_in_one_get_layout(dev_data));
ride_count = 137;
}
// I hate this with a burning passion.
// The number starts at the second half of the third byte on page 4, and is 32 bits long
// So we get the second half of the third byte, then bytes 4-6, and then the first half of the 7th byte
// B8 17 A2 A4 BD becomes 81 7A 2A 4B
serial =
(dev_data->mf_ul_data.data[4 * 4 + 2] & 0x0F) << 28 |
dev_data->mf_ul_data.data[4 * 4 + 3] << 20 | dev_data->mf_ul_data.data[4 * 4 + 4] << 12 |
dev_data->mf_ul_data.data[4 * 4 + 5] << 4 | (dev_data->mf_ul_data.data[4 * 4 + 6] >> 4);
// Format string for rides count
string_printf(
dev_data->parsed_data, "\e#All-In-One\nNumber: %u\nRides left: %u", serial, ride_count);
return true;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "nfc_supported_card.h"
bool all_in_one_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool all_in_one_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool all_in_one_parser_parse(NfcDeviceData* dev_data);
+45 -5
View File
@@ -1,14 +1,54 @@
#include "nfc_supported_card.h"
#include "troyka_parser.h"
#include "plantain_parser.h"
#include "troika_parser.h"
#include "plantain_4k_parser.h"
#include "troika_4k_parser.h"
#include "two_cities.h"
#include "all_in_one.h"
NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
[NfcSupportedCardTypeTroyka] =
[NfcSupportedCardTypePlantain] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = troyka_parser_verify,
.read = troyka_parser_read,
.parse = troyka_parser_parse,
.verify = plantain_parser_verify,
.read = plantain_parser_read,
.parse = plantain_parser_parse,
},
[NfcSupportedCardTypeTroika] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = troika_parser_verify,
.read = troika_parser_read,
.parse = troika_parser_parse,
},
[NfcSupportedCardTypePlantain4K] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = plantain_4k_parser_verify,
.read = plantain_4k_parser_read,
.parse = plantain_4k_parser_parse,
},
[NfcSupportedCardTypeTroika4K] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = troika_4k_parser_verify,
.read = troika_4k_parser_read,
.parse = troika_4k_parser_parse,
},
[NfcSupportedCardTypeTwoCities] =
{
.protocol = NfcDeviceProtocolMifareClassic,
.verify = two_cities_parser_verify,
.read = two_cities_parser_read,
.parse = two_cities_parser_parse,
},
[NfcSupportedCardTypeAllInOne] =
{
.protocol = NfcDeviceProtocolMifareUl,
.verify = all_in_one_parser_verify,
.read = all_in_one_parser_read,
.parse = all_in_one_parser_parse,
},
};
+6 -1
View File
@@ -7,7 +7,12 @@
#include <m-string.h>
typedef enum {
NfcSupportedCardTypeTroyka,
NfcSupportedCardTypePlantain,
NfcSupportedCardTypeTroika,
NfcSupportedCardTypePlantain4K,
NfcSupportedCardTypeTroika4K,
NfcSupportedCardTypeTwoCities,
NfcSupportedCardTypeAllInOne,
NfcSupportedCardTypeEnd,
} NfcSupportedCardType;
+153
View File
@@ -0,0 +1,153 @@
#include "nfc_supported_card.h"
#include "plantain_parser.h" // For luhn and string_push_uint64
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
static const MfClassicAuthContext plantain_keys_4k[] = {
{.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF},
{.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b},
{.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a},
{.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7},
{.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3},
{.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb},
{.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56},
{.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26},
{.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf},
{.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a},
{.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b},
{.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3},
{.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22},
{.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056},
{.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510},
{.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf},
{.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439},
{.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406},
{.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3},
{.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA},
{.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f},
{.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3},
{.sector = 30, .key_a = 0x0eb23cc8110b, .key_b = 0x04dc35277635},
{.sector = 31, .key_a = 0xbc4580b7f20b, .key_b = 0xd0a4131fb290},
{.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023},
{.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9},
{.sector = 34, .key_a = 0xfd8705e721b0, .key_b = 0x296fc317a513},
{.sector = 35, .key_a = 0x22052b480d11, .key_b = 0xe19504c39461},
{.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7},
{.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a},
{.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7},
{.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085},
};
bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
return false;
}
uint8_t sector = 8;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Plant4K", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) {
FURI_LOG_D("Plant4K", "Sector %d verified", sector);
return true;
}
return false;
}
bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(plantain_keys_4k); i++) {
mf_classic_reader_add_sector(
&reader,
plantain_keys_4k[i].sector,
plantain_keys_4k[i].key_a,
plantain_keys_4k[i].key_b);
FURI_LOG_T("plant4k", "Added sector %d", plantain_keys_4k[i].sector);
}
for(int i = 0; i < 5; i++) {
if(mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40) {
return true;
}
}
return false;
}
bool plantain_4k_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != plantain_keys_4k[8].key_a) return false;
// Point to block 0 of sector 4, value 0
uint8_t* temp_ptr = &data->block[4 * 4].value[0];
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = &data->block[0 * 4].value[0];
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
}
// Copy card number to uint64_t
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
}
// Convert card number to string
string_t card_number_str;
string_init(card_number_str);
// Should look like "361301047292848684"
// %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead
string_push_uint64(card_number, card_number_str);
// Add suffix with luhn checksum (1 digit) to the card number string
string_t card_number_suffix;
string_init(card_number_suffix);
// The number to calculate the checksum on doesn't fit into uint64_t, idk
//uint8_t luhn_checksum = plantain_calculate_luhn(card_number);
// // Convert luhn checksum to string
// string_t luhn_checksum_str;
// string_init(luhn_checksum_str);
// string_push_uint64(luhn_checksum, luhn_checksum_str);
string_cat_printf(card_number_suffix, "-");
// FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum);
string_cat_printf(card_number_str, string_get_cstr(card_number_suffix));
// Free all not needed strings
string_clear(card_number_suffix);
// string_clear(luhn_checksum_str);
string_printf(
dev_data->parsed_data,
"\e#Plantain\nN:%s\nBalance:%d\n",
string_get_cstr(card_number_str),
balance);
string_clear(card_number_str);
return true;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "nfc_supported_card.h"
bool plantain_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_4k_parser_parse(NfcDeviceData* dev_data);
+147
View File
@@ -0,0 +1,147 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
static const MfClassicAuthContext plantain_keys[] = {
{.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 2, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 3, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b},
{.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a},
{.sector = 6, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 8, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7},
{.sector = 9, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3},
{.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb},
{.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56},
{.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26},
{.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 15, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
};
bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) {
return false;
}
uint8_t sector = 8;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Plant", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x26973ea74321, MfClassicKeyA)) {
FURI_LOG_D("Plant", "Sector %d verified", sector);
return true;
}
return false;
}
bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(plantain_keys); i++) {
mf_classic_reader_add_sector(
&reader, plantain_keys[i].sector, plantain_keys[i].key_a, plantain_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
}
void string_push_uint64(uint64_t input, string_t output) {
const uint8_t base = 10;
do {
char c = input % base;
input /= base;
if(c < 10)
c += '0';
else
c += 'A' - 10;
string_push_back(output, c);
} while(input);
// reverse string
for(uint8_t i = 0; i < string_size(output) / 2; i++) {
char c = string_get_char(output, i);
string_set_char(output, i, string_get_char(output, string_size(output) - i - 1));
string_set_char(output, string_size(output) - i - 1, c);
}
}
uint8_t plantain_calculate_luhn(uint64_t number) {
// No.
UNUSED(number);
return 0;
}
bool plantain_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != plantain_keys[8].key_a) return false;
// Point to block 0 of sector 4, value 0
uint8_t* temp_ptr = &data->block[4 * 4].value[0];
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = &data->block[0 * 4].value[0];
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
}
// Copy card number to uint64_t
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
}
// Convert card number to string
string_t card_number_str;
string_init(card_number_str);
// Should look like "361301047292848684"
// %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead
string_push_uint64(card_number, card_number_str);
// Add suffix with luhn checksum (1 digit) to the card number string
string_t card_number_suffix;
string_init(card_number_suffix);
// The number to calculate the checksum on doesn't fit into uint64_t, idk
//uint8_t luhn_checksum = plantain_calculate_luhn(card_number);
// // Convert luhn checksum to string
// string_t luhn_checksum_str;
// string_init(luhn_checksum_str);
// string_push_uint64(luhn_checksum, luhn_checksum_str);
string_cat_printf(card_number_suffix, "-");
// FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum);
string_cat_printf(card_number_str, string_get_cstr(card_number_suffix));
// Free all not needed strings
string_clear(card_number_suffix);
// string_clear(luhn_checksum_str);
string_printf(
dev_data->parsed_data,
"\e#Plantain\nN:%s\nBalance:%d\n",
string_get_cstr(card_number_str),
balance);
string_clear(card_number_str);
return true;
}
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "nfc_supported_card.h"
bool plantain_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool plantain_parser_parse(NfcDeviceData* dev_data);
void string_push_uint64(uint64_t input, string_t output);
uint8_t plantain_calculate_luhn(uint64_t number);
+104
View File
@@ -0,0 +1,104 @@
#include "nfc_supported_card.h"
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
static const MfClassicAuthContext troika_4k_keys[] = {
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
{.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 4, .key_a = 0x73068f118c13, .key_b = 0x2b7f3253fac5},
{.sector = 5, .key_a = 0xFBC2793D540B, .key_b = 0xd3a297dc2698},
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 7, .key_a = 0xae3d65a3dad4, .key_b = 0x0f1c63013dbb},
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
{.sector = 10, .key_a = 0x9becdf3d9273, .key_b = 0xf8493407799d},
{.sector = 11, .key_a = 0x08b386463229, .key_b = 0x5efbaecef46b},
{.sector = 12, .key_a = 0xcd4c61c26e3d, .key_b = 0x31c7610de3b0},
{.sector = 13, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 14, .key_a = 0x0e8f64340ba4, .key_b = 0x4acec1205d75},
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 16, .key_a = 0x6b02733bb6ec, .key_b = 0x7038cd25c408},
{.sector = 17, .key_a = 0x403d706ba880, .key_b = 0xb39d19a280df},
{.sector = 18, .key_a = 0xc11f4597efb5, .key_b = 0x70d901648cb9},
{.sector = 19, .key_a = 0x0db520c78c1c, .key_b = 0x73e5b9d9d3a4},
{.sector = 20, .key_a = 0x3ebce0925b2f, .key_b = 0x372cc880f216},
{.sector = 21, .key_a = 0x16a27af45407, .key_b = 0x9868925175ba},
{.sector = 22, .key_a = 0xaba208516740, .key_b = 0xce26ecb95252},
{.sector = 23, .key_a = 0xCD64E567ABCD, .key_b = 0x8f79c4fd8a01},
{.sector = 24, .key_a = 0x764cd061f1e6, .key_b = 0xa74332f74994},
{.sector = 25, .key_a = 0x1cc219e9fec1, .key_b = 0xb90de525ceb6},
{.sector = 26, .key_a = 0x2fe3cb83ea43, .key_b = 0xfba88f109b32},
{.sector = 27, .key_a = 0x07894ffec1d6, .key_b = 0xefcb0e689db3},
{.sector = 28, .key_a = 0x04c297b91308, .key_b = 0xc8454c154cb5},
{.sector = 29, .key_a = 0x7a38e3511a38, .key_b = 0xab16584c972a},
{.sector = 30, .key_a = 0x7545df809202, .key_b = 0xecf751084a80},
{.sector = 31, .key_a = 0x5125974cd391, .key_b = 0xd3eafb5df46d},
{.sector = 32, .key_a = 0x7a86aa203788, .key_b = 0xe41242278ca2},
{.sector = 33, .key_a = 0xafcef64c9913, .key_b = 0x9db96dca4324},
{.sector = 34, .key_a = 0x04eaa462f70b, .key_b = 0xac17b93e2fae},
{.sector = 35, .key_a = 0xe734c210f27e, .key_b = 0x29ba8c3e9fda},
{.sector = 36, .key_a = 0xd5524f591eed, .key_b = 0x5daf42861b4d},
{.sector = 37, .key_a = 0xe4821a377b75, .key_b = 0xe8709e486465},
{.sector = 38, .key_a = 0x518dc6eea089, .key_b = 0x97c64ac98ca4},
{.sector = 39, .key_a = 0xbb52f8cce07f, .key_b = 0x6b6119752c70},
};
bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
return false;
}
uint8_t sector = 11;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Troika", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) {
FURI_LOG_D("Troika", "Sector %d verified", sector);
return true;
}
return false;
}
bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(troika_4k_keys); i++) {
mf_classic_reader_add_sector(
&reader, troika_4k_keys[i].sector, troika_4k_keys[i].key_a, troika_4k_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40;
}
bool troika_4k_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != troika_4k_keys[4].key_a) return false;
// Verify card type
if(data->type != MfClassicType4k) return false;
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
temp_ptr = &data->block[8 * 4].value[3];
uint32_t number = 0;
for(size_t i = 0; i < 4; i++) {
number <<= 8;
number |= temp_ptr[i];
}
number >>= 4;
string_printf(dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance);
return true;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "nfc_supported_card.h"
bool troika_4k_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_4k_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_4k_parser_parse(NfcDeviceData* dev_data);
@@ -3,7 +3,7 @@
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
static const MfClassicAuthContext troyka_keys[] = {
static const MfClassicAuthContext troika_keys[] = {
{.sector = 0, .key_a = 0xa0a1a2a3a4a5, .key_b = 0xfbf225dc5d58},
{.sector = 1, .key_a = 0xa82607b01c0d, .key_b = 0x2910989b6880},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
@@ -22,42 +22,50 @@ static const MfClassicAuthContext troyka_keys[] = {
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
};
bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType1k) {
return false;
}
MfClassicAuthContext auth_ctx = {
.key_a = MF_CLASSIC_NO_KEY,
.key_b = MF_CLASSIC_NO_KEY,
.sector = 8,
};
return mf_classic_auth_attempt(tx_rx, &auth_ctx, 0xa73f5dc1d333);
uint8_t sector = 11;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("Troika", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0x08b386463229, MfClassicKeyA)) {
FURI_LOG_D("Troika", "Sector %d verified", sector);
return true;
}
return false;
}
bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(troyka_keys); i++) {
for(size_t i = 0; i < COUNT_OF(troika_keys); i++) {
mf_classic_reader_add_sector(
&reader, troyka_keys[i].sector, troyka_keys[i].key_a, troyka_keys[i].key_b);
&reader, troika_keys[i].sector, troika_keys[i].key_a, troika_keys[i].key_b);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
}
bool troyka_parser_parse(NfcDeviceData* dev_data) {
bool troika_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
bool troyka_parsed = false;
bool troika_parsed = false;
do {
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != troyka_keys[8].key_a) break;
if(key != troika_keys[8].key_a) break;
// Verify card type
if(data->type != MfClassicType1k) break;
// Parse data
uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
@@ -71,9 +79,9 @@ bool troyka_parser_parse(NfcDeviceData* dev_data) {
number >>= 4;
string_printf(
dev_data->parsed_data, "\e#Troyka\nNum: %ld\nBalance: %d rur.", number, balance);
troyka_parsed = true;
dev_data->parsed_data, "\e#Troika\nNum: %ld\nBalance: %d rur.", number, balance);
troika_parsed = true;
} while(false);
return troyka_parsed;
return troika_parsed;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "nfc_supported_card.h"
bool troika_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troika_parser_parse(NfcDeviceData* dev_data);
-9
View File
@@ -1,9 +0,0 @@
#pragma once
#include "nfc_supported_card.h"
bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool troyka_parser_parse(NfcDeviceData* dev_data);
+171
View File
@@ -0,0 +1,171 @@
#include "nfc_supported_card.h"
#include "plantain_parser.h" // For plantain-specific stuff
#include <gui/modules/widget.h>
#include <nfc_worker_i.h>
#include "furi_hal.h"
static const MfClassicAuthContext two_cities_keys_4k[] = {
{.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 1, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 2, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 3, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 4, .key_a = 0xe56ac127dd45, .key_b = 0x19fc84a3784b},
{.sector = 5, .key_a = 0x77dabc9825e1, .key_b = 0x9764fec3154a},
{.sector = 6, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 7, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 8, .key_a = 0xa73f5dc1d333, .key_b = 0xe35173494a81},
{.sector = 9, .key_a = 0x69a32f1c2f19, .key_b = 0x6b8bd9860763},
{.sector = 10, .key_a = 0xea0fd73cb149, .key_b = 0x29c35fa068fb},
{.sector = 11, .key_a = 0xc76bf71a2509, .key_b = 0x9ba241db3f56},
{.sector = 12, .key_a = 0xacffffffffff, .key_b = 0x71f3a315ad26},
{.sector = 13, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 14, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff},
{.sector = 15, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 16, .key_a = 0x72f96bdd3714, .key_b = 0x462225cd34cf},
{.sector = 17, .key_a = 0x044ce1872bc3, .key_b = 0x8c90c70cff4a},
{.sector = 18, .key_a = 0xbc2d1791dec1, .key_b = 0xca96a487de0b},
{.sector = 19, .key_a = 0x8791b2ccb5c4, .key_b = 0xc956c3b80da3},
{.sector = 20, .key_a = 0x8e26e45e7d65, .key_b = 0x8e65b3af7d22},
{.sector = 21, .key_a = 0x0f318130ed18, .key_b = 0x0c420a20e056},
{.sector = 22, .key_a = 0x045ceca15535, .key_b = 0x31bec3d9e510},
{.sector = 23, .key_a = 0x9d993c5d4ef4, .key_b = 0x86120e488abf},
{.sector = 24, .key_a = 0xc65d4eaa645b, .key_b = 0xb69d40d1a439},
{.sector = 25, .key_a = 0x3a8a139c20b4, .key_b = 0x8818a9c5d406},
{.sector = 26, .key_a = 0xbaff3053b496, .key_b = 0x4b7cb25354d3},
{.sector = 27, .key_a = 0x7413b599c4ea, .key_b = 0xb0a2AAF3A1BA},
{.sector = 28, .key_a = 0x0ce7cd2cc72b, .key_b = 0xfa1fbb3f0f1f},
{.sector = 29, .key_a = 0x0be5fac8b06a, .key_b = 0x6f95887a4fd3},
{.sector = 30, .key_a = 0x26973ea74321, .key_b = 0xd27058c6e2c7},
{.sector = 31, .key_a = 0xeb0a8ff88ade, .key_b = 0x578a9ada41e3},
{.sector = 32, .key_a = 0x7a396f0d633d, .key_b = 0xad2bdc097023},
{.sector = 33, .key_a = 0xa3faa6daff67, .key_b = 0x7600e889adf9},
{.sector = 34, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 35, .key_a = 0x2aa05ed1856f, .key_b = 0xeaac88e5dc99},
{.sector = 36, .key_a = 0xa7141147d430, .key_b = 0xff16014fefc7},
{.sector = 37, .key_a = 0x8a8d88151a00, .key_b = 0x038b5f9b5a2a},
{.sector = 38, .key_a = 0xb27addfb64b0, .key_b = 0x152fd0c420a7},
{.sector = 39, .key_a = 0x7259fa0197c6, .key_b = 0x5583698df085},
};
bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
UNUSED(nfc_worker);
if(nfc_worker->dev_data->mf_classic_data.type != MfClassicType4k) {
return false;
}
uint8_t sector = 4;
uint8_t block = mf_classic_get_sector_trailer_block_num_by_sector(sector);
FURI_LOG_D("2cities", "Verifying sector %d", sector);
if(mf_classic_authenticate(tx_rx, block, 0xe56ac127dd45, MfClassicKeyA)) {
FURI_LOG_D("2cities", "Sector %d verified", sector);
return true;
}
return false;
}
bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
furi_assert(nfc_worker);
MfClassicReader reader = {};
FuriHalNfcDevData* nfc_data = &nfc_worker->dev_data->nfc_data;
reader.type = mf_classic_get_classic_type(nfc_data->atqa[0], nfc_data->atqa[1], nfc_data->sak);
for(size_t i = 0; i < COUNT_OF(two_cities_keys_4k); i++) {
mf_classic_reader_add_sector(
&reader,
two_cities_keys_4k[i].sector,
two_cities_keys_4k[i].key_a,
two_cities_keys_4k[i].key_b);
FURI_LOG_T("2cities", "Added sector %d", two_cities_keys_4k[i].sector);
}
return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 40;
}
bool two_cities_parser_parse(NfcDeviceData* dev_data) {
MfClassicData* data = &dev_data->mf_classic_data;
// Verify key
MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 4);
uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
if(key != two_cities_keys_4k[4].key_a) return false;
// =====
// PLANTAIN
// =====
// Point to block 0 of sector 4, value 0
uint8_t* temp_ptr = &data->block[4 * 4].value[0];
// Read first 4 bytes of block 0 of sector 4 from last to first and convert them to uint32_t
// 38 18 00 00 becomes 00 00 18 38, and equals to 6200 decimal
uint32_t balance =
((temp_ptr[3] << 24) | (temp_ptr[2] << 16) | (temp_ptr[1] << 8) | temp_ptr[0]) / 100;
// Read card number
// Point to block 0 of sector 0, value 0
temp_ptr = &data->block[0 * 4].value[0];
// Read first 7 bytes of block 0 of sector 0 from last to first and convert them to uint64_t
// 80 5C 23 8A 16 31 04 becomes 04 31 16 8A 23 5C 80, and equals to 36130104729284868 decimal
uint8_t card_number_arr[7];
for(size_t i = 0; i < 7; i++) {
card_number_arr[i] = temp_ptr[6 - i];
}
// Copy card number to uint64_t
uint64_t card_number = 0;
for(size_t i = 0; i < 7; i++) {
card_number = (card_number << 8) | card_number_arr[i];
}
// Convert card number to string
string_t card_number_str;
string_init(card_number_str);
// Should look like "361301047292848684"
// %llu doesn't work for some reason in sprintf, so we use string_push_uint64 instead
string_push_uint64(card_number, card_number_str);
// Add suffix with luhn checksum (1 digit) to the card number string
string_t card_number_suffix;
string_init(card_number_suffix);
// The number to calculate the checksum on doesn't fit into uint64_t, idk
//uint8_t luhn_checksum = two_cities_calculate_luhn(card_number);
// // Convert luhn checksum to string
// string_t luhn_checksum_str;
// string_init(luhn_checksum_str);
// string_push_uint64(luhn_checksum, luhn_checksum_str);
string_cat_printf(card_number_suffix, "-");
// FURI_LOG_D("plant4k", "Card checksum: %d", luhn_checksum);
string_cat_printf(card_number_str, string_get_cstr(card_number_suffix));
// Free all not needed strings
string_clear(card_number_suffix);
// string_clear(luhn_checksum_str);
// =====
// --PLANTAIN--
// =====
// TROIKA
// =====
uint8_t* troika_temp_ptr = &data->block[8 * 4 + 1].value[5];
uint16_t troika_balance = ((troika_temp_ptr[0] << 8) | troika_temp_ptr[1]) / 25;
troika_temp_ptr = &data->block[8 * 4].value[3];
uint32_t troika_number = 0;
for(size_t i = 0; i < 4; i++) {
troika_number <<= 8;
troika_number |= troika_temp_ptr[i];
}
troika_number >>= 4;
string_printf(
dev_data->parsed_data,
"\e#Troika+Plantain\nPN: %s\nPB: %d rur.\nTN: %d\nTB: %d rur.\n",
string_get_cstr(card_number_str),
balance,
troika_number,
troika_balance);
string_clear(card_number_str);
return true;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "nfc_supported_card.h"
bool two_cities_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool two_cities_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
bool two_cities_parser_parse(NfcDeviceData* dev_data);
+18 -2
View File
@@ -209,8 +209,24 @@ void mf_df_cat_file(MifareDesfireFile* file, string_t out) {
uint8_t* data = file->contents;
if(data) {
for(int rec = 0; rec < num; rec++) {
for(int ch = 0; ch < size; ch++) {
string_cat_printf(out, "%02x", data[rec * size + ch]);
string_cat_printf(out, "record %d\n", rec);
for(int ch = 0; ch < size; ch += 4) {
string_cat_printf(out, "%03x|", ch);
for(int i = 0; i < 4; i++) {
if(ch + i < size) {
string_cat_printf(out, "%02x ", data[rec * size + ch + i]);
} else {
string_cat_printf(out, " ");
}
}
for(int i = 0; i < 4 && ch + i < size; i++) {
if(isprint(data[rec * size + ch + i])) {
string_cat_printf(out, "%c", data[rec * size + ch + i]);
} else {
string_cat_printf(out, ".");
}
}
string_cat_printf(out, "\n");
}
string_cat_printf(out, " \n");
}
+6
View File
@@ -96,6 +96,12 @@ for wrapped_fn in wrapped_fn_list:
]
)
env.Append(
SDK_HEADERS=[
File("#/lib/print/wrappers.h"),
],
)
libenv = env.Clone(FW_LIB_NAME="print")
libenv.ApplyLibFlags()
libenv.Append(CCFLAGS=["-Wno-double-promotion"])
+2 -3
View File
@@ -1,11 +1,10 @@
#include <stdio.h>
#include <stdint.h>
#include "wrappers.h"
#include <stdbool.h>
#include <stdarg.h>
#include <furi/core/check.h>
#include <furi/core/thread.h>
#include <furi/core/common_defines.h>
#include <string.h>
#include "printf_tiny.h"
void _putchar(char character) {
+25
View File
@@ -0,0 +1,25 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
void _putchar(char character);
int __wrap_printf(const char* format, ...);
int __wrap_vsnprintf(char* str, size_t size, const char* format, va_list args);
int __wrap_puts(const char* str);
int __wrap_putchar(int ch);
int __wrap_putc(int ch, FILE* stream);
int __wrap_snprintf(char* str, size_t size, const char* format, ...);
int __wrap_fflush(FILE* stream);
__attribute__((__noreturn__)) void __wrap___assert(const char* file, int line, const char* e);
__attribute__((__noreturn__)) void
__wrap___assert_func(const char* file, int line, const char* func, const char* e);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -4,6 +4,14 @@ env.Append(
CPPPATH=[
"#/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"),
],
)
libenv = env.Clone(FW_LIB_NAME="subghz")
+8
View File
@@ -4,6 +4,10 @@
#include "subghz_keystore.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzEnvironment SubGhzEnvironment;
/**
@@ -64,3 +68,7 @@ void subghz_environment_set_nice_flor_s_rainbow_table_file_name(
*/
const char*
subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -2,6 +2,10 @@
#include "../types.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase;
typedef void (
@@ -77,3 +81,7 @@ struct SubGhzProtocolEncoderBase {
// Callback section
};
#ifdef __cplusplus
}
#endif
+6 -20
View File
@@ -92,7 +92,7 @@ void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) {
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
instance->encoder.size_upload = 52;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
@@ -173,7 +173,7 @@ bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flip
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_bett_get_upload(instance);
if(!subghz_protocol_encoder_bett_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
@@ -233,7 +233,8 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat
case BETTDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) <
(subghz_protocol_bett_const.te_delta * 15))) {
//Found Preambula
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = BETTDecoderStepCheckDuration;
}
break;
@@ -288,20 +289,6 @@ void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t durat
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_bett_check_remote_controller(SubGhzBlockGeneric* instance) {
uint32_t code_found_reverse =
subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);
instance->serial = (code_found_reverse & 0xFF) << 12 |
((code_found_reverse >> 8) & 0xFF) << 4 |
((code_found_reverse >> 20) & 0x0F);
instance->btn = ((code_found_reverse >> 16) & 0x0F);
}
uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderBETT* instance = context;
@@ -339,8 +326,7 @@ bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flip
void subghz_protocol_decoder_bett_get_string(void* context, string_t output) {
furi_assert(context);
SubGhzProtocolDecoderBETT* instance = context;
subghz_protocol_bett_check_remote_controller(&instance->generic);
uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);
uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF);
string_cat_printf(
output,
"%s %dbit\r\n"
@@ -350,7 +336,7 @@ void subghz_protocol_decoder_bett_get_string(void* context, string_t output) {
" -: " DIP_PATTERN "\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0xFFFFFF),
data,
SHOW_DIP_P(data, DIP_P),
SHOW_DIP_P(data, DIP_O),
SHOW_DIP_P(data, DIP_N));
+1 -1
View File
@@ -162,7 +162,7 @@ bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flip
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_came_get_upload(instance);
if(!subghz_protocol_encoder_came_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+2 -2
View File
@@ -155,7 +155,7 @@ static bool
break;
default:
furi_crash(TAG " unknown protocol.");
FURI_LOG_E(TAG, "Invalid bits count");
return false;
break;
}
@@ -224,7 +224,7 @@ bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_chamb_code_get_upload(instance);
if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+365
View File
@@ -0,0 +1,365 @@
#include "clemsa.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
// protocol BERNER / ELKA / TEDSEN / TELETASTER
#define TAG "SubGhzProtocolClemsa"
#define DIP_P 0b11 //(+)
#define DIP_O 0b10 //(0)
#define DIP_N 0b00 //(-)
#define DIP_PATTERN "%c%c%c%c%c%c%c%c"
#define SHOW_DIP_P(dip, check_dip) \
((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \
((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')
static const SubGhzBlockConst subghz_protocol_clemsa_const = {
.te_short = 385,
.te_long = 2695,
.te_delta = 150,
.min_count_bit_for_found = 18,
};
struct SubGhzProtocolDecoderClemsa {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
};
struct SubGhzProtocolEncoderClemsa {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
};
typedef enum {
ClemsaDecoderStepReset = 0,
ClemsaDecoderStepSaveDuration,
ClemsaDecoderStepCheckDuration,
} ClemsaDecoderStep;
const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = {
.alloc = subghz_protocol_decoder_clemsa_alloc,
.free = subghz_protocol_decoder_clemsa_free,
.feed = subghz_protocol_decoder_clemsa_feed,
.reset = subghz_protocol_decoder_clemsa_reset,
.get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data,
.serialize = subghz_protocol_decoder_clemsa_serialize,
.deserialize = subghz_protocol_decoder_clemsa_deserialize,
.get_string = subghz_protocol_decoder_clemsa_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = {
.alloc = subghz_protocol_encoder_clemsa_alloc,
.free = subghz_protocol_encoder_clemsa_free,
.deserialize = subghz_protocol_encoder_clemsa_deserialize,
.stop = subghz_protocol_encoder_clemsa_stop,
.yield = subghz_protocol_encoder_clemsa_yield,
};
const SubGhzProtocol subghz_protocol_clemsa = {
.name = SUBGHZ_PROTOCOL_CLEMSA_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_clemsa_decoder,
.encoder = &subghz_protocol_clemsa_encoder,
};
void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa));
instance->base.protocol = &subghz_protocol_clemsa;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 52;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_clemsa_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderClemsa* instance = context;
free(instance->encoder.upload);
free(instance);
}
/**
* Generating an upload from data.
* @param instance Pointer to a SubGhzProtocolEncoderClemsa instance
* @return true On success
*/
static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) {
furi_assert(instance);
size_t index = 0;
size_t size_upload = (instance->generic.data_count_bit * 2);
if(size_upload > instance->encoder.size_upload) {
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
return false;
} else {
instance->encoder.size_upload = size_upload;
}
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
if(bit_read(instance->generic.data, i - 1)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long);
}
}
if(bit_read(instance->generic.data, 0)) {
//send bit 1
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);
instance->encoder.upload[index++] = level_duration_make(
false,
(uint32_t)subghz_protocol_clemsa_const.te_short +
subghz_protocol_clemsa_const.te_long * 7);
} else {
//send bit 0
instance->encoder.upload[index++] =
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);
instance->encoder.upload[index++] = level_duration_make(
false,
(uint32_t)subghz_protocol_clemsa_const.te_long +
subghz_protocol_clemsa_const.te_long * 7);
}
return true;
}
bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderClemsa* instance = context;
bool res = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
FURI_LOG_E(TAG, "Deserialize error");
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_clemsa_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
//optional parameter parameter
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
} while(false);
return res;
}
void subghz_protocol_encoder_clemsa_stop(void* context) {
SubGhzProtocolEncoderClemsa* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_clemsa_yield(void* context) {
SubGhzProtocolEncoderClemsa* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa));
instance->base.protocol = &subghz_protocol_clemsa;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_clemsa_free(void* context) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
free(instance);
}
void subghz_protocol_decoder_clemsa_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
switch(instance->decoder.parser_step) {
case ClemsaDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <
subghz_protocol_clemsa_const.te_delta * 25)) {
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
}
break;
case ClemsaDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = ClemsaDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
break;
case ClemsaDecoderStepCheckDuration:
if(!level) {
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <
subghz_protocol_clemsa_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) <
subghz_protocol_clemsa_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <
subghz_protocol_clemsa_const.te_delta * 3) &&
(DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) <
subghz_protocol_clemsa_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
} else if(
DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <
subghz_protocol_clemsa_const.te_delta * 25) {
if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <
subghz_protocol_clemsa_const.te_delta)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
} else if((DURATION_DIFF(
instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <
subghz_protocol_clemsa_const.te_delta * 3)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
if(instance->decoder.decode_count_bit ==
subghz_protocol_clemsa_const.min_count_bit_for_found) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
} else {
instance->decoder.parser_step = ClemsaDecoderStepReset;
}
break;
}
}
/**
* Analysis of received data
* @param instance Pointer to a SubGhzBlockGeneric* instance
*/
static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) {
instance->serial = (instance->data >> 2) & 0xFFFF;
instance->btn = (instance->data & 0x03);
}
uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool subghz_protocol_decoder_clemsa_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* preset) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
bool ret = false;
do {
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if(instance->generic.data_count_bit !=
subghz_protocol_clemsa_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
ret = true;
} while(false);
return ret;
}
void subghz_protocol_decoder_clemsa_get_string(void* context, string_t output) {
furi_assert(context);
SubGhzProtocolDecoderClemsa* instance = context;
subghz_protocol_clemsa_check_remote_controller(&instance->generic);
//uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);
string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%05lX Btn %X\r\n"
" +: " DIP_PATTERN "\r\n"
" o: " DIP_PATTERN "\r\n"
" -: " DIP_PATTERN "\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data & 0x3FFFF),
instance->generic.btn,
SHOW_DIP_P(instance->generic.serial, DIP_P),
SHOW_DIP_P(instance->generic.serial, DIP_O),
SHOW_DIP_P(instance->generic.serial, DIP_N));
}
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_CLEMSA_NAME "Clemsa"
typedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa;
typedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa;
extern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder;
extern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder;
extern const SubGhzProtocol subghz_protocol_clemsa;
/**
* Allocate SubGhzProtocolEncoderClemsa.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance
*/
void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolEncoderClemsa.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
*/
void subghz_protocol_encoder_clemsa_free(void* context);
/**
* Deserialize and generating an upload to send.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Forced transmission stop.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
*/
void subghz_protocol_encoder_clemsa_stop(void* context);
/**
* Getting the level and duration of the upload to be loaded into DMA.
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
* @return LevelDuration
*/
LevelDuration subghz_protocol_encoder_clemsa_yield(void* context);
/**
* Allocate SubGhzProtocolDecoderClemsa.
* @param environment Pointer to a SubGhzEnvironment instance
* @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance
*/
void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment);
/**
* Free SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
*/
void subghz_protocol_decoder_clemsa_free(void* context);
/**
* Reset decoder SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
*/
void subghz_protocol_decoder_clemsa_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @return hash Hash sum
*/
uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context);
/**
* Serialize data SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzPresetDefinition
* @return true On success
*/
bool subghz_protocol_decoder_clemsa_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzPresetDefinition* preset);
/**
* Deserialize data SubGhzProtocolDecoderClemsa.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
* @param output Resulting text
*/
void subghz_protocol_decoder_clemsa_get_string(void* context, string_t output);
+1 -1
View File
@@ -154,7 +154,7 @@ bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat*
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_doitrand_get_upload(instance);
if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+1 -1
View File
@@ -147,7 +147,7 @@ bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* f
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_gate_tx_get_upload(instance);
if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+1 -1
View File
@@ -160,7 +160,7 @@ bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* fl
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_holtek_get_upload(instance);
if(!subghz_protocol_encoder_holtek_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+1 -1
View File
@@ -162,7 +162,7 @@ bool subghz_protocol_encoder_honeywell_wdb_deserialize(
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_honeywell_wdb_get_upload(instance);
if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+1 -1
View File
@@ -163,7 +163,7 @@ bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* f
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_hormann_get_upload(instance);
if(!subghz_protocol_encoder_hormann_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+1 -1
View File
@@ -179,7 +179,7 @@ bool subghz_protocol_encoder_intertechno_v3_deserialize(
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_intertechno_v3_get_upload(instance);
if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break;
instance->encoder.is_running = true;
res = true;
+15 -3
View File
@@ -280,7 +280,7 @@ bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* fl
flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn);
if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) break;
if(!flipper_format_rewind(flipper_format)) {
FURI_LOG_E(TAG, "Rewind error");
@@ -407,7 +407,7 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_short) <
subghz_protocol_keeloq_const.te_delta) &&
(DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_long) <
subghz_protocol_keeloq_const.te_delta)) {
subghz_protocol_keeloq_const.te_delta * 2)) {
if(instance->decoder.decode_count_bit <
subghz_protocol_keeloq_const.min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
@@ -415,7 +415,7 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur
instance->decoder.parser_step = KeeloqDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keeloq_const.te_long) <
subghz_protocol_keeloq_const.te_delta) &&
subghz_protocol_keeloq_const.te_delta * 2) &&
(DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) <
subghz_protocol_keeloq_const.te_delta)) {
if(instance->decoder.decode_count_bit <
@@ -521,6 +521,15 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector(
return 1;
}
break;
case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1:
man = subghz_protocol_keeloq_common_magic_serial_type1_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 = 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);
@@ -528,6 +537,7 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector(
*manufacture_name = string_get_cstr(manufacture_code->name);
return 1;
}
// Check for mirrored man
uint64_t man_rev = 0;
uint64_t man_rev_byte = 0;
@@ -535,11 +545,13 @@ static uint8_t subghz_protocol_keeloq_check_remote_controller_selector(
man_rev_byte = (uint8_t)(manufacture_code->key >> i);
man_rev = man_rev | man_rev_byte << (56 - i);
}
decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev);
if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) {
*manufacture_name = string_get_cstr(manufacture_code->name);
return 1;
}
//###########################
// Normal Learning
// https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37
+12
View File
@@ -86,3 +86,15 @@ inline uint64_t
data &= 0x0FFFFFFF;
return (((uint64_t)data << 32) | data) ^ xor;
}
/** Magic_serial_type1 Learning
* @param data - serial number (28bit)
* @param man - magic man (64bit)
* @return manufacture for this serial number (64bit)
*/
inline uint64_t
subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {
return man | ((uint64_t)data << 40) |
((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);
}

Some files were not shown because too many files have changed in this diff Show More