Merge pull request #23 from RfidResearchGroup/master

Update
This commit is contained in:
Bjoern Kerler 2020-04-12 17:07:39 +02:00 committed by GitHub
commit 202b50f216
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 2587 additions and 1182 deletions

View file

@ -3,6 +3,9 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased] ## [unreleased][unreleased]
- Port 'hf mfdes' Authentification to CommandNG structure, fix auth session key (@bkerler)
- Updates `hf mfdes` functions, improved logging and added new commands (@bkerler)
- Updated 'legic.lua' and 'legic_clone.lua' script - works with current command set (@Pizza_4u)
- Rewrote `hf mfdes` functions and added apdu debugging (@bkerler) - Rewrote `hf mfdes` functions and added apdu debugging (@bkerler)
- Add Mifare Desfire GetDFNames and improve HF MFDES Enum output (@bkerler) - Add Mifare Desfire GetDFNames and improve HF MFDES Enum output (@bkerler)
- Fix Mifare Desfire select appid handling (@bkerler) - Fix Mifare Desfire select appid handling (@bkerler)

View file

@ -5,3 +5,4 @@ PLATFORM=PM3RDV4
# If you want more than one PLATFORM_EXTRAS option, separate them by spaces: # If you want more than one PLATFORM_EXTRAS option, separate them by spaces:
#PLATFORM_EXTRAS=BTADDON #PLATFORM_EXTRAS=BTADDON
#STANDALONE=LF_SAMYRUN #STANDALONE=LF_SAMYRUN
STANDALONE=LF_ICEHID

View file

@ -1271,7 +1271,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break; break;
} }
case CMD_HF_DESFIRE_AUTH1: { case CMD_HF_DESFIRE_AUTH1: {
MifareDES_Auth1(packet->oldarg[0], packet->oldarg[1], packet->oldarg[2], packet->data.asBytes); MifareDES_Auth1(packet->data.asBytes);
break; break;
} }
case CMD_HF_DESFIRE_AUTH2: { case CMD_HF_DESFIRE_AUTH2: {
@ -1287,7 +1287,7 @@ static void PacketReceived(PacketCommandNG *packet) {
break; break;
} }
case CMD_HF_DESFIRE_COMMAND: { case CMD_HF_DESFIRE_COMMAND: {
MifareSendCommand(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); MifareSendCommand(packet->data.asBytes);
break; break;
} }
case CMD_HF_MIFARE_NACK_DETECT: { case CMD_HF_MIFARE_NACK_DETECT: {

View file

@ -388,30 +388,6 @@ void tdes_dec(void *out, void *in, const uint8_t *key) {
des_dec(out, out, (uint8_t *)key + 0); des_dec(out, out, (uint8_t *)key + 0);
} }
void tdes_2key_enc(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]) {
if (length % 8) return;
uint8_t i;
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
while (length > 0) {
for (i = 0; i < 8; i++)
tout[i] = (unsigned char)(tin[i] ^ iv[i]);
des_enc(tout, tin, (uint8_t *)key + 0);
des_dec(tout, tout, (uint8_t *)key + 8);
des_enc(tout, tout, (uint8_t *)key + 0);
memcpy(iv, tout, 8);
tin += 8;
tout += 8;
length -= 8;
}
}
void tdes_2key_dec(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]) { void tdes_2key_dec(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]) {
if (length % 8) return; if (length % 8) return;
@ -439,6 +415,82 @@ void tdes_2key_dec(void *out, const void *in, size_t length, const void *key, un
} }
} }
void tdes_2key_enc(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]) {
if (length % 8) return;
uint8_t i;
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
while (length > 0) {
for (i = 0; i < 8; i++)
tout[i] = (unsigned char)(tin[i] ^ iv[i]);
des_enc(tout, tin, (uint8_t *)key + 0);
des_dec(tout, tout, (uint8_t *)key + 8);
des_enc(tout, tout, (uint8_t *)key + 0);
memcpy(iv, tout, 8);
tin += 8;
tout += 8;
length -= 8;
}
}
void tdes_3key_enc(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]) {
if (length % 8) return;
uint8_t i;
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
while (length > 0) {
for (i = 0; i < 8; i++)
tout[i] = (unsigned char)(tin[i] ^ iv[i]);
des_enc(tout, tin, (uint8_t *)key + 0);
des_dec(tout, tout, (uint8_t *)key + 8);
des_enc(tout, tout, (uint8_t *)key + 16);
memcpy(iv, tout, 8);
tin += 8;
tout += 8;
length -= 8;
}
}
void tdes_3key_dec(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]) {
if (length % 8) return;
uint8_t i;
unsigned char temp[8];
uint8_t *tin = (uint8_t *) in;
uint8_t *tout = (uint8_t *) out;
while (length > 0) {
memcpy(temp, tin, 8);
des_dec(tout, tin, (uint8_t *)key + 0);
des_enc(tout, tout, (uint8_t *)key + 8);
des_dec(tout, tout, (uint8_t *)key + 16);
for (i = 0; i < 8; i++)
tout[i] = (unsigned char)(tout[i] ^ iv[i]);
memcpy(iv, temp, 8);
tin += 8;
tout += 8;
length -= 8;
}
}
/******************************************************************************/ /******************************************************************************/

View file

@ -104,6 +104,8 @@ void tdes_dec(void *out, void *in, const uint8_t *key);
void tdes_2key_enc(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]); void tdes_2key_enc(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]);
void tdes_2key_dec(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]); void tdes_2key_dec(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]);
void tdes_3key_enc(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]);
void tdes_3key_dec(void *out, const void *in, size_t length, const void *key, unsigned char iv[8]);
// Copied from des.h in desfire imp. // Copied from des.h in desfire imp.
typedef unsigned long DES_KS[16][2]; /* Single-key DES key schedule */ typedef unsigned long DES_KS[16][2]; /* Single-key DES key schedule */

View file

@ -61,7 +61,8 @@ enum DESFIRE_CRYPTOALGO {
T_DES = 0x00, T_DES = 0x00,
T_3DES = 0x01, T_3DES = 0x01,
T_3K3DES = 0x02, T_3K3DES = 0x02,
T_AES = 0x03 T_AES = 0x03,
T_2K3DES = 0x04
}; };

View file

@ -19,6 +19,7 @@
#include "desfire_key.h" #include "desfire_key.h"
#include "string.h" #include "string.h"
#include "dbprint.h"
static inline void update_key_schedules(desfirekey_t key); static inline void update_key_schedules(desfirekey_t key);
@ -74,6 +75,14 @@ void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key) {
Desfire_3k3des_key_new_with_version(data, key); Desfire_3k3des_key_new_with_version(data, key);
} }
void Desfire_2k3des_key_new_with_version(const uint8_t value[16], desfirekey_t key) {
if (key != NULL) {
key->type = T_2K3DES;
memcpy(key->data, value, 16);
update_key_schedules(key);
}
}
void Desfire_3k3des_key_new_with_version(const uint8_t value[24], desfirekey_t key) { void Desfire_3k3des_key_new_with_version(const uint8_t value[24], desfirekey_t key) {
if (key != NULL) { if (key != NULL) {
key->type = T_3K3DES; key->type = T_3K3DES;
@ -136,6 +145,13 @@ void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfire
memcpy(buffer + 12, rndb + 4, 4); memcpy(buffer + 12, rndb + 4, 4);
Desfire_3des_key_new_with_version(buffer, key); Desfire_3des_key_new_with_version(buffer, key);
break; break;
case T_2K3DES:
memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4);
memcpy(buffer + 8, rnda + 4, 4);
memcpy(buffer + 12, rndb + 4, 4);
Desfire_2k3des_key_new_with_version(buffer, key);
break;
case T_3K3DES: case T_3K3DES:
memcpy(buffer, rnda, 4); memcpy(buffer, rnda, 4);
memcpy(buffer + 4, rndb, 4); memcpy(buffer + 4, rndb, 4);

View file

@ -9,6 +9,7 @@ void Desfire_des_key_new_with_version(const uint8_t value[8], desfirekey_t key);
void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key); void Desfire_3des_key_new_with_version(const uint8_t value[16], desfirekey_t key);
void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key); void Desfire_3k3des_key_new(const uint8_t value[24], desfirekey_t key);
void Desfire_3k3des_key_new_with_version(const uint8_t value[24], desfirekey_t key); void Desfire_3k3des_key_new_with_version(const uint8_t value[24], desfirekey_t key);
void Desfire_2k3des_key_new_with_version(const uint8_t value[16], desfirekey_t key);
void Desfire_aes_key_new(const uint8_t value[16], desfirekey_t key); void Desfire_aes_key_new(const uint8_t value[16], desfirekey_t key);
void Desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version, desfirekey_t key); void Desfire_aes_key_new_with_version(const uint8_t value[16], uint8_t version, desfirekey_t key);
uint8_t Desfire_key_get_version(desfirekey_t key); uint8_t Desfire_key_get_version(desfirekey_t key);

View file

@ -2672,8 +2672,10 @@ void ReaderIso14443a(PacketCommandNG *c) {
uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00}; uint8_t buf[PM3_CMD_DATA_SIZE] = {0x00};
uint8_t par[MAX_PARITY_SIZE] = {0x00}; uint8_t par[MAX_PARITY_SIZE] = {0x00};
if ((param & ISO14A_CONNECT)) if ((param & ISO14A_CONNECT)) {
iso14_pcb_blocknum = 0;
clear_trace(); clear_trace();
}
set_tracing(true); set_tracing(true);

View file

@ -29,7 +29,7 @@
# define FWT_TIMEOUT_14B 35312 # define FWT_TIMEOUT_14B 35312
#endif #endif
#ifndef ISO14443B_DMA_BUFFER_SIZE #ifndef ISO14443B_DMA_BUFFER_SIZE
# define ISO14443B_DMA_BUFFER_SIZE 256 # define ISO14443B_DMA_BUFFER_SIZE 512 //changed this from 256
#endif #endif
#ifndef RECEIVE_MASK #ifndef RECEIVE_MASK
# define RECEIVE_MASK (ISO14443B_DMA_BUFFER_SIZE-1) # define RECEIVE_MASK (ISO14443B_DMA_BUFFER_SIZE-1)
@ -37,7 +37,7 @@
// Guard Time (per 14443-2) // Guard Time (per 14443-2)
#ifndef TR0 #ifndef TR0
# define TR0 0 # define TR0 32 //this value equals 8 ETU = 32 ssp clk (w/ 424 khz)
#endif #endif
// Synchronization time (per 14443-2) // Synchronization time (per 14443-2)
@ -261,6 +261,10 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) {
// 80/fs < TR1 < 200/fs // 80/fs < TR1 < 200/fs
// 10 ETU < TR1 < 24 ETU // 10 ETU < TR1 < 24 ETU
// Send TR1.
// 10-11 ETU * 4times samples ONES
for (int i = 0; i < 10; i++) { SEND4STUFFBIT(1); }
// Send SOF. // Send SOF.
// 10-11 ETU * 4times samples ZEROS // 10-11 ETU * 4times samples ZEROS
for (int i = 0; i < 10; i++) { SEND4STUFFBIT(0); } for (int i = 0; i < 10; i++) { SEND4STUFFBIT(0); }
@ -307,7 +311,7 @@ static void CodeIso14443bAsTag(const uint8_t *cmd, int len) {
//for(i = 0; i < 10; i++) { ToSendStuffBit(0); } //for(i = 0; i < 10; i++) { ToSendStuffBit(0); }
// why this? // why this?
for (int i = 0; i < 40; i++) { SEND4STUFFBIT(1); } for (int i = 0; i < 2; i++) { SEND4STUFFBIT(1); }
//for(i = 0; i < 40; i++) { ToSendStuffBit(1); } //for(i = 0; i < 40; i++) { ToSendStuffBit(1); }
// Convert from last byte pos to length // Convert from last byte pos to length

View file

@ -16,6 +16,7 @@
#include "commonutil.h" #include "commonutil.h"
#include "util.h" #include "util.h"
#include "mifare.h" #include "mifare.h"
#include "ticks.h"
#define MAX_APPLICATION_COUNT 28 #define MAX_APPLICATION_COUNT 28
#define MAX_FILE_COUNT 16 #define MAX_FILE_COUNT 16
@ -51,29 +52,38 @@ bool InitDesfireCard() {
return true; return true;
} }
void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain) { typedef struct {
uint8_t len;
uint8_t data[RECEIVE_SIZE];
} cmdres_t;
void MifareSendCommand(uint8_t *datain) {
struct p {
uint8_t flags;
uint8_t datalen;
uint8_t datain[FRAME_PAYLOAD_SIZE];
} PACKED;
struct p *payload = (struct p *) datain;
uint8_t flags = arg0;
size_t datalen = arg1;
uint8_t resp[RECEIVE_SIZE]; uint8_t resp[RECEIVE_SIZE];
memset(resp, 0, sizeof(resp)); memset(resp, 0, sizeof(resp));
if (DBGLEVEL >= DBG_EXTENDED) { if (DBGLEVEL >= DBG_EXTENDED) {
Dbprintf(" flags : %02X", flags); Dbprintf(" flags : %02X", payload->flags);
Dbprintf(" len : %02X", datalen); Dbprintf(" len : %02X", payload->datalen);
print_result(" RX : ", datain, datalen); print_result(" RX : ", payload->datain, payload->datalen);
} }
if (flags & CLEARTRACE) if (payload->flags & CLEARTRACE)
clear_trace(); clear_trace();
if (flags & INIT) { if (payload->flags & INIT) {
if (!InitDesfireCard()) { if (!InitDesfireCard()) {
return; return;
} }
} }
int len = DesfireAPDU(datain, datalen, resp); int len = DesfireAPDU(payload->datain, payload->datalen, resp);
if (DBGLEVEL >= DBG_EXTENDED) if (DBGLEVEL >= DBG_EXTENDED)
print_result("RESP <--: ", resp, len); print_result("RESP <--: ", resp, len);
@ -82,10 +92,18 @@ void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain) {
return; return;
} }
if (flags & DISCONNECT) if (payload->flags & DISCONNECT)
OnSuccess(); OnSuccess();
reply_mix(CMD_ACK, 1, len, 0, resp, len); //reply_mix(CMD_ACK, 1, len, 0, resp, len);
LED_B_ON();
cmdres_t rpayload;
rpayload.len = len;
memcpy(rpayload.data, resp, rpayload.len);
reply_ng(CMD_HF_DESFIRE_COMMAND, PM3_SUCCESS, (uint8_t *)&rpayload, sizeof(rpayload));
LED_B_OFF();
} }
void MifareDesfireGetInformation() { void MifareDesfireGetInformation() {
@ -103,7 +121,7 @@ void MifareDesfireGetInformation() {
uint8_t versionSW[7]; uint8_t versionSW[7];
uint8_t details[14]; uint8_t details[14];
} PACKED payload; } PACKED payload;
/* /*
1 = PCB 1 1 = PCB 1
2 = cid 2 2 = cid 2
@ -117,6 +135,9 @@ void MifareDesfireGetInformation() {
set_tracing(true); set_tracing(true);
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
// reset the pcb_blocknum,
pcb_blocknum = 0;
// card select - information // card select - information
if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) { if (!iso14443a_select_card(NULL, &card, NULL, true, 0, false)) {
if (DBGLEVEL >= DBG_ERROR) DbpString("Can't select card"); if (DBGLEVEL >= DBG_ERROR) DbpString("Can't select card");
@ -137,7 +158,7 @@ void MifareDesfireGetInformation() {
memcpy(payload.uid, card.uid, sizeof(payload.uid)); memcpy(payload.uid, card.uid, sizeof(payload.uid));
LED_A_ON(); LED_A_ON();
uint8_t cmd[] = {GET_VERSION, 0x00, 0x00, 0x00}; uint8_t cmd[] = {0x90, GET_VERSION, 0x00, 0x00, 0x00};
size_t cmd_len = sizeof(cmd); size_t cmd_len = sizeof(cmd);
len = DesfireAPDU(cmd, cmd_len, resp); len = DesfireAPDU(cmd, cmd_len, resp);
@ -152,7 +173,7 @@ void MifareDesfireGetInformation() {
memcpy(payload.versionHW, resp + 1, sizeof(payload.versionHW)); memcpy(payload.versionHW, resp + 1, sizeof(payload.versionHW));
// ADDITION_FRAME 1 // ADDITION_FRAME 1
cmd[0] = ADDITIONAL_FRAME; cmd[1] = ADDITIONAL_FRAME;
len = DesfireAPDU(cmd, cmd_len, resp); len = DesfireAPDU(cmd, cmd_len, resp);
if (!len) { if (!len) {
print_result("ERROR <--: ", resp, len); print_result("ERROR <--: ", resp, len);
@ -178,356 +199,423 @@ void MifareDesfireGetInformation() {
LED_B_ON(); LED_B_ON();
reply_ng(CMD_HF_DESFIRE_INFO, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload)); reply_ng(CMD_HF_DESFIRE_INFO, PM3_SUCCESS, (uint8_t *)&payload, sizeof(payload));
LED_B_OFF(); LED_B_OFF();
// reset the pcb_blocknum, // reset the pcb_blocknum,
pcb_blocknum = 0; pcb_blocknum = 0;
OnSuccess(); OnSuccess();
} }
void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { typedef struct {
// mode = arg0 uint8_t sessionkeylen;
// algo = arg1 uint8_t sessionkey[24];
// keyno = arg2 } authres_t;
void MifareDES_Auth1(uint8_t *datain) {
int len = 0; int len = 0;
//uint8_t PICC_MASTER_KEY8[8] = { 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47}; struct p {
uint8_t PICC_MASTER_KEY16[16] = { 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f }; uint8_t mode;
//uint8_t null_key_data16[16] = {0x00}; uint8_t algo;
//uint8_t new_key_data8[8] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}; uint8_t keyno;
//uint8_t new_key_data16[16] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; uint8_t keylen;
uint8_t key[24];
uint8_t resp[256] = {0x00}; } PACKED;
struct p *payload = (struct p *) datain;
size_t datalen = datain[0];
uint8_t cmd[40] = {0x00};
uint8_t encRndB[16] = {0x00};
uint8_t decRndB[16] = {0x00};
uint8_t both[32] = {0x00};
//InitDesfireCard();
LED_A_ON();
LED_B_OFF();
LED_C_OFF();
// 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32) // 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32)
// 4 different crypto arg1 DES, 3DES, 3K3DES, AES // 4 different crypto arg1 DES, 3DES, 3K3DES, AES
// 3 different communication modes, PLAIN,MAC,CRYPTO // 3 different communication modes, PLAIN,MAC,CRYPTO
// des, key 0, mbedtls_aes_context ctx;
switch (arg0) {
case 1: {
uint8_t keybytes[16];
uint8_t RndA[8] = {0x00};
uint8_t RndB[8] = {0x00};
if (arg1 == 2) { uint8_t keybytes[24];
if (datain[1] == 0xff) { uint8_t resp[256] = {0x00};
memcpy(keybytes, PICC_MASTER_KEY16, 16); uint8_t cmd[40] = {0x00};
} else {
memcpy(keybytes, datain + 1, datalen);
}
} else {
if (arg1 == 1) {
if (datain[1] == 0xff) {
uint8_t null_key_data8[8] = {0x00};
memcpy(keybytes, null_key_data8, 8);
} else {
memcpy(keybytes, datain + 1, datalen);
}
}
}
struct desfire_key defaultkey = {0}; // Crypt constants
desfirekey_t key = &defaultkey; uint8_t IV[16] = {0x00};
uint8_t RndA[16] = {0x00};
uint8_t RndB[16] = {0x00};
uint8_t encRndB[16] = {0x00};
uint8_t rotRndB[16] = {0x00}; //RndB'
uint8_t both[32] = {0x00}; // ek/dk_keyNo(RndA+RndB')
if (arg1 == 2) // Generate Random Value
Desfire_3des_key_new_with_version(keybytes, key); uint32_t value = prng_successor(GetTickCount(), 32);
else if (arg1 == 1) num_to_bytes(value, 4, &RndA[0]);
Desfire_des_key_new(keybytes, key); value = prng_successor(GetTickCount(), 32);
num_to_bytes(value, 4, &RndA[4]);
value = prng_successor(GetTickCount(), 32);
num_to_bytes(value, 4, &RndA[8]);
value = prng_successor(GetTickCount(), 32);
num_to_bytes(value, 4, &RndA[12]);
cmd[0] = AUTHENTICATE; // Default Keys
cmd[1] = arg2; //keynumber uint8_t PICC_MASTER_KEY8[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
len = DesfireAPDU(cmd, 2, resp); uint8_t PICC_MASTER_KEY16[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if (!len) { uint8_t PICC_MASTER_KEY24[24] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
if (DBGLEVEL >= DBG_ERROR) { //uint8_t null_key_data16[16] = {0x00};
DbpString("Authentication failed. Card timeout."); //uint8_t new_key_data8[8] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
} //uint8_t new_key_data16[16] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};
OnError(3);
return;
}
if (resp[2] == 0xaf) {
} else {
DbpString("Authentication failed. Invalid key number.");
OnError(3);
return;
}
memcpy(encRndB, resp + 3, 8); //InitDesfireCard();
if (arg1 == 2)
tdes_dec(&decRndB, &encRndB, key->data);
else if (arg1 == 1)
des_dec(&decRndB, &encRndB, key->data);
memcpy(RndB, decRndB, 8); // Part 1
rol(decRndB, 8); LED_A_ON();
LED_B_OFF();
LED_C_OFF();
// This should be random if (payload->key == NULL) {
uint8_t decRndA[8] = {0x00}; if (payload->algo == MFDES_AUTH_DES) {
memcpy(RndA, decRndA, 8); memcpy(keybytes, PICC_MASTER_KEY8, 8);
uint8_t encRndA[8] = {0x00}; } else if (payload->algo == MFDES_ALGO_AES || payload->algo == MFDES_ALGO_3DES || payload->algo == MFDES_ALGO_2K3DES) {
memcpy(keybytes, PICC_MASTER_KEY16, 16);
if (arg1 == 2) } else if (payload->algo == MFDES_ALGO_3DES) {
tdes_dec(&encRndA, &decRndA, key->data); memcpy(keybytes, PICC_MASTER_KEY24, 24);
else if (arg1 == 1)
des_dec(&encRndA, &decRndA, key->data);
memcpy(both, encRndA, 8);
for (int x = 0; x < 8; x++) {
decRndB[x] = decRndB[x] ^ encRndA[x];
}
if (arg1 == 2)
tdes_dec(&encRndB, &decRndB, key->data);
else if (arg1 == 1)
des_dec(&encRndB, &decRndB, key->data);
memcpy(both + 8, encRndB, 8);
cmd[0] = ADDITIONAL_FRAME;
memcpy(cmd + 1, both, 16);
len = DesfireAPDU(cmd, 17, resp);
if (!len) {
if (DBGLEVEL >= DBG_ERROR) {
DbpString("Authentication failed. Card timeout.");
}
OnError(3);
return;
}
if (resp[2] == 0x00) {
struct desfire_key sessionKey = {0};
desfirekey_t skey = &sessionKey;
Desfire_session_key_new(RndA, RndB, key, skey);
//print_result("SESSION : ", skey->data, 8);
memcpy(encRndA, resp + 3, 8);
if (arg1 == 2)
tdes_dec(&encRndA, &encRndA, key->data);
else if (arg1 == 1)
des_dec(&encRndA, &encRndA, key->data);
rol(decRndA, 8);
for (int x = 0; x < 8; x++) {
if (decRndA[x] != encRndA[x]) {
DbpString("Authentication failed. Cannot varify PICC.");
OnError(4);
return;
}
}
//Change the selected key to a new value.
/*
// Current key is a 3DES key, change it to a DES key
if (arg1 == 2) {
cmd[0] = CHANGE_KEY;
cmd[1] = arg2;
uint8_t newKey[16] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
uint8_t first, second;
uint8_t buff1[8] = {0x00};
uint8_t buff2[8] = {0x00};
uint8_t buff3[8] = {0x00};
memcpy(buff1,newKey, 8);
memcpy(buff2,newKey + 8, 8);
compute_crc(CRC_14443_A, newKey, 16, &first, &second);
memcpy(buff3, &first, 1);
memcpy(buff3 + 1, &second, 1);
tdes_dec(&buff1, &buff1, skey->data);
memcpy(cmd+2,buff1,8);
for (int x = 0; x < 8; x++) {
buff2[x] = buff2[x] ^ buff1[x];
}
tdes_dec(&buff2, &buff2, skey->data);
memcpy(cmd+10,buff2,8);
for (int x = 0; x < 8; x++) {
buff3[x] = buff3[x] ^ buff2[x];
}
tdes_dec(&buff3, &buff3, skey->data);
memcpy(cmd+18,buff3,8);
// The command always times out on the first attempt, this will retry until a response
// is recieved.
len = 0;
while(!len) {
len = DesfireAPDU(cmd,26,resp);
}
} else {
// Current key is a DES key, change it to a 3DES key
if (arg1 == 1) {
cmd[0] = CHANGE_KEY;
cmd[1] = arg2;
uint8_t newKey[16] = {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f};
uint8_t first, second;
uint8_t buff1[8] = {0x00};
uint8_t buff2[8] = {0x00};
uint8_t buff3[8] = {0x00};
memcpy(buff1,newKey, 8);
memcpy(buff2,newKey + 8, 8);
compute_crc(CRC_14443_A, newKey, 16, &first, &second);
memcpy(buff3, &first, 1);
memcpy(buff3 + 1, &second, 1);
des_dec(&buff1, &buff1, skey->data);
memcpy(cmd+2,buff1,8);
for (int x = 0; x < 8; x++) {
buff2[x] = buff2[x] ^ buff1[x];
}
des_dec(&buff2, &buff2, skey->data);
memcpy(cmd+10,buff2,8);
for (int x = 0; x < 8; x++) {
buff3[x] = buff3[x] ^ buff2[x];
}
des_dec(&buff3, &buff3, skey->data);
memcpy(cmd+18,buff3,8);
// The command always times out on the first attempt, this will retry until a response
// is recieved.
len = 0;
while(!len) {
len = DesfireAPDU(cmd,26,resp);
}
}
}
*/
OnSuccess();
if (arg1 == 2)
reply_old(CMD_ACK, 1, 0, 0, skey->data, 16);
else if (arg1 == 1)
reply_old(CMD_ACK, 1, 0, 0, skey->data, 8);
} else {
DbpString("Authentication failed.");
OnError(6);
return;
}
} }
break; } else {
case 2: memcpy(keybytes, payload->key, payload->keylen);
//SendDesfireCommand(AUTHENTICATE_ISO, &arg2, resp); }
break;
case 3: {
//defaultkey struct desfire_key defaultkey = {0};
uint8_t keybytes[16] = {0x00}; desfirekey_t key = &defaultkey;
if (datain[1] == 0xff) {
memcpy(keybytes, PICC_MASTER_KEY16, 16); if (payload->algo == MFDES_ALGO_AES) {
} else { mbedtls_aes_init(&ctx);
memcpy(keybytes, datain + 1, datalen); Desfire_aes_key_new(keybytes, key);
} else if (payload->algo == MFDES_ALGO_3DES) {
Desfire_3des_key_new_with_version(keybytes, key);
} else if (payload->algo == MFDES_ALGO_DES) {
Desfire_des_key_new(keybytes, key);
} else if (payload->algo == MFDES_ALGO_2K3DES) {
Desfire_2k3des_key_new_with_version(keybytes, key);
} else if (payload->algo == MFDES_ALGO_3K3DES) {
Desfire_3k3des_key_new_with_version(keybytes, key);
}
uint8_t subcommand = AUTHENTICATE;
if (payload->mode == MFDES_AUTH_AES)
subcommand = AUTHENTICATE_AES;
else if (payload->mode == MFDES_AUTH_ISO)
subcommand = AUTHENTICATE_ISO;
if (payload->mode != MFDES_AUTH_PICC) {
// Let's send our auth command
cmd[0] = 0x90;
cmd[1] = subcommand;
cmd[2] = 0x0;
cmd[3] = 0x0;
cmd[4] = 0x1;
cmd[5] = payload->keyno;
cmd[6] = 0x0;
len = DesfireAPDU(cmd, 7, resp);
} else {
cmd[0] = AUTHENTICATE;
cmd[1] = payload->keyno;
len = DesfireAPDU(cmd, 2, resp);
}
if (!len) {
if (DBGLEVEL >= DBG_ERROR) {
DbpString("Authentication failed. Card timeout.");
}
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 3);
return;
}
if (resp[2] == (uint8_t)0xaf) {
DbpString("Authentication failed. Invalid key number.");
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 3);
return;
}
int expectedlen = 1 + 8 + 2 + 2;
if (payload->algo == MFDES_ALGO_AES || payload->algo == MFDES_ALGO_3K3DES) {
expectedlen = 1 + 16 + 2 + 2;
}
if (len != expectedlen) {
if (DBGLEVEL >= DBG_ERROR) {
DbpString("Authentication failed. Length of answer doesn't match algo.");
print_result("Res-Buffer: ", resp, len);
}
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 3);
return;
}
// Part 2
if (payload->mode != MFDES_AUTH_PICC) {
memcpy(encRndB, resp + 1, payload->keylen);
} else {
memcpy(encRndB, resp + 2, payload->keylen);
}
// Part 3
if (payload->algo == MFDES_ALGO_AES) {
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) {
if (DBGLEVEL >= DBG_EXTENDED) {
DbpString("mbedtls_aes_setkey_dec failed");
} }
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 7);
return;
}
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, 16, IV, encRndB, RndB);
} else if (payload->algo == MFDES_ALGO_3DES)
tdes_dec(RndB, encRndB, key->data);
else if (payload->algo == MFDES_ALGO_DES)
des_dec(RndB, encRndB, key->data);
else if (payload->algo == MFDES_ALGO_2K3DES)
tdes_2key_dec(RndB, encRndB, 8, key->data, IV);
else if (payload->algo == MFDES_ALGO_3K3DES)
tdes_3key_dec(RndB, encRndB, 16, key->data, IV);
struct desfire_key defaultkey = {0x00}; // - Rotate RndB by 8 bits
desfirekey_t key = &defaultkey; memcpy(rotRndB, RndB, payload->keylen);
Desfire_aes_key_new(keybytes, key); rol(rotRndB, payload->keylen);
mbedtls_aes_context ctx; uint8_t encRndA[16] = {0x00};
uint8_t IV[16] = {0x00};
mbedtls_aes_init(&ctx);
cmd[0] = AUTHENTICATE_AES; // - Encrypt our response
cmd[1] = 0x0; if (payload->mode == MFDES_AUTH_DES || payload->mode == MFDES_AUTH_ISO || payload->mode == MFDES_AUTH_PICC) {
cmd[2] = 0x0; if (payload->algo == MFDES_ALGO_3DES) {
cmd[3] = 0x1; tdes_dec(encRndA, RndA, key->data);
cmd[4] = arg2; //keynumber memcpy(both, encRndA, 8);
cmd[5] = 0x0; } else if (payload->algo == MFDES_ALGO_DES) {
len = DesfireAPDU(cmd, 6, resp); des_dec(encRndA, RndA, key->data);
if (!len) { memcpy(both, encRndA, 8);
if (DBGLEVEL >= DBG_ERROR) { } else if (payload->algo == MFDES_ALGO_2K3DES) {
DbpString("Authentication failed. Card timeout."); tdes_2key_dec(encRndA, RndA, 8, key->data, IV);
} memcpy(both, encRndA, 8);
OnError(3); } else if (payload->algo == MFDES_ALGO_3K3DES) {
return; tdes_3key_dec(encRndA, RndA, 16, key->data, IV);
} memcpy(both, encRndA, 16);
}
memcpy(encRndB, resp + 1, 16);
// dekryptera tagnonce. for (int x = 0; x < 8; x++) {
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) { rotRndB[x] = rotRndB[x] ^ encRndA[x];
if (DBGLEVEL >= DBG_EXTENDED) { }
DbpString("mbedtls_aes_setkey_dec failed");
} if (payload->algo == MFDES_ALGO_3DES) {
OnError(7); tdes_dec(encRndB, rotRndB, key->data);
return; memcpy(both + 8, encRndB, 8);
} } else if (payload->algo == MFDES_ALGO_DES) {
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, 16, IV, encRndB, decRndB); des_dec(encRndB, rotRndB, key->data);
rol(decRndB, 16); memcpy(both + 8, encRndB, 8);
uint8_t nonce[16] = {0x00}; } else if (payload->algo == MFDES_ALGO_2K3DES) {
memcpy(both, nonce, 16); tdes_2key_dec(encRndB, rotRndB, 8, key->data, IV);
memcpy(both + 16, decRndB, 16); memcpy(both + 8, encRndB, 8);
uint8_t encBoth[32] = {0x00}; } else if (payload->algo == MFDES_ALGO_3K3DES) {
tdes_3key_dec(encRndB, rotRndB, 16, key->data, IV);
memcpy(both + 16, encRndB, 16);
}
} else if (payload->mode == MFDES_AUTH_AES) {
uint8_t tmp[32] = {0x00};
memcpy(tmp, RndA, 16);
memcpy(tmp + 16, rotRndB, 16);
if (payload->algo == MFDES_ALGO_AES) {
if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) { if (mbedtls_aes_setkey_enc(&ctx, key->data, 128) != 0) {
if (DBGLEVEL >= DBG_EXTENDED) { if (DBGLEVEL >= DBG_EXTENDED) {
DbpString("mbedtls_aes_setkey_enc failed"); DbpString("mbedtls_aes_setkey_enc failed");
} }
OnError(7); OnErrorNG(CMD_HF_DESFIRE_AUTH1, 7);
return; return;
} }
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, both, encBoth); mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_ENCRYPT, 32, IV, tmp, both);
cmd[0] = ADDITIONAL_FRAME;
cmd[1] = 0x00;
cmd[2] = 0x00;
cmd[3] = 0x20;
memcpy(cmd + 4, encBoth, 32);
cmd[36]=0x0;
len = DesfireAPDU(cmd, 37, resp); // 4 + 32 + 1 == 37
if (!len) {
if (DBGLEVEL >= DBG_ERROR) {
DbpString("Authentication failed. Card timeout.");
}
OnError(3);
return;
}
if ((resp[1+16] == 0x91)&&(resp[1+16+1] == 0x00)) {
// Create AES Session key
struct desfire_key sessionKey = {0};
desfirekey_t skey = &sessionKey;
Desfire_session_key_new(nonce, decRndB, key, skey);
print_result("SESSION : ", skey->data, 16);
} else {
DbpString("Authentication failed.");
OnError(7);
return;
}
break;
} }
} }
OnSuccess(); int bothlen = 16;
reply_mix(CMD_ACK, 1, len, 0, resp, len); if (payload->algo == MFDES_ALGO_AES || payload->algo == MFDES_ALGO_3K3DES) {
bothlen = 32;
}
if (payload->mode != MFDES_AUTH_PICC) {
cmd[0] = 0x90;
cmd[1] = ADDITIONAL_FRAME;
cmd[2] = 0x00;
cmd[3] = 0x00;
cmd[4] = bothlen;
memcpy(cmd + 5, both, bothlen);
cmd[bothlen + 5] = 0x0;
len = DesfireAPDU(cmd, 5 + bothlen + 1, resp);
} else {
cmd[0] = ADDITIONAL_FRAME;
memcpy(cmd + 1, both, 16);
len = DesfireAPDU(cmd, 1 + 16, resp);
}
if (!len) {
if (DBGLEVEL >= DBG_ERROR) {
DbpString("Authentication failed. Card timeout.");
}
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 3);
return;
}
if (payload->mode != MFDES_AUTH_PICC) {
if ((resp[len - 4] != 0x91) || (resp[len - 3] != 0x00)) {
DbpString("Authentication failed.");
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 6);
return;
}
} else {
if (resp[1] != 0x00) {
DbpString("Authentication failed.");
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 6);
return;
}
}
// Part 4
struct desfire_key sessionKey = {0};
desfirekey_t skey = &sessionKey;
Desfire_session_key_new(RndA, RndB, key, skey);
if (DBGLEVEL >= DBG_EXTENDED)
print_result("SESSIONKEY : ", sessionKey.data, payload->keylen);
if (payload->mode != MFDES_AUTH_PICC) {
memcpy(encRndA, resp + 1, payload->keylen);
} else {
memcpy(encRndA, resp + 2, payload->keylen);
}
if (payload->mode == MFDES_AUTH_DES || payload->mode == MFDES_AUTH_PICC) {
if (payload->algo == MFDES_ALGO_3DES)
tdes_dec(encRndA, encRndA, key->data);
else if (payload->algo == MFDES_ALGO_DES)
des_dec(encRndA, encRndA, key->data);
else if (payload->algo == MFDES_ALGO_2K3DES)
tdes_2key_dec(encRndA, encRndA, 8, key->data, IV);
else if (payload->algo == MFDES_ALGO_3K3DES)
tdes_3key_dec(encRndA, encRndA, 16, key->data, IV);
} else if (payload->mode == MFDES_AUTH_AES) {
if (mbedtls_aes_setkey_dec(&ctx, key->data, 128) != 0) {
if (DBGLEVEL >= DBG_EXTENDED) {
DbpString("mbedtls_aes_setkey_dec failed");
}
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 7);
return;
}
mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, 16, IV, encRndA, encRndA);
}
rol(RndA, payload->keylen);
if (DBGLEVEL >= DBG_EXTENDED) {
print_result("RndA : ", RndA, payload->keylen);
print_result("RndB: ", RndB, payload->keylen);
print_result("encRndA : ", encRndA, payload->keylen);
}
for (int x = 0; x < payload->keylen; x++) {
if (RndA[x] != encRndA[x]) {
DbpString("Authentication failed. Cannot verify Session Key.");
OnErrorNG(CMD_HF_DESFIRE_AUTH1, 4);
return;
}
}
//Change the selected key to a new value.
/*
// Current key is a 3DES key, change it to a DES key
if (payload->algo == 2) {
cmd[0] = 0x90;
cmd[1] = CHANGE_KEY;
cmd[2] = payload->keyno;
uint8_t newKey[16] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77};
uint8_t first, second;
uint8_t buff1[8] = {0x00};
uint8_t buff2[8] = {0x00};
uint8_t buff3[8] = {0x00};
memcpy(buff1,newKey, 8);
memcpy(buff2,newKey + 8, 8);
compute_crc(CRC_14443_A, newKey, 16, &first, &second);
memcpy(buff3, &first, 1);
memcpy(buff3 + 1, &second, 1);
tdes_dec(&buff1, &buff1, skey->data);
memcpy(cmd+2,buff1,8);
for (int x = 0; x < 8; x++) {
buff2[x] = buff2[x] ^ buff1[x];
}
tdes_dec(&buff2, &buff2, skey->data);
memcpy(cmd+10,buff2,8);
for (int x = 0; x < 8; x++) {
buff3[x] = buff3[x] ^ buff2[x];
}
tdes_dec(&buff3, &buff3, skey->data);
memcpy(cmd+19,buff3,8);
// The command always times out on the first attempt, this will retry until a response
// is recieved.
len = 0;
while(!len) {
len = DesfireAPDU(cmd,27,resp);
}
} else {
// Current key is a DES key, change it to a 3DES key
if (payload->algo == 1) {
cmd[0] = 0x90;
cmd[1] = CHANGE_KEY;
cmd[2] = payload->keyno;
uint8_t newKey[16] = {0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f};
uint8_t first, second;
uint8_t buff1[8] = {0x00};
uint8_t buff2[8] = {0x00};
uint8_t buff3[8] = {0x00};
memcpy(buff1,newKey, 8);
memcpy(buff2,newKey + 8, 8);
compute_crc(CRC_14443_A, newKey, 16, &first, &second);
memcpy(buff3, &first, 1);
memcpy(buff3 + 1, &second, 1);
des_dec(&buff1, &buff1, skey->data);
memcpy(cmd+3,buff1,8);
for (int x = 0; x < 8; x++) {
buff2[x] = buff2[x] ^ buff1[x];
}
des_dec(&buff2, &buff2, skey->data);
memcpy(cmd+11,buff2,8);
for (int x = 0; x < 8; x++) {
buff3[x] = buff3[x] ^ buff2[x];
}
des_dec(&buff3, &buff3, skey->data);
memcpy(cmd+19,buff3,8);
// The command always times out on the first attempt, this will retry until a response
// is recieved.
len = 0;
while(!len) {
len = DesfireAPDU(cmd,27,resp);
}
}
}
*/
//OnSuccess();
//reply_old(CMD_ACK, 1, 0, 0, skey->data, payload->keylen);
//reply_mix(CMD_ACK, 1, len, 0, resp, len);
LED_B_ON();
authres_t rpayload;
rpayload.sessionkeylen = payload->keylen;
memcpy(rpayload.sessionkey, sessionKey.data, rpayload.sessionkeylen);
reply_ng(CMD_HF_DESFIRE_AUTH1, PM3_SUCCESS, (uint8_t *)&rpayload, sizeof(rpayload));
LED_B_OFF();
} }
// 3 different ISO ways to send data to a DESFIRE (direct, capsuled, capsuled ISO) // 3 different ISO ways to send data to a DESFIRE (direct, capsuled, capsuled ISO)
@ -557,10 +645,10 @@ int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout) {
// if we received an I- or R(ACK)-Block with a block number equal to the // if we received an I- or R(ACK)-Block with a block number equal to the
// current block number, toggle the current block number // current block number, toggle the current block number
if (len >= 4 // PCB+CID+CRC = 4 bytes if (len >= 4 // PCB+CID+CRC = 4 bytes
&& ((resp[0] & 0xC0) == 0 // I-Block && ((resp[0] & 0xC0) == 0 // I-Block
|| (resp[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0 || (resp[0] & 0xD0) == 0x80) // R-Block with ACK bit set to 0
&& (resp[0] & 0x01) == pcb_blocknum) { // equal block numbers && (resp[0] & 0x01) == pcb_blocknum) { // equal block numbers
pcb_blocknum ^= 1; //toggle next block pcb_blocknum ^= 1; //toggle next block
} }
memcpy(dataout, resp, len); memcpy(dataout, resp, len);
@ -570,7 +658,7 @@ int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout) {
// CreateAPDU // CreateAPDU
size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout) { size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout) {
size_t cmdlen = MIN(len + 4, PM3_CMD_DATA_SIZE - 1); size_t cmdlen = MIN(len + 3, PM3_CMD_DATA_SIZE - 1);
uint8_t cmd[cmdlen]; uint8_t cmd[cmdlen];
memset(cmd, 0, cmdlen); memset(cmd, 0, cmdlen);
@ -578,18 +666,18 @@ size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout) {
cmd[0] = 0x02; // 0x0A = send cid, 0x02 = no cid. cmd[0] = 0x02; // 0x0A = send cid, 0x02 = no cid.
cmd[0] |= pcb_blocknum; // OR the block number into the PCB cmd[0] |= pcb_blocknum; // OR the block number into the PCB
if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("pcb_blocknum %d == %d ", pcb_blocknum, cmd[0] ); if (DBGLEVEL >= DBG_EXTENDED) Dbprintf("pcb_blocknum %d == %d ", pcb_blocknum, cmd[0]);
cmd[1] = 0x90; // CID: 0x00 //TODO: allow multiple selected cards //cmd[1] = 0x90; // CID: 0x00 //TODO: allow multiple selected cards
memcpy(cmd + 2, datain, len); memcpy(cmd + 1, datain, len);
AddCrc14A(cmd, len + 2); AddCrc14A(cmd, len + 1);
/* /*
hf 14a apdu -sk 90 60 00 00 00 hf 14a apdu -sk 90 60 00 00 00
hf 14a apdu -k 90 AF 00 00 00 hf 14a apdu -k 90 AF 00 00 00
hf 14a apdu 90AF000000 hf 14a apdu 90AF000000
*/ */
memcpy(dataout, cmd, cmdlen); memcpy(dataout, cmd, cmdlen);
return cmdlen; return cmdlen;
} }
@ -612,3 +700,8 @@ void OnError(uint8_t reason) {
reply_mix(CMD_ACK, 0, reason, 0, 0, 0); reply_mix(CMD_ACK, 0, reason, 0, 0, 0);
OnSuccess(); OnSuccess();
} }
void OnErrorNG(uint16_t cmd, uint8_t reason) {
reply_ng(cmd, reason, NULL, 0);
OnSuccess();
}

