From 1975868ed8abb5da83df9ec06a806c1e3e1008df Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Sun, 3 Jul 2022 01:44:38 -0700 Subject: [PATCH] PicoPass / iClass (#1298) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add mdedtls for des3 implementation * add localss from RfidResearchGroup/proxmark3 * picopass reader app and rfal for communicating with picopass cards * always turn off field * close storage when keys are not found * Add mbedtls as submodule * add mbedtl_config * Switched to only including specific mbedtls files I need. Thank you @kevinwallace * cherry-pick kevinwallace sconsify * scons for mbedtls/loclass * Reset to ready state on error * unsigned FC/CN * clean FC/CN if not decoded Co-authored-by: hedger Co-authored-by: あく Co-authored-by: Kevin Wallace --- .gitmodules | 3 + applications/picopass/application.fam | 11 + applications/picopass/picopass.c | 436 ++++++++++++++++++++++++ applications/picopass/picopass.h | 9 + firmware.scons | 2 + lib/SConscript | 2 + lib/ST25RFAL002/include/rfal_picopass.h | 65 ++++ lib/ST25RFAL002/source/rfal_picopass.c | 172 ++++++++++ lib/loclass.scons | 19 ++ lib/loclass/optimized_cipher.c | 338 ++++++++++++++++++ lib/loclass/optimized_cipher.h | 90 +++++ lib/loclass/optimized_cipherutils.c | 137 ++++++++ lib/loclass/optimized_cipherutils.h | 64 ++++ lib/loclass/optimized_elite.c | 234 +++++++++++++ lib/loclass/optimized_elite.h | 58 ++++ lib/loclass/optimized_ikeys.c | 321 +++++++++++++++++ lib/loclass/optimized_ikeys.h | 66 ++++ lib/mbedtls | 1 + lib/mbedtls.scons | 20 ++ 19 files changed, 2048 insertions(+) create mode 100644 applications/picopass/application.fam create mode 100644 applications/picopass/picopass.c create mode 100644 applications/picopass/picopass.h create mode 100644 lib/ST25RFAL002/include/rfal_picopass.h create mode 100644 lib/ST25RFAL002/source/rfal_picopass.c create mode 100644 lib/loclass.scons create mode 100644 lib/loclass/optimized_cipher.c create mode 100644 lib/loclass/optimized_cipher.h create mode 100644 lib/loclass/optimized_cipherutils.c create mode 100644 lib/loclass/optimized_cipherutils.h create mode 100644 lib/loclass/optimized_elite.c create mode 100644 lib/loclass/optimized_elite.h create mode 100644 lib/loclass/optimized_ikeys.c create mode 100644 lib/loclass/optimized_ikeys.h create mode 160000 lib/mbedtls create mode 100644 lib/mbedtls.scons diff --git a/.gitmodules b/.gitmodules index 5846b705..b580a8c7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,3 +25,6 @@ [submodule "lib/scons"] path = lib/scons url = https://github.com/SCons/scons.git +[submodule "lib/mbedtls"] + path = lib/mbedtls + url = https://github.com/Mbed-TLS/mbedtls.git diff --git a/applications/picopass/application.fam b/applications/picopass/application.fam new file mode 100644 index 00000000..3ad72d27 --- /dev/null +++ b/applications/picopass/application.fam @@ -0,0 +1,11 @@ +App( + appid="picopass", + name="PicoPass Reader", + apptype=FlipperAppType.PLUGIN, + entry_point="picopass_app", + cdefines=["APP_PICOPASS"], + requires=["gui"], + stack_size=1 * 1024, + icon="A_Plugins_14", + order=30, +) diff --git a/applications/picopass/picopass.c b/applications/picopass/picopass.c new file mode 100644 index 00000000..477b7b86 --- /dev/null +++ b/applications/picopass/picopass.c @@ -0,0 +1,436 @@ +#include "picopass.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TAG "PicoPass" + +#define PICOPASS_APP_ICLASS_KEY_PATH "/any/picopass/iclass_key.bin" +#define PICOPASS_APP_ICLASS_DECRYPT_KEY_PATH "/any/picopass/iclass_decryptionkey.bin" + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + bool valid; + uint8_t bitLength; + uint8_t FacilityCode; + uint16_t CardNumber; +} WiegandRecord; + +typedef struct { + bool biometrics; + uint8_t encryption; + uint8_t credential[8]; + uint8_t pin0[8]; + uint8_t pin1[8]; + WiegandRecord record; +} PACS; + +enum State { INIT, KEYS_MISSING, READY, RESULT }; +typedef struct { + enum State state; + PACS pacs; +} PluginState; + +uint8_t iclass_key[8] = {0}; // NB: not the permuted version +uint8_t iclass_decryptionkey[16] = {0}; +ApplicationArea AA1; + +static bool picopass_load_keys() { + Storage* storage = furi_record_open("storage"); + File* file = storage_file_alloc(storage); + + if(!storage_file_open(file, PICOPASS_APP_ICLASS_KEY_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open iClass key"); + storage_file_free(file); + furi_record_close("storage"); + return false; + }; + storage_file_read(file, iclass_key, sizeof(iclass_key)); + storage_file_close(file); + FURI_LOG_D(TAG, "iClass key loaded"); + + if(!storage_file_open( + file, PICOPASS_APP_ICLASS_DECRYPT_KEY_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open iClass decryption key"); + storage_file_free(file); + furi_record_close("storage"); + return false; + }; + storage_file_read(file, iclass_decryptionkey, sizeof(iclass_decryptionkey)); + storage_file_close(file); + FURI_LOG_D(TAG, "iClass decryption key loaded"); + + storage_file_free(file); + furi_record_close("storage"); + return true; +} + +static void render_callback(Canvas* const canvas, void* ctx) { + const PluginState* plugin_state = acquire_mutex((ValueMutex*)ctx, 25); + if(plugin_state == NULL) { + return; + } + // border around the edge of the screen + canvas_draw_frame(canvas, 0, 0, 128, 64); + + canvas_set_font(canvas, FontPrimary); + + if(plugin_state->state == INIT) { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Loading..."); + } else if(plugin_state->state == KEYS_MISSING) { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Keys missing"); + } else if(plugin_state->state == READY) { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "Push center to scan"); + } else if(plugin_state->state == RESULT) { + char raw_credential[25] = {0}; + sprintf( + raw_credential, + "%02x %02x %02x %02x %02x %02x %02x %02x", + plugin_state->pacs.credential[0], + plugin_state->pacs.credential[1], + plugin_state->pacs.credential[2], + plugin_state->pacs.credential[3], + plugin_state->pacs.credential[4], + plugin_state->pacs.credential[5], + plugin_state->pacs.credential[6], + plugin_state->pacs.credential[7]); + canvas_draw_str_aligned(canvas, 64, 34, AlignCenter, AlignTop, raw_credential); + + if(plugin_state->pacs.record.valid) { + char parsed[20] = {0}; + sprintf( + parsed, + "FC: %03u CN: %05u", + plugin_state->pacs.record.FacilityCode, + plugin_state->pacs.record.CardNumber); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignBottom, parsed); + } + } + + release_mutex((ValueMutex*)ctx, plugin_state); +} + +static void input_callback(InputEvent* input_event, osMessageQueueId_t event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + osMessageQueuePut(event_queue, &event, 0, osWaitForever); +} + +static void picopass_state_init(PluginState* const plugin_state) { + plugin_state->state = INIT; + if(picopass_load_keys()) { + plugin_state->state = READY; + } else { + plugin_state->state = KEYS_MISSING; + } +} + +ReturnCode decrypt(uint8_t* enc_data, uint8_t* dec_data) { + uint8_t key[32] = {0}; + memcpy(key, iclass_decryptionkey, sizeof(iclass_decryptionkey)); + mbedtls_des3_context ctx; + mbedtls_des3_init(&ctx); + mbedtls_des3_set2key_dec(&ctx, key); + mbedtls_des3_crypt_ecb(&ctx, enc_data, dec_data); + mbedtls_des3_free(&ctx); + return ERR_NONE; +} + +ReturnCode parseWiegand(uint8_t* data, WiegandRecord* record) { + uint32_t* halves = (uint32_t*)data; + if(halves[0] == 0) { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[1])); + record->bitLength = 31 - leading0s; + } else { + uint8_t leading0s = __builtin_clz(REVERSE_BYTES_U32(halves[0])); + record->bitLength = 63 - leading0s; + } + FURI_LOG_D(TAG, "bitLength: %d", record->bitLength); + + if(record->bitLength == 26) { + uint8_t* v4 = data + 4; + v4[0] = 0; + + uint32_t bot = v4[3] | (v4[2] << 8) | (v4[1] << 16) | (v4[0] << 24); + + record->CardNumber = (bot >> 1) & 0xFFFF; + record->FacilityCode = (bot >> 17) & 0xFF; + record->valid = true; + } else { + record->CardNumber = 0; + record->FacilityCode = 0; + record->valid = false; + } + return ERR_NONE; +} + +ReturnCode disable_field(ReturnCode rc) { + st25r3916TxRxOff(); + rfalLowPowerModeStart(); + return rc; +} + +ReturnCode picopass_read_card(ApplicationArea* AA1) { + rfalPicoPassIdentifyRes idRes; + rfalPicoPassSelectRes selRes; + rfalPicoPassReadCheckRes rcRes; + rfalPicoPassCheckRes chkRes; + + ReturnCode err; + + uint8_t div_key[8] = {0}; + uint8_t mac[4] = {0}; + uint8_t ccnr[12] = {0}; + + st25r3916TxRxOn(); + rfalLowPowerModeStop(); + rfalWorker(); + err = rfalPicoPassPollerInitialize(); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerInitialize error %d\n", err); + return disable_field(err); + } + + err = rfalFieldOnAndStartGT(); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalFieldOnAndStartGT error %d\n", err); + return disable_field(err); + } + + err = rfalPicoPassPollerCheckPresence(); + if(err != ERR_RF_COLLISION) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheckPresence error %d\n", err); + return disable_field(err); + } + + err = rfalPicoPassPollerIdentify(&idRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerIdentify error %d\n", err); + return disable_field(err); + } + + err = rfalPicoPassPollerSelect(idRes.CSN, &selRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerSelect error %d\n", err); + return disable_field(err); + } + + err = rfalPicoPassPollerReadCheck(&rcRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadCheck error %d", err); + return disable_field(err); + } + memcpy(ccnr, rcRes.CCNR, sizeof(rcRes.CCNR)); // last 4 bytes left 0 + + diversifyKey(selRes.CSN, iclass_key, div_key); + opt_doReaderMAC(ccnr, div_key, mac); + + err = rfalPicoPassPollerCheck(mac, &chkRes); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerCheck error %d", err); + return disable_field(err); + } + + for(size_t i = 0; i < 4; i++) { + FURI_LOG_D(TAG, "rfalPicoPassPollerReadBlock block %d", i + 6); + err = rfalPicoPassPollerReadBlock(i + 6, &(AA1->block[i])); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "rfalPicoPassPollerReadBlock error %d", err); + return disable_field(err); + } + } + return disable_field(ERR_NONE); +} + +int32_t picopass_app(void* p) { + UNUSED(p); + osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(PluginEvent), NULL); + + PluginState* plugin_state = malloc(sizeof(PluginState)); + picopass_state_init(plugin_state); + ValueMutex state_mutex; + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + FURI_LOG_E("Hello_world", "cannot create mutex\r\n"); + free(plugin_state); + return 255; + } + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, render_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + // Open GUI and register view_port + Gui* gui = furi_record_open("gui"); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + PluginEvent event; + ReturnCode err; + for(bool processing = true; processing;) { + osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100); + PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); + + if(event_status == osOK) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + FURI_LOG_D(TAG, "Input Up"); + break; + case InputKeyDown: + FURI_LOG_D(TAG, "Input Down"); + break; + case InputKeyRight: + FURI_LOG_D(TAG, "Input Right"); + break; + case InputKeyLeft: + FURI_LOG_D(TAG, "Input Left"); + break; + case InputKeyOk: + FURI_LOG_D(TAG, "Input OK"); + err = picopass_read_card(&AA1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "picopass_read_card error %d", err); + plugin_state->state = READY; + break; + } + FURI_LOG_D(TAG, "read OK"); + + plugin_state->pacs.biometrics = AA1.block[0].data[4]; + plugin_state->pacs.encryption = AA1.block[0].data[7]; + if(plugin_state->pacs.encryption == 0x17) { + FURI_LOG_D(TAG, "3DES Encrypted"); + err = decrypt(AA1.block[1].data, plugin_state->pacs.credential); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + break; + } + FURI_LOG_D(TAG, "Decrypted 7"); + + err = decrypt(AA1.block[2].data, plugin_state->pacs.pin0); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + break; + } + FURI_LOG_D(TAG, "Decrypted 8"); + + err = decrypt(AA1.block[3].data, plugin_state->pacs.pin1); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "decrypt error %d", err); + break; + } + FURI_LOG_D(TAG, "Decrypted 9"); + } else if(plugin_state->pacs.encryption == 0x14) { + FURI_LOG_D(TAG, "No Encryption"); + memcpy( + plugin_state->pacs.credential, + AA1.block[1].data, + RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy( + plugin_state->pacs.pin0, + AA1.block[2].data, + RFAL_PICOPASS_MAX_BLOCK_LEN); + memcpy( + plugin_state->pacs.pin1, + AA1.block[3].data, + RFAL_PICOPASS_MAX_BLOCK_LEN); + } else if(plugin_state->pacs.encryption == 0x15) { + FURI_LOG_D(TAG, "DES Encrypted"); + } else { + FURI_LOG_D(TAG, "Unknown encryption"); + break; + } + + FURI_LOG_D( + TAG, + "credential %02x%02x%02x%02x%02x%02x%02x%02x", + plugin_state->pacs.credential[0], + plugin_state->pacs.credential[1], + plugin_state->pacs.credential[2], + plugin_state->pacs.credential[3], + plugin_state->pacs.credential[4], + plugin_state->pacs.credential[5], + plugin_state->pacs.credential[6], + plugin_state->pacs.credential[7]); + FURI_LOG_D( + TAG, + "pin0 %02x%02x%02x%02x%02x%02x%02x%02x", + plugin_state->pacs.pin0[0], + plugin_state->pacs.pin0[1], + plugin_state->pacs.pin0[2], + plugin_state->pacs.pin0[3], + plugin_state->pacs.pin0[4], + plugin_state->pacs.pin0[5], + plugin_state->pacs.pin0[6], + plugin_state->pacs.pin0[7]); + FURI_LOG_D( + TAG, + "pin1 %02x%02x%02x%02x%02x%02x%02x%02x", + plugin_state->pacs.pin1[0], + plugin_state->pacs.pin1[1], + plugin_state->pacs.pin1[2], + plugin_state->pacs.pin1[3], + plugin_state->pacs.pin1[4], + plugin_state->pacs.pin1[5], + plugin_state->pacs.pin1[6], + plugin_state->pacs.pin1[7]); + + err = parseWiegand( + plugin_state->pacs.credential, &plugin_state->pacs.record); + if(err != ERR_NONE) { + FURI_LOG_E(TAG, "parse error %d", err); + break; + } + if(plugin_state->pacs.record.valid) { + FURI_LOG_D( + TAG, + "FC: %03d CN: %05d", + plugin_state->pacs.record.FacilityCode, + plugin_state->pacs.record.CardNumber); + } + plugin_state->state = RESULT; + + break; + case InputKeyBack: + FURI_LOG_D(TAG, "Input Back"); + processing = false; + break; + } + } + } + } else { + // FURI_LOG_D(TAG, "osMessageQueue: event timeout"); + // event timeout + } + + view_port_update(view_port); + release_mutex(&state_mutex, plugin_state); + } + + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close("gui"); + view_port_free(view_port); + osMessageQueueDelete(event_queue); + + return 0; +} diff --git a/applications/picopass/picopass.h b/applications/picopass/picopass.h new file mode 100644 index 00000000..e24d97d7 --- /dev/null +++ b/applications/picopass/picopass.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include +#include +#include + +#define PP_MAX_DUMP_SIZE 1024 +#define FURI_HAL_PICOPASS_UID_MAX_LEN 10 diff --git a/firmware.scons b/firmware.scons index 4c9988ba..4bed960f 100644 --- a/firmware.scons +++ b/firmware.scons @@ -195,6 +195,8 @@ fwelf = fwenv["FW_ELF"] = fwenv.Program( # 2nd round "flipperformat", "toolbox", + "mbedtls", + "loclass", ], ) diff --git a/lib/SConscript b/lib/SConscript index ee8b8356..a3617c5d 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -72,6 +72,8 @@ libs = env.BuildModules( "subghz", "appframe", "misc", + "mbedtls", + "loclass", ], ) diff --git a/lib/ST25RFAL002/include/rfal_picopass.h b/lib/ST25RFAL002/include/rfal_picopass.h new file mode 100644 index 00000000..c1b07981 --- /dev/null +++ b/lib/ST25RFAL002/include/rfal_picopass.h @@ -0,0 +1,65 @@ + +#ifndef RFAL_PICOPASS_H +#define RFAL_PICOPASS_H + +/* + ****************************************************************************** + * INCLUDES + ****************************************************************************** + */ +#include "platform.h" +#include "rfal_rf.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, +}; + +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; + +typedef struct { + rfalPicoPassReadBlockRes block[4]; +} ApplicationArea; + +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); + +#endif /* RFAL_PICOPASS_H */ diff --git a/lib/ST25RFAL002/source/rfal_picopass.c b/lib/ST25RFAL002/source/rfal_picopass.c new file mode 100644 index 00000000..c99fa224 --- /dev/null +++ b/lib/ST25RFAL002/source/rfal_picopass.c @@ -0,0 +1,172 @@ + +#include "rfal_picopass.h" +#include "utils.h" + +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; + /* + * ./reveng -w 16 -s 0c07cc47 0c064556 0c083bbf 0c09b2ae + width=16 poly=0x1021 init=0xd924 refin=true refout=true xorout=0x0000 check=0x1329 residue=0x0000 name=(none) +0c 06 45 56 +0c 07 cc 47 +0c 08 3b bf +0c 09 b2 ae + */ + + uint8_t readCmds[4][4] = { + {RFAL_PICOPASS_CMD_READ, 6, 0x45, 0x56}, + {RFAL_PICOPASS_CMD_READ, 7, 0xcc, 0x47}, + {RFAL_PICOPASS_CMD_READ, 8, 0x3b, 0xbf}, + {RFAL_PICOPASS_CMD_READ, 9, 0xb2, 0xae}}; + + uint8_t* txBuf = readCmds[blockNum - 6]; + 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); + // printf("check rx: %d %s\n", recvLen, hex2Str(readRes->data, RFAL_PICOPASS_MAX_BLOCK_LEN)); + + return ret; +} diff --git a/lib/loclass.scons b/lib/loclass.scons new file mode 100644 index 00000000..ba43dd6e --- /dev/null +++ b/lib/loclass.scons @@ -0,0 +1,19 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/loclass", + ], + CPPDEFINES=[ + ], +) + + +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") diff --git a/lib/loclass/optimized_cipher.c b/lib/loclass/optimized_cipher.c new file mode 100644 index 00000000..68b36af8 --- /dev/null +++ b/lib/loclass/optimized_cipher.c @@ -0,0 +1,338 @@ +//----------------------------------------------------------------------------- +// 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 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; + opt_select_LUT[r] = (z0 & 4) | (z1 & 2) | (z2 & 1); + } + print_result("", opt_select_LUT, 256); +} +***********************************************************************************/ + +#define 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)) + +/* + * Some background on the expression above can be found here... +uint8_t xopt__select(bool x, bool y, uint8_t r) +{ + + //r: r0 r1 r2 r3 r4 r5 r6 r7 + //r_ls2: r2 r3 r4 r5 r6 r7 0 0 + // z0 + // z1 + +// uint8_t z0 = (r0 & r2) ^ (r1 & ~r3) ^ (r2 | r4); // <-- original + uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); + +// uint8_t z1 = (r0 | r2) ^ ( r5 | r7) ^ r1 ^ r6 ^ x ^ y; // <-- original + uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1); + +// uint8_t z2 = (r3 & ~r5) ^ (r4 & r6 ) ^ r7 ^ x; // <-- original + uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r ^ x; + + return (z0 & 4) | (z1 & 2) | (z2 & 1); +} +*/ + +static void opt_successor(const uint8_t *k, State_t *s, uint8_t y) { +// #define opt_T(s) (0x1 & ((s->t >> 15) ^ (s->t >> 14) ^ (s->t >> 10) ^ (s->t >> 8) ^ (s->t >> 5) ^ (s->t >> 4)^ (s->t >> 1) ^ s->t)) + // uint8_t Tt = opt_T(s); + 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 = opt_select_LUT[s->r] & 0x04; + opt_select |= (opt_select_LUT[s->r] ^ ((Tt ^ y) << 1)) & 0x02; + opt_select |= (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 opt_suc(const uint8_t *k, State_t *s, const uint8_t *in, uint8_t length, bool add32Zeroes) { + for (int i = 0; i < length; i++) { + uint8_t head; + head = in[i]; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + + head >>= 1; + opt_successor(k, s, head); + } + //For tag MAC, an additional 32 zeroes + if (add32Zeroes) { + for (int i = 0; i < 16; i++) { + opt_successor(k, s, 0); + opt_successor(k, s, 0); + } + } +} + +static void opt_output(const uint8_t *k, State_t *s, uint8_t *buffer) { + for (uint8_t times = 0; times < 4; times++) { + uint8_t bout = 0; + bout |= (s->r & 0x4) >> 2; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) >> 1; + opt_successor(k, s, 0); + bout |= (s->r & 0x4); + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 1; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 2; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 3; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 4; + opt_successor(k, s, 0); + bout |= (s->r & 0x4) << 5; + opt_successor(k, s, 0); + buffer[times] = bout; + } +} + +static void opt_MAC(uint8_t *k, uint8_t *input, uint8_t *out) { + State_t _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + + opt_suc(k, &_init, input, 12, false); + opt_output(k, &_init, out); +} + +static void opt_MAC_N(uint8_t *k, uint8_t *input, uint8_t in_size, uint8_t *out) { + State_t _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + + opt_suc(k, &_init, input, in_size, false); + opt_output(k, &_init, out); +} + +void 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}; + opt_MAC(div_key_p, cc_nr_p, dest); + memcpy(mac, dest, 4); +} + +void opt_doReaderMAC_2(State_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { + opt_suc(div_key_p, &_init, nr, 4, false); + opt_output(div_key_p, &_init, mac); +} + + +void 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}; + opt_MAC_N(div_key_p, in_p, in_size, dest); + memcpy(mac, dest, 4); +} + +void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) { + State_t _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + opt_suc(div_key_p, &_init, cc_p, 12, true); + 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 + */ +State_t opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) { + State_t _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + 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 opt_doTagMAC_2(State_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p) { + opt_suc(div_key_p, &_init, nr, 4, true); + opt_output(div_key_p, &_init, mac); +} + + +void 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 }; + hash2(key, keytable); + 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 + permutekey_rev(key_sel, key_sel_p); + diversifyKey(csn, key_sel_p, div_key); + } else { + diversifyKey(csn, key, div_key); + } +} diff --git a/lib/loclass/optimized_cipher.h b/lib/loclass/optimized_cipher.h new file mode 100644 index 00000000..06f85b08 --- /dev/null +++ b/lib/loclass/optimized_cipher.h @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// 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 +#include +#include +#include + +/** +* 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; +} State_t; + +/** The reader MAC is MAC(key, CC * NR ) + **/ +void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); + +void opt_doReaderMAC_2(State_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 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 + */ +State_t 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 opt_doTagMAC_2(State_t _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); + +void doMAC_N(uint8_t *in_p, uint8_t in_size, uint8_t *div_key_p, uint8_t mac[4]); +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite); +#endif // OPTIMIZED_CIPHER_H diff --git a/lib/loclass/optimized_cipherutils.c b/lib/loclass/optimized_cipherutils.c new file mode 100644 index 00000000..299d1480 --- /dev/null +++ b/lib/loclass/optimized_cipherutils.c @@ -0,0 +1,137 @@ +//----------------------------------------------------------------------------- +// 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 + +/** + * + * @brief Return and remove the first bit (x0) in the stream : + * @param stream + * @return + */ +bool headBit(BitstreamIn_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: + * @param stream + * @return + */ +bool tailBit(BitstreamIn_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 pushBit(BitstreamOut_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 push6bits(BitstreamOut_t *stream, uint8_t bits) { + pushBit(stream, bits & 0x20); + pushBit(stream, bits & 0x10); + pushBit(stream, bits & 0x08); + pushBit(stream, bits & 0x04); + pushBit(stream, bits & 0x02); + pushBit(stream, bits & 0x01); +} + +/** + * @brief bitsLeft + * @param stream + * @return number of bits left in stream + */ +int bitsLeft(BitstreamIn_t *stream) { + return stream->numbits - stream->position; +} +/** + * @brief numBits + * @param stream + * @return Number of bits stored in stream + */ +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { + while (len--) { + dest[len] = (uint8_t) n; + n >>= 8; + } +} + +uint64_t 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 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 reverse_arraybytes(uint8_t *arr, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + arr[i] = reversebytes(arr[i]); + } +} + +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + dest[i] = reversebytes(arr[i]); + } +} + diff --git a/lib/loclass/optimized_cipherutils.h b/lib/loclass/optimized_cipherutils.h new file mode 100644 index 00000000..07bb012f --- /dev/null +++ b/lib/loclass/optimized_cipherutils.h @@ -0,0 +1,64 @@ +//----------------------------------------------------------------------------- +// 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 +#include +#include + +typedef struct { + uint8_t *buffer; + uint8_t numbits; + uint8_t position; +} BitstreamIn_t; + +typedef struct { + uint8_t *buffer; + uint8_t numbits; + uint8_t position; +} BitstreamOut_t; + +bool headBit(BitstreamIn_t *stream); +bool tailBit(BitstreamIn_t *stream); +void pushBit(BitstreamOut_t *stream, bool bit); +int bitsLeft(BitstreamIn_t *stream); + +void push6bits(BitstreamOut_t *stream, uint8_t bits); +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest); +uint64_t x_bytes_to_num(uint8_t *src, size_t len); +uint8_t reversebytes(uint8_t b); +void reverse_arraybytes(uint8_t *arr, size_t len); +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len); +#endif // CIPHERUTILS_H diff --git a/lib/loclass/optimized_elite.c b/lib/loclass/optimized_elite.c new file mode 100644 index 00000000..8940a51e --- /dev/null +++ b/lib/loclass/optimized_elite.c @@ -0,0 +1,234 @@ +//----------------------------------------------------------------------------- +// 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 +#include +#include +#include +#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 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 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 permutekey_rev + * @param key + * @param dest + */ +void 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 hash1 + * @brief rr + * @param val + * @return + */ +static uint8_t rr(uint8_t val) { + return val >> 1 | ((val & 1) << 7); +} + +/** + * Helper function for hash1 + * @brief rl + * @param val + * @return + */ +static uint8_t rl(uint8_t val) { + return val << 1 | ((val & 0x80) >> 7); +} + +/** + * Helper function for hash1 + * @brief swap + * @param val + * @return + */ +static uint8_t 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 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] = rr(swap(csn[2] + k[1])); + k[3] = rl(swap(csn[3] + k[0])); + k[4] = ~rr(csn[4] + k[2]) + 1; + k[5] = ~rl(csn[5] + k[3]) + 1; + k[6] = rr(csn[6] + (k[4] ^ 0x3c)); + k[7] = 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 rk : (F 82 ) 8 × N → (F 82 ) 8 as +rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] +rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) +**/ +static void 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] = rl(outp_key[j]); + } + return; +} + +static mbedtls_des_context ctx_enc; +static mbedtls_des_context ctx_dec; + +static void desdecrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_dec(&ctx_dec, key_std_format); + mbedtls_des_crypt_ecb(&ctx_dec, input, output); +} + +static void desencrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_enc(&ctx_enc, key_std_format); + mbedtls_des_crypt_ecb(&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 hash1 hash1 + * @param key_sel output key_sel=h[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 + 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 + desdecrypt_iclass(z[0], key64_negated, y[0]); + + for (i = 1; i < 8; i++) { + rk(key64, i, temp_output); + desdecrypt_iclass(temp_output, z[i - 1], z[i]); + 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); + } + } +} diff --git a/lib/loclass/optimized_elite.h b/lib/loclass/optimized_elite.h new file mode 100644 index 00000000..f1b41d54 --- /dev/null +++ b/lib/loclass/optimized_elite.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// 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 +#include + +void permutekey(const uint8_t key[8], uint8_t dest[8]); +/** + * Permutes a key from iclass specific format to NIST format + * @brief permutekey_rev + * @param key + * @param dest + */ +void 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 hash1(const uint8_t *csn, uint8_t *k); +void hash2(uint8_t *key64, uint8_t *outp_keytable); + +#endif diff --git a/lib/loclass/optimized_ikeys.c b/lib/loclass/optimized_ikeys.c new file mode 100644 index 00000000..ec414f1a --- /dev/null +++ b/lib/loclass/optimized_ikeys.c @@ -0,0 +1,321 @@ +//----------------------------------------------------------------------------- +// 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 hash0 which + outputs the diversified key k. + + k = 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 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 hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + +**/ +#include "optimized_ikeys.h" + +#include +#include +#include +#include +#include "optimized_cipherutils.h" + +static uint8_t 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 +}; + +static mbedtls_des_context ctx_enc; + +/** + * @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 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 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 swapZvalues(uint64_t c) { + uint64_t newz = 0; + pushbackSixBitByte(&newz, getSixBitByte(c, 0), 7); + pushbackSixBitByte(&newz, getSixBitByte(c, 1), 6); + pushbackSixBitByte(&newz, getSixBitByte(c, 2), 5); + pushbackSixBitByte(&newz, getSixBitByte(c, 3), 4); + pushbackSixBitByte(&newz, getSixBitByte(c, 4), 3); + pushbackSixBitByte(&newz, getSixBitByte(c, 5), 2); + pushbackSixBitByte(&newz, getSixBitByte(c, 6), 1); + pushbackSixBitByte(&newz, 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 ck(int i, int j, uint64_t z) { + if (i == 1 && j == -1) { + // ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + } else if (j == -1) { + // ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + return ck(i - 1, i - 2, z); + } + + if (getSixBitByte(z, i) == getSixBitByte(z, j)) { + //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 = getSixBitByte(z, c); + if (c == i) + pushbackSixBitByte(&newz, j, c); + else + pushbackSixBitByte(&newz, val, c); + } + return ck(i, j - 1, newz); + } else { + return 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] ) = ck(3, 2, z [0] . . . z [3] ) · ck(3, 2, z [4] . . . z [7] ) + + where ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + ck(i, j, z [0] . . . z [3] ) = + ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. +**/ + +static uint64_t check(uint64_t z) { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = ck(3, 2, z); + + // ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = ck(3, 2, z << 24); + + //The ck function will place the values + // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; +} + +static void permute(BitstreamIn_t *p_in, uint64_t z, int l, int r, BitstreamOut_t *out) { + if (bitsLeft(p_in) == 0) + return; + + bool pn = tailBit(p_in); + if (pn) { // pn = 1 + uint8_t zl = getSixBitByte(z, l); + + push6bits(out, zl + 1); + permute(p_in, z, l + 1, r, out); + } else { // otherwise + uint8_t zr = getSixBitByte(z, r); + + push6bits(out, zr); + permute(p_in, z, l, r + 1, out); + } +} + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * 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 hash0(uint64_t c, uint8_t k[8]) { + c = 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 = getSixBitByte(c, n); + uint8_t zn4 = getSixBitByte(c, n + 4); + uint8_t _zn = (zn % (63 - n)) + n; + uint8_t _zn4 = (zn4 % (64 - n)) + n; + pushbackSixBitByte(&zP, _zn, n); + pushbackSixBitByte(&zP, _zn4, n + 4); + } + + uint64_t zCaret = check(zP); + uint8_t p = pi[x % 35]; + + if (x & 1) //Check if x7 is 1 + p = ~p; + + BitstreamIn_t p_in = { &p, 8, 0 }; + uint8_t outbuffer[] = {0, 0, 0, 0, 0, 0, 0, 0}; + BitstreamOut_t out = {outbuffer, 0, 0}; + 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 = 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 = 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 diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { + // Prepare the DES key + mbedtls_des_setkey_enc(&ctx_enc, key); + + uint8_t crypted_csn[8] = {0}; + + // Calculate DES(CSN, KEY) + mbedtls_des_crypt_ecb(&ctx_enc, csn, crypted_csn); + + //Calculate HASH0(DES)) + uint64_t c_csn = x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); + + hash0(c_csn, div_key); +} + diff --git a/lib/loclass/optimized_ikeys.h b/lib/loclass/optimized_ikeys.h new file mode 100644 index 00000000..fd990cac --- /dev/null +++ b/lib/loclass/optimized_ikeys.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// 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 + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * 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 hash0(uint64_t c, uint8_t k[8]); +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ + +void diversifyKey(uint8_t *csn, 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 diff --git a/lib/mbedtls b/lib/mbedtls new file mode 160000 index 00000000..d65aeb37 --- /dev/null +++ b/lib/mbedtls @@ -0,0 +1 @@ +Subproject commit d65aeb37349ad1a50e0f6c9b694d4b5290d60e49 diff --git a/lib/mbedtls.scons b/lib/mbedtls.scons new file mode 100644 index 00000000..35de7e6f --- /dev/null +++ b/lib/mbedtls.scons @@ -0,0 +1,20 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/mbedtls", + "#/lib/mbedtls/include", + ], + CPPDEFINES=[ + ], +) + + +libenv = env.Clone(FW_LIB_NAME="mbedtls") +libenv.ApplyLibFlags() + +sources = ["mbedtls/library/des.c", "mbedtls/library/platform_util.c"] + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib")