From a25a5322dd91e498c87794ed661be8969660ea24 Mon Sep 17 00:00:00 2001 From: iceman1001 Date: Fri, 10 Jul 2020 16:37:56 +0200 Subject: [PATCH] fpga: added iCLASS on deviceside --- armsrc/BigBuf.c | 81 +++++---- armsrc/Makefile | 2 +- armsrc/iclass.c | 55 ++++-- armsrc/iso15693.c | 47 ++--- armsrc/optimized_cipher.c | 28 +++ armsrc/optimized_cipher.h | 2 + armsrc/optimized_cipherutils.c | 140 ++++++++++++++ armsrc/optimized_cipherutils.h | 66 +++++++ armsrc/optimized_elite.c | 238 ++++++++++++++++++++++++ armsrc/optimized_elite.h | 62 +++++++ armsrc/optimized_ikeys.c | 324 +++++++++++++++++++++++++++++++++ armsrc/optimized_ikeys.h | 69 +++++++ client/src/cmdhficlass.c | 57 +++--- 13 files changed, 1072 insertions(+), 99 deletions(-) create mode 100644 armsrc/optimized_cipherutils.c create mode 100644 armsrc/optimized_cipherutils.h create mode 100644 armsrc/optimized_elite.c create mode 100644 armsrc/optimized_elite.h create mode 100644 armsrc/optimized_ikeys.c create mode 100644 armsrc/optimized_ikeys.h diff --git a/armsrc/BigBuf.c b/armsrc/BigBuf.c index 4642d0079..8637443a0 100644 --- a/armsrc/BigBuf.c +++ b/armsrc/BigBuf.c @@ -22,29 +22,29 @@ extern uint8_t _stack_start, __bss_end__; static uint8_t *BigBuf = &__bss_end__; /* BigBuf memory layout: -Pointer to highest available memory: BigBuf_hi - high BigBuf_size - reserved = BigBuf_malloc() subtracts amount from BigBuf_hi, +Pointer to highest available memory: s_bigbuf_hi + high s_bigbuf_size + reserved = BigBuf_malloc() subtracts amount from s_bigbuf_hi, low 0x00 */ -static uint32_t BigBuf_size = 0; +static uint32_t s_bigbuf_size = 0; // High memory mark -static uint32_t BigBuf_hi = 0; +static uint32_t s_bigbuf_hi = 0; // pointer to the emulator memory. static uint8_t *emulator_memory = NULL; // trace related variables -static uint32_t traceLen = 0; +static uint32_t trace_len = 0; static bool tracing = true; // compute the available size for BigBuf void BigBuf_initialize(void) { - BigBuf_size = (uint32_t)&_stack_start - (uint32_t)&__bss_end__; - BigBuf_hi = BigBuf_size; - traceLen = 0; + s_bigbuf_size = (uint32_t)&_stack_start - (uint32_t)&__bss_end__; + s_bigbuf_hi = s_bigbuf_size; + trace_len = 0; } // get the address of BigBuf @@ -53,7 +53,7 @@ uint8_t *BigBuf_get_addr(void) { } uint32_t BigBuf_get_size(void) { - return BigBuf_size; + return s_bigbuf_size; } // get the address of the emulator memory. Allocate part of Bigbuf for it, if not yet done @@ -64,6 +64,11 @@ uint8_t *BigBuf_get_EM_addr(void) { return emulator_memory; } +/* +uint32_t BigBuf_get_EM_size(void) { + return CARD_MEMORY_SIZE; +} +*/ // clear ALL of BigBuf void BigBuf_Clear(void) { @@ -72,9 +77,9 @@ void BigBuf_Clear(void) { // clear ALL of BigBuf void BigBuf_Clear_ext(bool verbose) { - memset(BigBuf, 0, BigBuf_size); + memset(BigBuf, 0, s_bigbuf_size); if (verbose) - Dbprintf("Buffer cleared (%i bytes)", BigBuf_size); + Dbprintf("Buffer cleared (%i bytes)", s_bigbuf_size); } void BigBuf_Clear_EM(void) { @@ -82,23 +87,23 @@ void BigBuf_Clear_EM(void) { } void BigBuf_Clear_keep_EM(void) { - memset(BigBuf, 0, BigBuf_hi); + memset(BigBuf, 0, s_bigbuf_hi); } // allocate a chunk of memory from BigBuf. We allocate high memory first. The unallocated memory // at the beginning of BigBuf is always for traces/samples uint8_t *BigBuf_malloc(uint16_t chunksize) { - if (BigBuf_hi < chunksize) + if (s_bigbuf_hi < chunksize) return NULL; // no memory left chunksize = (chunksize + 3) & 0xfffc; // round to next multiple of 4 - BigBuf_hi -= chunksize; // aligned to 4 Byte boundary - return (uint8_t *)BigBuf + BigBuf_hi; + s_bigbuf_hi -= chunksize; // aligned to 4 Byte boundary + return (uint8_t *)BigBuf + s_bigbuf_hi; } // free ALL allocated chunks. The whole BigBuf is available for traces or samples again. void BigBuf_free(void) { - BigBuf_hi = BigBuf_size; + s_bigbuf_hi = s_bigbuf_size; emulator_memory = NULL; // shouldn't this empty BigBuf also? } @@ -106,33 +111,33 @@ void BigBuf_free(void) { // free allocated chunks EXCEPT the emulator memory void BigBuf_free_keep_EM(void) { if (emulator_memory != NULL) - BigBuf_hi = emulator_memory - (uint8_t *)BigBuf; + s_bigbuf_hi = emulator_memory - (uint8_t *)BigBuf; else - BigBuf_hi = BigBuf_size; + s_bigbuf_hi = s_bigbuf_size; // shouldn't this empty BigBuf also? } void BigBuf_print_status(void) { DbpString(_CYAN_("Memory")); - Dbprintf(" BigBuf_size.............%d", BigBuf_size); - Dbprintf(" Available memory........%d", BigBuf_hi); + Dbprintf(" BigBuf_size.............%d", s_bigbuf_size); + Dbprintf(" Available memory........%d", s_bigbuf_hi); DbpString(_CYAN_("Tracing")); Dbprintf(" tracing ................%d", tracing); - Dbprintf(" traceLen ...............%d", traceLen); + Dbprintf(" traceLen ...............%d", trace_len); } // return the maximum trace length (i.e. the unallocated size of BigBuf) uint16_t BigBuf_max_traceLen(void) { - return BigBuf_hi; + return s_bigbuf_hi; } void clear_trace(void) { - traceLen = 0; + trace_len = 0; } void set_tracelen(uint32_t value) { - traceLen = value; + trace_len = value; } void set_tracing(bool enable) { @@ -148,7 +153,7 @@ bool get_tracing(void) { * @return */ uint32_t BigBuf_get_traceLen(void) { - return traceLen; + return trace_len; } /** @@ -164,12 +169,12 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ } uint8_t *trace = BigBuf_get_addr(); - tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + traceLen); + tracelog_hdr_t *hdr = (tracelog_hdr_t *)(trace + trace_len); uint32_t num_paritybytes = (iLen - 1) / 8 + 1; // number of valid paritybytes in *parity // Return when trace is full - if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - traceLen) { + if (TRACELOG_HDR_LEN + iLen + num_paritybytes >= BigBuf_max_traceLen() - trace_len) { tracing = false; // don't trace any more if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("trace is full"); } return false; @@ -185,33 +190,35 @@ bool RAMFUNC LogTrace(const uint8_t *btBytes, uint16_t iLen, uint32_t timestamp_ if (duration > 0x7FFF) { if (DBGLEVEL >= DBG_DEBUG) { Dbprintf("Error in LogTrace: duration too long for 15 bits encoding: 0x%08x start:0x%08x end:0x%08x", duration, timestamp_start, timestamp_end); - Dbprintf("Forcing duration = 0"); +// Dbprintf("Forcing duration = 0"); } - duration = 0; + + duration /= 32; + // duration >>= 5; +// duration = 0; } hdr->timestamp = timestamp_start; hdr->duration = duration; hdr->data_len = iLen; hdr->isResponse = !readerToTag; - traceLen += TRACELOG_HDR_LEN; + trace_len += TRACELOG_HDR_LEN; // data bytes if (btBytes != NULL && iLen != 0) { - memcpy(trace + traceLen, btBytes, iLen); + memcpy(trace + trace_len, btBytes, iLen); } - traceLen += iLen; + trace_len += iLen; // parity bytes if (num_paritybytes != 0) { if (parity != NULL) { - memcpy(trace + traceLen, parity, num_paritybytes); + memcpy(trace + trace_len, parity, num_paritybytes); } else { - memset(trace + traceLen, 0x00, num_paritybytes); + memset(trace + trace_len, 0x00, num_paritybytes); } } - traceLen += num_paritybytes; - + trace_len += num_paritybytes; return true; } diff --git a/armsrc/Makefile b/armsrc/Makefile index 44ca3ab6b..2768ab3a4 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -30,7 +30,7 @@ SRC_ISO14443b = iso14443b.c SRC_FELICA = felica.c SRC_CRAPTO1 = crypto1.c des.c desfire_crypto.c mifaredesfire.c aes.c platform_util.c SRC_CRC = crc.c crc16.c crc32.c -SRC_ICLASS = iclass.c optimized_cipher.c +SRC_ICLASS = iclass.c optimized_cipherutils.c optimized_ikeys.c optimized_elite.c optimized_cipher.c SRC_LEGIC = legicrf.c legicrfsim.c legic_prng.c SRC_NFCBARCODE = thinfilm.c diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 478bafa43..a68565830 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -781,9 +781,10 @@ static void iclass_send_as_reader(uint8_t *frame, int len, uint32_t *start_time) CodeIso15693AsReader(frame, len); TransmitTo15693Tag(ToSend, ToSendMax, start_time); - uint32_t end_time = *start_time + 32 * (8 * ToSendMax - 4); // substract the 4 padding bits after EOF + uint32_t end_time = *start_time + (32 * ((8 * ToSendMax) - 4)); // substract the 4 padding bits after EOF - LogTrace(frame, len, *start_time * 4, end_time * 4, NULL, true); + if (LogTrace(frame, len, (*start_time * 4), (end_time * 4), NULL, true) == false) + DbpString("send_as_reader: failed logtrace"); } static bool iclass_send_cmd_with_retries(uint8_t* cmd, size_t cmdsize, uint8_t* resp, size_t max_resp_size, @@ -1112,35 +1113,57 @@ void iClass_ReadCheck(uint8_t blockno, uint8_t keytype) { // used with function select_and_auth (cmdhficlass.c) // which needs to authenticate before doing more things like read/write -void iClass_Authentication(uint8_t *mac) { +// selects and authenticate to a card, sends back div_key and mac to client. +void iClass_Authentication(uint8_t *bytes) { + + struct p { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; + } PACKED; + struct p *payload = (struct p *)bytes; + + // device response message + struct { + bool isOK; + uint8_t div_key[8]; + uint8_t mac[4]; + } PACKED packet; Iso15693InitReader(); StartCountSspClk(); uint8_t card_data[3 * 8] = {0xFF}; - bool use_credit_key = false; uint32_t eof_time = 0; - bool isOK = select_iclass_tag(card_data, use_credit_key, &eof_time); - if (isOK == false) { - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + packet.isOK = select_iclass_tag(card_data, payload->use_credit_key, &eof_time); + if (packet.isOK == false) { + reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&packet, sizeof(packet)); return; } uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; uint8_t check[9] = { ICLASS_CMD_CHECK }; -// uint8_t mac[4]; -// opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, mac ) + uint8_t ccnr[12] = {0}; + memcpy(ccnr, card_data + 16, 8); + + if (payload->use_raw) + memcpy(packet.div_key, payload->key, 8); + else + iclass_calc_div_key(card_data, payload->key, packet.div_key, payload->use_elite); + + opt_doReaderMAC(ccnr, packet.div_key, packet.mac); // copy MAC to check command (readersignature) - check[5] = mac[0]; - check[6] = mac[1]; - check[7] = mac[2]; - check[8] = mac[3]; + check[5] = packet.mac[0]; + check[6] = packet.mac[1]; + check[7] = packet.mac[2]; + check[8] = packet.mac[3]; uint8_t resp[ICLASS_BUFFER_SIZE]; - isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); - reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&isOK, sizeof(uint8_t)); + packet.isOK = iclass_send_cmd_with_retries(check, sizeof(check), resp, sizeof(resp), 4, 3, start_time, ICLASS_READER_TIMEOUT_OTHERS, &eof_time); + reply_ng(CMD_HF_ICLASS_AUTH, PM3_SUCCESS, (uint8_t *)&packet, sizeof(packet)); } typedef struct iclass_premac { @@ -1300,6 +1323,8 @@ void iClass_Dump(uint8_t start_blockno, uint8_t numblks) { // return pointer to dump memory in arg3 // iceman: why not return | dataout - getbigbuf ? Should give exact location. + Dbprintf("ICE:: dataout, %u max trace %u, bb start %u, data-bb %u ", dataout, BigBuf_max_traceLen(), BigBuf_get_addr(), dataout - BigBuf_get_addr() ); + Dbprintf("ICE:: bb size %u, malloced %u (255*8)", BigBuf_get_size(), BigBuf_get_size() - (dataout - BigBuf_get_addr()) ); reply_mix(CMD_ACK, isOK, blkcnt, BigBuf_max_traceLen(), 0, 0); BigBuf_free(); } diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index f8214b5e4..39a64a8c4 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -688,9 +688,9 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo FpgaDisableSscDma(); uint32_t sof_time = *eof_time - - DecodeTag.len * 8 * 8 * 16 // time for byte transfers - - 32 * 16 // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer + - (DecodeTag.len * 8 * 8 * 16) // time for byte transfers + - (32 * 16) // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer if (DBGLEVEL >= DBG_EXTENDED) { Dbprintf("samples = %d, ret = %d, Decoder: state = %d, lastBit = %d, len = %d, bitCount = %d, posCount = %d", @@ -705,7 +705,8 @@ int GetIso15693AnswerFromTag(uint8_t* response, uint16_t max_len, uint16_t timeo Dbprintf("timing: sof_time = %d, eof_time = %d", (sof_time * 4), (*eof_time * 4)); } - LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false); + if (LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (*eof_time * 4), NULL, false) == false) + DbpString("GetIso15693AnswerFromTag: failed logtrace"); if (ret < 0) { return ret; @@ -1090,7 +1091,8 @@ int GetIso15693CommandFromReader(uint8_t *received, size_t max_len, uint32_t *eo - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128:2048) // time for byte transfers - 32 // time for SOF transfer - 16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true); + if (LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*32, *eof_time*32, NULL, true) == false) + DbpString("GetIso15693CommandFromReader: failed logtrace"); } return DecodeReader.byteCount; @@ -1223,16 +1225,18 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } } - if (!TagIsActive) { // no need to try decoding reader data if the tag is sending + // no need to try decoding reader data if the tag is sending + if (TagIsActive == false) { + if (Handle15693SampleFromReader(sniffdata & 0x02, &DecodeReader)) { - uint32_t eof_time = dma_start_time + samples*16 + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + uint32_t eof_time = dma_start_time + (samples * 16) + 8 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (DecodeReader.byteCount > 0) { uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers - - 32*16 // time for SOF transfer - - 16*16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers + - 32 * 16 // time for SOF transfer + - 16 * 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); } // And ready to receive another command. DecodeReaderReset(&DecodeReader); @@ -1244,13 +1248,13 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { } else if (Handle15693SampleFromReader(sniffdata & 0x01, &DecodeReader)) { - uint32_t eof_time = dma_start_time + samples*16 + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF + uint32_t eof_time = dma_start_time + (samples * 16) + 16 - DELAY_READER_TO_ARM_SNIFF; // end of EOF if (DecodeReader.byteCount > 0) { uint32_t sof_time = eof_time - - DecodeReader.byteCount * (DecodeReader.Coding==CODING_1_OUT_OF_4?128*16:2048*16) // time for byte transfers - - 32*16 // time for SOF transfer - - 16*16; // time for EOF transfer - LogTrace(DecodeReader.output, DecodeReader.byteCount, sof_time*4, eof_time*4, NULL, true); + - DecodeReader.byteCount * (DecodeReader.Coding == CODING_1_OUT_OF_4 ? 128 * 16 : 2048 * 16) // time for byte transfers + - 32 * 16 // time for SOF transfer + - 16 * 16; // time for EOF transfer + LogTrace(DecodeReader.output, DecodeReader.byteCount, (sof_time * 4), (eof_time * 4), NULL, true); } // And ready to receive another command DecodeReaderReset(&DecodeReader); @@ -1269,15 +1273,16 @@ void SniffIso15693(uint8_t jam_search_len, uint8_t *jam_search_string) { if (!ReaderIsActive && ExpectTagAnswer) { // no need to try decoding tag data if the reader is currently sending or no answer expected yet if (Handle15693SamplesFromTag(sniffdata >> 2, &DecodeTag)) { - uint32_t eof_time = dma_start_time + samples*16 - DELAY_TAG_TO_ARM_SNIFF; // end of EOF + uint32_t eof_time = dma_start_time + (samples * 16) - DELAY_TAG_TO_ARM_SNIFF; // end of EOF if (DecodeTag.lastBit == SOF_PART2) { - eof_time -= 8*16; // needed 8 additional samples to confirm single SOF (iCLASS) + eof_time -= (8 * 16); // needed 8 additional samples to confirm single SOF (iCLASS) } uint32_t sof_time = eof_time - DecodeTag.len * 8 * 8 * 16 // time for byte transfers - - 32 * 16 // time for SOF transfer - - (DecodeTag.lastBit != SOF_PART2?32*16:0); // time for EOF transfer - LogTrace(DecodeTag.output, DecodeTag.len, sof_time*4, eof_time*4, NULL, false); + - (32 * 16) // time for SOF transfer + - (DecodeTag.lastBit != SOF_PART2 ? (32 * 16) : 0); // time for EOF transfer + + LogTrace(DecodeTag.output, DecodeTag.len, (sof_time * 4), (eof_time * 4), NULL, false); // And ready to receive another response. DecodeTagReset(&DecodeTag); DecodeReaderReset(&DecodeReader); diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 24bb8d7ee..021b8ae22 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -77,7 +77,15 @@ -- piwi 2019 **/ +/** + add the possibility to do iCLASS on device only + -- iceman 2020 +**/ + #include "optimized_cipher.h" +#include "optimized_elite.h" +#include "optimized_ikeys.h" +#include "optimized_cipherutils.h" static const uint8_t opt_select_LUT[256] = { 00, 03, 02, 01, 02, 03, 00, 01, 04, 07, 07, 04, 06, 07, 05, 04, @@ -290,3 +298,23 @@ void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *di opt_suc(div_key_p, &_init, nr, 4, true); opt_output(div_key_p, &_init, mac); } + + +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite) { + if (elite) { + uint8_t keytable[128] = {0}; + uint8_t key_index[8] = {0}; + uint8_t key_sel[8] = { 0 }; + uint8_t key_sel_p[8] = { 0 }; + hash2(key, keytable); + hash1(csn, key_index); + for (uint8_t i = 0; i < 8 ; i++) + key_sel[i] = keytable[key_index[i]]; + + //Permute from iclass format to standard format + permutekey_rev(key_sel, key_sel_p); + diversifyKey(csn, key_sel_p, div_key); + } else { + diversifyKey(csn, key, div_key); + } +} diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index c6df25ab8..e65b6c4cb 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -46,4 +46,6 @@ State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p); */ void opt_doTagMAC_2(State _init, uint8_t *nr, uint8_t mac[4], const uint8_t *div_key_p); + +void iclass_calc_div_key(uint8_t *csn, uint8_t *key, uint8_t *div_key, bool elite); #endif // OPTIMIZED_CIPHER_H diff --git a/armsrc/optimized_cipherutils.c b/armsrc/optimized_cipherutils.c new file mode 100644 index 000000000..c51f83f9b --- /dev/null +++ b/armsrc/optimized_cipherutils.c @@ -0,0 +1,140 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ +#include "optimized_cipherutils.h" +#include + +/** + * + * @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) << (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 + */ +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest) { + while (len--) { + dest[len] = (uint8_t) n; + n >>= 8; + } +} + +uint64_t x_bytes_to_num(uint8_t *src, size_t len) { + uint64_t num = 0; + while (len--) { + num = (num << 8) | (*src); + src++; + } + return num; +} + +uint8_t reversebytes(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b; +} + +void reverse_arraybytes(uint8_t *arr, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + arr[i] = reversebytes(arr[i]); + } +} + +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len) { + uint8_t i; + for (i = 0; i < len ; i++) { + dest[i] = reversebytes(arr[i]); + } +} + diff --git a/armsrc/optimized_cipherutils.h b/armsrc/optimized_cipherutils.h new file mode 100644 index 000000000..63ba8b8aa --- /dev/null +++ b/armsrc/optimized_cipherutils.h @@ -0,0 +1,66 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ +#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); + +void push6bits(BitstreamOut *stream, uint8_t bits); +void x_num_to_bytes(uint64_t n, size_t len, uint8_t *dest); +uint64_t x_bytes_to_num(uint8_t *src, size_t len); +uint8_t reversebytes(uint8_t b); +void reverse_arraybytes(uint8_t *arr, size_t len); +void reverse_arraycopy(uint8_t *arr, uint8_t *dest, size_t len); +#endif // CIPHERUTILS_H diff --git a/armsrc/optimized_elite.c b/armsrc/optimized_elite.c new file mode 100644 index 000000000..d2c57ac68 --- /dev/null +++ b/armsrc/optimized_elite.c @@ -0,0 +1,238 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + * + ****************************************************************************/ +#include "optimized_elite.h" + +#include +#include +#include +#include "mbedtls/des.h" +#include "optimized_ikeys.h" + +/** + * @brief Permutes a key from standard NIST format to Iclass specific format + * from http://www.proxmark.org/forum/viewtopic.php?pid=11220#p11220 + * + * If you 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); + } +} +/** + * 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 + */ +static inline uint8_t rr(uint8_t val) { + return val >> 1 | ((val & 1) << 7); +} + +/** + * Helper function for hash1 + * @brief rl + * @param val + * @return + */ +static inline uint8_t rl(uint8_t val) { + return val << 1 | ((val & 0x80) >> 7); +} + +/** + * Helper function for hash1 + * @brief swap + * @param val + * @return + */ +static inline 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] = rl(swap(csn[3] + k[0])); + k[4] = ~rr(csn[4] + k[2]) + 1; + k[5] = ~rl(csn[5] + k[3]) + 1; + k[6] = rr(csn[6] + (k[4] ^ 0x3c)); + k[7] = rl(csn[7] + (k[5] ^ 0xc3)); + + k[7] &= 0x7F; + k[6] &= 0x7F; + k[5] &= 0x7F; + k[4] &= 0x7F; + k[3] &= 0x7F; + k[2] &= 0x7F; + k[1] &= 0x7F; + k[0] &= 0x7F; +} +/** +Definition 14. Define the rotate key function rk : (F 82 ) 8 × N → (F 82 ) 8 as +rk(x [0] . . . x [7] , 0) = x [0] . . . x [7] +rk(x [0] . . . x [7] , n + 1) = rk(rl(x [0] ) . . . rl(x [7] ), n) +**/ +static void rk(uint8_t *key, uint8_t n, uint8_t *outp_key) { + memcpy(outp_key, key, 8); + uint8_t j; + while (n-- > 0) { + for (j = 0; j < 8 ; j++) + outp_key[j] = rl(outp_key[j]); + } + return; +} + +static mbedtls_des_context ctx_enc; +static mbedtls_des_context ctx_dec; + +static void desdecrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_dec(&ctx_dec, key_std_format); + mbedtls_des_crypt_ecb(&ctx_dec, input, output); +} + +static void desencrypt_iclass(uint8_t *iclass_key, uint8_t *input, uint8_t *output) { + uint8_t key_std_format[8] = {0}; + permutekey_rev(iclass_key, key_std_format); + mbedtls_des_setkey_enc(&ctx_enc, key_std_format); + mbedtls_des_crypt_ecb(&ctx_enc, input, output); +} + +/** + * @brief Insert uint8_t[8] custom master key to calculate hash2 and return key_select. + * @param key unpermuted custom key + * @param hash1 hash1 + * @param key_sel output key_sel=h[hash1[i]] + */ +void hash2(uint8_t *key64, uint8_t *outp_keytable) { + /** + *Expected: + * High Security Key Table + + 00 F1 35 59 A1 0D 5A 26 7F 18 60 0B 96 8A C0 25 C1 + 10 BF A1 3B B0 FF 85 28 75 F2 1F C6 8F 0E 74 8F 21 + 20 14 7A 55 16 C8 A9 7D B3 13 0C 5D C9 31 8D A9 B2 + 30 A3 56 83 0F 55 7E DE 45 71 21 D2 6D C1 57 1C 9C + 40 78 2F 64 51 42 7B 64 30 FA 26 51 76 D3 E0 FB B6 + 50 31 9F BF 2F 7E 4F 94 B4 BD 4F 75 91 E3 1B EB 42 + 60 3F 88 6F B8 6C 2C 93 0D 69 2C D5 20 3C C1 61 95 + 70 43 08 A0 2F FE B3 26 D7 98 0B 34 7B 47 70 A0 AB + + **** The 64-bit HS Custom Key Value = 5B7C62C491C11B39 ******/ + uint8_t key64_negated[8] = {0}; + uint8_t z[8][8] = {{0}, {0}}; + uint8_t temp_output[8] = {0}; + + //calculate complement of key + int i; + for (i = 0; i < 8; i++) + key64_negated[i] = ~key64[i]; + + // Once again, key is on iclass-format + desencrypt_iclass(key64, key64_negated, z[0]); + + uint8_t y[8][8] = {{0}, {0}}; + + // y[0]=DES_dec(z[0],~key) + // Once again, key is on iclass-format + desdecrypt_iclass(z[0], key64_negated, y[0]); + + for (i = 1; i < 8; i++) { + rk(key64, i, temp_output); + desdecrypt_iclass(temp_output, z[i - 1], z[i]); + desencrypt_iclass(temp_output, y[i - 1], y[i]); + } + + if (outp_keytable != NULL) { + for (i = 0 ; i < 8 ; i++) { + memcpy(outp_keytable + i * 16, y[i], 8); + memcpy(outp_keytable + 8 + i * 16, z[i], 8); + } + } +} diff --git a/armsrc/optimized_elite.h b/armsrc/optimized_elite.h new file mode 100644 index 000000000..281ecf0bb --- /dev/null +++ b/armsrc/optimized_elite.h @@ -0,0 +1,62 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + + +#ifndef ELITE_CRACK_H +#define ELITE_CRACK_H + +#include +#include + +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]); +/** + * 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); +void hash2(uint8_t *key64, uint8_t *outp_keytable); + +#endif diff --git a/armsrc/optimized_ikeys.c b/armsrc/optimized_ikeys.c new file mode 100644 index 000000000..eeb00e562 --- /dev/null +++ b/armsrc/optimized_ikeys.c @@ -0,0 +1,324 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + +/** +From "Dismantling iclass": + This section describes in detail the built-in key diversification algorithm of iClass. + Besides the obvious purpose of deriving a card key from a master key, this + algorithm intends to circumvent weaknesses in the cipher by preventing the + usage of certain ‘weak’ keys. In order to compute a diversified key, the iClass + reader first encrypts the card identity id with the master key K, using single + DES. The resulting ciphertext is then input to a function called hash0 which + outputs the diversified key k. + + k = hash0(DES enc (id, K)) + + Here the DES encryption of id with master key K outputs a cryptogram c + of 64 bits. These 64 bits are divided as c = x, y, z [0] , . . . , z [7] ∈ F 82 × F 82 × (F 62 ) 8 + which is used as input to the hash0 function. This function introduces some + obfuscation by performing a number of permutations, complement and modulo + operations, see Figure 2.5. Besides that, it checks for and removes patterns like + similar key bytes, which could produce a strong bias in the cipher. Finally, the + output of hash0 is the diversified card key k = k [0] , . . . , k [7] ∈ (F 82 ) 8 . + +**/ +#include "optimized_ikeys.h" + +#include +#include +#include +#include "mbedtls/des.h" +#include "optimized_cipherutils.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 mbedtls_des_context ctx_enc; + +/** + * @brief The key diversification algorithm uses 6-bit bytes. + * This implementation uses 64 bit uint to pack seven of them into one + * variable. When they are there, they are placed as follows: + * XXXX XXXX N0 .... N7, occupying the last 48 bits. + * + * This function picks out one from such a collection + * @param all + * @param n bitnumber + * @return + */ +static uint8_t getSixBitByte(uint64_t c, int n) { + return (c >> (42 - 6 * n)) & 0x3F; +} + +/** + * @brief Puts back a six-bit 'byte' into a uint64_t. + * @param c buffer + * @param z the value to place there + * @param n bitnumber. + */ +static void pushbackSixBitByte(uint64_t *c, uint8_t z, int n) { + //0x XXXX YYYY ZZZZ ZZZZ ZZZZ + // ^z0 ^z7 + //z0: 1111 1100 0000 0000 + + uint64_t masked = z & 0x3F; + uint64_t eraser = 0x3F; + masked <<= 42 - 6 * n; + eraser <<= 42 - 6 * n; + + //masked <<= 6*n; + //eraser <<= 6*n; + + eraser = ~eraser; + (*c) &= eraser; + (*c) |= masked; + +} +/** + * @brief Swaps the z-values. + * If the input value has format XYZ0Z1...Z7, the output will have the format + * XYZ7Z6...Z0 instead + * @param c + * @return + */ +static uint64_t swapZvalues(uint64_t c) { + uint64_t newz = 0; + pushbackSixBitByte(&newz, getSixBitByte(c, 0), 7); + pushbackSixBitByte(&newz, getSixBitByte(c, 1), 6); + pushbackSixBitByte(&newz, getSixBitByte(c, 2), 5); + pushbackSixBitByte(&newz, getSixBitByte(c, 3), 4); + pushbackSixBitByte(&newz, getSixBitByte(c, 4), 3); + pushbackSixBitByte(&newz, getSixBitByte(c, 5), 2); + pushbackSixBitByte(&newz, getSixBitByte(c, 6), 1); + pushbackSixBitByte(&newz, getSixBitByte(c, 7), 0); + newz |= (c & 0xFFFF000000000000); + return newz; +} + +/** +* @return 4 six-bit bytes chunked into a uint64_t,as 00..00a0a1a2a3 +*/ +static uint64_t ck(int i, int j, uint64_t z) { + if (i == 1 && j == -1) { + // ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + return z; + } else if (j == -1) { + // ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + return ck(i - 1, i - 2, z); + } + + if (getSixBitByte(z, i) == getSixBitByte(z, j)) { + //ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ) + uint64_t newz = 0; + int c; + for (c = 0; c < 4; c++) { + uint8_t val = getSixBitByte(z, c); + if (c == i) + pushbackSixBitByte(&newz, j, c); + else + pushbackSixBitByte(&newz, val, c); + } + return ck(i, j - 1, newz); + } else { + return ck(i, j - 1, z); + } +} +/** + + Definition 8. + Let the function check : (F 62 ) 8 → (F 62 ) 8 be defined as + check(z [0] . . . z [7] ) = ck(3, 2, z [0] . . . z [3] ) · ck(3, 2, z [4] . . . z [7] ) + + where ck : N × N × (F 62 ) 4 → (F 62 ) 4 is defined as + + ck(1, −1, z [0] . . . z [3] ) = z [0] . . . z [3] + ck(i, −1, z [0] . . . z [3] ) = ck(i − 1, i − 2, z [0] . . . z [3] ) + ck(i, j, z [0] . . . z [3] ) = + ck(i, j − 1, z [0] . . . z [i] ← j . . . z [3] ), if z [i] = z [j] ; + ck(i, j − 1, z [0] . . . z [3] ), otherwise + + otherwise. +**/ + +static uint64_t check(uint64_t z) { + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + + // ck(3, 2, z [0] . . . z [3] ) + uint64_t ck1 = ck(3, 2, z); + + // ck(3, 2, z [4] . . . z [7] ) + uint64_t ck2 = ck(3, 2, z << 24); + + //The ck function will place the values + // in the middle of z. + ck1 &= 0x00000000FFFFFF000000; + ck2 &= 0x00000000FFFFFF000000; + + return ck1 | ck2 >> 24; +} + +static void permute(BitstreamIn *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); + + push6bits(out, zl + 1); + permute(p_in, z, l + 1, r, out); + } else { // otherwise + uint8_t zr = getSixBitByte(z, r); + + push6bits(out, zr); + permute(p_in, z, l, r + 1, out); + } +} + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void hash0(uint64_t c, uint8_t k[8]) { + c = swapZvalues(c); + + //These 64 bits are divided as c = x, y, z [0] , . . . , z [7] + // x = 8 bits + // y = 8 bits + // z0-z7 6 bits each : 48 bits + uint8_t x = (c & 0xFF00000000000000) >> 56; + uint8_t y = (c & 0x00FF000000000000) >> 48; + uint64_t zP = 0; + + for (int n = 0; n < 4 ; n++) { + uint8_t zn = getSixBitByte(c, n); + uint8_t zn4 = getSixBitByte(c, n + 4); + uint8_t _zn = (zn % (63 - n)) + n; + uint8_t _zn4 = (zn4 % (64 - n)) + n; + pushbackSixBitByte(&zP, _zn, n); + pushbackSixBitByte(&zP, _zn4, n + 4); + } + + uint64_t zCaret = check(zP); + uint8_t p = pi[x % 35]; + + if (x & 1) //Check if x7 is 1 + p = ~p; + + BitstreamIn 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 + //Shift z-values down onto the lower segment + + uint64_t zTilde = x_bytes_to_num(outbuffer, sizeof(outbuffer)); + + zTilde >>= 16; + + for (int i = 0; i < 8; i++) { + // the key on index i is first a bit from y + // then six bits from z, + // then a bit from p + + // Init with zeroes + k[i] = 0; + // First, place yi leftmost in k + //k[i] |= (y << i) & 0x80 ; + + // First, place y(7-i) leftmost in k + k[i] |= (y << (7 - i)) & 0x80 ; + + uint8_t zTilde_i = getSixBitByte(zTilde, i); + // zTildeI is now on the form 00XXXXXX + // with one leftshift, it'll be + // 0XXXXXX0 + // So after leftshift, we can OR it into k + // However, when doing complement, we need to + // again MASK 0XXXXXX0 (0x7E) + zTilde_i <<= 1; + + //Finally, add bit from p or p-mod + //Shift bit i into rightmost location (mask only after complement) + uint8_t p_i = p >> i & 0x1; + + if (k[i]) { // yi = 1 + k[i] |= ~zTilde_i & 0x7E; + k[i] |= p_i & 1; + k[i] += 1; + + } else { // otherwise + k[i] |= zTilde_i & 0x7E; + k[i] |= (~p_i) & 1; + } + } +} +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ +void diversifyKey(uint8_t *csn, uint8_t *key, uint8_t *div_key) { + // Prepare the DES key + mbedtls_des_setkey_enc(&ctx_enc, key); + + uint8_t crypted_csn[8] = {0}; + + // Calculate DES(CSN, KEY) + mbedtls_des_crypt_ecb(&ctx_enc, csn, crypted_csn); + + //Calculate HASH0(DES)) + uint64_t c_csn = x_bytes_to_num(crypted_csn, sizeof(crypted_csn)); + + hash0(c_csn, div_key); +} + diff --git a/armsrc/optimized_ikeys.h b/armsrc/optimized_ikeys.h new file mode 100644 index 000000000..91fa406ad --- /dev/null +++ b/armsrc/optimized_ikeys.h @@ -0,0 +1,69 @@ +/***************************************************************************** + * WARNING + * + * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. + * + * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL + * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, + * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. + * + * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. + * + ***************************************************************************** + * + * This file is part of loclass. It is a reconstructon of the cipher engine + * used in iClass, and RFID techology. + * + * The implementation is based on the work performed by + * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and + * Milosch Meriac in the paper "Dismantling IClass". + * + * Copyright (C) 2014 Martin Holst Swende + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation, or, at your option, any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with loclass. If not, see . + * + * + ****************************************************************************/ + +#ifndef IKEYS_H +#define IKEYS_H + +#include + +/** + * @brief + *Definition 11. Let the function hash0 : F 82 × F 82 × (F 62 ) 8 → (F 82 ) 8 be defined as + * hash0(x, y, z [0] . . . z [7] ) = k [0] . . . k [7] where + * z'[i] = (z[i] mod (63-i)) + i i = 0...3 + * z'[i+4] = (z[i+4] mod (64-i)) + i i = 0...3 + * ẑ = check(z'); + * @param c + * @param k this is where the diversified key is put (should be 8 bytes) + * @return + */ +void hash0(uint64_t c, uint8_t k[8]); +/** + * @brief Performs Elite-class key diversification + * @param csn + * @param key + * @param div_key + */ + +void diversifyKey(uint8_t csn[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 --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index 1eda5678b..62ba0f70a 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -1158,28 +1158,22 @@ static bool select_only(uint8_t *CSN, uint8_t *CCNR, bool use_credit_key, bool v } static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool use_credit_key, bool elite, bool rawkey, bool verbose) { - 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}; - - if (select_only(CSN, CCNR, use_credit_key, verbose) == false) { - if (verbose) PrintAndLogEx(FAILED, "selecting tag failed"); -// DropField(); - return false; - } - - //get div_key - if (rawkey) - memcpy(div_key, KEY, 8); - else - HFiClassCalcDivKey(CSN, KEY, div_key, elite); - - if (verbose) PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); - - doMAC(CCNR, div_key, MAC); - + + struct { + uint8_t key[8]; + bool use_raw; + bool use_elite; + bool use_credit_key; + } PACKED payload; + + memcpy(payload.key, KEY, 8); + payload.use_raw = rawkey; + payload.use_elite = elite; + payload.use_credit_key = use_credit_key; + + SendCommandNG(CMD_HF_ICLASS_AUTH, (uint8_t*)&payload, sizeof(payload)); PacketResponseNG resp; - clearCommandBuffer(); - SendCommandNG(CMD_HF_ICLASS_AUTH, MAC, 4); + clearCommandBuffer(); if (WaitForResponseTimeout(CMD_HF_ICLASS_AUTH, &resp, 2000) == 0) { if (verbose) PrintAndLogEx(WARNING, "Command execute timeout"); return false; @@ -1190,12 +1184,25 @@ static bool select_and_auth(uint8_t *KEY, uint8_t *MAC, uint8_t *div_key, bool u return false; } - uint8_t isOK = resp.data.asBytes[0]; - if (isOK == 0) { + struct p { + bool isOK; + uint8_t div_key[8]; + uint8_t mac[4]; + } PACKED; + struct p *packet = (struct p *)resp.data.asBytes; + + if (packet->isOK == 0) { if (verbose) PrintAndLogEx(FAILED, "authentication error"); return false; } - + + if (div_key) + memcpy(div_key, packet->div_key, sizeof(packet->div_key)); + + if (MAC) + memcpy(MAC, packet->mac, sizeof(packet->mac)); + + if (verbose) PrintAndLogEx(SUCCESS, "authing with %s: %s", rawkey ? "raw key" : "diversified key", sprint_hex(div_key, 8)); return true; } @@ -2972,7 +2979,7 @@ int readIclass(bool loop, bool verbose) { uint8_t readStatus = resp.oldarg[0] & 0xff; - PrintAndLogEx(NORMAL, "ICE: %x", readStatus); +// PrintAndLogEx(NORMAL, "ICE: %x", readStatus); // no tag found or button pressed if ((readStatus == 0 && !loop) || readStatus == 0xFF) {