From b19caaefc277335ed623e0a3b6c80be31fce439c Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Sat, 21 Feb 2015 20:47:40 +0100 Subject: [PATCH 1/9] Fixed minor bugs in iclass fullsim, does not work yet though --- armsrc/iclass.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index a976217d4..81ecd01bf 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1085,7 +1085,7 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) // Tag CSN uint8_t *modulated_response; - int modulated_response_size; + int modulated_response_size = 0; uint8_t* trace_data = NULL; int trace_data_size = 0; @@ -1132,8 +1132,10 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) CodeIClassTagAnswer(card_challenge_data, sizeof(card_challenge_data)); memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; - //This is used for responding to READ-block commands + //This is used for responding to READ-block commands or other data which is dynamically generated uint8_t *data_response = BigBuf_malloc(8 * 2 + 2); + //This is used for responding to READ-block commands or other data which is dynamically generated + uint8_t *data_generic_trace = BigBuf_malloc(8 * 2 + 2); // Start from off (no field generated) //FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1201,7 +1203,8 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) //Reader just sent us NR and MAC(k,cc * nr) //The diversified key should be stored on block 3 //However, from a typical dump, the key will not be there - uint8_t *diversified_key = { 0 }; + uint8_t diversified_key[8] = { 0 }; + //Get the diversified key from emulator memory memcpy(diversified_key, emulator+(8*3),8); uint8_t ccnr[12] = { 0 }; @@ -1210,7 +1213,8 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) //Put nr there memcpy(ccnr+8, receivedCmd+1,4); //Now, calc MAC - doMAC(ccnr,diversified_key, trace_data); + doMAC(ccnr,diversified_key, data_generic_trace); + trace_data = data_generic_trace; trace_data_size = 4; CodeIClassTagAnswer(trace_data , trace_data_size); memcpy(data_response, ToSend, ToSendMax); @@ -1253,6 +1257,13 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) modulated_response = data_response; modulated_response_size = ToSendMax; } + else if(receivedCmd[0] == ICLASS_CMD_PAGESEL) + {//Pagesel + //Pagesel enables to select a page in the selected chip memory and return its configuration block + //Chips with a single page will not answer to this command + // It appears we're fine ignoring this. + //Otherwise, we should answer 8bytes (block) + 2bytes CRC + } else { //#db# Unknown command received from reader (len=5): 26 1 0 f6 a 44 44 44 44 // Never seen this command before From 10a8875c723002d015b8f8872f1d50759f090c89 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Thu, 26 Feb 2015 20:35:35 +0100 Subject: [PATCH 2/9] Implemented new optimized version of MAC-calculation for iclass --- armsrc/Makefile | 3 +- armsrc/cipher.c | 272 ---------------------------------------- armsrc/cipher.h | 49 -------- armsrc/cipherutils.c | 292 ------------------------------------------- armsrc/cipherutils.h | 76 ----------- armsrc/iclass.c | 6 +- armsrc/lfsampling.c | 9 +- 7 files changed, 9 insertions(+), 698 deletions(-) delete mode 100644 armsrc/cipher.c delete mode 100644 armsrc/cipher.h delete mode 100644 armsrc/cipherutils.c delete mode 100644 armsrc/cipherutils.h diff --git a/armsrc/Makefile b/armsrc/Makefile index be08e56b1..75ccdece2 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -43,8 +43,7 @@ ARMSRC = fpgaloader.c \ legic_prng.c \ iclass.c \ BigBuf.c \ - cipher.c \ - cipherutils.c\ + optimized_cipher.c # stdint.h provided locally until GCC 4.5 becomes C99 compliant APP_CFLAGS += -I. diff --git a/armsrc/cipher.c b/armsrc/cipher.c deleted file mode 100644 index 7c9cc873a..000000000 --- a/armsrc/cipher.c +++ /dev/null @@ -1,272 +0,0 @@ -/***************************************************************************** - * WARNING - * - * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. - * - * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL - * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, - * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. - * - * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. - * - ***************************************************************************** - * - * This file is part of loclass. It is a reconstructon of the cipher engine - * used in iClass, and RFID techology. - * - * The implementation is based on the work performed by - * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and - * Milosch Meriac in the paper "Dismantling IClass". - * - * Copyright (C) 2014 Martin Holst Swende - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * 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 "cipher.h" -#include "cipherutils.h" -#include -#include -#include -#include -#ifndef ON_DEVICE -#include "fileutils.h" -#endif - - -/** -* 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; - } - 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 doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) -{ - uint8_t cc_nr[13] = { 0 }; - uint8_t div_key[8]; - //cc_nr=(uint8_t*)malloc(length+1); - - memcpy(cc_nr,cc_nr_p,12); - memcpy(div_key,div_key_p,8); - - reverse_arraybytes(cc_nr,12); - BitstreamIn bitstream = {cc_nr,12 * 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)); - memcpy(mac, dest, 4); - //free(cc_nr); - return; -} -#ifndef ON_DEVICE -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}; - //From the paper - uint8_t div_key[8] = {0xE0,0x33,0xCA,0x41,0x9A,0xEE,0x43,0xF9}; - uint8_t correct_MAC[4] = {0x1d,0x49,0xC9,0xDA}; - - uint8_t calculated_mac[4] = {0}; - doMAC(cc_nr,div_key, calculated_mac); - - if(memcmp(calculated_mac, correct_MAC,4) == 0) - { - prnlog("[+] MAC calculation OK!"); - - }else - { - prnlog("[+] FAILED: MAC calculation failed:"); - printarr(" Calculated_MAC", calculated_mac, 4); - printarr(" Correct_MAC ", correct_MAC, 4); - return 1; - } - - return 0; -} -#endif diff --git a/armsrc/cipher.h b/armsrc/cipher.h deleted file mode 100644 index bdea94322..000000000 --- a/armsrc/cipher.h +++ /dev/null @@ -1,49 +0,0 @@ -/***************************************************************************** - * WARNING - * - * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. - * - * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL - * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, - * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. - * - * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. - * - ***************************************************************************** - * - * This file is part of loclass. It is a reconstructon of the cipher engine - * used in iClass, and RFID techology. - * - * The implementation is based on the work performed by - * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and - * Milosch Meriac in the paper "Dismantling IClass". - * - * Copyright (C) 2014 Martin Holst Swende - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * 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 CIPHER_H -#define CIPHER_H -#include - -void doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); -#ifndef ON_DEVICE -int testMAC(); -#endif - -#endif // CIPHER_H diff --git a/armsrc/cipherutils.c b/armsrc/cipherutils.c deleted file mode 100644 index c00e2be2d..000000000 --- a/armsrc/cipherutils.c +++ /dev/null @@ -1,292 +0,0 @@ -/***************************************************************************** - * WARNING - * - * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. - * - * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL - * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, - * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. - * - * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. - * - ***************************************************************************** - * - * This file is part of loclass. It is a reconstructon of the cipher engine - * used in iClass, and RFID techology. - * - * The implementation is based on the work performed by - * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and - * Milosch Meriac in the paper "Dismantling IClass". - * - * Copyright (C) 2014 Martin Holst Swende - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * 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 -#include -#include -#include "cipherutils.h" -#ifndef ON_DEVICE -#include "fileutils.h" -#endif -/** - * - * @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]); - } -} -#ifndef ON_DEVICE -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); - free(output); -} -#endif -//----------------------------- -// Code for testing below -//----------------------------- - -#ifndef ON_DEVICE -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) - { - prnlog(" Bitstream test 1 ok"); - }else - { - prnlog(" Bitstream test 1 failed"); - uint8_t i; - for(i = 0 ; i < sizeof(input) ; 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) - { - prnlog(" Bitstream test 2 ok"); - }else - { - prnlog(" Bitstream test 2 failed"); - uint8_t i; - for(i = 0 ; i < sizeof(input) ; 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; -} -#endif diff --git a/armsrc/cipherutils.h b/armsrc/cipherutils.h deleted file mode 100644 index ee4939e3e..000000000 --- a/armsrc/cipherutils.h +++ /dev/null @@ -1,76 +0,0 @@ -/***************************************************************************** - * WARNING - * - * THIS CODE IS CREATED FOR EXPERIMENTATION AND EDUCATIONAL USE ONLY. - * - * USAGE OF THIS CODE IN OTHER WAYS MAY INFRINGE UPON THE INTELLECTUAL - * PROPERTY OF OTHER PARTIES, SUCH AS INSIDE SECURE AND HID GLOBAL, - * AND MAY EXPOSE YOU TO AN INFRINGEMENT ACTION FROM THOSE PARTIES. - * - * THIS CODE SHOULD NEVER BE USED TO INFRINGE PATENTS OR INTELLECTUAL PROPERTY RIGHTS. - * - ***************************************************************************** - * - * This file is part of loclass. It is a reconstructon of the cipher engine - * used in iClass, and RFID techology. - * - * The implementation is based on the work performed by - * Flavio D. Garcia, Gerhard de Koning Gans, Roel Verdult and - * Milosch Meriac in the paper "Dismantling IClass". - * - * Copyright (C) 2014 Martin Holst Swende - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * 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); -#ifndef ON_DEVICE -int testCipherUtils(void); -int testMAC(); -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 -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); -#endif // CIPHERUTILS_H diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 81ecd01bf..ca6d9a7ec 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -47,8 +47,9 @@ // different initial value (CRC_ICLASS) #include "iso14443crc.h" #include "iso15693tools.h" -#include "cipher.h" #include "protocols.h" +#include "optimized_cipher.h" + static int timeout = 4096; @@ -1213,13 +1214,14 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) //Put nr there memcpy(ccnr+8, receivedCmd+1,4); //Now, calc MAC - doMAC(ccnr,diversified_key, data_generic_trace); + opt_doMAC(ccnr,diversified_key, data_generic_trace); trace_data = data_generic_trace; trace_data_size = 4; CodeIClassTagAnswer(trace_data , trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; + //exitLoop = true; }else { //Not fullsim, we don't respond // We do not know what to answer, so lets keep quiet diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 7af065ea1..6094bd348 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -12,7 +12,7 @@ #include "string.h" #include "lfsampling.h" -#include "cipherutils.h" + sample_config config = { 1, 8, 1, 95, 0 } ; void printConfig() @@ -55,20 +55,19 @@ sample_config* getSamplingConfig() { return &config; } -/* + typedef struct { uint8_t * buffer; uint32_t numbits; uint32_t position; } BitstreamOut; -*/ /** * @brief Pushes bit onto the stream * @param stream * @param bit */ -/*void pushBit( BitstreamOut* stream, uint8_t bit) +void pushBit( BitstreamOut* stream, uint8_t bit) { int bytepos = stream->position >> 3; // divide by 8 int bitpos = stream->position & 7; @@ -76,7 +75,7 @@ typedef struct { stream->position++; stream->numbits++; } -*/ + /** * Setup the FPGA to listen for samples. This method downloads the FPGA bitstream * if not already loaded, sets divisor and starts up the antenna. From c99dc845b1057be4ba2e535730345b4e6e8bfc58 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Fri, 27 Feb 2015 20:19:10 +0100 Subject: [PATCH 3/9] Added the ciphers also.. doh --- armsrc/optimized_cipher.c | 239 ++++++++++++++++++++++++++++++++++++++ armsrc/optimized_cipher.h | 5 + 2 files changed, 244 insertions(+) create mode 100644 armsrc/optimized_cipher.c create mode 100644 armsrc/optimized_cipher.h diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c new file mode 100644 index 000000000..067e90226 --- /dev/null +++ b/armsrc/optimized_cipher.c @@ -0,0 +1,239 @@ +/***************************************************************************** + * 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. + * + * 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 . + * + * + * + ****************************************************************************/ + +/** + + This file contains an optimized version of the MAC-calculation algorithm. Some measurements on + a std laptop showed it runs in about 1/3 of the time: + + Std: 0.428962 + Opt: 0.151609 + + Additionally, it is self-reliant, not requiring e.g. bitstreams from the cipherutils, thus can + be easily dropped into a code base. + + The optimizations have been performed in the following steps: + * Parameters passed by reference instead of by value. + * Iteration instead of recursion, un-nesting recursive loops into for-loops. + * Handling of bytes instead of individual bits, for less shuffling and masking + * Less creation of "objects", structs, and instead reuse of alloc:ed memory + * Inlining some functions via #define:s + + As a consequence, this implementation is less generic. Also, I haven't bothered documenting this. + For a thorough documentation, check out the MAC-calculation within cipher.c instead. + + -- MHS 2015 +**/ + +#include "optimized_cipher.h" +#include +#include +#include +#include +#include +#include + +/** +* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 +* consisting of the following four components: +* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; +* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; +* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . +* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . +**/ +typedef struct { + uint8_t l; + uint8_t r; + uint8_t b; + uint16_t t; +} State; + + +#define opt_T(s) (0x1 & ((s->t >> 15) ^ (s->t >> 14)^ (s->t >> 10)^ (s->t >> 8)^ (s->t >> 5)^ (s->t >> 4)^ (s->t >> 1)^ s->t)) + +#define opt_B(s) (((s->b >> 6) ^ (s->b >> 5) ^ (s->b >> 4) ^ (s->b)) & 0x1) + +#define opt__select(x,y,r) (4 & (((r & (r << 2)) >> 5) ^ ((r & ~(r << 2)) >> 4) ^ ( (r | r << 2) >> 3)))\ + |(2 & (((r | r << 2) >> 6) ^ ( (r | r << 2) >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1)))\ + |(1 & (((r & ~(r << 2)) >> 4) ^ ((r & (r << 2)) >> 3) ^ r ^ x)) + +/* + * Some background on the expression above can be found here... +uint8_t xopt__select(bool x, bool y, uint8_t r) +{ + uint8_t r_ls2 = r << 2; + uint8_t r_and_ls2 = r & r_ls2; + uint8_t r_or_ls2 = r | r_ls2; + + //r: r0 r1 r2 r3 r4 r5 r6 r7 + //r_ls2: r2 r3 r4 r5 r6 r7 0 0 + // z0 + // z1 + +// uint8_t z0 = (r0 & r2) ^ (r1 & ~r3) ^ (r2 | r4); // <-- original + uint8_t z0 = (r_and_ls2 >> 5) ^ ((r & ~r_ls2) >> 4) ^ ( r_or_ls2 >> 3); + +// uint8_t z1 = (r0 | r2) ^ ( r5 | r7) ^ r1 ^ r6 ^ x ^ y; // <-- original + uint8_t z1 = (r_or_ls2 >> 6) ^ ( r_or_ls2 >> 1) ^ (r >> 5) ^ r ^ ((x^y) << 1); + +// uint8_t z2 = (r3 & ~r5) ^ (r4 & r6 ) ^ r7 ^ x; // <-- original + uint8_t z2 = ((r & ~r_ls2) >> 4) ^ (r_and_ls2 >> 3) ^ r ^ x; + + return (z0 & 4) | (z1 & 2) | (z2 & 1); +} +*/ + +void opt_successor(uint8_t* k, State *s, bool y, State* successor) +{ + + uint8_t Tt = 1 & opt_T(s); + + successor->t = (s->t >> 1); + successor->t |= (Tt ^ (s->r >> 7 & 0x1) ^ (s->r >> 3 & 0x1)) << 15; + + successor->b = s->b >> 1; + successor->b |= (opt_B(s) ^ (s->r & 0x1)) << 7; + + successor->r = (k[opt__select(Tt,y,s->r)] ^ successor->b) + s->l ; + successor->l = successor->r+s->r; + +} + +void opt_suc(uint8_t* k,State* s, uint8_t *in) +{ + State x2; + int i; + uint8_t head = 0; + for(i =0 ; i < 12 ; i++) + { + head = 1 & (in[i] >> 7); + opt_successor(k,s,head,&x2); + + head = 1 & (in[i] >> 6); + opt_successor(k,&x2,head,s); + + head = 1 & (in[i] >> 5); + opt_successor(k,s,head,&x2); + + head = 1 & (in[i] >> 4); + opt_successor(k,&x2,head,s); + + head = 1 & (in[i] >> 3); + opt_successor(k,s,head,&x2); + + head = 1 & (in[i] >> 2); + opt_successor(k,&x2,head,s); + + head = 1 & (in[i] >> 1); + opt_successor(k,s,head,&x2); + + head = 1 & in[i]; + opt_successor(k,&x2,head,s); + + } + +} + +void opt_output(uint8_t* k,State* s, uint8_t *buffer) +{ + uint8_t times = 0; + uint8_t bout = 0; + State temp = {0,0,0,0}; + for( ; times < 4 ; times++) + { + bout =0; + bout |= (s->r & 0x4) << 5; + opt_successor(k,s,0,&temp); + bout |= (temp.r & 0x4) << 4; + opt_successor(k,&temp,0,s); + bout |= (s->r & 0x4) << 3; + opt_successor(k,s,0,&temp); + bout |= (temp.r & 0x4) << 2; + opt_successor(k,&temp,0,s); + bout |= (s->r & 0x4) << 1; + opt_successor(k,s,0,&temp); + bout |= (temp.r & 0x4) ; + opt_successor(k,&temp,0,s); + bout |= (s->r & 0x4) >> 1; + opt_successor(k,s,0,&temp); + bout |= (temp.r & 0x4) >> 2; + opt_successor(k,&temp,0,s); + buffer[times] = bout; + } + +} + +void opt_MAC(uint8_t* k, uint8_t* input, uint8_t* out) +{ + State _init = { + ((k[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((k[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + + opt_suc(k,&_init,input); + //printf("\noutp "); + opt_output(k,&_init, out); +} +uint8_t rev_byte(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 opt_reverse_arraybytecpy(uint8_t* dest, uint8_t *src, size_t len) +{ + uint8_t i; + for( i =0; i< len ; i++) + dest[i] = rev_byte(src[i]); +} + +void opt_doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) +{ + static uint8_t cc_nr[13]; + static uint8_t div_key[8]; + + opt_reverse_arraybytecpy(cc_nr, cc_nr_p,12); + memcpy(div_key,div_key_p,8); + uint8_t dest []= {0,0,0,0,0,0,0,0}; + opt_MAC(div_key,cc_nr, dest); + //The output MAC must also be reversed + opt_reverse_arraybytecpy(mac, dest,12); + return; +} diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h new file mode 100644 index 000000000..0f4f0ec20 --- /dev/null +++ b/armsrc/optimized_cipher.h @@ -0,0 +1,5 @@ +#ifndef OPTIMIZED_CIPHER_H +#define OPTIMIZED_CIPHER_H +#include +void opt_doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); +#endif // OPTIMIZED_CIPHER_H From 61fe90736be4400f09fb5f56e2de48d11a0ae0a7 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 2 Mar 2015 00:38:36 +0100 Subject: [PATCH 4/9] Implemented the correct way to calculate MAC from a tag, feeding it an extra 32 zeroes. Also divided it up into two parts, one of which can be precalculated by the device before the simulation begins --- armsrc/iclass.c | 25 ++++----- armsrc/optimized_cipher.c | 103 ++++++++++++++++++++++++++++---------- armsrc/optimized_cipher.h | 45 ++++++++++++++++- 3 files changed, 131 insertions(+), 42 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index ca6d9a7ec..f289d24e5 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1053,6 +1053,8 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) // free eventually allocated BigBuf memory BigBuf_free_keep_EM(); + State cipher_state; +// State cipher_state_reserve; uint8_t *csn = BigBuf_get_EM_addr(); uint8_t *emulator = csn; uint8_t sof_data[] = { 0x0F} ; @@ -1069,12 +1071,18 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) ComputeCrc14443(CRC_ICLASS, anticoll_data, 8, &anticoll_data[8], &anticoll_data[9]); ComputeCrc14443(CRC_ICLASS, csn_data, 8, &csn_data[8], &csn_data[9]); + //The diversified key should be stored on block 3 + uint8_t diversified_key[8] = { 0 }; + //Get the diversified key from emulator memory + memcpy(diversified_key, emulator+(8*3),8); // e-Purse uint8_t card_challenge_data[8] = { 0x00 }; if(simulationMode == MODE_FULLSIM) { //Card challenge, a.k.a e-purse is on block 2 memcpy(card_challenge_data,emulator + (8 * 2) , 8); + //Precalculate the cipher state, feeding it the CC + opt_doTagMAC_1(card_challenge_data,diversified_key); } int exitLoop = 0; @@ -1200,21 +1208,10 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) } else if(receivedCmd[0] == ICLASS_CMD_CHECK) { // Reader random and reader MAC!!! if(simulationMode == MODE_FULLSIM) - { //This is what we must do.. - //Reader just sent us NR and MAC(k,cc * nr) - //The diversified key should be stored on block 3 - //However, from a typical dump, the key will not be there - uint8_t diversified_key[8] = { 0 }; + { + //NR, from reader, is in receivedCmd +1 + opt_doTagMAC_2(cipher_state,receivedCmd+1,data_generic_trace,diversified_key); - //Get the diversified key from emulator memory - memcpy(diversified_key, emulator+(8*3),8); - uint8_t ccnr[12] = { 0 }; - //Put our cc there (block 2) - memcpy(ccnr, emulator + (8 * 2), 8); - //Put nr there - memcpy(ccnr+8, receivedCmd+1,4); - //Now, calc MAC - opt_doMAC(ccnr,diversified_key, data_generic_trace); trace_data = data_generic_trace; trace_data_size = 4; CodeIClassTagAnswer(trace_data , trace_data_size); diff --git a/armsrc/optimized_cipher.c b/armsrc/optimized_cipher.c index 067e90226..444b93d09 100644 --- a/armsrc/optimized_cipher.c +++ b/armsrc/optimized_cipher.c @@ -68,21 +68,6 @@ #include #include -/** -* Definition 1 (Cipher state). A cipher state of iClass s is an element of F 40/2 -* consisting of the following four components: -* 1. the left register l = (l 0 . . . l 7 ) ∈ F 8/2 ; -* 2. the right register r = (r 0 . . . r 7 ) ∈ F 8/2 ; -* 3. the top register t = (t 0 . . . t 15 ) ∈ F 16/2 . -* 4. the bottom register b = (b 0 . . . b 7 ) ∈ F 8/2 . -**/ -typedef struct { - uint8_t l; - uint8_t r; - uint8_t b; - uint16_t t; -} State; - #define opt_T(s) (0x1 & ((s->t >> 15) ^ (s->t >> 14)^ (s->t >> 10)^ (s->t >> 8)^ (s->t >> 5)^ (s->t >> 4)^ (s->t >> 1)^ s->t)) @@ -118,7 +103,7 @@ uint8_t xopt__select(bool x, bool y, uint8_t r) } */ -void opt_successor(uint8_t* k, State *s, bool y, State* successor) +void opt_successor(const uint8_t* k, State *s, bool y, State* successor) { uint8_t Tt = 1 & opt_T(s); @@ -134,12 +119,12 @@ void opt_successor(uint8_t* k, State *s, bool y, State* successor) } -void opt_suc(uint8_t* k,State* s, uint8_t *in) +void opt_suc(const uint8_t* k,State* s, uint8_t *in, uint8_t length, bool add32Zeroes) { State x2; int i; uint8_t head = 0; - for(i =0 ; i < 12 ; i++) + for(i =0 ; i < length ; i++) { head = 1 & (in[i] >> 7); opt_successor(k,s,head,&x2); @@ -166,10 +151,16 @@ void opt_suc(uint8_t* k,State* s, uint8_t *in) opt_successor(k,&x2,head,s); } - + //For tag MAC, an additional 32 zeroes + if(add32Zeroes) + for(i =0 ; i < 16 ; i++) + { + opt_successor(k,s,0,&x2); + opt_successor(k,&x2,0,s); + } } -void opt_output(uint8_t* k,State* s, uint8_t *buffer) +void opt_output(const uint8_t* k,State* s, uint8_t *buffer) { uint8_t times = 0; uint8_t bout = 0; @@ -207,7 +198,7 @@ void opt_MAC(uint8_t* k, uint8_t* input, uint8_t* out) 0xE012 // t }; - opt_suc(k,&_init,input); + opt_suc(k,&_init,input,12, false); //printf("\noutp "); opt_output(k,&_init, out); } @@ -224,16 +215,74 @@ void opt_reverse_arraybytecpy(uint8_t* dest, uint8_t *src, size_t len) dest[i] = rev_byte(src[i]); } -void opt_doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) +void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]) { - static uint8_t cc_nr[13]; - static uint8_t div_key[8]; + static uint8_t cc_nr[12]; opt_reverse_arraybytecpy(cc_nr, cc_nr_p,12); - memcpy(div_key,div_key_p,8); uint8_t dest []= {0,0,0,0,0,0,0,0}; - opt_MAC(div_key,cc_nr, dest); + opt_MAC(div_key_p,cc_nr, dest); //The output MAC must also be reversed - opt_reverse_arraybytecpy(mac, dest,12); + opt_reverse_arraybytecpy(mac, dest,4); + return; +} +void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]) +{ + static uint8_t cc_nr[8+4+4]; + opt_reverse_arraybytecpy(cc_nr, cc_p,12); + State _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + opt_suc(div_key_p,&_init,cc_nr, 12,true); + uint8_t dest []= {0,0,0,0}; + opt_output(div_key_p,&_init, dest); + //The output MAC must also be reversed + opt_reverse_arraybytecpy(mac, dest,4); + return; + +} +/** + * The tag MAC can be divided (both can, but no point in dividing the reader mac) into + * two functions, since the first 8 bytes are known, we can pre-calculate the state + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p) +{ + static uint8_t cc_nr[8]; + opt_reverse_arraybytecpy(cc_nr, cc_p,8); + State _init = { + ((div_key_p[0] ^ 0x4c) + 0xEC) & 0xFF,// l + ((div_key_p[0] ^ 0x4c) + 0x21) & 0xFF,// r + 0x4c, // b + 0xE012 // t + }; + opt_suc(div_key_p,&_init,cc_nr, 8,false); + return _init; +} +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @param div_key_p - the key to use + */ +void opt_doTagMAC_2(State _init, uint8_t* nr, uint8_t mac[4], const uint8_t* div_key_p) +{ + static uint8_t _nr [4]; + opt_reverse_arraybytecpy(_nr, nr, 4); + opt_suc(div_key_p,&_init,_nr, 4, true); + //opt_suc(div_key_p,&_init,nr, 4, false); + uint8_t dest []= {0,0,0,0}; + opt_output(div_key_p,&_init, dest); + //The output MAC must also be reversed + opt_reverse_arraybytecpy(mac, dest,4); return; } diff --git a/armsrc/optimized_cipher.h b/armsrc/optimized_cipher.h index 0f4f0ec20..c10aea28b 100644 --- a/armsrc/optimized_cipher.h +++ b/armsrc/optimized_cipher.h @@ -1,5 +1,48 @@ #ifndef OPTIMIZED_CIPHER_H #define OPTIMIZED_CIPHER_H #include -void opt_doMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); + +/** +* 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; + +/** The reader MAC is MAC(key, CC * NR ) + **/ +void opt_doReaderMAC(uint8_t *cc_nr_p, uint8_t *div_key_p, uint8_t mac[4]); +/** + * The tag MAC is MAC(key, CC * NR * 32x0)) + */ +void opt_doTagMAC(uint8_t *cc_p, const uint8_t *div_key_p, uint8_t mac[4]); + +/** + * The tag MAC can be divided (both can, but no point in dividing the reader mac) into + * two functions, since the first 8 bytes are known, we can pre-calculate the state + * reached after feeding CC to the cipher. + * @param cc_p + * @param div_key_p + * @return the cipher state + */ +State opt_doTagMAC_1(uint8_t *cc_p, const uint8_t *div_key_p); +/** + * The second part of the tag MAC calculation, since the CC is already calculated into the state, + * this function is fed only the NR, and internally feeds the remaining 32 0-bits to generate the tag + * MAC response. + * @param _init - precalculated cipher state + * @param nr - the reader challenge + * @param mac - where to store the MAC + * @param div_key_p - the key to use + */ +void opt_doTagMAC_2(State _init, uint8_t* nr, uint8_t mac[4], const uint8_t* div_key_p); + #endif // OPTIMIZED_CIPHER_H From e5cd4ee4b4265bebaaece7091fb5784e7c5a92e7 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 2 Mar 2015 20:14:35 +0100 Subject: [PATCH 5/9] Tag MAC finally works! (Full sim not yet, though) --- armsrc/iclass.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index f289d24e5..4d8479097 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1071,18 +1071,20 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) ComputeCrc14443(CRC_ICLASS, anticoll_data, 8, &anticoll_data[8], &anticoll_data[9]); ComputeCrc14443(CRC_ICLASS, csn_data, 8, &csn_data[8], &csn_data[9]); - //The diversified key should be stored on block 3 uint8_t diversified_key[8] = { 0 }; - //Get the diversified key from emulator memory - memcpy(diversified_key, emulator+(8*3),8); // e-Purse uint8_t card_challenge_data[8] = { 0x00 }; if(simulationMode == MODE_FULLSIM) { + //The diversified key should be stored on block 3 + //Get the diversified key from emulator memory + memcpy(diversified_key, emulator+(8*3),8); + //Card challenge, a.k.a e-purse is on block 2 memcpy(card_challenge_data,emulator + (8 * 2) , 8); //Precalculate the cipher state, feeding it the CC - opt_doTagMAC_1(card_challenge_data,diversified_key); + cipher_state = opt_doTagMAC_1(card_challenge_data,diversified_key); + } int exitLoop = 0; @@ -1164,9 +1166,9 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) LED_A_ON(); bool buttonPressed = false; - + uint8_t response_delay = 1; while(!exitLoop) { - + response_delay = 1; LED_B_OFF(); //Signal tracer // Can be used to get a trigger for an oscilloscope.. @@ -1218,6 +1220,7 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; modulated_response_size = ToSendMax; + response_delay = 0;//We need to hurry here... //exitLoop = true; }else { //Not fullsim, we don't respond @@ -1288,7 +1291,7 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) A legit tag has about 380us delay between reader EOT and tag SOF. **/ if(modulated_response_size > 0) { - SendIClassAnswer(modulated_response, modulated_response_size, 1); + SendIClassAnswer(modulated_response, modulated_response_size, response_delay); t2r_time = GetCountSspClk(); } From 27eabcdccb0179e8ab003e496a46fb1621f08701 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 2 Mar 2015 21:05:44 +0100 Subject: [PATCH 6/9] Added brackets around crc field in protocol listings --- client/cmdhf.c | 67 ++++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/client/cmdhf.c b/client/cmdhf.c index d279c9e64..074a37e28 100644 --- a/client/cmdhf.c +++ b/client/cmdhf.c @@ -288,35 +288,7 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui uint8_t *parityBytes = trace + tracepos; tracepos += parity_len; - - //--- Draw the data column - //char line[16][110]; - char line[16][110]; - - for (int j = 0; j < data_len && j/16 < 16; j++) { - - int oddparity = 0x01; - int k; - - for (k=0 ; k<8 ; k++) { - oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); - } - - uint8_t parityBits = parityBytes[j>>3]; - if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { - snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); - - } else { - snprintf(line[j/16]+(( j % 16) * 4),110, "%02x ", frame[j]); - } - } - if(data_len == 0) - { - if(data_len == 0){ - sprintf(line[0],""); - } - } - //--- Draw the CRC column + //Check the CRC status uint8_t crcStatus = 2; if (data_len > 2) { @@ -344,6 +316,43 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui //0 CRC-command, CRC not ok //1 CRC-command, CRC ok //2 Not crc-command + + //--- Draw the data column + //char line[16][110]; + char line[16][110]; + + for (int j = 0; j < data_len && j/16 < 16; j++) { + + int oddparity = 0x01; + int k; + + for (k=0 ; k<8 ; k++) { + oddparity ^= (((frame[j] & 0xFF) >> k) & 0x01); + } + uint8_t parityBits = parityBytes[j>>3]; + if (isResponse && (oddparity != ((parityBits >> (7-(j&0x0007))) & 0x01))) { + snprintf(line[j/16]+(( j % 16) * 4),110, "%02x! ", frame[j]); + + } else { + snprintf(line[j/16]+(( j % 16) * 4),110, "%02x ", frame[j]); + } + + } + if(crcStatus == 1) + {//CRC-command + char *pos1 = line[(data_len-2)/16]+(((data_len-2) % 16) * 4)-1; + (*pos1) = '['; + char *pos2 = line[(data_len)/16]+(((data_len) % 16) * 4)-2; + (*pos2) = ']'; + } + if(data_len == 0) + { + if(data_len == 0){ + sprintf(line[0],""); + } + } + //--- Draw the CRC column + char *crc = (crcStatus == 0 ? "!crc" : (crcStatus == 1 ? " ok " : " ")); EndOfTransmissionTimestamp = timestamp + duration; From c8387e85e39182527d30317efa127642ddfe5d79 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Mon, 2 Mar 2015 21:30:22 +0100 Subject: [PATCH 7/9] iClass full simulation of tags now officially works, with some cheating regarding reader update and write commands --- armsrc/iclass.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 4d8479097..260e6a603 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -1042,6 +1042,10 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain Dbprintf("Done..."); } +void AppendCrc(uint8_t* data, int len) +{ + ComputeCrc14443(CRC_ICLASS,data,len,data+len,data+len+1); +} /** * @brief Does the actual simulation @@ -1144,9 +1148,11 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) memcpy(resp_cc, ToSend, ToSendMax); resp_cc_len = ToSendMax; //This is used for responding to READ-block commands or other data which is dynamically generated - uint8_t *data_response = BigBuf_malloc(8 * 2 + 2); - //This is used for responding to READ-block commands or other data which is dynamically generated - uint8_t *data_generic_trace = BigBuf_malloc(8 * 2 + 2); + //First the 'trace'-data, not encoded for FPGA + uint8_t *data_generic_trace = BigBuf_malloc(8 + 2);//8 bytes data + 2byte CRC is max tag answer + //Then storage for the modulated data + //Each bit is doubled when modulated for FPGA, and we also have SOF and EOF (2 bytes) + uint8_t *data_response = BigBuf_malloc( (8+2) * 2 + 2); // Start from off (no field generated) //FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); @@ -1252,8 +1258,28 @@ int doIClassSimulation( int simulationMode, uint8_t *reader_mac_buf) } else if(simulationMode == MODE_FULLSIM && receivedCmd[0] == ICLASS_CMD_READ_OR_IDENTIFY && len == 4){ //Read block uint16_t blk = receivedCmd[1]; - trace_data = emulator+(blk << 3); - trace_data_size = 8; + //Take the data... + memcpy(data_generic_trace, emulator+(blk << 3),8); + //Add crc + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; + CodeIClassTagAnswer(trace_data , trace_data_size); + memcpy(data_response, ToSend, ToSendMax); + modulated_response = data_response; + modulated_response_size = ToSendMax; + }else if(receivedCmd[0] == ICLASS_CMD_UPDATE && simulationMode == MODE_FULLSIM) + {//Probably the reader wants to update the nonce. Let's just ignore that for now. + // OBS! If this is implemented, don't forget to regenerate the cipher_state + //We're expected to respond with the data+crc, exactly what's already in the receivedcmd + //receivedcmd is now UPDATE 1b | ADDRESS 1b| DATA 8b| Signature 4b or CRC 2b| + + //Take the data... + memcpy(data_generic_trace, receivedCmd+2,8); + //Add crc + AppendCrc(data_generic_trace, 8); + trace_data = data_generic_trace; + trace_data_size = 10; CodeIClassTagAnswer(trace_data , trace_data_size); memcpy(data_response, ToSend, ToSendMax); modulated_response = data_response; From 9cefee6f42196a117cee2ad917ec4bedd88fdab3 Mon Sep 17 00:00:00 2001 From: ikarus Date: Wed, 4 Mar 2015 11:00:39 +0100 Subject: [PATCH 8/9] Fixed issue #36 & improved file consistency. --- armsrc/crapto1.c | 64 ++++++++++++++++------------ armsrc/crypto1.c | 35 +++++++++------- client/nonce2key/crapto1.c | 85 ++++++++++++++++++++------------------ client/nonce2key/crypto1.c | 31 +++++++------- 4 files changed, 117 insertions(+), 98 deletions(-) diff --git a/armsrc/crapto1.c b/armsrc/crapto1.c index 9d491d127..bcd311711 100644 --- a/armsrc/crapto1.c +++ b/armsrc/crapto1.c @@ -1,21 +1,21 @@ /* crapto1.c - 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 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. + 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 Street, Fifth Floor, - Boston, MA 02110-1301, US$ + 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 Street, Fifth Floor, + Boston, MA 02110-1301, US$ - Copyright (C) 2008-2008 bla + Copyright (C) 2008-2008 bla */ #include "crapto1.h" #include @@ -24,9 +24,9 @@ static uint8_t filterlut[1 << 20]; static void __attribute__((constructor)) fill_lut() { - uint32_t i; - for(i = 0; i < 1 << 20; ++i) - filterlut[i] = filter(i); + uint32_t i; + for(i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); } #define filter(x) (filterlut[(x) & 0xfffff]) #endif @@ -34,6 +34,7 @@ static void __attribute__((constructor)) fill_lut() static void quicksort(uint32_t* const start, uint32_t* const stop) { uint32_t *it = start + 1, *rit = stop; + uint32_t tmp; if(it > rit) return; @@ -43,13 +44,19 @@ static void quicksort(uint32_t* const start, uint32_t* const stop) ++it; else if(*rit > *start) --rit; - else - *it ^= (*it ^= *rit, *rit ^= *it); + else { + tmp = *it; + *it = *rit; + *rit = tmp; + } if(*rit >= *start) --rit; - if(rit != start) - *rit ^= (*rit ^= *start, *start ^= *rit); + if(rit != start) { + tmp = *rit; + *rit = *start; + *start = tmp; + } quicksort(start, rit - 1); quicksort(rit + 1, stop); @@ -145,12 +152,12 @@ recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, eks >>= 1; in >>= 2; extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, - LF_POLY_ODD << 1, 0); + LF_POLY_ODD << 1, 0); if(o_head > o_tail) return sl; extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, - LF_POLY_EVEN << 1 | 1, in & 3); + LF_POLY_EVEN << 1 | 1, in & 3); if(e_head > e_tail) return sl; } @@ -163,7 +170,7 @@ recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, o_tail = binsearch(o_head, o = o_tail); e_tail = binsearch(e_head, e = e_tail); sl = recover(o_tail--, o, oks, - e_tail--, e, eks, rem, sl, in); + e_tail--, e, eks, rem, sl, in); } else if(*o_tail > *e_tail) o_tail = binsearch(o_head, o_tail) - 1; @@ -319,9 +326,12 @@ uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) { int out; uint8_t ret; + uint32_t tmp; s->odd &= 0xffffff; - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + tmp = s->odd; + s->odd = s->even; + s->even = tmp; out = s->even & 1; out ^= LF_POLY_EVEN & (s->even >>= 1); @@ -414,7 +424,7 @@ uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) */ static struct Crypto1State* check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], - uint32_t odd, uint32_t even, struct Crypto1State* sl) + uint32_t odd, uint32_t even, struct Crypto1State* sl) { uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; @@ -440,7 +450,7 @@ check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], } return sl + good; -} +} /** lfsr_common_prefix @@ -459,7 +469,7 @@ lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) if(!s || !odd || !even) { free(statelist); statelist = 0; - goto out; + goto out; } for(o = odd; *o + 1; ++o) diff --git a/armsrc/crypto1.c b/armsrc/crypto1.c index 9d103c7f5..747653482 100644 --- a/armsrc/crypto1.c +++ b/armsrc/crypto1.c @@ -1,21 +1,21 @@ /* crypto1.c - 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 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. + 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 Street, Fifth Floor, Boston, - MA 02110-1301, US + 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 Street, Fifth Floor, Boston, + MA 02110-1301, US - Copyright (C) 2008-2008 bla + Copyright (C) 2008-2008 bla */ #include "crapto1.h" #include @@ -37,8 +37,8 @@ void crypto1_create(struct Crypto1State *s, uint64_t key) void crypto1_destroy(struct Crypto1State *state) { // free(state); - state->odd = 0; - state->even = 0; + state->odd = 0; + state->even = 0; } void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) { @@ -51,6 +51,7 @@ void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) { uint32_t feedin; + uint32_t tmp; uint8_t ret = filter(s->odd); feedin = ret & !!is_encrypted; @@ -59,7 +60,9 @@ uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) feedin ^= LF_POLY_EVEN & s->even; s->even = s->even << 1 | parity(feedin); - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + tmp = s->odd; + s->odd = s->even; + s->even = tmp; return ret; } diff --git a/client/nonce2key/crapto1.c b/client/nonce2key/crapto1.c index 6c0fcafa0..1015e27a7 100644 --- a/client/nonce2key/crapto1.c +++ b/client/nonce2key/crapto1.c @@ -1,21 +1,21 @@ /* crapto1.c - 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 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. + 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 Street, Fifth Floor, - Boston, MA 02110-1301, US$ + 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 Street, Fifth Floor, + Boston, MA 02110-1301, US$ - Copyright (C) 2008-2008 bla + Copyright (C) 2008-2008 bla */ #include "crapto1.h" #include @@ -24,9 +24,9 @@ static uint8_t filterlut[1 << 20]; static void __attribute__((constructor)) fill_lut() { - uint32_t i; - for(i = 0; i < 1 << 20; ++i) - filterlut[i] = filter(i); + uint32_t i; + for(i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); } #define filter(x) (filterlut[(x) & 0xfffff]) #endif @@ -46,7 +46,7 @@ typedef struct bucket_info { } bucket_info[2][0x100]; uint32_t numbuckets; } bucket_info_t; - + static void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, uint32_t* const ostart, uint32_t* const ostop, @@ -55,28 +55,28 @@ static void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, uint32_t *p1, *p2; uint32_t *start[2]; uint32_t *stop[2]; - + start[0] = estart; stop[0] = estop; start[1] = ostart; stop[1] = ostop; - + // init buckets to be empty for (uint32_t i = 0; i < 2; i++) { for (uint32_t j = 0x00; j <= 0xff; j++) { bucket[i][j].bp = bucket[i][j].head; } } - + // sort the lists into the buckets based on the MSB (contribution bits) - for (uint32_t i = 0; i < 2; i++) { + for (uint32_t i = 0; i < 2; i++) { for (p1 = start[i]; p1 <= stop[i]; p1++) { uint32_t bucket_index = (*p1 & 0xff000000) >> 24; *(bucket[i][bucket_index].bp++) = *p1; } } - + // write back intersecting buckets as sorted list. // fill in bucket_info with head and tail of the bucket contents in the list and number of non-empty buckets. uint32_t nonempty_bucket; @@ -147,9 +147,9 @@ extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in *p ^= in; } else { // drop *p-- = *(*end)--; - } + } } - + } @@ -159,7 +159,7 @@ extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) { - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) if(filter(*tbl) ^ filter(*tbl | 1)) { // replace *tbl |= filter(*tbl) ^ bit; } else if(filter(*tbl) == bit) { // insert @@ -206,13 +206,13 @@ recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, } bucket_sort_intersect(e_head, e_tail, o_head, o_tail, &bucket_info, bucket); - + for (int i = bucket_info.numbuckets - 1; i >= 0; i--) { sl = recover(bucket_info.bucket_info[1][i].head, bucket_info.bucket_info[1][i].tail, oks, - bucket_info.bucket_info[0][i].head, bucket_info.bucket_info[0][i].tail, eks, + bucket_info.bucket_info[0][i].head, bucket_info.bucket_info[0][i].tail, eks, rem, sl, in, bucket); } - + return sl; } /** lfsr_recovery @@ -251,7 +251,7 @@ struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) } } - + // initialize statelists: add all possible states which would result into the rightmost 2 bits of the keystream for(i = 1 << 20; i >= 0; --i) { if(filter(i) == (oks & 1)) @@ -282,7 +282,7 @@ out: for (uint32_t i = 0; i < 2; i++) for (uint32_t j = 0; j <= 0xff; j++) free(bucket[i][j].head); - + return statelist; } @@ -382,9 +382,12 @@ struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) void lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) { int out; + uint32_t tmp; s->odd &= 0xffffff; - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + tmp = s->odd; + s->odd = s->even; + s->even = tmp; out = s->even & 1; out ^= LF_POLY_EVEN & (s->even >>= 1); @@ -481,7 +484,7 @@ uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) */ static struct Crypto1State* brute_top(uint32_t prefix, uint32_t rresp, unsigned char parities[8][8], - uint32_t odd, uint32_t even, struct Crypto1State* sl, uint8_t no_chk) + uint32_t odd, uint32_t even, struct Crypto1State* sl, uint8_t no_chk) { struct Crypto1State s; uint32_t ks1, nr, ks2, rr, ks3, good, c; @@ -489,20 +492,20 @@ brute_top(uint32_t prefix, uint32_t rresp, unsigned char parities[8][8], for(c = 0; c < 8; ++c) { s.odd = odd ^ fastfwd[1][c]; s.even = even ^ fastfwd[0][c]; - + lfsr_rollback_bit(&s, 0, 0); lfsr_rollback_bit(&s, 0, 0); lfsr_rollback_bit(&s, 0, 0); - + lfsr_rollback_word(&s, 0, 0); lfsr_rollback_word(&s, prefix | c << 5, 1); - + sl->odd = s.odd; sl->even = s.even; - + if (no_chk) break; - + ks1 = crypto1_word(&s, prefix | c << 5, 1); ks2 = crypto1_word(&s,0,0); ks3 = crypto1_word(&s, 0,0); @@ -521,7 +524,7 @@ brute_top(uint32_t prefix, uint32_t rresp, unsigned char parities[8][8], } return ++sl; -} +} /** lfsr_common_prefix @@ -542,13 +545,13 @@ lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8], odd = lfsr_prefix_ks(ks, 1); even = lfsr_prefix_ks(ks, 0); - statelist = malloc((sizeof *statelist) << 21); //how large should be? + statelist = malloc((sizeof *statelist) << 21); //how large should be? if(!statelist || !odd || !even) { free(statelist); free(odd); free(even); - return 0; + return 0; } s = statelist; @@ -560,7 +563,7 @@ lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8], s = brute_top(pfx, rr, par, *o, *e, s, no_par); } - s->odd = s->even = -1; + s->odd = s->even = -1; //printf("state count = %d\n",s-statelist); free(odd); diff --git a/client/nonce2key/crypto1.c b/client/nonce2key/crypto1.c index fbf888987..42a44b2d0 100644 --- a/client/nonce2key/crypto1.c +++ b/client/nonce2key/crypto1.c @@ -1,21 +1,21 @@ /* crypto1.c - 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 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. + 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 Street, Fifth Floor, Boston, - MA 02110-1301, US + 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 Street, Fifth Floor, Boston, + MA 02110-1301, US - Copyright (C) 2008-2008 bla + Copyright (C) 2008-2008 bla */ #include "crapto1.h" #include @@ -49,6 +49,7 @@ void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) { uint32_t feedin; + uint32_t tmp; uint8_t ret = filter(s->odd); feedin = ret & !!is_encrypted; @@ -57,7 +58,9 @@ uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) feedin ^= LF_POLY_EVEN & s->even; s->even = s->even << 1 | parity(feedin); - s->odd ^= (s->odd ^= s->even, s->even ^= s->odd); + tmp = s->odd; + s->odd = s->even; + s->even = tmp; return ret; } From 7843130a589d29acc2f27a3a96c057e01f881025 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Fri, 6 Mar 2015 07:39:34 +0100 Subject: [PATCH 9/9] fix: (issue #72) LF simulation didn't work with lo_edge_detect.v --- fpga/fpga_lf.bit | Bin 42175 -> 42175 bytes fpga/lo_edge_detect.v | 8 +++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fpga/fpga_lf.bit b/fpga/fpga_lf.bit index 51b0681cd6a082550a19dccd794ff40439e393f9..bd4d821bbd7db5d6ff01190fc2204e259e467c50 100644 GIT binary patch literal 42175 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 diff --git a/fpga/lo_edge_detect.v b/fpga/lo_edge_detect.v index dc97fc6f5..bb1301574 100644 --- a/fpga/lo_edge_detect.v +++ b/fpga/lo_edge_detect.v @@ -35,10 +35,12 @@ wire tag_modulation = ssp_dout & !lf_field; wire reader_modulation = !ssp_dout & lf_field & pck_divclk; // No logic, straight through. -assign pwr_oe1 = 1'b0; // not used in LF mode +assign pwr_oe1 = 1'b0; // not used in LF mode +assign pwr_oe3 = 1'b0; // base antenna load = 33 Ohms +// when modulating, add another 33 Ohms and 10k Ohms in parallel: assign pwr_oe2 = tag_modulation; -assign pwr_oe3 = tag_modulation; -assign pwr_oe4 = tag_modulation; +assign pwr_oe4 = tag_modulation; + assign ssp_clk = cross_lo; assign pwr_lo = reader_modulation; assign pwr_hi = 1'b0;