View file

@ -14,13 +14,14 @@
#include "common.h" #include "common.h"
bool InitDesfireCard(); bool InitDesfireCard();
void MifareSendCommand(uint8_t arg0, uint8_t arg1, uint8_t *datain); void MifareSendCommand(uint8_t *datain);
void MifareDesfireGetInformation(); void MifareDesfireGetInformation();
void MifareDES_Auth1(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain); void MifareDES_Auth1(uint8_t *datain);
void ReaderMifareDES(uint32_t param, uint32_t param2, uint8_t *datain); void ReaderMifareDES(uint32_t param, uint32_t param2, uint8_t *datain);
int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout); int DesfireAPDU(uint8_t *cmd, size_t cmd_len, uint8_t *dataout);
size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout); size_t CreateAPDU(uint8_t *datain, size_t len, uint8_t *dataout);
void OnSuccess(); void OnSuccess();
void OnError(uint8_t reason); void OnError(uint8_t reason);
void OnErrorNG(uint16_t cmd, uint8_t reason);
#endif #endif

View file

@ -697,7 +697,7 @@ int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData) {
int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData) { int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData) {
int len; int len;
uint8_t data[17] = {MFDES_AUTHENTICATION_FRAME}; uint8_t data[17] = {MFDES_ADDITIONAL_FRAME};
memcpy(data + 1, key, 16); memcpy(data + 1, key, 16);
uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00};

View file

@ -253,7 +253,7 @@ CMDSRCS = crapto1/crapto1.c \
wiegand_formats.c \ wiegand_formats.c \
wiegand_formatutils.c \ wiegand_formatutils.c \
cardhelper.c \ cardhelper.c \
settings.c preferences.c
cpu_arch = $(shell uname -m) cpu_arch = $(shell uname -m)
ifneq ($(findstring 86, $(cpu_arch)), ) ifneq ($(findstring 86, $(cpu_arch)), )

View file

@ -38,6 +38,7 @@ int CLIParserParseArg(int argc, char **argv, void *vargtable[], size_t vargtable
if (arg_nullcheck(argtable) != 0) { if (arg_nullcheck(argtable) != 0) {
/* NULL entries were detected, some allocations must have failed */ /* NULL entries were detected, some allocations must have failed */
printf("ERROR: Insufficient memory\n"); printf("ERROR: Insufficient memory\n");
fflush(stdout);
return 2; return 2;
} }
/* Parse the command line as defined by argtable[] */ /* Parse the command line as defined by argtable[] */
@ -54,6 +55,7 @@ int CLIParserParseArg(int argc, char **argv, void *vargtable[], size_t vargtable
if (programHelp) if (programHelp)
printf("%s \n", programHelp); printf("%s \n", programHelp);
fflush(stdout);
return 1; return 1;
} }
@ -62,7 +64,7 @@ int CLIParserParseArg(int argc, char **argv, void *vargtable[], size_t vargtable
/* Display the error details contained in the arg_end struct.*/ /* Display the error details contained in the arg_end struct.*/
arg_print_errors(stdout, ((struct arg_end *)argtable[vargtableLen - 1]), programName); arg_print_errors(stdout, ((struct arg_end *)argtable[vargtableLen - 1]), programName);
printf("Try '%s --help' for more information.\n", programName); printf("Try '%s --help' for more information.\n", programName);
fflush(stdout);
return 3; return 3;
} }
@ -155,18 +157,24 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int
int ibuf = 0; int ibuf = 0;
uint8_t tmp_buf[256] = {0}; uint8_t tmp_buf[256] = {0};
int res = CLIParamStrToBuf(argstr, tmp_buf, maxdatalen * 2, &ibuf); // *2 because here HEX int res = CLIParamStrToBuf(argstr, tmp_buf, maxdatalen * 2, &ibuf); // *2 because here HEX
if (res || !ibuf) if (res || !ibuf){
printf("Parameter error: buffer overflow.\n");
fflush(stdout);
return res; return res;
}
switch (param_gethex_to_eol((char *)tmp_buf, 0, data, maxdatalen, datalen)) { switch (param_gethex_to_eol((char *)tmp_buf, 0, data, maxdatalen, datalen)) {
case 1: case 1:
printf("Parameter error: Invalid HEX value.\n"); printf("Parameter error: Invalid HEX value.\n");
fflush(stdout);
return 1; return 1;
case 2: case 2:
printf("Parameter error: parameter too large.\n"); printf("Parameter error: parameter too large.\n");
fflush(stdout);
return 2; return 2;
case 3: case 3:
printf("Parameter error: Hex string must have even number of digits.\n"); printf("Parameter error: Hex string must have even number of digits.\n");
fflush(stdout);
return 3; return 3;
} }

View file

@ -2096,9 +2096,9 @@ static void HFiClassCalcNewKey(uint8_t *CSN, uint8_t *OLDKEY, uint8_t *NEWKEY, u
xor_div_key[i] = old_div_key[i] ^ new_div_key[i]; xor_div_key[i] = old_div_key[i] ^ new_div_key[i];
} }
if (verbose) { if (verbose) {
PrintAndLogEx(SUCCESS, "Old div key : %s\n", sprint_hex(old_div_key, 8)); PrintAndLogEx(SUCCESS, "Old div key : %s", sprint_hex(old_div_key, 8));
PrintAndLogEx(SUCCESS, "New div key : %s\n", sprint_hex(new_div_key, 8)); PrintAndLogEx(SUCCESS, "New div key : %s", sprint_hex(new_div_key, 8));
PrintAndLogEx(SUCCESS, "Xor div key : %s\n", sprint_hex(xor_div_key, 8)); PrintAndLogEx(SUCCESS, "Xor div key : " _YELLOW_("%s") "\n", sprint_hex(xor_div_key, 8));
} }
} }
@ -2111,7 +2111,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) {
uint8_t dataLen = 0; uint8_t dataLen = 0;
char tempStr[50] = {0}; char tempStr[50] = {0};
bool givenCSN = false; bool givenCSN = false;
bool oldElite = false; bool old_elite = false;
bool elite = false; bool elite = false;
bool errors = false; bool errors = false;
uint8_t cmdp = 0; uint8_t cmdp = 0;
@ -2122,7 +2122,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) {
case 'e': case 'e':
dataLen = param_getstr(Cmd, cmdp, tempStr, sizeof(tempStr)); dataLen = param_getstr(Cmd, cmdp, tempStr, sizeof(tempStr));
if (dataLen == 2) if (dataLen == 2)
oldElite = true; old_elite = true;
elite = true; elite = true;
cmdp++; cmdp++;
break; break;
@ -2184,7 +2184,7 @@ static int CmdHFiClassCalcNewKey(const char *Cmd) {
} }
} }
HFiClassCalcNewKey(CSN, OLDKEY, NEWKEY, xor_div_key, elite, oldElite, true); HFiClassCalcNewKey(CSN, OLDKEY, NEWKEY, xor_div_key, elite, old_elite, true);
return PM3_SUCCESS; return PM3_SUCCESS;
} }

View file

