[FL-2627] Flipper applications: SDK, build and debug system (#1387)

* Added support for running applications from SD card (FAPs - Flipper Application Packages)
* Added plugin_dist target for fbt to build FAPs
* All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default
* Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them
* Added debugging support for FAPs with fbt debug & VSCode
* Added public firmware API with automated versioning

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2022-09-15 02:11:38 +10:00
committed by Aleksandr Kutuzov
parent 0f6f9ad52e
commit b9a766d909
895 changed files with 8862 additions and 1465 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
+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,45 @@
#pragma once
#include <stdint.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)
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
#pragma once
#include <flipper_application/elf/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;
@@ -0,0 +1,477 @@
#include "flipper_application_i.h"
#include <furi.h>
#define TAG "fapp-i"
#define RESOLVER_THREAD_YIELD_STEP 30
#define IS_FLAGS_SET(v, m) ((v & m) == m)
#define SECTION_OFFSET(e, n) (e->section_table + n * sizeof(Elf32_Shdr))
#define SYMBOL_OFFSET(e, n) (e->_table + n * sizeof(Elf32_Shdr))
bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path) {
Elf32_Ehdr h;
Elf32_Shdr sH;
if(!storage_file_open(e->fd, path, FSAM_READ, FSOM_OPEN_EXISTING) ||
!storage_file_seek(e->fd, 0, true) ||
storage_file_read(e->fd, &h, sizeof(h)) != sizeof(h) ||
!storage_file_seek(e->fd, h.e_shoff + h.e_shstrndx * sizeof(sH), true) ||
storage_file_read(e->fd, &sH, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr)) {
return false;
}
e->entry = h.e_entry;
e->sections = h.e_shnum;
e->section_table = h.e_shoff;
e->section_table_strings = sH.sh_offset;
return true;
}
static bool flipper_application_load_metadata(FlipperApplication* e, Elf32_Shdr* sh) {
if(sh->sh_size < sizeof(e->manifest)) {
return false;
}
return storage_file_seek(e->fd, sh->sh_offset, true) &&
storage_file_read(e->fd, &e->manifest, sh->sh_size) == sh->sh_size;
}
static bool flipper_application_load_debug_link(FlipperApplication* e, Elf32_Shdr* sh) {
e->state.debug_link_size = sh->sh_size;
e->state.debug_link = malloc(sh->sh_size);
return storage_file_seek(e->fd, sh->sh_offset, true) &&
storage_file_read(e->fd, e->state.debug_link, sh->sh_size) == sh->sh_size;
}
static FindFlags_t flipper_application_preload_section(
FlipperApplication* e,
Elf32_Shdr* sh,
const char* name,
int n) {
FURI_LOG_D(TAG, "Processing: %s", name);
const struct {
const char* name;
uint16_t* ptr_section_idx;
FindFlags_t flags;
} lookup_sections[] = {
{".text", &e->text.sec_idx, FoundText},
{".rodata", &e->rodata.sec_idx, FoundRodata},
{".data", &e->data.sec_idx, FoundData},
{".bss", &e->bss.sec_idx, FoundBss},
{".rel.text", &e->text.rel_sec_idx, FoundRelText},
{".rel.rodata", &e->rodata.rel_sec_idx, FoundRelRodata},
{".rel.data", &e->data.rel_sec_idx, FoundRelData},
};
for(size_t i = 0; i < COUNT_OF(lookup_sections); i++) {
if(strcmp(name, lookup_sections[i].name) == 0) {
*lookup_sections[i].ptr_section_idx = n;
return lookup_sections[i].flags;
}
}
if(strcmp(name, ".symtab") == 0) {
e->symbol_table = sh->sh_offset;
e->symbol_count = sh->sh_size / sizeof(Elf32_Sym);
return FoundSymTab;
} else if(strcmp(name, ".strtab") == 0) {
e->symbol_table_strings = sh->sh_offset;
return FoundStrTab;
} else if(strcmp(name, ".fapmeta") == 0) {
// Load metadata immediately
if(flipper_application_load_metadata(e, sh)) {
return FoundFappManifest;
}
} else if(strcmp(name, ".gnu_debuglink") == 0) {
if(flipper_application_load_debug_link(e, sh)) {
return FoundDebugLink;
}
}
return FoundERROR;
}
static bool
read_string_from_offset(FlipperApplication* e, off_t offset, char* buffer, size_t buffer_size) {
bool success = false;
off_t old = storage_file_tell(e->fd);
if(storage_file_seek(e->fd, offset, true) &&
(storage_file_read(e->fd, buffer, buffer_size) == buffer_size)) {
success = true;
}
storage_file_seek(e->fd, old, true);
return success;
}
static bool read_section_name(FlipperApplication* e, off_t off, char* buf, size_t max) {
return read_string_from_offset(e, e->section_table_strings + off, buf, max);
}
static bool read_symbol_name(FlipperApplication* e, off_t off, char* buf, size_t max) {
return read_string_from_offset(e, e->symbol_table_strings + off, buf, max);
}
static bool read_section_header(FlipperApplication* e, int n, Elf32_Shdr* h) {
off_t offset = SECTION_OFFSET(e, n);
return storage_file_seek(e->fd, offset, true) &&
storage_file_read(e->fd, h, sizeof(Elf32_Shdr)) == sizeof(Elf32_Shdr);
}
static bool read_section(FlipperApplication* e, int n, Elf32_Shdr* h, char* name, size_t nlen) {
if(!read_section_header(e, n, h)) {
return false;
}
if(!h->sh_name) {
return true;
}
return read_section_name(e, h->sh_name, name, nlen);
}
bool flipper_application_load_section_table(FlipperApplication* e) {
furi_check(e->state.mmap_entry_count == 0);
size_t n;
FindFlags_t found = FoundERROR;
FURI_LOG_D(TAG, "Scan ELF indexs...");
for(n = 1; n < e->sections; n++) {
Elf32_Shdr section_header;
char name[33] = {0};
if(!read_section_header(e, n, &section_header)) {
return false;
}
if(section_header.sh_name &&
!read_section_name(e, section_header.sh_name, name, sizeof(name))) {
return false;
}
FURI_LOG_T(TAG, "Examining section %d %s", n, name);
FindFlags_t section_flags =
flipper_application_preload_section(e, &section_header, name, n);
found |= section_flags;
if((section_flags & FoundGdbSection) != 0) {
e->state.mmap_entry_count++;
}
if(IS_FLAGS_SET(found, FoundAll)) {
return true;
}
}
FURI_LOG_D(TAG, "Load symbols done");
return IS_FLAGS_SET(found, FoundValid);
}
static const char* type_to_str(int symt) {
#define STRCASE(name) \
case name: \
return #name;
switch(symt) {
STRCASE(R_ARM_NONE)
STRCASE(R_ARM_ABS32)
STRCASE(R_ARM_THM_PC22)
STRCASE(R_ARM_THM_JUMP24)
default:
return "R_<unknow>";
}
#undef STRCASE
}
static void relocate_jmp_call(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
UNUSED(type);
uint16_t upper_insn = ((uint16_t*)relAddr)[0];
uint16_t lower_insn = ((uint16_t*)relAddr)[1];
uint32_t S = (upper_insn >> 10) & 1;
uint32_t J1 = (lower_insn >> 13) & 1;
uint32_t J2 = (lower_insn >> 11) & 1;
int32_t offset = (S << 24) | /* S -> offset[24] */
((~(J1 ^ S) & 1) << 23) | /* J1 -> offset[23] */
((~(J2 ^ S) & 1) << 22) | /* J2 -> offset[22] */
((upper_insn & 0x03ff) << 12) | /* imm10 -> offset[12:21] */
((lower_insn & 0x07ff) << 1); /* imm11 -> offset[1:11] */
if(offset & 0x01000000) offset -= 0x02000000;
offset += symAddr - relAddr;
S = (offset >> 24) & 1;
J1 = S ^ (~(offset >> 23) & 1);
J2 = S ^ (~(offset >> 22) & 1);
upper_insn = ((upper_insn & 0xf800) | (S << 10) | ((offset >> 12) & 0x03ff));
((uint16_t*)relAddr)[0] = upper_insn;
lower_insn = ((lower_insn & 0xd000) | (J1 << 13) | (J2 << 11) | ((offset >> 1) & 0x07ff));
((uint16_t*)relAddr)[1] = lower_insn;
}
static bool relocate_symbol(Elf32_Addr relAddr, int type, Elf32_Addr symAddr) {
switch(type) {
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:
relocate_jmp_call(relAddr, type, symAddr);
FURI_LOG_D(
TAG, " R_ARM_THM_CALL/JMP relocated is 0x%08X", (unsigned int)*((uint32_t*)relAddr));
break;
default:
FURI_LOG_D(TAG, " Undefined relocation %d", type);
return false;
}
return true;
}
static ELFSection_t* section_of(FlipperApplication* e, int index) {
if(e->text.sec_idx == index) {
return &e->text;
} else if(e->data.sec_idx == index) {
return &e->data;
} else if(e->bss.sec_idx == index) {
return &e->bss;
} else if(e->rodata.sec_idx == index) {
return &e->rodata;
}
return NULL;
}
static Elf32_Addr address_of(FlipperApplication* e, Elf32_Sym* sym, const char* sName) {
if(sym->st_shndx == SHN_UNDEF) {
Elf32_Addr addr = 0;
if(e->api_interface->resolver_callback(sName, &addr)) {
return addr;
}
} else {
ELFSection_t* symSec = section_of(e, 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;
}
static bool read_symbol(FlipperApplication* e, int n, Elf32_Sym* sym, char* name, size_t nlen) {
bool success = false;
off_t old = storage_file_tell(e->fd);
off_t pos = e->symbol_table + n * sizeof(Elf32_Sym);
if(storage_file_seek(e->fd, pos, true) &&
storage_file_read(e->fd, sym, sizeof(Elf32_Sym)) == sizeof(Elf32_Sym)) {
if(sym->st_name)
success = read_symbol_name(e, sym->st_name, name, nlen);
else {
Elf32_Shdr shdr;
success = read_section(e, sym->st_shndx, &shdr, name, nlen);
}
}
storage_file_seek(e->fd, old, true);
return success;
}
static bool
relocation_cache_get(RelocationAddressCache_t cache, int symEntry, Elf32_Addr* symAddr) {
Elf32_Addr* addr = RelocationAddressCache_get(cache, symEntry);
if(addr) {
*symAddr = *addr;
return true;
} else {
return false;
}
}
static void
relocation_cache_put(RelocationAddressCache_t cache, int symEntry, Elf32_Addr symAddr) {
RelocationAddressCache_set_at(cache, symEntry, symAddr);
}
#define MAX_SYMBOL_NAME_LEN 128u
static bool relocate(FlipperApplication* e, Elf32_Shdr* h, ELFSection_t* s) {
if(s->data) {
Elf32_Rel rel;
size_t relEntries = h->sh_size / sizeof(rel);
size_t relCount;
(void)storage_file_seek(e->fd, h->sh_offset, true);
FURI_LOG_D(TAG, " Offset Info Type Name");
int relocate_result = true;
char symbol_name[MAX_SYMBOL_NAME_LEN + 1] = {0};
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(e->fd, &rel, sizeof(Elf32_Rel)) != sizeof(Elf32_Rel)) {
FURI_LOG_E(TAG, " reloc read fail");
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(!relocation_cache_get(e->relocation_cache, symEntry, &symAddr)) {
Elf32_Sym sym;
if(!read_symbol(e, symEntry, &sym, symbol_name, MAX_SYMBOL_NAME_LEN)) {
FURI_LOG_E(TAG, " symbol read fail");
return false;
}
FURI_LOG_D(
TAG,
" %08X %08X %-16s %s",
(unsigned int)rel.r_offset,
(unsigned int)rel.r_info,
type_to_str(relType),
symbol_name);
symAddr = address_of(e, &sym, symbol_name);
relocation_cache_put(e->relocation_cache, symEntry, symAddr);
}
if(symAddr != ELF_INVALID_ADDRESS) {
FURI_LOG_D(
TAG,
" symAddr=%08X relAddr=%08X",
(unsigned int)symAddr,
(unsigned int)relAddr);
if(!relocate_symbol(relAddr, relType, symAddr)) {
relocate_result = false;
}
} else {
FURI_LOG_D(TAG, " No symbol address of %s", symbol_name);
relocate_result = false;
}
}
return relocate_result;
} else
FURI_LOG_I(TAG, "Section not loaded");
return false;
}
static bool flipper_application_load_section_data(FlipperApplication* e, ELFSection_t* s) {
Elf32_Shdr section_header;
if(s->sec_idx == 0) {
FURI_LOG_I(TAG, "Section is not present");
return true;
}
if(!read_section_header(e, s->sec_idx, &section_header)) {
return false;
}
if(section_header.sh_size == 0) {
FURI_LOG_I(TAG, "No data for section");
return true;
}
s->data = aligned_malloc(section_header.sh_size, section_header.sh_addralign);
// e->state.mmap_entry_count++;
if(section_header.sh_type == SHT_NOBITS) {
/* section is empty (.bss?) */
/* no need to memset - allocator already did that */
/* memset(s->data, 0, h->sh_size); */
FURI_LOG_D(TAG, "0x%X", s->data);
return true;
}
if((!storage_file_seek(e->fd, section_header.sh_offset, true)) ||
(storage_file_read(e->fd, s->data, section_header.sh_size) != section_header.sh_size)) {
FURI_LOG_E(TAG, " seek/read fail");
flipper_application_free_section(s);
return false;
}
FURI_LOG_D(TAG, "0x%X", s->data);
return true;
}
static bool flipper_application_relocate_section(FlipperApplication* e, ELFSection_t* s) {
Elf32_Shdr section_header;
if(s->rel_sec_idx) {
FURI_LOG_D(TAG, "Relocating section");
if(read_section_header(e, s->rel_sec_idx, &section_header))
return relocate(e, &section_header, s);
else {
FURI_LOG_E(TAG, "Error reading section header");
return false;
}
} else
FURI_LOG_D(TAG, "No relocation index"); /* Not an error */
return true;
}
FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e) {
FlipperApplicationLoadStatus status = FlipperApplicationLoadStatusSuccess;
RelocationAddressCache_init(e->relocation_cache);
size_t start = furi_get_tick();
struct {
ELFSection_t* section;
const char* name;
} sections[] = {
{&e->text, ".text"},
{&e->rodata, ".rodata"},
{&e->data, ".data"},
{&e->bss, ".bss"},
};
for(size_t i = 0; i < COUNT_OF(sections); i++) {
if(!flipper_application_load_section_data(e, sections[i].section)) {
FURI_LOG_E(TAG, "Error loading section '%s'", sections[i].name);
status = FlipperApplicationLoadStatusUnspecifiedError;
}
}
if(status == FlipperApplicationLoadStatusSuccess) {
for(size_t i = 0; i < COUNT_OF(sections); i++) {
if(!flipper_application_relocate_section(e, sections[i].section)) {
FURI_LOG_E(TAG, "Error relocating section '%s'", sections[i].name);
status = FlipperApplicationLoadStatusMissingImports;
}
}
}
if(status == FlipperApplicationLoadStatusSuccess) {
e->state.mmap_entries =
malloc(sizeof(FlipperApplicationMemoryMapEntry) * e->state.mmap_entry_count);
uint32_t mmap_entry_idx = 0;
for(size_t i = 0; i < COUNT_OF(sections); i++) {
const void* data_ptr = sections[i].section->data;
if(data_ptr) {
FURI_LOG_I(TAG, "0x%X %s", (uint32_t)data_ptr, sections[i].name);
e->state.mmap_entries[mmap_entry_idx].address = (uint32_t)data_ptr;
e->state.mmap_entries[mmap_entry_idx].name = sections[i].name;
mmap_entry_idx++;
}
}
furi_check(mmap_entry_idx == e->state.mmap_entry_count);
/* Fixing up entry point */
e->entry += (uint32_t)e->text.data;
}
FURI_LOG_D(TAG, "Relocation cache size: %u", RelocationAddressCache_size(e->relocation_cache));
RelocationAddressCache_clear(e->relocation_cache);
FURI_LOG_I(TAG, "Loaded in %ums", (size_t)(furi_get_tick() - start));
return status;
}
void flipper_application_free_section(ELFSection_t* s) {
if(s->data) {
aligned_free(s->data);
}
s->data = NULL;
}
@@ -0,0 +1,131 @@
#include "flipper_application.h"
#include "flipper_application_i.h"
#define TAG "fapp"
/* 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->api_interface = api_interface;
app->fd = storage_file_alloc(storage);
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;
if(app->state.debug_link_size) {
free(app->state.debug_link);
}
if(app->state.mmap_entries) {
free(app->state.mmap_entries);
}
ELFSection_t* sections[] = {&app->text, &app->rodata, &app->data, &app->bss};
for(size_t i = 0; i < COUNT_OF(sections); i++) {
flipper_application_free_section(sections[i]);
}
storage_file_free(app->fd);
free(app);
}
/* Parse headers, load manifest */
FlipperApplicationPreloadStatus
flipper_application_preload(FlipperApplication* app, const char* path) {
if(!flipper_application_load_elf_headers(app, path) ||
!flipper_application_load_section_table(app)) {
return FlipperApplicationPreloadStatusInvalidFile;
}
if((app->manifest.base.manifest_magic != FAP_MANIFEST_MAGIC) &&
(app->manifest.base.manifest_version == FAP_MANIFEST_SUPPORTED_VERSION)) {
return FlipperApplicationPreloadStatusInvalidManifest;
}
if(app->manifest.base.api_version.major != app->api_interface->api_version_major /* ||
app->manifest.base.api_version.minor > app->api_interface->api_version_minor */) {
return FlipperApplicationPreloadStatusApiMismatch;
}
return FlipperApplicationPreloadStatusSuccess;
}
const FlipperApplicationManifest* flipper_application_get_manifest(FlipperApplication* app) {
return &app->manifest;
}
FlipperApplicationLoadStatus flipper_application_map_to_memory(FlipperApplication* app) {
last_loaded_app = app;
return flipper_application_load_sections(app);
}
const FlipperApplicationState* flipper_application_get_state(FlipperApplication* app) {
return &app->state;
}
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, (entry_t*)app->entry);
furi_thread_set_context(app->thread, args);
return app->thread;
}
FuriThread* flipper_application_get_thread(FlipperApplication* app) {
return app->thread;
}
void const* flipper_application_get_entry_address(FlipperApplication* app) {
return (void*)app->entry;
}
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,129 @@
#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 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 Get state object for loaded application
* @param app Application pointer
* @return Pointer to state object
*/
const FlipperApplicationState* flipper_application_get_state(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);
/**
* @brief Get previously spawned thread
* @param app Application pointer
* @return Created thread
*/
FuriThread* flipper_application_get_thread(FlipperApplication* app);
/**
* @brief Return relocated and valid address of app's entry point
* @param app Application pointer
* @return Address of app's entry point
*/
void const* flipper_application_get_entry_address(FlipperApplication* app);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,99 @@
#pragma once
#include "elf.h"
#include "flipper_application.h"
#include <m-dict.h>
#ifdef __cplusplus
extern "C" {
#endif
DICT_DEF2(RelocationAddressCache, 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;
} ELFSection_t;
struct FlipperApplication {
const ElfApiInterface* api_interface;
File* fd;
FlipperApplicationState state;
FlipperApplicationManifest manifest;
size_t sections;
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;
ELFSection_t text;
ELFSection_t rodata;
ELFSection_t data;
ELFSection_t bss;
FuriThread* thread;
RelocationAddressCache_t relocation_cache;
};
typedef enum {
FoundERROR = 0,
FoundSymTab = (1 << 0),
FoundStrTab = (1 << 2),
FoundText = (1 << 3),
FoundRodata = (1 << 4),
FoundData = (1 << 5),
FoundBss = (1 << 6),
FoundRelText = (1 << 7),
FoundRelRodata = (1 << 8),
FoundRelData = (1 << 9),
FoundRelBss = (1 << 10),
FoundFappManifest = (1 << 11),
FoundDebugLink = (1 << 12),
FoundValid = FoundSymTab | FoundStrTab | FoundFappManifest,
FoundExec = FoundValid | FoundText,
FoundGdbSection = FoundText | FoundRodata | FoundData | FoundBss,
FoundAll = FoundSymTab | FoundStrTab | FoundText | FoundRodata | FoundData | FoundBss |
FoundRelText | FoundRelRodata | FoundRelData | FoundRelBss | FoundDebugLink,
} FindFlags_t;
/**
* @brief Load and validate basic ELF file headers
* @param e Application instance
* @param path FS path to application file
* @return true if ELF file is valid
*/
bool flipper_application_load_elf_headers(FlipperApplication* e, const char* path);
/**
* @brief Iterate over all sections and save related indexes
* @param e Application instance
* @return true if all required sections are found
*/
bool flipper_application_load_section_table(FlipperApplication* e);
/**
* @brief Load section data to memory and process relocations
* @param e Application instance
* @return Status code
*/
FlipperApplicationLoadStatus flipper_application_load_sections(FlipperApplication* e);
/**
* @brief Release section data
* @param s section pointer
*/
void flipper_application_free_section(ELFSection_t* s);
#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"),
],
)
-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
+3
View File
@@ -12,6 +12,9 @@ env.Append(
CPPDEFINES=[
"PB_ENABLE_MALLOC",
],
SDK_HEADERS=[
File("#/lib/micro-ecc/uECC.h"),
],
)
+13 -2
View File
@@ -1182,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;
+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
+7
View File
@@ -4,6 +4,13 @@ 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"),
],
)
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
+8
View File
@@ -3,6 +3,10 @@
#include "types.h"
#include "protocols/base.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzReceiver SubGhzReceiver;
typedef void (*SubGhzReceiverCallback)(
@@ -63,3 +67,7 @@ void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag fil
*/
SubGhzProtocolDecoderBase*
subghz_receiver_search_decoder_base_by_name(SubGhzReceiver* instance, const char* decoder_name);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -4,6 +4,10 @@
#include <m-array.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
string_t name;
uint64_t key;
@@ -70,3 +74,7 @@ bool subghz_keystore_raw_encrypted_save(
* @return true On success
*/
bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -2,6 +2,10 @@
#include <furi_hal.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context);
typedef struct SubGhzTxRxWorker SubGhzTxRxWorker;
@@ -79,3 +83,7 @@ void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance);
* @return bool - true if running
*/
bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -2,6 +2,10 @@
#include <furi_hal.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzWorker SubGhzWorker;
typedef void (*SubGhzWorkerOverrunCallback)(void* context);
@@ -62,3 +66,7 @@ void subghz_worker_stop(SubGhzWorker* instance);
* @return bool - true if running
*/
bool subghz_worker_is_running(SubGhzWorker* instance);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -4,6 +4,10 @@
#include "environment.h"
#include "protocols/base.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct SubGhzTransmitter SubGhzTransmitter;
/**
@@ -45,3 +49,7 @@ bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat*
* @return LevelDuration
*/
LevelDuration subghz_transmitter_yield(void* context);
#ifdef __cplusplus
}
#endif
+17
View File
@@ -7,6 +7,23 @@ env.Append(
CPPPATH=[
"#/lib/toolbox",
],
SDK_HEADERS=[
File("#/lib/toolbox/manchester_decoder.h"),
File("#/lib/toolbox/manchester_encoder.h"),
File("#/lib/toolbox/path.h"),
File("#/lib/toolbox/random_name.h"),
File("#/lib/toolbox/hmac_sha256.h"),
File("#/lib/toolbox/crc32_calc.h"),
File("#/lib/toolbox/dir_walk.h"),
File("#/lib/toolbox/md5.h"),
File("#/lib/toolbox/args.h"),
File("#/lib/toolbox/saved_struct.h"),
File("#/lib/toolbox/version.h"),
File("#/lib/toolbox/tar/tar_archive.h"),
File("#/lib/toolbox/stream/stream.h"),
File("#/lib/toolbox/stream/file_stream.h"),
File("#/lib/toolbox/stream/string_stream.h"),
],
)
+11
View File
@@ -1,3 +1,10 @@
#pragma once
#include "sha256.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct hmac_context {
void (*init_hash)(const struct hmac_context* context);
@@ -25,3 +32,7 @@ void hmac_sha256_update(
unsigned message_size);
void hmac_sha256_finish(const hmac_sha256_context* ctx, const uint8_t* K, uint8_t* hash_result);
#ifdef __cplusplus
}
#endif
+8
View File
@@ -4,6 +4,14 @@
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
bool saved_struct_load(const char* path, void* data, size_t size, uint8_t magic, uint8_t version);
bool saved_struct_save(const char* path, void* data, size_t size, uint8_t magic, uint8_t version);
#ifdef __cplusplus
}
#endif
+10
View File
@@ -1,3 +1,9 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define SHA256_DIGEST_SIZE 32
#define SHA256_BLOCK_SIZE 64
@@ -12,3 +18,7 @@ void sha256_start(sha256_context* ctx);
void sha256_finish(sha256_context* ctx, unsigned char output[32]);
void sha256_update(sha256_context* ctx, const unsigned char* input, unsigned int ilen);
void sha256_process(sha256_context* ctx);
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -6,7 +6,7 @@
#include <bt/bt_service/bt_keys_filename.h>
#include <dolphin/helpers/dolphin_state_filename.h>
#include <desktop/helpers/slideshow_filename.h>
#include <desktop/desktop_settings/desktop_settings_filename.h>
#include <desktop/desktop_settings_filename.h>
#include <notification/notification_settings_filename.h>
#define LFS_BACKUP_DEFAULT_LOCATION EXT_PATH(LFS_BACKUP_DEFAULT_FILENAME)