@ -675,7 +675,7 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
// it's basically a ISO14443a tag, so try annotation from there // it's basically a ISO14443a tag, so try annotation from there
if (applyIso14443a(exp, size, cmd, cmdsize) == 0) { if (applyIso14443a(exp, size, cmd, cmdsize) == 0) {
// S-block 11xxx010 // S-block 11xxx010
if ((cmd[0] & 0xC0) && (cmdsize == 3)) { if ((cmd[0] & 0xC0) && (cmdsize == 3)) {
switch ((cmd[0] & 0x30)) { switch ((cmd[0] & 0x30)) {
@ -707,9 +707,8 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
if ((cmd[0] & 0x04) == 0x04) // nad byte following if ((cmd[0] & 0x04) == 0x04) // nad byte following
pos++; pos++;
for (uint8_t i = 0; i < 2; i++, pos++) { for (uint8_t i = 0; i < 2; i++, pos++) {
switch (cmd[pos]) { switch (cmd[pos]) {
case MFDES_CREATE_APPLICATION: case MFDES_CREATE_APPLICATION:
snprintf(exp, size, "CREATE APPLICATION"); snprintf(exp, size, "CREATE APPLICATION");
@ -819,9 +818,12 @@ void annotateMfDesfire(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) {
case MFDES_GET_KEY_VERSION: case MFDES_GET_KEY_VERSION:
snprintf(exp, size, "GET KEY VERSION"); snprintf(exp, size, "GET KEY VERSION");
break; break;
case MFDES_AUTHENTICATION_FRAME: case MFDES_ADDITIONAL_FRAME:
snprintf(exp, size, "AUTH FRAME / NEXT FRAME"); snprintf(exp, size, "AUTH FRAME / NEXT FRAME");
break; break;
case MFDES_READSIG:
snprintf(exp, size, "READ SIGNATURE");
break;
default: default:
break; break;
} }

View file

@ -3564,7 +3564,7 @@ void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_secto
snprintf(strB, sizeof(strB), "%012" PRIx64, e_sector[i].Key[1]); snprintf(strB, sizeof(strB), "%012" PRIx64, e_sector[i].Key[1]);
if (e_sector[i].foundKey[0] > 1) { if (e_sector[i].foundKey[0] > 1) {
PrintAndLogEx(SUCCESS, "| "_YELLOW_("%03d")"| " _GREEN_("%s")" | " _YELLOW_("%c")"| " _GREEN_("%s")" | " _YELLOW_("%c")"|" PrintAndLogEx(SUCCESS, "| "_YELLOW_("%03d")"| " _GREEN_("%s")" | " _YELLOW_("%c")"| " _GREEN_("%s")" | " _YELLOW_("%c")"|"
, i , i
, strA, e_sector[i].foundKey[0] , strA, e_sector[i].foundKey[0]
, strB, e_sector[i].foundKey[1] , strB, e_sector[i].foundKey[1]

File diff suppressed because it is too large Load diff

View file

@ -14,10 +14,11 @@
int CmdHFMFDes(const char *Cmd); int CmdHFMFDes(const char *Cmd);
/*
char *getCardSizeStr(uint8_t fsize); char *getCardSizeStr(uint8_t fsize);
char *getProtocolStr(uint8_t id);
char *getVersionStr(uint8_t major, uint8_t minor); char *getVersionStr(uint8_t major, uint8_t minor);
void getKeySettings(uint8_t *aid); int getKeySettings(uint8_t *aid);
*/
// Ev1 card limits // Ev1 card limits
#define MAX_NUM_KEYS 0x0F #define MAX_NUM_KEYS 0x0F
@ -26,57 +27,16 @@ void getKeySettings(uint8_t *aid);
#define MAX_FRAME_SIZE 60 #define MAX_FRAME_SIZE 60
#define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5) #define FRAME_PAYLOAD_SIZE (MAX_FRAME_SIZE - 5)
// Ev2 card limits
// Ev3 card limits
// Light card limits
// Light Ev1 card limits
#define NOT_YET_AUTHENTICATED 0xFF #define NOT_YET_AUTHENTICATED 0xFF
// status- and error codes |
#define OPERATION_OK 0x00 // Successful operation
#define NO_CHANGES 0x0C // No changes done to backup files
// ,CommitTransaction/
// AbortTransaction not necessary
#define OUT_OF_EEPROM_ERROR 0x0E // Insufficient NV-Memory to
// complete command
#define ILLEGAL_COMMAND_CODE 0x1C // Command code not supported
#define INTEGRITY_ERROR 0x1E // CRC or MAC does not match data
// Padding bytes not valid
#define NO_SUCH_KEY 0x40 // Invalid key number specified
#define LENGTH_ERROR 0x7E // Length of command string invalid
#define PERMISSION_DENIED 0x9D // Current configuration status
// does not allow the requested
// command
#define PARAMETER_ERROR 0x9E // Value of the parameter(s) inval.
#define APPLICATION_NOT_FOUND 0xA0 // Requested AID not present on PIC
#define APPL_INTEGRITY_ERROR 0xA1 // [1] // Unrecoverable error within app-
// lication, app will be disabled
#define AUTHENTICATION_ERROR 0xAE // Current authentication status
// does not allow the requested
// command
#define ADDITIONAL_FRAME 0xAF // Additional data frame is
// expected to be sent
#define BOUNDARY_ERROR 0xBE // Attempt to read/write data from/
// to beyond the file's/record's
// limits. Attempt to exceed the
// limits of a value file.
#define PICC_INTEGRITY_ERROR 0xC1 // [1] // Unrecoverable error within PICC
// ,PICC will be disabled
#define COMMAND_ABORTED 0xCA // Previous Command was not fully
// completed Not all Frames were
// requested or provided by PCD
#define PICC_DISABLED_ERROR 0xCD // [1] // PICC was disabled by an unrecoverable error
#define COUNT_ERROR 0xCE // Number of Applications limited
// to 28, no additional
// CreateApplication possible
#define DUPLICATE_ERROR 0xDE // Creation of file/application
// failed because file/application
// with same number already exists
#define EEPROM_ERROR 0xEE // [1] // Could not complete NV-write
// operation due to loss of power,
// internal backup/rollback
// mechanism activated
#define FILE_NOT_FOUND_ERROR 0xF0 // Specified file number does not
// exist
#define FILE_INTEGRITY_ERROR 0xF1 // [1] // Unrecoverable error within file,
// file will be disabled
//
// [1] These errors are not expected to appear during normal operation
#endif #endif

View file

@ -27,11 +27,19 @@
#include "protocols.h" #include "protocols.h"
#include "crypto/libpcrypto.h" #include "crypto/libpcrypto.h"
static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; static const uint8_t DefaultKey[16] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001}; uint16_t CardAddresses[] = {0x9000, 0x9001, 0x9002, 0x9003, 0x9004, 0xA000, 0xA001, 0xA080, 0xA081, 0xC000, 0xC001};
typedef enum {
MFP_UNKNOWN = 0,
DESFIRE_MF3ICD40,
DESFIRE_EV1,
DESFIRE_EV2,
DESFIRE_EV3,
DESFIRE_LIGHT,
PLUS_EV1,
} nxp_cardtype_t;
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
/* /*
@ -56,15 +64,21 @@ static char *getCardSizeStr(uint8_t fsize) {
return buf; return buf;
} }
static char *getProtocolStr(uint8_t id) { static char *getProtocolStr(uint8_t id, bool hw) {
static char buf[40] = {0x00}; static char buf[50] = {0x00};
char *retStr = buf; char *retStr = buf;
if (id == 0x05) if (id == 0x04) {
sprintf(retStr, "0x%02X ( " _YELLOW_("ISO 14443-3, 14443-4") ")", id); sprintf(retStr, "0x%02X ( " _YELLOW_("ISO 14443-3 MIFARE, 14443-4") ")", id);
else } else if (id == 0x05) {
if (hw)
sprintf(retStr, "0x%02X ( " _YELLOW_("ISO 14443-2, 14443-3") ")", id);
else
sprintf(retStr, "0x%02X ( " _YELLOW_("ISO 14443-3, 14443-4") ")", id);
} else {
sprintf(retStr, "0x%02X ( " _YELLOW_("Unknown") ")", id); sprintf(retStr, "0x%02X ( " _YELLOW_("Unknown") ")", id);
}
return buf; return buf;
} }
@ -91,6 +105,59 @@ static char *getVersionStr(uint8_t major, uint8_t minor) {
return buf; return buf;
} }
static char *getTypeStr(uint8_t type) {
static char buf[40] = {0x00};
char *retStr = buf;
switch (type) {
case 1:
sprintf(retStr, "0x%02X ( " _YELLOW_("DESFire") ")", type);
break;
case 2:
sprintf(retStr, "0x%02X ( " _YELLOW_("Plus") ")", type);
break;
case 3:
sprintf(retStr, "0x%02X ( " _YELLOW_("Ultralight") ")", type);
break;
case 4:
sprintf(retStr, "0x%02X ( " _YELLOW_("NTAG") ")", type);
break;
default:
break;
}
return buf;
}
static nxp_cardtype_t getCardType(uint8_t major, uint8_t minor) {
// DESFire MF3ICD40
if (major == 0x00 && minor == 0x00 )
return DESFIRE_MF3ICD40;
// DESFire EV1
if (major == 0x01 && minor == 0x00 )
return DESFIRE_EV1;
// DESFire EV2
if (major == 0x12 && minor == 0x00 )
return DESFIRE_EV2;
// DESFire EV3
// if (major == 0x13 && minor == 0x00 )
// return DESFIRE_EV3;
// DESFire Light
if (major == 0x30 && minor == 0x00 )
return DESFIRE_LIGHT;
// Plus EV1
if (major == 0x11 && minor == 0x00 )
return PLUS_EV1;
return MFP_UNKNOWN;
}
// --- GET SIGNATURE // --- GET SIGNATURE
static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) {
@ -115,13 +182,15 @@ static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature
if (is_valid) if (is_valid)
break; break;
} }
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
if (is_valid == false) { if (is_valid == false) {
PrintAndLogEx(SUCCESS, "Signature verification " _RED_("failed")); PrintAndLogEx(SUCCESS, "Signature verification " _RED_("failed"));
return PM3_ESOFT; return PM3_ESOFT;
} }
PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature"));
PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_plus_public_keys[i].desc); PrintAndLogEx(INFO, " IC signature public key name: " _GREEN_("%s"), nxp_plus_public_keys[i].desc);
PrintAndLogEx(INFO, "IC signature public key value: %.32s", nxp_plus_public_keys[i].value); PrintAndLogEx(INFO, "IC signature public key value: %.32s", nxp_plus_public_keys[i].value);
PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 16); PrintAndLogEx(INFO, " : %.32s", nxp_plus_public_keys[i].value + 16);
@ -162,19 +231,19 @@ static int plus_print_version(uint8_t *version) {
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information")); PrintAndLogEx(INFO, "--- " _CYAN_("Hardware Information"));
PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version[0])); PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version[0]));
PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), version[1]); PrintAndLogEx(INFO, " Type: %s", getTypeStr(version[1]));
PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), version[2]); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), version[2]);
PrintAndLogEx(INFO, " Version: %s", getVersionStr(version[3], version[4])); PrintAndLogEx(INFO, " Version: %s", getVersionStr(version[3], version[4]));
PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(version[5])); PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(version[5]));
PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(version[6])); PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(version[6], true));
PrintAndLogEx(NORMAL, ""); PrintAndLogEx(NORMAL, "");
PrintAndLogEx(INFO, "--- " _CYAN_("Software Information")); PrintAndLogEx(INFO, "--- " _CYAN_("Software Information"));
PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version[0])); PrintAndLogEx(INFO, " Vendor Id: " _YELLOW_("%s"), getTagInfo(version[7]));
PrintAndLogEx(INFO, " Type: " _YELLOW_("0x%02X"), version[1]); PrintAndLogEx(INFO, " Type: %s", getTypeStr(version[8]));
PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), version[2]); PrintAndLogEx(INFO, " Subtype: " _YELLOW_("0x%02X"), version[9]);
PrintAndLogEx(INFO, " Version: " _YELLOW_("%d.%d"), version[3], version[4]); PrintAndLogEx(INFO, " Version: " _YELLOW_("%d.%d"), version[10], version[11]);
PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(version[5])); PrintAndLogEx(INFO, " Storage size: %s", getCardSizeStr(version[12]));
PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(version[6])); PrintAndLogEx(INFO, " Protocol: %s", getProtocolStr(version[13], false));
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int get_plus_version(uint8_t *version, int *version_len) { static int get_plus_version(uint8_t *version, int *version_len) {
@ -235,17 +304,28 @@ static int CmdHFMFPInfo(const char *Cmd) {
if (select_status == 1 || select_status == 2) { if (select_status == 1 || select_status == 2) {
PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint")); PrintAndLogEx(INFO, "--- " _CYAN_("Fingerprint"));
if (supportVersion && supportSignature) { bool isPlus = false;
PrintAndLogEx(INFO, " Tech: " _GREEN_("MIFARE Plus EV1"));
} else { if (supportVersion) {
PrintAndLogEx(INFO, " Tech: " _YELLOW_("MIFARE Plus SE/X"));
int cardtype = getCardType(version[3], version[4]);
if (cardtype == 6) {
if (supportSignature) {
PrintAndLogEx(INFO, " Tech: " _GREEN_("MIFARE Plus EV1"));
} else {
PrintAndLogEx(INFO, " Tech: " _YELLOW_("MIFARE Plus SE/X"));
}
isPlus = true;
} else {
}
} }
// MIFARE Type Identification Procedure // MIFARE Type Identification Procedure
// https://www.nxp.com/docs/en/application-note/AN10833.pdf // https://www.nxp.com/docs/en/application-note/AN10833.pdf
uint16_t ATQA = card.atqa[0] + (card.atqa[1] << 8); uint16_t ATQA = card.atqa[0] + (card.atqa[1] << 8);
bool isPlus = false;
if (ATQA & 0x0004) { if (ATQA & 0x0004) {
PrintAndLogEx(INFO, " SIZE: " _GREEN_("2K") "(%s UID)", (ATQA & 0x0040) ? "7" : "4"); PrintAndLogEx(INFO, " SIZE: " _GREEN_("2K") "(%s UID)", (ATQA & 0x0040) ? "7" : "4");
@ -289,6 +369,10 @@ static int CmdHFMFPInfo(const char *Cmd) {
uint8_t cmd[3 + 16] = {0xa8, 0x90, 0x90, 0x00}; uint8_t cmd[3 + 16] = {0xa8, 0x90, 0x90, 0x00};
int res = ExchangeRAW14a(cmd, sizeof(cmd), true, false, data, sizeof(data), &datalen, false); int res = ExchangeRAW14a(cmd, sizeof(cmd), true, false, data, sizeof(data), &datalen, false);
// DESFire answers 0x1C
// Plus answers 0x0B, 0x09
PrintAndLogEx(INFO, "ICEE: %s", sprint_hex(data, datalen));
if (memcmp(data, "\x67\x00", 2) == 0) { if (memcmp(data, "\x67\x00", 2) == 0) {
PrintAndLogEx(INFO, "\tMost likely a MIFARE DESFire tag"); PrintAndLogEx(INFO, "\tMost likely a MIFARE DESFire tag");
PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfdes info`")); PrintAndLogEx(HINT, "Hint: Try " _YELLOW_("`hf mfdes info`"));
@ -1105,10 +1189,13 @@ static int CmdHFMFPChk(const char *Cmd) {
if (keyListLen == 0) { if (keyListLen == 0) {
PrintAndLogEx(ERR, "Key list is empty. Nothing to check."); PrintAndLogEx(ERR, "Key list is empty. Nothing to check.");
return PM3_EINVARG; return PM3_EINVARG;
} else {
PrintAndLogEx(INFO, "Loaded " _YELLOW_("%zu") "keys", keyListLen);
} }
if (!verbose) if (!verbose)
printf("Search keys:"); printf("Search keys:");
while (true) { while (true) {
res = MFPKeyCheck(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose); res = MFPKeyCheck(startSector, endSector, startKeyAB, endKeyAB, keyList, keyListLen, foundKeys, verbose);
if (res == PM3_EOPABORTED) if (res == PM3_EOPABORTED)

View file

@ -37,6 +37,7 @@
#include "ui.h" #include "ui.h"
#include "util_posix.h" #include "util_posix.h"
#include "commonutil.h" // ARRAYLEN #include "commonutil.h" // ARRAYLEN
#include "preferences.h"
static int CmdHelp(const char *Cmd); static int CmdHelp(const char *Cmd);
@ -241,6 +242,11 @@ static int CmdRev(const char *Cmd) {
return PM3_SUCCESS; return PM3_SUCCESS;
} }
static int CmdPref(const char *Cmd) {
CmdPreferences(Cmd);
return PM3_SUCCESS;
}
static command_t CommandTable[] = { static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help. Use '<command> help' for details of a particular command."}, {"help", CmdHelp, AlwaysAvailable, "This help. Use '<command> help' for details of a particular command."},
{"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"}, {"auto", CmdAuto, IfPm3Present, "Automated detection process for unknown tags"},
@ -259,6 +265,7 @@ static command_t CommandTable[] = {
{"wiegand", CmdWiegand, AlwaysAvailable, "{ Wiegand format manipulation... }"}, {"wiegand", CmdWiegand, AlwaysAvailable, "{ Wiegand format manipulation... }"},
{"", CmdHelp, AlwaysAvailable, ""}, {"", CmdHelp, AlwaysAvailable, ""},
{"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"}, {"hints", CmdHints, AlwaysAvailable, "Turn hints on / off"},
{"pref", CmdPref, AlwaysAvailable, "Edit preferences"},
{"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"}, {"msleep", CmdMsleep, AlwaysAvailable, "Add a pause in milliseconds"},
{"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"}, {"rem", CmdRem, AlwaysAvailable, "Add a text line in log file"},
{"quit", CmdQuit, AlwaysAvailable, ""}, {"quit", CmdQuit, AlwaysAvailable, ""},

View file

@ -38,7 +38,7 @@
// this define is needed for scandir/alphasort to work // this define is needed for scandir/alphasort to work
#define _GNU_SOURCE #define _GNU_SOURCE
#include "fileutils.h" #include "fileutils.h"
#include "settings.h" #include "preferences.h"
#include <dirent.h> #include <dirent.h>
#include <ctype.h> #include <ctype.h>
@ -427,7 +427,7 @@ int saveFileJSON(const char *preferredName, JSONFileType ftype, uint8_t *data, s
} }
break; break;
case jsfSettings: case jsfSettings:
settings_save_callback (root); preferences_save_callback (root);
break; break;
default: default:
break; break;
@ -868,7 +868,7 @@ int loadFileJSON(const char *preferredName, void *data, size_t maxdatalen, size_
*datalen = sptr; *datalen = sptr;
} }
if (!strcmp(ctype,"settings")) { if (!strcmp(ctype,"settings")) {
settings_load_callback (root); preferences_load_callback (root);
} }
PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), fileName); PrintAndLogEx(SUCCESS, "loaded from JSON file " _YELLOW_("%s"), fileName);
out: out:

View file

@ -91,14 +91,20 @@ CRC1 = crc8 over addr 0x00..0x03+0x07..0x0E (special 'gantner crc8')
CRC2 = MCD + MSB0..2+ addr 0x06 + addr 0x05 + addr 0x07 + Stamp (regular Master-Token-CRC) CRC2 = MCD + MSB0..2+ addr 0x06 + addr 0x05 + addr 0x07 + Stamp (regular Master-Token-CRC)
--]] --]]
--[[
Known issues; needs to be fixed:
* last byte in last segment is handled incorrectly when it is the last bytes on the card itself (MIM256: => byte 256)
--]]
example = "script run legic" example = "script run legic"
author = "Mosci" author = "Mosci, uhei"
version = "1.0.3" version = "1.0.4"
desc = desc =
[[ [[
This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024) This script helps you to read, create and modify Legic Prime Tags (MIM22, MIM256, MIM1024)
The virtual tag (and therefore the file to be saved) is always a MIM1024 tag.
it's kinda interactive with following commands in three categories: it's kinda interactive with following commands in three categories:
Data I/O Segment Manipulation Token-Data Data I/O Segment Manipulation Token-Data
@ -108,8 +114,8 @@ it's kinda interactive with following commands in three categories:
ed => edit Segment Data tk => toggle KGH-Flag ed => edit Segment Data tk => toggle KGH-Flag
File I/O rs => remove Segment File I/O rs => remove Segment
----------------- cc => check Segment-CRC ----------------- cc => check Segment-CRC
lf => load File ck => check KGH lf => load bin File ck => check KGH
sf => save File ds => dump Segments sf => save eml/bin File ds => dump Segments
xf => xor to File xf => xor to File
@ -128,8 +134,8 @@ it's kinda interactive with following commands in three categories:
without the need of changing anything - MCD,MSN,MCC will be read from the tag without the need of changing anything - MCD,MSN,MCC will be read from the tag
before and applied to the output. before and applied to the output.
lf: 'load file' - load a (xored) file from the local Filesystem into the 'virtual inTag' lf: 'load file' - load a (xored) binary file (*.bin) from the local Filesystem into the 'virtual inTag'
sf: 'save file' - saves the 'virtual inTag' to the local Filesystem (xored with Tag-MCC) sf: 'save file' - saves the 'virtual inTag' to the local Filesystem as eml and bin (xored with Tag-MCC)
xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values) xf: 'xor file' - saves the 'virtual inTag' to the local Filesystem (xored with choosen MCC - use '00' for plain values)
ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed ct: 'copy tag' - copy the 'virtual Tag' to a second 'virtual TAG' - not usefull yet, but inernally needed
@ -242,6 +248,16 @@ function istable(t)
return type(t) == 'table' return type(t) == 'table'
end end
---
-- To have two char string for a byte
local function padString(str)
if (#str == 1) then
return '0'..str
end
return str
end
--- ---
-- creates a 'deep copy' of a table (a=b only references) -- creates a 'deep copy' of a table (a=b only references)
function deepCopy(object) function deepCopy(object)
@ -387,15 +403,15 @@ end
function bytesToTag(bytes, tag) function bytesToTag(bytes, tag)
if istable(tag) == false then return oops("tag is no table in: bytesToTag ("..type(tag)..")") end if istable(tag) == false then return oops("tag is no table in: bytesToTag ("..type(tag)..")") end
tag.MCD =bytes[1]; tag.MCD =padString(bytes[1]);
tag.MSN0=bytes[2]; tag.MSN0=padString(bytes[2]);
tag.MSN1=bytes[3]; tag.MSN1=padString(bytes[3]);
tag.MSN2=bytes[4]; tag.MSN2=padString(bytes[4]);
tag.MCC =bytes[5]; tag.MCC =padString(bytes[5]);
tag.DCFl=bytes[6]; tag.DCFl=padString(bytes[6]);
tag.DCFh=bytes[7]; tag.DCFh=padString(bytes[7]);
tag.raw =bytes[8]; tag.raw =padString(bytes[8]);
tag.SSC =bytes[9]; tag.SSC =padString(bytes[9]);
tag.Type=getTokenType(tag.DCFl); tag.Type=getTokenType(tag.DCFl);
tag.OLE=bbit("0x"..tag.DCFl,7,1) tag.OLE=bbit("0x"..tag.DCFl,7,1)
tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4)) tag.WRP=("%d"):format(bbit("0x"..bytes[8],0,4))
@ -500,42 +516,26 @@ function tagToBytes(tag)
return bytes return bytes
end end
---
--- PM3 I/O --- --- PM3 I/O ---
---
-- read from pm3 into virtual-tag
function readFromPM3()
local tag, bytes, infile
infile="legic.temp"
-- core.console("hf legic reader")
-- core.console("hf legic esave "..infile)
core.console("hf legic dump o "..infile)
tag=readFile(infile..".bin")
return tag
end
local function padString(str)
if (#str == 1) then
return '0'..str
end
return str
end
---
-- write virtual Tag to real Tag -- write virtual Tag to real Tag
function writeToTag(tag) function writeToTag(tag)
local bytes local bytes
local filename = 'MylegicClone.hex'
local taglen = 22 local taglen = 22
if(utils.confirm(acred.."\nplace the (empty) Tag onto the PM3\nand confirm writing to this Tag: "..acoff) == false) then local writeDCF = false
if(utils.confirm(acred.."\nPlace the (empty) Tag onto the PM3\nand confirm writing to this Tag: "..acoff) == false) then
return return
end end
if(utils.confirm(acred.."\nShould the decremental field (DCF) be written?: "..acoff) == true) then
writeDCF = true
end
-- get used bytes / tag-len -- get used bytes / tag-len
if (istable(tag.SEG)) then if (istable(tag.SEG)) then
if (istable(tag.Bck)) then if (istable(tag.Bck)) then
for i=0, #tag.SEG do for i=0, #tag.SEG do
taglen = taglen + tag.SEG[i] . len + 5 taglen = taglen + tag.SEG[i] . len
end end
end end
local uid_old = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2 local uid_old = tag.MCD..tag.MSN0..tag.MSN1..tag.MSN2
@ -571,37 +571,32 @@ function writeToTag(tag)
bytes[22] = calcMtCrc(bytes) bytes[22] = calcMtCrc(bytes)
end end
if (bytes) then if (bytes) then
print("write temp-file '"..filename.."'") bytes = xorBytes(bytes,tag.MCC)
print(accyan)
writeFile(bytes, filename..".bin")
print(acoff)
end end
end end
-- write data to file -- write data to file
if (taglen > 0) then if (taglen > 0) then
WriteBytes = input(acyellow.."enter number of bytes to write?"..acoff, taglen) WriteBytes = input(acyellow.."enter number of bytes to write?"..acoff, taglen)
-- load file into pm3-buffer
if (type(filename) ~= "string") then
filename = input(acyellow.."filename to load to pm3-buffer?"..acoff, "legic.temp")
end
cmd = 'hf legic eload 2 '..filename
core.console(cmd)
-- write pm3-buffer to Tag -- write pm3-buffer to Tag
for i=0, WriteBytes do for i=1, WriteBytes do
if (i > 6) then if (i > 7) then
cmd = ("hf legic write o %x d %s "):format(i, padString(bytes[i])) cmd = ("hf legic wrbl o %02x d %s "):format(i-1, padString(bytes[i]))
print(acgreen..cmd..acoff) print(acgreen..cmd..acoff)
core.console(cmd) core.console(cmd)
core.clearCommandBuffer() core.clearCommandBuffer()
elseif (i == 7) then
if (writeDCF) then
-- write DCF in reverse order (requires 'mosci-patch')
cmd = ('hf legic wrbl o 05 d %s%s'):format(padString(bytes[i-1]), padString(bytes[i]))
print(acgreen..cmd..acoff)
core.console(cmd)
core.clearCommandBuffer()
else
print(acgreen.."skip byte 0x05-0x06 - DCF"..acoff)
end
elseif (i == 6) then elseif (i == 6) then
-- write DCF in reverse order (requires 'mosci-patch')
cmd = ('hf legic write o 05 d %s%s'):format(padString(bytes[i-1]), padString(bytes[i]))
print(acgreen..cmd..acoff)
core.console(cmd)
core.clearCommandBuffer()
elseif (i == 5) then
print(acgreen.."skip byte 0x05 - will be written next step"..acoff) print(acgreen.."skip byte 0x05 - will be written next step"..acoff)
else else
print(acgreen.."skip byte 0x00-0x04 - unwritable area"..acoff) print(acgreen.."skip byte 0x00-0x04 - unwritable area"..acoff)
@ -641,12 +636,12 @@ end
local function save_BIN(data, filename) local function save_BIN(data, filename)
local outfile local outfile
local counter = 1 local counter = 1
local ext = filename:match("^.+(%..+)$") or '' local ext = ".bin"
local fn = filename local fn = filename..ext
-- Make sure we don't overwrite a file -- Make sure we don't overwrite a file
while file_check(fn) do while file_check(fn) do
fn = filename:gsub(ext, tostring(counter)..ext) fn = filename..ext:gsub(ext, "-"..tostring(counter)..ext)
counter = counter + 1 counter = counter + 1
end end
@ -664,26 +659,27 @@ end
--- ---
-- write bytes to file -- write bytes to file
function writeFile(bytes, filename) function writeFile(bytes, filename)
if (filename ~= 'MylegicClone.hex') then local emlext = ".eml"
if (file_check(filename)) then if (filename ~= 'MyLegicClone') then
local answer = confirm("\nthe output-file "..filename.." already exists!\nthis will delete the previous content!\ncontinue?") if (file_check(filename..emlext)) then
local answer = confirm("\nthe output-file "..filename..emlext.." already exists!\nthis will delete the previous content!\ncontinue?")
if not answer then return print("user abort") end if not answer then return print("user abort") end
end end
end end
local line local line
local bcnt = 0 local bcnt = 0
local fho, err = io.open(filename, "w") local fho, err = io.open(filename..emlext, "w")
if err then if err then
return oops("OOps ... failed to open output-file ".. filename) return oops("OOps ... failed to open output-file ".. filename..emlext)
end end
bytes = xorBytes(bytes, bytes[5]) bytes = xorBytes(bytes, bytes[5])
for i = 1, #bytes do for i = 1, #bytes do
if (bcnt == 0) then if (bcnt == 0) then
line = bytes[i] line = padString(bytes[i])
elseif (bcnt <= 7) then elseif (bcnt <= 7) then
line = line.." "..bytes[i] line = line.." "..padString(bytes[i])
end end
if (bcnt == 7) then if (bcnt == 7) then
-- write line to new file -- write line to new file
@ -699,7 +695,7 @@ function writeFile(bytes, filename)
-- save binary -- save binary
local fn_bin, fn_bin_num = save_BIN(bytes, filename) local fn_bin, fn_bin_num = save_BIN(bytes, filename)
print("\nwrote "..acyellow..(#bytes * 3)..acoff.." bytes to " ..acyellow..filename..acoff) print("\nwrote "..acyellow..(#bytes * 3)..acoff.." bytes to " ..acyellow..filename..emlext..acoff)
if fn_bin and fn_bin_num then if fn_bin and fn_bin_num then
print("\nwrote "..acyellow..fn_bin_num..acoff.." bytes to BINARY file "..acyellow..fn_bin..acoff) print("\nwrote "..acyellow..fn_bin_num..acoff.." bytes to BINARY file "..acyellow..fn_bin..acoff)
@ -708,6 +704,21 @@ function writeFile(bytes, filename)
return true return true
end end
---
-- read from pm3 into virtual-tag
function readFromPM3()
local tag, bytes, infile
--infile="legic.temp"
infile=os.tmpname()
core.console("hf legic dump f "..infile)
tag=readFile(infile..".bin")
os.remove(infile)
os.remove(infile..".bin")
os.remove(infile..".eml")
os.remove(infile..".json")
return tag
end
--- Map related --- --- Map related ---
--- ---
-- make tagMap -- make tagMap
@ -2265,8 +2276,8 @@ function modifyHelp()
ed => edit Segment Data tk => toggle KGH-Flag ed => edit Segment Data tk => toggle KGH-Flag
File I/O rs => remove Segment File I/O rs => remove Segment
----------------- cc => check Segment-CRC ----------------- cc => check Segment-CRC
lf => load File ck => check KGH lf => load bin File ck => check KGH
sf => save File ds => dump Segments sf => save eml/bin File ds => dump Segments
xf => xor to File xf => xor to File
@ -2352,10 +2363,10 @@ function modifyMode()
-- save values of mainTAG to a file (xored with MCC of mainTAG) -- save values of mainTAG to a file (xored with MCC of mainTAG)
["sf"] = function(x) ["sf"] = function(x)
if istable(inTAG) then if istable(inTAG) then
outfile = input("enter filename:", "legic.temp") outfile = input("enter filename:", "hf-legic-"..inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2)
bytes = tagToBytes(inTAG) bytes = tagToBytes(inTAG)
--bytes=xorBytes(bytes, inTAG.MCC) --bytes=xorBytes(bytes, inTAG.MCC)
if bytes then if (bytes) then
writeFile(bytes, outfile) writeFile(bytes, outfile)
end end
end end
@ -2364,7 +2375,7 @@ function modifyMode()
-- save values of mainTAG to a file (xored with 'specific' MCC) -- save values of mainTAG to a file (xored with 'specific' MCC)
["xf"] = function(x) ["xf"] = function(x)
if istable(inTAG) then if istable(inTAG) then
outfile = input("enter filename:", "legic.temp") outfile = input("enter filename:", "hf-legic-"..inTAG.MCD..inTAG.MSN0..inTAG.MSN1..inTAG.MSN2)
crc = input("enter new crc: ('00' for a plain dump)", inTAG.MCC) crc = input("enter new crc: ('00' for a plain dump)", inTAG.MCC)
print("obfuscate with: "..crc) print("obfuscate with: "..crc)
bytes=tagToBytes(inTAG) bytes=tagToBytes(inTAG)

564
client/preferences.c Normal file
View file

@ -0,0 +1,564 @@
/*****************************************************************************
* 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.
*
*****************************************************************************
*
* This file is part of loclass. It is a reconstructon 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".
*
* Copyright (C) 2014 Martin Holst Swende
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, or, at your option, any later version.
*
* This file 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.
*
* You should have received a copy of the GNU General Public License
* along with loclass. If not, see <http://www.gnu.org/licenses/>.
*
*
****************************************************************************/
//-----------------------------------------------------------------------------
// Preferences Functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Notes
// To add a new setting
// Add the new setting to the session_arg_t; in ui.h
// Add the default value for the setting in the settings_load page below
// Update the preferences_load_callback to load your setting into the stucture
// Update the preferences_save_callback to enusre your setting gets saved when needed.
// use the preference as needed : session.<preference name>
// Can use (session.preferences_loaded) to check if json settings file was used
//-----------------------------------------------------------------------------
#include "preferences.h"
#include "comms.h"
#include "emv/emvjson.h"
#include <string.h>
#include "cmdparser.h"
#include <ctype.h>
static int CmdHelp(const char *Cmd);
// Load all settings into memory (struct)
int preferences_load (void) {
// Set all defaults
session.client_debug_level = OFF;
session.window_plot_xpos = 10;
session.window_plot_ypos = 30;
session.window_plot_hsize = 400;
session.window_plot_wsize = 800;
session.window_overlay_xpos = session.window_plot_xpos;
session.window_overlay_ypos = 60+session.window_plot_ypos + session.window_plot_hsize;
session.window_overlay_hsize = 200;
session.window_overlay_wsize = session.window_plot_wsize;
session.emoji_mode = ALIAS;
session.show_hints = false;
session.supports_colors = false;
// loadFileJson wants these, so pass in place holder values, though not used
// in settings load;
uint8_t dummyData = 0x00;
size_t dummyDL = 0x00;
if (loadFileJSON(preferencesFilename, &dummyData, sizeof(dummyData), &dummyDL) == PM3_SUCCESS) {
session.preferences_loaded = true;
}
// Note, if session.settings_loaded == false then the settings_save
// will be called in main () to save settings as set in defaults and main() checks.
return PM3_SUCCESS;
}
// Save all settings from memory (struct) to file
int preferences_save (void) {
// Note sure if backup has value ?
char backupFilename[500];
snprintf (backupFilename,sizeof(backupFilename),"%s.bak",preferencesFilename);
if (fileExists (backupFilename)) {
if (remove (backupFilename) != 0) {
PrintAndLogEx (FAILED, "Error - could not delete old settings backup file \"%s\"",backupFilename);
return PM3_ESOFT;
}
}
if (fileExists (preferencesFilename)) {
if (rename (preferencesFilename,backupFilename) != 0) {
PrintAndLogEx (FAILED, "Error - could not backup settings file \"%s\" to \"%s\"",preferencesFilename,backupFilename);
return PM3_ESOFT;
}
}
uint8_t dummyData = 0x00;
size_t dummyDL = 0x00;
if (saveFileJSON(preferencesFilename, jsfSettings, &dummyData, dummyDL) != PM3_SUCCESS)
PrintAndLogEx (ERR, "Error saving preferences to \"%s\"",preferencesFilename);
return PM3_SUCCESS;
}
void preferences_save_callback (json_t *root) {
JsonSaveStr (root,"FileType","settings");
// Log level, convert to text
switch (session.client_debug_level) {
case OFF: JsonSaveStr (root,"client.debug.level","off"); break;
case SIMPLE: JsonSaveStr (root,"client.debug.level","simple"); break;
case FULL: JsonSaveStr (root,"client.debug.level","full"); break;
default:
JsonSaveStr (root,"logging.level","NORMAL");
}
// Plot window
JsonSaveInt (root,"window.plot.xpos",session.window_plot_xpos);
JsonSaveInt (root,"window.plot.ypos",session.window_plot_ypos);
JsonSaveInt (root,"window.plot.hsize",session.window_plot_hsize);
JsonSaveInt (root,"window.plot.wsize",session.window_plot_wsize);
// Overlay/Slider window
JsonSaveInt (root,"window.overlay.xpos",session.window_overlay_xpos);
JsonSaveInt (root,"window.overlay.ypos",session.window_overlay_ypos);
JsonSaveInt (root,"window.overlay.hsize",session.window_overlay_hsize);
JsonSaveInt (root,"window.overlay.wsize",session.window_overlay_wsize);
// Emoji
switch (session.emoji_mode) {
case ALIAS: JsonSaveStr (root,"show.emoji","alias"); break;
case EMOJI: JsonSaveStr (root,"show.emoji","emoji"); break;
case ALTTEXT: JsonSaveStr (root,"show.emoji","alttext"); break;
case ERASE: JsonSaveStr (root,"show.emoji","erase"); break;
default:
JsonSaveStr (root,"show.emoji","ALIAS");
}
JsonSaveBoolean (root,"show.hints",session.show_hints);
JsonSaveBoolean (root,"os.supports.colors",session.supports_colors);
}
void preferences_load_callback (json_t *root) {
json_error_t up_error = {0};
bool b1;
int i1;
const char *s1;
char tempStr [500]; // to use str_lower() since json unpack uses const char *
// Logging Level
if (json_unpack_ex(root,&up_error, 0, "{s:s}","client.debug.level",&s1) == 0) {
strncpy (tempStr,s1,sizeof(tempStr)-1);
str_lower (tempStr);
if (strncmp (tempStr,"off",3) == 0) session.client_debug_level = OFF;
if (strncmp (tempStr,"simple",6) == 0) session.client_debug_level = SIMPLE;
if (strncmp (tempStr,"full",4) == 0) session.client_debug_level = FULL;
}
// window plot
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.plot.xpos",&i1) == 0)
session.window_plot_xpos = i1;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.plot.ypos",&i1) == 0)
session.window_plot_ypos = i1;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.plot.hsize",&i1) == 0)
session.window_plot_hsize = i1;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.plot.wsize",&i1) == 0)
session.window_plot_wsize = i1;
// overlay/slider plot
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.overlay.xpos",&i1) == 0)
session.window_overlay_xpos = i1;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.overlay.ypos",&i1) == 0)
session.window_overlay_ypos = i1;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.overlay.hsize",&i1) == 0)
session.window_overlay_hsize = i1;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.overlay.wsize",&i1) == 0)
session.window_overlay_wsize = i1;
// show options
if (json_unpack_ex(root,&up_error, 0, "{s:s}","show.emoji",&s1) == 0) {
strncpy (tempStr,s1,sizeof(tempStr)-1);
str_lower (tempStr);
if (strncmp (tempStr,"alias",5) == 0) session.emoji_mode = ALIAS;
if (strncmp (tempStr,"emoji",5) == 0) session.emoji_mode = EMOJI;
if (strncmp (tempStr,"alttext",7) == 0) session.emoji_mode = ALTTEXT;
if (strncmp (tempStr,"erase",5) == 0) session.emoji_mode = ERASE;
}
if (json_unpack_ex(root,&up_error, 0, "{s:b}","show.hints",&b1) == 0)
session.show_hints = b1;
if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.supports.colors",&b1) == 0)
session.supports_colors = b1;
}
// Help Functions
static int usage_pref_set() {
PrintAndLogEx(NORMAL, "Usage: pref set [(h)elp] [(e)moji ...] [(c)olor ...] [(hi)nts ...] [debug ...]");
PrintAndLogEx(NORMAL, " [(p)lot ...] [(o)verlay ...]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " help - This help");
PrintAndLogEx(NORMAL, " emoji <(ali)as | (em)oji | (alt)text | (er)ase> - Set the level of emoji support");
PrintAndLogEx(NORMAL, " alias : show alias");
PrintAndLogEx(NORMAL, " emoji : show emoji");
PrintAndLogEx(NORMAL, " alttext : show alternative text");
PrintAndLogEx(NORMAL, " erase : dont show any emoji");
PrintAndLogEx(NORMAL, " color <(o)ff|(a)nsi> - Color support level");
PrintAndLogEx(NORMAL, " off : dont use color");
PrintAndLogEx(NORMAL, " ansi : use ansi color (linux, mac, windows terminal)");
PrintAndLogEx(NORMAL, " hints <(of)f | on> - Show hints on/off");
PrintAndLogEx(NORMAL, " debug <(o)ff | (s)imple | (f)ull> - Client debug level");
PrintAndLogEx(NORMAL, " off : no debug output");
PrintAndLogEx(NORMAL, " simple : information level debug");
PrintAndLogEx(NORMAL, " full : full debug information");
PrintAndLogEx(NORMAL, " plot [x <val>] [y <val>] [h <val>] [w <val>] - Position the plot window");
PrintAndLogEx(NORMAL, " overlay [x <val>] [y <val>] [h <val>] [w <val>] - Position the overlay/slider window");
return PM3_SUCCESS;
}
static int usage_pref_show() {
PrintAndLogEx(NORMAL, "Usage: pref show [help] [emoji|color]");
PrintAndLogEx(NORMAL, "Options:");
PrintAndLogEx(NORMAL, " help - This help");
PrintAndLogEx(NORMAL, " emoji - show current settings for emoji");
PrintAndLogEx(NORMAL, " color - show current settings for color");
return PM3_SUCCESS;
}
// Preference Processing Functions
typedef enum preferenceId {prefNONE,prefHELP,prefEMOJI,prefCOLOR,prefPLOT,prefOVERLAY,prefHINTS,prefCLIENTDEBUG} preferenceId_t;
// Enumerate text to ID
preferenceId_t prefGetID (char* cmdOpt)
{
str_lower (cmdOpt);
if (strncmp (cmdOpt,"hi",2) == 0) return prefHINTS;
if (strncmp (cmdOpt,"h",1) == 0) return prefHELP;
if (strncmp (cmdOpt,"e",1) == 0) return prefEMOJI;
if (strncmp (cmdOpt,"c",1) == 0) return prefCOLOR;
if (strncmp (cmdOpt,"p",1) == 0) return prefPLOT;
if (strncmp (cmdOpt,"o",1) == 0) return prefOVERLAY;
if (strncmp (cmdOpt,"d",1) == 0) return prefCLIENTDEBUG;
return NONE;
}
void showEmojiState (void) {
switch (session.emoji_mode) {
case ALIAS: PrintAndLogEx(NORMAL, " emoji.................. "_GREEN_("show alias"));
break;
case EMOJI: PrintAndLogEx(NORMAL, " emoji.................. "_GREEN_("show emoji"));
break;
case ALTTEXT: PrintAndLogEx(NORMAL, " emoji.................. "_GREEN_("show alt text"));
break;
case ERASE: PrintAndLogEx(NORMAL, " emoji.................. "_GREEN_("dont show emoji"));
break;
default:
PrintAndLogEx(NORMAL, " emoji.................. "_RED_("unknown"));
}
}
void showColorState (void) {
/*
switch (session.supports_colors) {
case false: PrintAndLogEx(NORMAL, "Color : "_GREEN_("off"));
break;
case true: PrintAndLogEx(NORMAL, "Color : "_GREEN_("ansi"));
break;
default:
PrintAndLogEx(NORMAL, "Color support set to : "_RED_("unknown"));
}
*/
// this will change to 1 of a set from bool
if (session.supports_colors)
PrintAndLogEx(NORMAL, " color.................. "_GREEN_("ansi"));
else
PrintAndLogEx(NORMAL, " color.................. "_GREEN_("off"));
}
void showClientDebugState (void) {
switch (session.client_debug_level) {
case OFF: PrintAndLogEx (NORMAL," client debug........... "_GREEN_("off"));
break;
case SIMPLE: PrintAndLogEx (NORMAL," client debug........... "_GREEN_("simple"));
break;
case FULL: PrintAndLogEx (NORMAL," client debug........... "_GREEN_("full"));
break;
default:
PrintAndLogEx(NORMAL, " client debug........... "_RED_("unknown"));
}
}
void showPlotPosState (void){
PrintAndLogEx (NORMAL," Plot window............ X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"),
session.window_plot_xpos,session.window_plot_ypos,session.window_plot_hsize,session.window_plot_wsize);
}
void showOverlayPosState (void){
PrintAndLogEx (NORMAL," Slider/Overlay window.. X "_GREEN_("%4d")" Y "_GREEN_("%4d")" H "_GREEN_("%4d")" W "_GREEN_("%4d"),
session.window_overlay_xpos,session.window_overlay_ypos,session.window_overlay_hsize,session.window_overlay_wsize);
}
void showHintsState (void){
if (session.show_hints)
PrintAndLogEx (NORMAL," Hints.................. "_GREEN_("on"));
else
PrintAndLogEx (NORMAL," Hints.................. "_GREEN_("off"));
}
static int CmdPrefShow (const char *Cmd) {
uint8_t cmdp = 0;
preferenceId_t CmdPref;
bool errors = false;
char strOpt[50];
PrintAndLogEx(NORMAL,"");
PrintAndLogEx(NORMAL,_BLUE_("Preferences"));
if (!session. preferences_loaded) {
PrintAndLogEx (ERR,"Preferneces not loaded");
return PM3_ESOFT;
}
if (param_getchar(Cmd, cmdp) == 0x00) { // No options - Show all
showEmojiState ();
showColorState ();
showPlotPosState ();
showOverlayPosState ();
showClientDebugState();
showHintsState ();
}
else {
while ((param_getchar(Cmd, cmdp) != 0x00) && !errors) {
if (param_getstr(Cmd, cmdp, strOpt, sizeof(strOpt)) != 0) {
CmdPref = prefGetID(strOpt);
}
else
CmdPref = prefNONE;
switch (CmdPref) {
case prefHELP:
return usage_pref_show();
case prefEMOJI:
showEmojiState ();
break;
case prefCOLOR: // color
showColorState ();
break;
case prefPLOT:
showPlotPosState ();
break;
case prefOVERLAY:
showOverlayPosState ();
break;
case prefCLIENTDEBUG:
showClientDebugState();
break;
case prefHINTS:
showHintsState();
break;
case prefNONE:
PrintAndLogEx (ERR,"Invalid option supplied");
errors = true;
break;
// errors
}
cmdp ++;
}
}
PrintAndLogEx(NORMAL,"");
return PM3_SUCCESS;
}
static int CmdPrefSet (const char *Cmd)
{
uint8_t cmdp = 0;
preferenceId_t CmdPref;
bool errors = false;
// char charOpt;
char strOpt[50];
int x,y,h,w;
if (param_getchar(Cmd, cmdp) == 0x00)
return usage_pref_set();
while ((param_getchar(Cmd, cmdp) != 0x00) && !errors) {
if (param_getstr(Cmd, cmdp, strOpt, sizeof(strOpt)) != 0) {
CmdPref = prefGetID(strOpt);
}
else
CmdPref = prefNONE;
switch (CmdPref) {
case prefHELP:
return usage_pref_set();
case prefEMOJI:
showEmojiState ();
cmdp++;
if (param_getstr(Cmd, cmdp, strOpt, sizeof(strOpt)) != 0) {
str_lower(strOpt);
if (strncmp (strOpt,"ali",3) == 0) { session.emoji_mode = ALIAS; showEmojiState (); break; }
if (strncmp (strOpt,"em",2) == 0) { session.emoji_mode = EMOJI; showEmojiState (); break; }
if (strncmp (strOpt,"alt",3) == 0) { session.emoji_mode = ALTTEXT; showEmojiState (); break; }
if (strncmp (strOpt,"er",2) == 0) { session.emoji_mode = ERASE; showEmojiState (); break; }
// if we get this far, then an error in the mode
PrintAndLogEx(ERR,"Invalid emoji option");
errors = true;
}
else
errors = true;
break;
case prefCOLOR: // color
showColorState ();
cmdp++;
if (param_getstr(Cmd, cmdp, strOpt, sizeof(strOpt)) != 0) {
str_lower(strOpt);
if (strncmp(strOpt,"a",1) == 0) { session.supports_colors = true; showColorState (); break; }
if (strncmp(strOpt,"o",1) == 0) { session.supports_colors = false; showColorState (); break; }
// if we get this far, then an error in the mode
PrintAndLogEx(ERR,"Invalid color option");
errors = true;
}
else
errors = true;
break;
case prefPLOT:
showPlotPosState ();
cmdp++;
x = y = h = w = -99999; // Some invalid value
for (int i = 0; i < 4; i++) { // upto 4 values X, Y, H, WARNING
if (param_getchar(Cmd, cmdp) != 0){
switch (tolower(param_getchar(Cmd, cmdp++))) {
case 'x': x = param_get32ex(Cmd,cmdp++,-99999,10); break;
case 'y': y = param_get32ex(Cmd,cmdp++,-99999,10); break;
case 'h': h = param_get32ex(Cmd,cmdp++,-99999,10); break;
case 'w': w = param_get32ex(Cmd,cmdp++,-99999,10); break;
default:
errors = true;
}
}
}
if (x != -99999) session.window_plot_xpos = x;
if (y != -99999) session.window_plot_ypos = y;
if (h != -99999) session.window_plot_hsize = h;
if (w != -99999) session.window_plot_wsize = w;
// Need to work out how to change live....
// calling data plot seems to work
showPlotPosState ();
break;
case prefOVERLAY:
showOverlayPosState ();
cmdp++;
x = y = h = w = -99999; // Some invalid value
for (int i = 0; i < 4; i++) { // upto 4 values X, Y, H, WARNING
if (param_getchar(Cmd, cmdp) != 0){
switch (tolower(param_getchar(Cmd, cmdp++))) {
case 'x': x = param_get32ex(Cmd,cmdp++,-99999,10); break;
case 'y': y = param_get32ex(Cmd,cmdp++,-99999,10); break;
case 'h': h = param_get32ex(Cmd,cmdp++,-99999,10); break;
case 'w': w = param_get32ex(Cmd,cmdp++,-99999,10); break;
default:
errors = true;
}
}
}
if (x != -99999) session.window_overlay_xpos = x;
if (y != -99999) session.window_overlay_ypos = y;
if (h != -99999) session.window_overlay_hsize = h;
if (w != -99999) session.window_overlay_wsize = w;
showOverlayPosState ();
// Need to work out how to change live....
break;
case prefCLIENTDEBUG:
showClientDebugState();
cmdp++;
if (param_getstr(Cmd, cmdp, strOpt, sizeof(strOpt)) != 0) {
str_lower(strOpt);
if (strncmp(strOpt,"o",1) == 0) { session.client_debug_level = OFF; g_debugMode = OFF; showClientDebugState(); break; }
if (strncmp(strOpt,"s",1) == 0) { session.client_debug_level = SIMPLE; g_debugMode = SIMPLE; showClientDebugState(); break; }
if (strncmp(strOpt,"f",1) == 0) { session.client_debug_level = FULL; g_debugMode = FULL; showClientDebugState(); break; }
// if we get this far, then an error in the mode
PrintAndLogEx(ERR,"Invalid client debug option");
errors = true;
}
else
errors = true;
break;
case prefHINTS:
showHintsState ();
cmdp++;
if (param_getstr(Cmd, cmdp, strOpt, sizeof(strOpt)) != 0) {
str_lower(strOpt);
if (strncmp(strOpt,"on",2) == 0) { session.show_hints = true; showHintsState (); break; }
if (strncmp(strOpt,"of",2) == 0) { session.show_hints = false; showHintsState (); break; }
// if we get this far, then an error in the mode
PrintAndLogEx(ERR,"Invalid hint option");
errors = true;
}
else
errors = true;
break;
case prefNONE:
PrintAndLogEx (ERR,"Invalid option supplied");
errors = true;
break;
}
cmdp ++;
}
preferences_save();
return PM3_SUCCESS;
}
static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help"},
{"set", CmdPrefSet, AlwaysAvailable, "Set a preference"},
{"show", CmdPrefShow, AlwaysAvailable, "Show (a preference)"},
{NULL, NULL, NULL, NULL}
};
static int CmdHelp(const char *Cmd) {
(void)Cmd; // Cmd is not used so far
CmdsHelp(CommandTable);
return PM3_SUCCESS;
}
int CmdPreferences (const char *Cmd)
{
clearCommandBuffer();
return CmdsParse(CommandTable, Cmd);
}

25
client/preferences.h Normal file
View file

@ -0,0 +1,25 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Settings Functions
//-----------------------------------------------------------------------------
#ifndef PREFERENCES_H_
#define PREFERENCES_H_
#include "fileutils.h"
#define preferencesFilename "preferences.json"
int CmdPreferences (const char *Cmd);
int preferences_load (void);
int preferences_save (void);
void preferences_save_callback (json_t *root);
void preferences_load_callback (json_t *root);
#endif

View file

@ -26,6 +26,7 @@
#include <string.h> #include <string.h>
#include "proxgui.h" #include "proxgui.h"
#include <QtGui> #include <QtGui>
#include "ui.h"
extern "C" { extern "C" {
#include "util_darwin.h" #include "util_darwin.h"
@ -168,7 +169,11 @@ void ProxWidget::vchange_dthr_down(int v) {
} }
ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) { ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) {
this->master = master; this->master = master;
resize(800, 400); // Set the initail postion and size from settings
if (session.preferences_loaded)
setGeometry (session.window_plot_xpos,session.window_plot_ypos,session.window_plot_wsize,session.window_plot_hsize);
else
resize(800, 400);
// Setup the controller widget // Setup the controller widget
controlWidget = new QWidget(); controlWidget = new QWidget();
@ -205,9 +210,13 @@ ProxWidget::ProxWidget(QWidget *parent, ProxGuiQT *master) : QWidget(parent) {
// shows plot window on the screen. // shows plot window on the screen.
show(); show();
// Move controller widget below plot if (session.preferences_loaded)
controlWidget->move(x(), y() + frameSize().height()); controlWidget->setGeometry (session.window_overlay_xpos,session.window_overlay_ypos,session.window_overlay_wsize,session.window_overlay_hsize);
controlWidget->resize(size().width(), 200); else {
// Move controller widget below plot
controlWidget->move(x(), y() + frameSize().height());
controlWidget->resize(size().width(), 200);
}
// Olverlays / slider window title // Olverlays / slider window title
QString ct = QString("[*]Slider [ %1 ]").arg((char *)gui_serial_port_name); QString ct = QString("[*]Slider [ %1 ]").arg((char *)gui_serial_port_name);

View file

@ -27,9 +27,58 @@
#include "comms.h" #include "comms.h"
#include "fileutils.h" #include "fileutils.h"
#include "flash.h" #include "flash.h"
#include "settings.h" #include "preferences.h"
// Used to enable/disable use of preferences json file
// #define USE_PREFERENCE_FILE
#ifdef _WIN32
static void utf8_showBanner (void) {
char sq[] = { 0xE2,0x96,0x88,0x00 }; // square block
char tr[] = { 0xE2,0x95,0x97,0x00 }; // top rigth corner
char tl[] = { 0xE2,0x95,0x94,0x00 }; // top left corner
char br[] = { 0xE2,0x95,0x9D,0x00 }; // bottom right corner
char bl[] = { 0xE2,0x95,0x9A,0x00 }; // bottom left corner
char hl[] = { 0xE2,0x95,0x90,0x00 }; // horiz line
char vl[] = { 0xE2,0x95,0x91,0x00 }; // vert line
char msg1 [60];
char msg2 [60];
char msg3 [60];
strcpy (msg1," :snowflake: iceman@icesql.net :coffee:");
strcpy (msg2," https://github.com/rfidresearchgroup/proxmark3/");
strcpy (msg3,"pre-release v4.0");
g_printAndLog = PRINTANDLOG_PRINT;
PrintAndLogEx(NORMAL, "\n");
PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s %s%s%s%s %s%s%s%s %s%s%s%s%s "),sq,sq,sq,sq,sq,sq,tr,sq,sq,sq,tr,sq,sq,sq,tr,sq,sq,sq,sq,tr);
PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s %s%s%s%s%s %s%s%s%s"),sq,sq,tl,hl,hl,sq,sq,tr,sq,sq,sq,sq,tr,sq,sq,sq,sq,vl,hl,hl,sq,vl);
PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s %s%s%s%s%s%s"),sq,sq,sq,sq,sq,sq,tl,br,sq,sq,tl,sq,sq,sq,sq,tl,sq,sq,vl,sq,sq,sq,sq,tl,br);
PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s%s%s%s%s %s%s%s%s%s%s%s%s%s%s%s %s%s%s%s")"%s",sq,sq,tr,hl,hl,hl,br,sq,sq,vl,bl,sq,sq,tl,br,sq,sq,vl,hl,hl,sq,vl,msg1);
PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s %s%s%s %s%s%s %s%s%s %s%s%s%s%s%s")"%s",sq,sq,vl,sq,sq,vl,bl,hl,br,sq,sq,vl,sq,sq,sq,sq,tl,br,msg2);
PrintAndLogEx(NORMAL, " " _BLUE_("%s%s%s %s%s%s %s%s%s %s%s%s%s%s ")"%s",bl,hl,br,bl,hl,br,bl,hl,br,bl,hl,hl,hl,br,msg3);
PrintAndLogEx(NORMAL, "");
fflush(stdout);
g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG;
}
#endif
static void showBanner(void) { static void showBanner(void) {
#ifdef _WIN32
// If on windows and using UTF-8 then we need utf-8 ascii art for banner.
if (GetConsoleCP() == 65001) {
utf8_showBanner ();
return;
}
#endif
g_printAndLog = PRINTANDLOG_PRINT; g_printAndLog = PRINTANDLOG_PRINT;
PrintAndLogEx(NORMAL, "\n"); PrintAndLogEx(NORMAL, "\n");
@ -56,6 +105,7 @@ static void showBanner(void) {
g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG; g_printAndLog = PRINTANDLOG_PRINT | PRINTANDLOG_LOG;
} }
static int check_comm(void) { static int check_comm(void) {
// If communications thread goes down. Device disconnected then this should hook up PM3 again. // If communications thread goes down. Device disconnected then this should hook up PM3 again.
if (IsCommunicationThreadDead() && session.pm3_present) { if (IsCommunicationThreadDead() && session.pm3_present) {
@ -489,12 +539,15 @@ finish2:
return ret; return ret;
} }
#ifndef USE_PREFERENCE_FILE
// Check if windows AnsiColor Support is enabled in the registery // Check if windows AnsiColor Support is enabled in the registery
// [HKEY_CURRENT_USER\Console] // [HKEY_CURRENT_USER\Console]
// "VirtualTerminalLevel"=dword:00000001 // "VirtualTerminalLevel"=dword:00000001
// 2nd Key needs to be enabled... This key takes the console out of legacy mode. // 2nd Key needs to be enabled... This key takes the console out of legacy mode.
// [HKEY_CURRENT_USER\Console] // [HKEY_CURRENT_USER\Console]
// "ForceV2"=dword:00000001 // "ForceV2"=dword:00000001
static bool DetectWindowsAnsiSupport(void) { static bool DetectWindowsAnsiSupport(void) {
bool ret = false; bool ret = false;
#if defined(_WIN32) #if defined(_WIN32)
@ -543,6 +596,8 @@ static bool DetectWindowsAnsiSupport(void) {
return ret; return ret;
} }
#endif
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
srand(time(0)); srand(time(0));
@ -582,12 +637,20 @@ int main(int argc, char *argv[]) {
set_my_executable_path(); set_my_executable_path();
set_my_user_directory(); set_my_user_directory();
// Settings Load and Test #ifdef USE_PREFERENCE_FILE
// settings_load (); // Load Settings and assign
// This will allow the command line to override the settings.json values
preferences_load ();
// Change height/width (Rows,Cols) - Testing
// printf ("\e[8;50;100t");
// printf ("\e[3;50;50t"); // x,y
//printf ("Path : %s \n",my_user_directory);
// quick patch for debug level
g_debugMode = session.client_debug_level;
// settings_save (); // settings_save ();
// printf ("Ver : %s\n",mySettings.version);
// End Settings // End Settings
#endif
for (int i = 1; i < argc; i++) { for (int i = 1; i < argc; i++) {
if (argv[i][0] != '-') { if (argv[i][0] != '-') {
@ -766,8 +829,11 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
#ifndef USE_PREFERENCE_FILE
// comment next 2 lines to use session values set from settings_load
session.supports_colors = DetectWindowsAnsiSupport(); session.supports_colors = DetectWindowsAnsiSupport();
session.emoji_mode = ALTTEXT; session.emoji_mode = ALTTEXT;
#endif
session.stdinOnTTY = isatty(STDIN_FILENO); session.stdinOnTTY = isatty(STDIN_FILENO);
session.stdoutOnTTY = isatty(STDOUT_FILENO); session.stdoutOnTTY = isatty(STDOUT_FILENO);
@ -837,6 +903,14 @@ int main(int argc, char *argv[]) {
if (!script_cmds_file && !script_cmd && session.stdinOnTTY && session.stdoutOnTTY && !flash_mode) if (!script_cmds_file && !script_cmd && session.stdinOnTTY && session.stdoutOnTTY && !flash_mode)
showBanner(); showBanner();
#ifdef USE_PREFERENCE_FILE
// Save settings if not load from settings json file.
// Doing this here will ensure other checks and updates are saved to over rule default
// e.g. Linux color use check
if (!session.preferences_loaded)
preferences_save ();
#endif
#ifdef HAVE_GUI #ifdef HAVE_GUI
# ifdef _WIN32 # ifdef _WIN32

View file

@ -0,0 +1,18 @@
[
{
"AID": "D3494F",
"Vendor": "HID",
"Country": "United States",
"Name": "SIO DESFire Ev1",
"Description": "",
"Type": ""
},
{
"AID": "4F5931",
"Vendor": "Transport of London",
"Country": "UK",
"Name": "Oyster Card",
"Description": "",
"Type": ""
},
]

View file

@ -1,194 +0,0 @@
/*****************************************************************************
* 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.
*
*****************************************************************************
*
* This file is part of loclass. It is a reconstructon 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".
*
* Copyright (C) 2014 Martin Holst Swende
*
* This is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation, or, at your option, any later version.
*
* This file 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.
*
* You should have received a copy of the GNU General Public License
* along with loclass. If not, see <http://www.gnu.org/licenses/>.
*
*
****************************************************************************/
//-----------------------------------------------------------------------------
// Settings Functions
//-----------------------------------------------------------------------------
#include "settings.h"
#include "comms.h"
#include "emv/emvjson.h"
// Load all settings into memory (struct)
int settings_load (void) {
// loadFileJson wants these, so pass in place holder values, though not used
// in settings load;
uint8_t dummyData = 0x00;
size_t dummyDL = 0x00;
// clear all settings
memset (&mySettings,0x00,sizeof(mySettings));
if (loadFileJSON(settingsFilename, &dummyData, sizeof(dummyData), &dummyDL) == PM3_SUCCESS) {
printf ("==> Settings Loaded\n");
mySettings.loaded = true;
}
// Test results
/*
bool os_windows_usecolor;
bool os_windows_useansicolor;
int window_xpos;
int window_ypos;
int window_hsize;
int window_wsize;
bool use_emojis
bool use_hints
*/
printf (" Settings Version : [%s]\n", mySettings.version);
printf (" os_windows_usecolor (bool) : [%d]\n", mySettings.os_windows_usecolor);
printf (" os_windows_useAnsicolor (bool) : [%d]\n", mySettings.os_windows_useansicolor);
printf (" window_xpos (int) : [%d]\n", mySettings.window_xpos);
printf (" window_ypos (int) : [%d]\n", mySettings.window_ypos);
printf (" window_hsize (int) : [%d]\n", mySettings.window_hsize);
printf (" window_wsize (int) : [%d]\n", mySettings.window_wsize);
printf (" use emoji (bool) : [%d]\n", mySettings.use_emojis);
printf (" use hints (bool) : [%d]\n", mySettings.use_hints);
return PM3_SUCCESS;
}
// Save all settings from memory (struct) to file
int settings_save(void) {
// Note sure if backup has value ?
char backupFilename[500];
snprintf(backupFilename, sizeof(backupFilename),"%s.bak",settingsFilename);
if (fileExists (backupFilename)) {
if (remove (backupFilename) != 0) {
PrintAndLogEx (FAILED, "Error - could not delete old settings backup file \"%s\"",backupFilename);
return PM3_ESOFT;
}
}
if (fileExists (settingsFilename)) {
if (rename (settingsFilename,backupFilename) != 0) {
PrintAndLogEx (FAILED, "Error - could not backup settings file \"%s\" to \"%s\"",settingsFilename,backupFilename);
return PM3_ESOFT;
}
}
uint8_t dummyData = 0x00;
size_t dummyDL = 0x00;
if (saveFileJSON(settingsFilename, jsfSettings, &dummyData, dummyDL) == PM3_SUCCESS)
PrintAndLogEx (NORMAL, "settings have been saved to \"%s\"",settingsFilename);
return PM3_SUCCESS;
}
void settings_save_callback(json_t *root) {
printf ("==> Save Settings\n");
//JsonSaveStr(root, "FileType", "settings");
//JsonSaveStr (root,"Test1.Test2","test settings");
/*
"version": "1.0 Nov 2019",
"os.windows.usecolor": true,
"os.windows.useAnsiColor": true,
"window.xpos": 10,
"window.ypos": 10,
"window.hsize": 300,
"window.wsize": 600
*/
JsonSaveStr (root,"FileType","settings");
JsonSaveStr (root,"version","1.0 Nov 2019");//mySettings.version);
JsonSaveBoolean (root,"os.windows.useColor", mySettings.os_windows_usecolor);
JsonSaveBoolean (root,"os.windows.useAnsiColor", mySettings.os_windows_useansicolor);
JsonSaveInt (root,"window.xpos", mySettings.window_xpos);
JsonSaveInt (root,"window.ypos", mySettings.window_ypos);
JsonSaveInt (root,"window.hsize", mySettings.window_hsize);
JsonSaveInt (root,"window.wsize", mySettings.window_wsize);
JsonSaveBoolean (root,"client.useEmojis", mySettings.use_emojis);
JsonSaveBoolean (root,"client.useHints", mySettings.use_hints);
}
void settings_load_callback(json_t *root) {
json_error_t up_error = {0};
int b1;
int i1;
const char *s1;
if (json_unpack_ex(root, &up_error , 0, "{s:s}","version", &s1) == 0)
strncpy (mySettings.version,s1,sizeof (mySettings.version) - 1);
else
strncpy (mySettings.version,"unknown",sizeof (mySettings.version) - 1);
// os.windows...
if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.windows.useColor",&b1) == 0)
mySettings.os_windows_usecolor = b1;
else // default
mySettings.os_windows_useansicolor = false;
if (json_unpack_ex(root,&up_error, 0, "{s:b}","os.windows.useAnsiColor",&b1) == 0)
mySettings.os_windows_useansicolor = b1;
else // default
mySettings.os_windows_useansicolor = false;
// window...
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.xpos",&i1) == 0)
mySettings.window_xpos = i1;
else // default
mySettings.window_xpos = 0;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.ypos",&i1) == 0)
mySettings.window_ypos = i1;
else // default
mySettings.window_ypos = 0;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.hsize",&i1) == 0)
mySettings.window_hsize = i1;
else // default
mySettings.window_hsize = 0;
if (json_unpack_ex(root,&up_error, 0, "{s:i}","window.wsize",&i1) == 0)
mySettings.window_wsize = i1;
else // default
mySettings.window_wsize = 0;
// Use EMOJIS
if (json_unpack_ex(root,&up_error, 0, "{s:b}","client.useEmojis",&b1) == 0)
mySettings.use_emojis = b1;
else // default
mySettings.use_emojis = false;
// Use Hints
if (json_unpack_ex(root,&up_error, 0, "{s:b}","client.useHints",&b1) == 0)
mySettings.use_hints = b1;
else // default
mySettings.use_hints = false;
}

View file

@ -1,40 +0,0 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2009 Michael Gernoth <michael at gernoth.net>
// Copyright (C) 2010 iZsh <izsh at fail0verflow.com>
//
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
// at your option, any later version. See the LICENSE.txt file for the text of
// the license.
//-----------------------------------------------------------------------------
// Settings Functions
//-----------------------------------------------------------------------------
#ifndef settings_h
#define settings_h
#include "fileutils.h"
#define settingsFilename "settings.json"
typedef struct {
bool loaded;
char version[20];
bool os_windows_usecolor;
bool os_windows_useansicolor;
int window_xpos;
int window_ypos;
int window_hsize;
int window_wsize;
bool use_emojis;
bool use_hints;
} settings_t;
// Settings struct so as to be available to other modules by including settings.h
settings_t mySettings;
int settings_load (void);
int settings_save (void);
void settings_save_callback (json_t *root);
void settings_load_callback (json_t *root);
#endif

View file

@ -19,8 +19,10 @@
typedef enum logLevel {NORMAL, SUCCESS, INFO, FAILED, WARNING, ERR, DEBUG, INPLACE, HINT} logLevel_t; typedef enum logLevel {NORMAL, SUCCESS, INFO, FAILED, WARNING, ERR, DEBUG, INPLACE, HINT} logLevel_t;
typedef enum emojiMode {ALIAS, EMOJI, ALTTEXT, ERASE} emojiMode_t; typedef enum emojiMode {ALIAS, EMOJI, ALTTEXT, ERASE} emojiMode_t;
typedef enum clientdebugLevel {OFF,SIMPLE,FULL} clientdebugLevel_t;
typedef struct { typedef struct {
bool preferences_loaded;
bool stdinOnTTY; bool stdinOnTTY;
bool stdoutOnTTY; bool stdoutOnTTY;
bool supports_colors; bool supports_colors;
@ -28,6 +30,15 @@ typedef struct {
bool pm3_present; bool pm3_present;
bool help_dump_mode; bool help_dump_mode;
bool show_hints; bool show_hints;
int window_plot_xpos;
int window_plot_ypos;
int window_plot_hsize;
int window_plot_wsize;
int window_overlay_xpos;
int window_overlay_ypos;
int window_overlay_hsize;
int window_overlay_wsize;
clientdebugLevel_t client_debug_level;
} session_arg_t; } session_arg_t;
extern session_arg_t session; extern session_arg_t session;

View file

@ -212,7 +212,6 @@ void hex_to_buffer(const uint8_t *buf, const uint8_t *hex_data, const size_t hex
sprintf(tmp, " "); sprintf(tmp, " ");
// remove last space // remove last space
--tmp;
*tmp = '\0'; *tmp = '\0';
return; return;
} }

View file

@ -18,7 +18,7 @@ Always use the latest repository commits from *master* branch. There are always
* [File not found](#file-not-found) * [File not found](#file-not-found)
* [Pixmap / pixbuf warnings](#pixmap--pixbuf-warnings) * [Pixmap / pixbuf warnings](#pixmap--pixbuf-warnings)
* [Usb cable](#usb-cable) * [Usb cable](#usb-cable)
* [WSL 2 explorer.exe . doesnt work](WSL-2) * [WSL 2 explorer.exe . doesnt work](#WSL-2)
## `pm3` or `pm3-flash*` doesn't see my Proxmark ## `pm3` or `pm3-flash*` doesn't see my Proxmark

View file

@ -80,6 +80,22 @@ typedef enum DESFIRE_COMMAND {
BAR = 0x10, BAR = 0x10,
} desfire_command_t; } desfire_command_t;
typedef enum {
MFDES_AUTH_DES = 1,
MFDES_AUTH_ISO = 2,
MFDES_AUTH_AES = 3,
MFDES_AUTH_PICC = 4
} mifare_des_authmode_t;
typedef enum {
MFDES_ALGO_DES = 1,
MFDES_ALGO_3DES = 2,
MFDES_ALGO_2K3DES = 3,
MFDES_ALGO_3K3DES = 4,
MFDES_ALGO_AES = 5
} mifare_des_authalgo_t;
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// ISO 14443B // ISO 14443B
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View file

@ -349,26 +349,20 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
// 6x xx = ERROR // 6x xx = ERROR
// MIFARE DESFire command set: // MIFARE DESFire command set:
#define MFDES_GET_VERSION 0x60
#define MFDES_AUTHENTICATE 0x0A // AUTHENTICATE_NATIVE #define MFDES_AUTHENTICATE 0x0A // AUTHENTICATE_NATIVE
#define MFDES_AUTHENTICATE_ISO 0x1A // AUTHENTICATE_STANDARD #define MFDES_AUTHENTICATE_ISO 0x1A // AUTHENTICATE_STANDARD
#define MFDES_AUTHENTICATE_AES 0xAA #define MFDES_AUTHENTICATE_AES 0xAA
#define MFDES_CREDIT 0x0C #define MFDES_CREDIT 0x0C
#define MFDES_LIMITED_CREDIT 0x1C #define MFDES_LIMITED_CREDIT 0x1C
#define MFDES_DEBIT 0xDC
#define MFDES_WRITE_RECORD 0x3B #define MFDES_WRITE_RECORD 0x3B
#define MFDES_READSIG 0x3C #define MFDES_READSIG 0x3C
#define MFDES_WRITE_DATA 0x3D #define MFDES_WRITE_DATA 0x3D
#define MFDES_GET_KEY_SETTINGS 0x45 #define MFDES_GET_KEY_SETTINGS 0x45
#define MFDES_CHANGE_KEY_SETTINGS 0x54 #define MFDES_CHANGE_KEY_SETTINGS 0x54
#define MFDES_SELECT_APPLICATION 0x5A #define MFDES_SELECT_APPLICATION 0x5A
#define MFDES_CHANGE_FILE_SETTINGS 0x5F #define MFDES_CHANGE_FILE_SETTINGS 0x5F
#define MFDES_GET_VERSION 0x60
#define MFDES_GET_ISOFILE_IDS 0x61 #define MFDES_GET_ISOFILE_IDS 0x61
#define MFDES_GET_KEY_VERSION 0x64 #define MFDES_GET_KEY_VERSION 0x64
#define MFDES_GET_APPLICATION_IDS 0x6A #define MFDES_GET_APPLICATION_IDS 0x6A
@ -376,19 +370,10 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MFDES_GET_FREE_MEMORY 0x6E #define MFDES_GET_FREE_MEMORY 0x6E
#define MFDES_GET_DF_NAMES 0x6D #define MFDES_GET_DF_NAMES 0x6D
#define MFDES_GET_FILE_IDS 0x6F #define MFDES_GET_FILE_IDS 0x6F
#define MFDES_ABORT_TRANSACTION 0xA7 #define MFDES_ABORT_TRANSACTION 0xA7
#define MFDES_AUTHENTICATION_FRAME 0xAF
#define MFDES_ADDITIONAL_FRAME 0xAF #define MFDES_ADDITIONAL_FRAME 0xAF
#define MFDES_ADDITIONAL_FRAME_RESP 0x91AF
#define MFDES_SUCCESS_FRAME_RESP 0x9100
#define MFDES_EAUTH_RESP 0x91AE
#define MFDES_ENO_SUCH_KEY_RESP 0x9140
#define MFDES_READ_RECORDS 0xBB #define MFDES_READ_RECORDS 0xBB
#define MFDES_READ_DATA 0xBD #define MFDES_READ_DATA 0xBD
#define MFDES_CREATE_CYCLIC_RECORD_FILE 0xC0 #define MFDES_CREATE_CYCLIC_RECORD_FILE 0xC0
#define MFDES_CREATE_LINEAR_RECORD_FILE 0xC1 #define MFDES_CREATE_LINEAR_RECORD_FILE 0xC1
#define MFDES_CHANGE_KEY 0xC4 #define MFDES_CHANGE_KEY 0xC4
@ -397,15 +382,41 @@ ISO 7816-4 Basic interindustry commands. For command APDU's.
#define MFDES_CREATE_BACKUP_DATA_FILE 0xCB #define MFDES_CREATE_BACKUP_DATA_FILE 0xCB
#define MFDES_CREATE_VALUE_FILE 0xCC #define MFDES_CREATE_VALUE_FILE 0xCC
#define MFDES_CREATE_STD_DATA_FILE 0xCD #define MFDES_CREATE_STD_DATA_FILE 0xCD
#define MFDES_CLEAR_RECORD_FILE 0xEB
#define MFDES_DELETE_APPLICATION 0xDA #define MFDES_DELETE_APPLICATION 0xDA
#define MFDES_DEBIT 0xDC
#define MFDES_DELETE_FILE 0xDF #define MFDES_DELETE_FILE 0xDF
#define MFDES_CLEAR_RECORD_FILE 0xEB
#define MFDES_GET_FILE_SETTINGS 0xF5 #define MFDES_GET_FILE_SETTINGS 0xF5
#define MFDES_FORMAT_PICC 0xFC #define MFDES_FORMAT_PICC 0xFC
// MIFARE DESFire status & error codes:
#define MFDES_S_OPERATION_OK 0x00
#define MFDES_S_NO_CHANGES 0x0C
#define MFDES_S_SIGNATURE 0x90
#define MFDES_S_ADDITIONAL_FRAME 0xAF
#define MFDES_E_OUT_OF_EEPROM 0x0E
#define MFDES_E_ILLEGAL_COMMAND_CODE 0x1C
#define MFDES_E_INTEGRITY_ERROR 0x1E
#define MFDES_E_NO_SUCH_KEY 0x40
#define MFDES_E_LENGTH 0x7E
#define MFDES_E_PERMISSION_DENIED 0x9D
#define MFDES_E_PARAMETER_ERROR 0x9E
#define MFDES_E_APPLICATION_NOT_FOUND 0xA0
#define MFDES_E_APPL_INTEGRITY 0xA1
#define MFDES_E_AUTHENTIFICATION_ERROR 0xAE
#define MFDES_E_BOUNDARY 0xBE
#define MFDES_E_PICC_INTEGRITY 0xC1
#define MFDES_E_COMMAND_ABORTED 0xCA
#define MFDES_E_PICC_DISABLED 0xCD
#define MFDES_E_COUNT 0xCE
#define MFDES_E_DUPLICATE 0xDE
#define MFDES_E_EEPROM 0xEE
#define MFDES_E_FILE_NOT_FOUND 0xF0
#define MFDES_E_FILE_INTEGRITY 0xF1
// LEGIC Commands // LEGIC Commands
#define LEGIC_MIM_22 0x0D #define LEGIC_MIM_22 0x0D
#define LEGIC_MIM_256 0x1D #define LEGIC_MIM_256 0x1D