diff --git a/Firmware/Chameleon-Mini/Application/Crypto1.c b/Firmware/Chameleon-Mini/Application/Crypto1.c index e172381c..f4f3d182 100644 --- a/Firmware/Chameleon-Mini/Application/Crypto1.c +++ b/Firmware/Chameleon-Mini/Application/Crypto1.c @@ -1,11 +1,18 @@ #include "Crypto1.h" +/* avoid compiler complaining at the shift macros */ +#pragma GCC diagnostic ignored "-Wuninitialized" + +// uncomment if platform is not avr +// #define NO_INLINE_ASM 1 + #define PRNG_MASK 0x002D0000UL /* x^16 + x^14 + x^13 + x^11 + 1 */ #define PRNG_SIZE 4 /* Bytes */ +#define NONCE_SIZE 4 /* Bytes */ -#define LFSR_MASK_EVEN 0x2010E1UL +#define LFSR_MASK_EVEN 0x2010E1UL #define LFSR_MASK_ODD 0x3A7394UL /* x^48 + x^43 + x^39 + x^38 + x^36 + x^34 + x^33 + x^31 + x^29 + * x^24 + x^23 + x^21 + x^19 + x^13 + x^9 + x^7 + x^6 + x^5 + 1 */ @@ -26,44 +33,252 @@ ( x0 | ( (x1 | x4) & (x3 ^ x4) ) ) ^ ( ( x0 ^ (x1 & x3) ) & ( (x2 ^ x3) | (x1 & x4) ) ) \ ) -#define SWAPENDIAN(x)\ - (x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16) -/* Create tables from function fa, fb and fc for faster access */ -static const uint8_t TableAB[5][16] = { - { /* fa with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 0 */ - FA(0,0,0,0) << 0, FA(0,0,0,1) << 0, FA(0,0,1,0) << 0, FA(0,0,1,1) << 0, - FA(0,1,0,0) << 0, FA(0,1,0,1) << 0, FA(0,1,1,0) << 0, FA(0,1,1,1) << 0, - FA(1,0,0,0) << 0, FA(1,0,0,1) << 0, FA(1,0,1,0) << 0, FA(1,0,1,1) << 0, - FA(1,1,0,0) << 0, FA(1,1,0,1) << 0, FA(1,1,1,0) << 0, FA(1,1,1,1) << 0, - }, - { /* fb with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 1 */ - FB(0,0,0,0) << 1, FB(0,0,0,1) << 1, FB(0,0,1,0) << 1, FB(0,0,1,1) << 1, - FB(0,1,0,0) << 1, FB(0,1,0,1) << 1, FB(0,1,1,0) << 1, FB(0,1,1,1) << 1, - FB(1,0,0,0) << 1, FB(1,0,0,1) << 1, FB(1,0,1,0) << 1, FB(1,0,1,1) << 1, - FB(1,1,0,0) << 1, FB(1,1,0,1) << 1, FB(1,1,1,0) << 1, FB(1,1,1,1) << 1, - }, - { /* fb with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 2 */ - FB(0,0,0,0) << 2, FB(0,0,0,1) << 2, FB(0,0,1,0) << 2, FB(0,0,1,1) << 2, - FB(0,1,0,0) << 2, FB(0,1,0,1) << 2, FB(0,1,1,0) << 2, FB(0,1,1,1) << 2, - FB(1,0,0,0) << 2, FB(1,0,0,1) << 2, FB(1,0,1,0) << 2, FB(1,0,1,1) << 2, - FB(1,1,0,0) << 2, FB(1,1,0,1) << 2, FB(1,1,1,0) << 2, FB(1,1,1,1) << 2, +/* For AVR only */ +#ifndef NO_INLINE_ASM + +/* Buffer size and parity offset */ +#include "../Codec/ISO14443-2A.h" + +/* Table lookup for odd parity */ +#include "../Common.h" + +/* Special macros for optimized usage of the xmega */ +/* see http://rn-wissen.de/wiki/index.php?title=Inline-Assembler_in_avr-gcc */ + +/* Split byte into odd and even nibbles- */ +/* Used for LFSR setup. */ +#define SPLIT_BYTE(__even, __odd, __byte) \ + __asm__ __volatile__ ( \ + "lsr %2" "\n\t" \ + "ror %0" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %1" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %0" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %1" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %0" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %1" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %0" "\n\t" \ + "lsr %2" "\n\t" \ + "ror %1" \ + : "+r" (__even), \ + "+r" (__odd), \ + "+r" (__byte) \ + : \ + : "r0" ) + +/* Shift half LFSR state stored in three registers */ +/* Input is bit 0 of __in */ +#define SHIFT24(__b0, __b1, __b2, __in) \ + __asm__ __volatile__ ( \ + "lsr %3" "\n\t" \ + "ror %2" "\n\t" \ + "ror %1" "\n\t" \ + "ror %0" \ + : "+r" (__b0), \ + "+r" (__b1), \ + "+r" (__b2), \ + "+r" (__in) \ + : \ + : ) + +/* Shift half LFSR state stored in three registers */ +/* Input is bit 0 of __in */ +/* decrypt with __stream if bit 0 of __decrypt is set */ +#define SHIFT24_COND_DECRYPT(__b0, __b1, __b2, __in, __stream, __decrypt) \ + __asm__ __volatile__ ( \ + "sbrc %5, 0" "\n\t" \ + "eor %3, %4" "\n\t" \ + "lsr %3" "\n\t" \ + "ror %2" "\n\t" \ + "ror %1" "\n\t" \ + "ror %0" \ + : "+r" (__b0), \ + "+r" (__b1), \ + "+r" (__b2), \ + "+r" (__in) \ + : "r" (__stream), \ + "r" (__decrypt) \ + : "r0" ) + +/* Shift a byte with input from an other byte */ +/* Input is bit 0 of __in */ +#define SHIFT8(__byte, __in) \ + __asm__ __volatile__ ( \ + "lsr %1" "\n\t" \ + "ror %0" \ + : "+r" (__byte), \ + "+r" (__in) \ + : \ + : "r0" ) +/* End AVR specific */ +#else + +/* Plattform independend code */ + +/* avoid including avr-Files in case of test */ +#ifndef CODEC_BUFFER_SIZE +#define CODEC_BUFFER_SIZE 256 +#endif +#ifndef ISO14443A_BUFFER_PARITY_OFFSET +#define ISO14443A_BUFFER_PARITY_OFFSET (CODEC_BUFFER_SIZE/2) +#endif + +#define SHIFT24(__b0, __b1, __b2, __in) \ + __b0 = (__b0>>1) | (__b1<<7); \ + __b1 = (__b1>>1) | (__b2<<7); \ + __b2 = (__b2>>1) | ((__in)<<7) + +#define SHIFT24_COND_DECRYPT(__b0, __b1, __b2, __in, __stream, __decrypt) \ + __b0 = (__b0>>1) | (__b1<<7); \ + __b1 = (__b1>>1) | (__b2<<7); \ + __b2 = (__b2>>1) | (((__in)^((__stream)&(__decrypt)))<<7) + +#define SHIFT8(__byte, __in) __byte = (__byte>>1) | ((__in)<<7) + +#define SPLIT_BYTE(__even, __odd, __byte) \ + __even = (__even >> 1) | (__byte<<7); __byte>>=1; \ + __odd = (__odd >> 1) | (__byte<<7); __byte>>=1; \ + __even = (__even >> 1) | (__byte<<7); __byte>>=1; \ + __odd = (__odd >> 1) | (__byte<<7); __byte>>=1; \ + __even = (__even >> 1) | (__byte<<7); __byte>>=1; \ + __odd = (__odd >> 1) | (__byte<<7); __byte>>=1; \ + __even = (__even >> 1) | (__byte<<7); __byte>>=1; \ + __odd = (__odd >> 1) | (__byte<<7) + + +/* Generate odd parity bit */ +#define ODD_PARITY(val) \ + (__extension__({ \ + uint8_t __p = (uint8_t)(val); \ + __p ^= ((__p >> 4)|(__p << 4)) ; \ + __p ^= __p >> 2 ; \ + ((--__p) >> 1) & 1; /* see "avr/util.h" */ \ + })) +#endif + +/* Space/speed tradoff. */ +/* We want speed, so we have to pay with size. */ +/* If we combine the A und B Filtertables and precalculate the values */ +/* for each state byte, we get the following tables which gives a */ +/* faster calculation of the filter output */ +/* Table of the filter A/B output per byte */ +static const uint8_t abFilterTable[3][256] = +{ + /* for Odd[0] */ + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, - { /* fa with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 3 */ - FA(0,0,0,0) << 3, FA(0,0,0,1) << 3, FA(0,0,1,0) << 3, FA(0,0,1,1) << 3, - FA(0,1,0,0) << 3, FA(0,1,0,1) << 3, FA(0,1,1,0) << 3, FA(0,1,1,1) << 3, - FA(1,0,0,0) << 3, FA(1,0,0,1) << 3, FA(1,0,1,0) << 3, FA(1,0,1,1) << 3, - FA(1,1,0,0) << 3, FA(1,1,0,1) << 3, FA(1,1,1,0) << 3, FA(1,1,1,1) << 3, + /* for Odd[1] */ + { + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x04, 0x04, 0x04, 0x06, 0x06, 0x04, 0x04, 0x06, + 0x04, 0x06, 0x06, 0x06, 0x06, 0x04, 0x04, 0x06 }, - { /* fb with Input {3,2,1,0} = (0,0,0,0) to (1,1,1,1) shifted by 4 */ - FB(0,0,0,0) << 4, FB(0,0,0,1) << 4, FB(0,0,1,0) << 4, FB(0,0,1,1) << 4, - FB(0,1,0,0) << 4, FB(0,1,0,1) << 4, FB(0,1,1,0) << 4, FB(0,1,1,1) << 4, - FB(1,0,0,0) << 4, FB(1,0,0,1) << 4, FB(1,0,1,0) << 4, FB(1,0,1,1) << 4, - FB(1,1,0,0) << 4, FB(1,1,0,1) << 4, FB(1,1,1,0) << 4, FB(1,1,1,1) << 4, + /* for Odd[2] */ + { + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x00, 0x08, + 0x10, 0x18, 0x18, 0x18, 0x10, 0x10, 0x10, 0x18, + 0x10, 0x10, 0x18, 0x10, 0x18, 0x18, 0x10, 0x18 } }; -static const uint8_t TableC[32] = { +/* Standard FC table, feedback at bit 0 */ +static const uint8_t TableC0[32] = { /* fc with Input {4,3,2,1,0} = (0,0,0,0,0) to (1,1,1,1,1) */ FC(0,0,0,0,0), FC(0,0,0,0,1), FC(0,0,0,1,0), FC(0,0,0,1,1), FC(0,0,1,0,0), FC(0,0,1,0,1), FC(0,0,1,1,0), FC(0,0,1,1,1), @@ -72,326 +287,786 @@ static const uint8_t TableC[32] = { FC(1,0,0,0,0), FC(1,0,0,0,1), FC(1,0,0,1,0), FC(1,0,0,1,1), FC(1,0,1,0,0), FC(1,0,1,0,1), FC(1,0,1,1,0), FC(1,0,1,1,1), FC(1,1,0,0,0), FC(1,1,0,0,1), FC(1,1,0,1,0), FC(1,1,0,1,1), - FC(1,1,1,0,0), FC(1,1,1,0,1), FC(1,1,1,1,0), FC(1,1,1,1,1), + FC(1,1,1,0,0), FC(1,1,1,0,1), FC(1,1,1,1,0), FC(1,1,1,1,1) +}; + +/* Special table for byte processing, feedback at bit 7 */ +static const uint8_t TableC7[32] = { + /* fc with Input {4,3,2,1,0} = (0,0,0,0,0) to (1,1,1,1,1) */ + FC(0,0,0,0,0)<<7, FC(0,0,0,0,1)<<7, FC(0,0,0,1,0)<<7, FC(0,0,0,1,1)<<7, + FC(0,0,1,0,0)<<7, FC(0,0,1,0,1)<<7, FC(0,0,1,1,0)<<7, FC(0,0,1,1,1)<<7, + FC(0,1,0,0,0)<<7, FC(0,1,0,0,1)<<7, FC(0,1,0,1,0)<<7, FC(0,1,0,1,1)<<7, + FC(0,1,1,0,0)<<7, FC(0,1,1,0,1)<<7, FC(0,1,1,1,0)<<7, FC(0,1,1,1,1)<<7, + FC(1,0,0,0,0)<<7, FC(1,0,0,0,1)<<7, FC(1,0,0,1,0)<<7, FC(1,0,0,1,1)<<7, + FC(1,0,1,0,0)<<7, FC(1,0,1,0,1)<<7, FC(1,0,1,1,0)<<7, FC(1,0,1,1,1)<<7, + FC(1,1,0,0,0)<<7, FC(1,1,0,0,1)<<7, FC(1,1,0,1,0)<<7, FC(1,1,0,1,1)<<7, + FC(1,1,1,0,0)<<7, FC(1,1,1,0,1)<<7, FC(1,1,1,1,0)<<7, FC(1,1,1,1,1)<<7 }; -/* Split Crypto1 state into even and odd bits to speed up the output filter network */ -static uint8_t StateEven[LFSR_SIZE/2] = {0}; -static uint8_t StateOdd[LFSR_SIZE/2] = {0}; +/* Special table for nibble processing (e.g. ack), feedback at bit 3 */ +static const uint8_t TableC3[32] = { + /* fc with Input {4,3,2,1,0} = (0,0,0,0,0) to (1,1,1,1,1) */ + FC(0,0,0,0,0)<<3, FC(0,0,0,0,1)<<3, FC(0,0,0,1,0)<<3, FC(0,0,0,1,1)<<3, + FC(0,0,1,0,0)<<3, FC(0,0,1,0,1)<<3, FC(0,0,1,1,0)<<3, FC(0,0,1,1,1)<<3, + FC(0,1,0,0,0)<<3, FC(0,1,0,0,1)<<3, FC(0,1,0,1,0)<<3, FC(0,1,0,1,1)<<3, + FC(0,1,1,0,0)<<3, FC(0,1,1,0,1)<<3, FC(0,1,1,1,0)<<3, FC(0,1,1,1,1)<<3, + FC(1,0,0,0,0)<<3, FC(1,0,0,0,1)<<3, FC(1,0,0,1,0)<<3, FC(1,0,0,1,1)<<3, + FC(1,0,1,0,0)<<3, FC(1,0,1,0,1)<<3, FC(1,0,1,1,0)<<3, FC(1,0,1,1,1)<<3, + FC(1,1,0,0,0)<<3, FC(1,1,0,0,1)<<3, FC(1,1,0,1,0)<<3, FC(1,1,0,1,1)<<3, + FC(1,1,1,0,0)<<3, FC(1,1,1,0,1)<<3, FC(1,1,1,1,0)<<3, FC(1,1,1,1,1)<<3 +}; + +/* Filter Output Macros */ +/* Output at bit 7 for optimized byte processing */ +#define CRYPTO1_FILTER_OUTPUT_B7_24(__O0, __O1, __O2) TableC7[ abFilterTable[0][__O0] | \ + abFilterTable[1][__O1] | \ + abFilterTable[2][__O2]] + +/* Output at bit 3 for optimized nibble processing */ +#define CRYPTO1_FILTER_OUTPUT_B3_24(__O0, __O1, __O2) TableC3[ abFilterTable[0][__O0] | \ + abFilterTable[1][__O1] | \ + abFilterTable[2][__O2]] + +/* Output at bit 0 for general purpose */ +#define CRYPTO1_FILTER_OUTPUT_B0_24(__O0, __O1, __O2) TableC0[ abFilterTable[0][__O0] | \ + abFilterTable[1][__O1] | \ + abFilterTable[2][__O2]] + +/* Split Crypto1 state into even and odd bits */ +/* to speed up the output filter network */ +/* Put both into one struct to enable relative adressing */ +typedef struct +{ + uint8_t Even[LFSR_SIZE/2]; + uint8_t Odd[LFSR_SIZE/2]; +} Crypto1LfsrState_t; +static Crypto1LfsrState_t State = {{0},{0}}; + + +/* Debug output of state */ +void Crypto1GetState(uint8_t* pEven, uint8_t* pOdd) +{ + if (pEven) + { + pEven[0] = State.Even[0]; + pEven[1] = State.Even[1]; + pEven[2] = State.Even[2]; + } + if (pOdd) + { + pOdd[0] = State.Odd[0]; + pOdd[1] = State.Odd[1]; + pOdd[2] = State.Odd[2]; + } + +} /* Proceed LFSR by one clock cycle */ +/* Prototype to force inlining */ +static __inline__ uint8_t Crypto1LFSRbyteFeedback (uint8_t E0, + uint8_t E1, + uint8_t E2, + uint8_t O0, + uint8_t O1, + uint8_t O2) __attribute__((always_inline)); +static uint8_t Crypto1LFSRbyteFeedback (uint8_t E0, + uint8_t E1, + uint8_t E2, + uint8_t O0, + uint8_t O1, + uint8_t O2) +{ + uint8_t Feedback; + + /* Calculate feedback according to LFSR taps. XOR all state bytes + * into a single bit. */ + Feedback = E0 & (uint8_t) (LFSR_MASK_EVEN ); + Feedback ^= E1 & (uint8_t) (LFSR_MASK_EVEN >> 8); + Feedback ^= E2 & (uint8_t) (LFSR_MASK_EVEN >> 16); + + Feedback ^= O0 & (uint8_t) (LFSR_MASK_ODD ); + Feedback ^= O1 & (uint8_t) (LFSR_MASK_ODD >> 8); + Feedback ^= O2 & (uint8_t) (LFSR_MASK_ODD >> 16); + + /* fold 8 into 1 bit */ + Feedback ^= ((Feedback >> 4)|(Feedback << 4)); /* Compiler uses a swap for this (fast!) */ + Feedback ^= Feedback >> 2; + Feedback ^= Feedback >> 1; + + return(Feedback); +} + +/* Proceed LFSR by one clock cycle */ +/* Prototype to force inlining */ +static __inline__ void Crypto1LFSR (uint8_t In) __attribute__((always_inline)); static void Crypto1LFSR(uint8_t In) { - uint8_t Feedback = 0; + register uint8_t Temp0, Temp1, Temp2; + uint8_t Feedback; + + /* Load even state. */ + Temp0 = State.Even[0]; + Temp1 = State.Even[1]; + Temp2 = State.Even[2]; + /* Calculate feedback according to LFSR taps. XOR all 6 state bytes - * into a single bit. */ - Feedback ^= StateEven[0] & (uint8_t) (LFSR_MASK_EVEN >> 0); - Feedback ^= StateEven[1] & (uint8_t) (LFSR_MASK_EVEN >> 8); - Feedback ^= StateEven[2] & (uint8_t) (LFSR_MASK_EVEN >> 16); + * into a single bit. */ + Feedback = Temp0 & (uint8_t) (LFSR_MASK_EVEN >> 0); + Feedback ^= Temp1 & (uint8_t) (LFSR_MASK_EVEN >> 8); + Feedback ^= Temp2 & (uint8_t) (LFSR_MASK_EVEN >> 16); - Feedback ^= StateOdd[0] & (uint8_t) (LFSR_MASK_ODD >> 0); - Feedback ^= StateOdd[1] & (uint8_t) (LFSR_MASK_ODD >> 8); - Feedback ^= StateOdd[2] & (uint8_t) (LFSR_MASK_ODD >> 16); + Feedback ^= State.Odd[0] & (uint8_t) (LFSR_MASK_ODD >> 0); + Feedback ^= State.Odd[1] & (uint8_t) (LFSR_MASK_ODD >> 8); + Feedback ^= State.Odd[2] & (uint8_t) (LFSR_MASK_ODD >> 16); - Feedback ^= Feedback >> 4; + Feedback ^= ((Feedback >> 4)|(Feedback << 4)); /* Compiler uses a swap for this (fast!) */ Feedback ^= Feedback >> 2; Feedback ^= Feedback >> 1; /* Now the shifting of the Crypto1 state gets more complicated when - * split up into even/odd parts. After some hard thinking, one can - * see that after one LFSR clock cycle - * - the new even state becomes the old odd state - * - the new odd state becomes the old even state right-shifted by 1. - * For shifting the even state, we convert it into a 32 bit int first */ - uint32_t Temp = 0; - Temp |= ((uint32_t) StateEven[0] << 0); - Temp |= ((uint32_t) StateEven[1] << 8); - Temp |= ((uint32_t) StateEven[2] << 16); - - /* Proceed LFSR. Try to force compiler not to shift the unneded upper bits. */ - Temp = (Temp >> 1) & 0x00FFFFFF; - - /* Calculate MSBit of even state as input bit to LFSR */ - if ( (Feedback & 0x01) ^ In ) { - Temp |= (uint32_t) 1 << (8 * LFSR_SIZE/2 - 1); - } + * split up into even/odd parts. After some hard thinking, one can + * see that after one LFSR clock cycle + * - the new even state becomes the old odd state + * - the new odd state becomes the old even state right-shifted by 1. */ + SHIFT24(Temp0, Temp1, Temp2, Feedback); /* Convert even state back into byte array and swap odd/even state * as explained above. */ - StateEven[0] = StateOdd[0]; - StateEven[1] = StateOdd[1]; - StateEven[2] = StateOdd[2]; + State.Even[0] = State.Odd[0]; + State.Even[1] = State.Odd[1]; + State.Even[2] = State.Odd[2]; - StateOdd[0] = (uint8_t) (Temp >> 0); - StateOdd[1] = (uint8_t) (Temp >> 8); - StateOdd[2] = (uint8_t) (Temp >> 16); + State.Odd[0] = Temp0; + State.Odd[1] = Temp1; + State.Odd[2] = Temp2; } uint8_t Crypto1FilterOutput(void) { - /* Calculate the functions fa, fb. - * Note that only bits {4...23} of the odd state - * get fed into these function. - * The tables are designed to hold mask values, which - * can simply be ORed together to produce the resulting - * 5 bits that are used to lookup the output bit. - */ - uint8_t Sum = 0; - - Sum |= TableAB[0][(StateOdd[0] >> 4) & 0x0F]; - Sum |= TableAB[1][(StateOdd[1] >> 0) & 0x0F]; - Sum |= TableAB[2][(StateOdd[1] >> 4) & 0x0F]; - Sum |= TableAB[3][(StateOdd[2] >> 0) & 0x0F]; - Sum |= TableAB[4][(StateOdd[2] >> 4) & 0x0F]; - - return TableC[Sum]; + return( CRYPTO1_FILTER_OUTPUT_B0_24(State.Odd[0], State.Odd[1], State.Odd[2])); } -void Crypto1Setup(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[4]) +/* Setup LFSR split into odd and even states, feed in uid ^nonce */ +/* Version for first (not nested) authentication. */ +void Crypto1Setup(uint8_t Key[6], + uint8_t Uid[4], + uint8_t CardNonce[4]) { - uint8_t i; - - /* Again, one trade off when splitting up the state into even/odd parts - * is that loading the key into the state becomes a little more difficult. - * The inner loop generates 8 even and 8 odd bits from 16 key bits and - * the outer loop stores them. */ - for (i=0; i<(LFSR_SIZE/2); i++) { - uint8_t EvenByte = 0; - uint8_t OddByte = 0; - uint16_t KeyWord = ((uint16_t) Key[2*i+1] << 8) | Key[2*i+0]; - uint8_t j; - - for (j=0; j<8; j++) { - EvenByte >>= 1; - OddByte >>= 1; - - if (KeyWord & (1<<0)) { - EvenByte |= 0x80; - } - - if (KeyWord & (1<<1)) { - OddByte |= 0x80; - } - - KeyWord >>= 2; - } - - StateEven[i] = EvenByte; - StateOdd[i] = OddByte; + /* state registers */ + register uint8_t Even0, Even1, Even2; + register uint8_t Odd0, Odd1, Odd2; + uint8_t KeyStream; + uint8_t Feedback; + uint8_t Out; + uint8_t In; + uint8_t ByteCount; + + KeyStream = *Key++; + SPLIT_BYTE(Even0, Odd0, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even0, Odd0, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even1, Odd1, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even1, Odd1, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even2, Odd2, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even2, Odd2, KeyStream); + + for ( ByteCount = 0; ByteCount < NONCE_SIZE; ByteCount++) + { + In = *CardNonce ^ *Uid++; + + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 1 */ + In >>= 1; + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 2 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 3 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 4 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 5 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 6 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 7 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Encrypt Nonce */ + *CardNonce++ ^= KeyStream; /* Encrypt byte */ } + /* save state */ + State.Even[0] = Even0; + State.Even[1] = Even1; + State.Even[2] = Even2; + State.Odd[0] = Odd0; + State.Odd[1] = Odd1; + State.Odd[2] = Odd2; +} - /* Use Uid XOR CardNonce as feed-in and do 32 clocks on the - * Crypto1 LFSR.*/ - uint32_t Temp = 0; - - Temp |= (uint32_t) (Uid[0] ^ CardNonce[0]) << 0; - Temp |= (uint32_t) (Uid[1] ^ CardNonce[1]) << 8; - Temp |= (uint32_t) (Uid[2] ^ CardNonce[2]) << 16; - Temp |= (uint32_t) (Uid[3] ^ CardNonce[3]) << 24; - - for (i=0; i<32; i++) { - uint8_t Out = Crypto1FilterOutput(); - - Crypto1LFSR(Temp & 0x01); - Temp >>= 1; - - /* Store the keystream for later use */ - if (Out) { - Temp |= (uint32_t) 1 << 31; - } +/* Setup LFSR split into odd and even states, feed in uid ^nonce */ +/* Vesion for nested authentication. */ +/* Also generates encrypted parity bits at CardNonce[4]..[7] */ +/* Use: Decrypt = false for the tag, Decrypt = true for the reader */ +void Crypto1SetupNested(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[8], bool Decrypt) +{ + /* state registers */ + register uint8_t Even0, Even1, Even2; + register uint8_t Odd0, Odd1, Odd2; + uint8_t KeyStream; + uint8_t Feedback; + uint8_t Out; + uint8_t In; + uint8_t ByteCount; + + KeyStream = *Key++; + SPLIT_BYTE(Even0, Odd0, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even0, Odd0, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even1, Odd1, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even1, Odd1, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even2, Odd2, KeyStream); + KeyStream = *Key++; + SPLIT_BYTE(Even2, Odd2, KeyStream); + + /* Get first filter output */ + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + + for ( ByteCount = 0; ByteCount < NONCE_SIZE; ByteCount++) + { + In = *CardNonce ^ *Uid++; + + /* we can reuse the filter output used to decrypt the parity bit! */ + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Even0,Even1,Even2, Feedback, Out, Decrypt); + + /* Bit 1 */ + In >>= 1; + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Odd0,Odd1,Odd2, Feedback, Out, Decrypt); + + /* Bit 2 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Even0,Even1,Even2, Feedback, Out, Decrypt); + + /* Bit 3 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Odd0,Odd1,Odd2, Feedback, Out, Decrypt); + + /* Bit 4 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Even0,Even1,Even2, Feedback, Out, Decrypt); + + /* Bit 5 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Odd0,Odd1,Odd2, Feedback, Out, Decrypt); + + /* Bit 6 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Even0,Even1,Even2, Feedback, Out, Decrypt); + + /* Bit 7 */ + In >>= 1; + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + SHIFT8(KeyStream, Out); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + Feedback ^= In; + SHIFT24_COND_DECRYPT(Odd0,Odd1,Odd2, Feedback, Out, Decrypt); + + /* Generate parity bit */ + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + In = *CardNonce; + Feedback = ODD_PARITY(In); + CardNonce[NONCE_SIZE] = Out ^ Feedback; /* Encrypted parity at Offset 4*/ + + /* Encrypt byte */ + *CardNonce++ = In ^ KeyStream; } - - /* Crypto1 state register is now set up to be used for authentication. - * In case of nested authentication, we need to use the produced keystream - * to encrypt the CardNonce. For this case we do the encryption in-place. */ - CardNonce[0] ^= (uint8_t) (Temp >> 0); - CardNonce[1] ^= (uint8_t) (Temp >> 8); - CardNonce[2] ^= (uint8_t) (Temp >> 16); - CardNonce[3] ^= (uint8_t) (Temp >> 24); + /* save state */ + State.Even[0] = Even0; + State.Even[1] = Even1; + State.Even[2] = Even2; + State.Odd[0] = Odd0; + State.Odd[1] = Odd1; + State.Odd[2] = Odd2; } -void Crypto1SetupReader(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[4], bool Nested) +/* Crypto1Auth is similar to Crypto1Byte but */ +/* EncryptedReaderNonce is decrypted and fed back */ +void Crypto1Auth(uint8_t EncryptedReaderNonce[NONCE_SIZE]) { + /* registers to hold temporary LFSR state */ + register uint8_t Even0,Even1,Even2; + register uint8_t Odd0,Odd1,Odd2; + uint8_t In; + uint8_t Feedback; uint8_t i; - /* Again, one trade off when splitting up the state into even/odd parts - * is that loading the key into the state becomes a little more difficult. - * The inner loop generates 8 even and 8 odd bits from 16 key bits and - * the outer loop stores them. */ - for (i=0; i<(LFSR_SIZE/2); i++) { - uint8_t EvenByte = 0; - uint8_t OddByte = 0; - uint16_t KeyWord = ((uint16_t) Key[2*i+1] << 8) | Key[2*i+0]; - uint8_t j; - - for (j=0; j<8; j++) { - EvenByte >>= 1; - OddByte >>= 1; - - if (KeyWord & (1<<0)) { - EvenByte |= 0x80; - } - - if (KeyWord & (1<<1)) { - OddByte |= 0x80; - } - - KeyWord >>= 2; - } - - StateEven[i] = EvenByte; - StateOdd[i] = OddByte; - } + /* read state */ + Even0 = State.Even[0]; + Even1 = State.Even[1]; + Even2 = State.Even[2]; + Odd0 = State.Odd[0]; + Odd1 = State.Odd[1]; + Odd2 = State.Odd[2]; - if (Nested) + /* 4 Bytes */ + for(i = 0; i < NONCE_SIZE; i++) { - /* Use Uid XOR DECRYPTED CardNonce as feed-in and do 32 clocks on the - * Crypto1 LFSR.*/ - for (i=0; i<32; i++) - { - CardNonce[i/8] ^= Crypto1FilterOutput() << (i % 8); // decrypt bit - - Crypto1LFSR(((Uid[i/8]^CardNonce[i/8]) >> (i % 8)) & 0x01); // feed back decrypted CardNonce bit XOR uid bit - } - } else { - /* Use Uid XOR CardNonce as feed-in and do 32 clocks on the - * Crypto1 LFSR.*/ - uint32_t Temp = 0; - - Temp |= (uint32_t) (Uid[0] ^ CardNonce[0]) << 0; - Temp |= (uint32_t) (Uid[1] ^ CardNonce[1]) << 8; - Temp |= (uint32_t) (Uid[2] ^ CardNonce[2]) << 16; - Temp |= (uint32_t) (Uid[3] ^ CardNonce[3]) << 24; - - for (i=0; i<32; i++) - { - Crypto1LFSR(Temp & 0x01); - Temp >>= 1; - } + In = EncryptedReaderNonce[i]; + + /* Bit 0 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 1 */ + /* remember Odd/Even swap has been omitted! */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 2 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 3 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 4 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 5 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 6 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2) + ^ Feedback + ^ In; + In >>= 1; + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 7 */ + Feedback = CRYPTO1_FILTER_OUTPUT_B0_24(Even0,Even1,Even2); + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2) + ^ Feedback + ^ In; + SHIFT24(Odd0,Odd1,Odd2, Feedback); } + /* save state */ + State.Even[0] = Even0; + State.Even[1] = Even1; + State.Even[2] = Even2; + State.Odd[0] = Odd0; + State.Odd[1] = Odd1; + State.Odd[2] = Odd2; } -void Crypto1Auth(uint8_t EncryptedReaderNonce[4]) +/* Crypto1Nibble generates keystrem for a nibble (4 bit) */ +/* no input to the LFSR */ +uint8_t Crypto1Nibble(void) { - uint32_t Temp = 0; - - /* For ease of processing, we convert the encrypted reader nonce - * into a 32 bit integer */ - Temp |= (uint32_t) EncryptedReaderNonce[0] << 0; - Temp |= (uint32_t) EncryptedReaderNonce[1] << 8; - Temp |= (uint32_t) EncryptedReaderNonce[2] << 16; - Temp |= (uint32_t) EncryptedReaderNonce[3] << 24; - - uint8_t i; - - for (i=0; i<32; i++) { - /* Decrypt one output bit of the given encrypted nonce using the - * filter output as keystream. */ - uint8_t Out = Crypto1FilterOutput(); - uint8_t Bit = Out ^ (Temp & 0x01); - - /* Feed back the bit to load the LFSR with the (decrypted) nonce */ - Crypto1LFSR(Bit); - Temp >>= 1; - } + /* state registers */ + register uint8_t Even0, Even1, Even2; + register uint8_t Odd0, Odd1, Odd2; + uint8_t KeyStream; + uint8_t Feedback; + uint8_t Out; + + /* read state */ + Even0 = State.Even[0]; + Even1 = State.Even[1]; + Even2 = State.Even[2]; + Odd0 = State.Odd[0]; + Odd1 = State.Odd[1]; + Odd2 = State.Odd[2]; + + /* Bit 0, initialise keystream */ + KeyStream = CRYPTO1_FILTER_OUTPUT_B3_24(Odd0, Odd1, Odd2); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 1 */ + Out = CRYPTO1_FILTER_OUTPUT_B3_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 2 */ + Out = CRYPTO1_FILTER_OUTPUT_B3_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 3 */ + Out = CRYPTO1_FILTER_OUTPUT_B3_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* save state */ + State.Even[0] = Even0; + State.Even[1] = Even1; + State.Even[2] = Even2; + State.Odd[0] = Odd0; + State.Odd[1] = Odd1; + State.Odd[2] = Odd2; + + return(KeyStream); } -uint8_t Crypto1Byte(void) +/* Crypto1ByteArray transcrypts array of bytes */ +/* No input to the LFSR */ +/* Avoids load/store of the LFSR-state for each byte! */ +/* Enhacement for the original function Crypto1Byte() */ +void Crypto1ByteArray(uint8_t* Buffer, uint8_t Count) { + /* state registers */ + register uint8_t Even0, Even1, Even2; + register uint8_t Odd0, Odd1, Odd2; uint8_t KeyStream = 0; - uint8_t i; - - /* Generate 8 keystream-bits */ - for (i=0; i<8; i++) { - - /* Calculate output of function-network and cycle LFSR with no - * additional input, thus linearly! */ - uint8_t Out = Crypto1FilterOutput(); - Crypto1LFSR(0); - - /* Store keystream bit */ - KeyStream >>= 1; - - if (Out) { - KeyStream |= (1<<7); - } + uint8_t Feedback; + uint8_t Out; + + /* read state */ + Even0 = State.Even[0]; + Even1 = State.Even[1]; + Even2 = State.Even[2]; + Odd0 = State.Odd[0]; + Odd1 = State.Odd[1]; + Odd2 = State.Odd[2]; + + while(Count--) + { + /* Bit 0, initialise keystream */ + KeyStream = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 1 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 2 */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 3 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 4 */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 5 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 6 */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 7 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Transcrypt and increment buffer address */ + *Buffer++ ^= KeyStream; } - return KeyStream; + /* save state */ + State.Even[0] = Even0; + State.Even[1] = Even1; + State.Even[2] = Even2; + State.Odd[0] = Odd0; + State.Odd[1] = Odd1; + State.Odd[2] = Odd2; } -uint8_t Crypto1Nibble(void) +/* Crypto1ByteArrayWithParity encrypts an array of bytes */ +/* and generates the parity bits */ +/* No input to the LFSR */ +/* Avoids load/store of the LFSR-state for each byte! */ +/* The filter output used to encrypt the parity is */ +/* reused to encrypt bit 0 in the next byte. */ +void Crypto1ByteArrayWithParity(uint8_t* Buffer, uint8_t Count) { + /* state registers */ + register uint8_t Even0, Even1, Even2; + register uint8_t Odd0, Odd1, Odd2; uint8_t KeyStream = 0; - uint8_t i; - - /* Generate 4 keystream-bits */ - for (i=0; i<4; i++) { - - /* Calculate output of function-network and cycle LFSR with no - * additional input, thus linearly! */ - uint8_t Out = Crypto1FilterOutput(); - Crypto1LFSR(0); - - /* Store keystream bit */ - KeyStream >>= 1; - - if (Out) { - KeyStream |= (1<<3); - } + uint8_t Feedback; + uint8_t Out; + + /* read state */ + Even0 = State.Even[0]; + Even1 = State.Even[1]; + Even2 = State.Even[2]; + Odd0 = State.Odd[0]; + Odd1 = State.Odd[1]; + Odd2 = State.Odd[2]; + + /* First pass needs output, next pass uses parity bit! */ + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + + while(Count--) + { + /* Bit 0, initialise keystream from parity */ + SHIFT8(KeyStream,Out); + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 1 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 2 */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 3 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 4 */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 5 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Bit 6 */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Odd0, Odd1, Odd2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Even0,Even1,Even2,Odd0,Odd1,Odd2); + SHIFT24(Even0,Even1,Even2, Feedback); + + /* Bit 7 */ + /* remember Odd/Even swap has been omitted! */ + Out = CRYPTO1_FILTER_OUTPUT_B7_24(Even0,Even1,Even2); + KeyStream = (KeyStream>>1) | Out; + Feedback = Crypto1LFSRbyteFeedback(Odd0,Odd1,Odd2,Even0,Even1,Even2); + SHIFT24(Odd0,Odd1,Odd2, Feedback); + + /* Next bit encodes parity */ + Out = CRYPTO1_FILTER_OUTPUT_B0_24(Odd0, Odd1, Odd2); + Buffer[ISO14443A_BUFFER_PARITY_OFFSET] = ODD_PARITY(*Buffer) ^ Out; + + /* encode Byte */ + *Buffer++ ^= KeyStream; } - - return KeyStream; + /* save state */ + State.Even[0] = Even0; + State.Even[1] = Even1; + State.Even[2] = Even2; + State.Odd[0] = Odd0; + State.Odd[1] = Odd1; + State.Odd[2] = Odd2; } -void Crypto1PRNG(uint8_t State[4], uint16_t ClockCount) +/* Function Crypto1PRNG */ +/* New version of the PRNG wich can calculate multiple */ +/* feedback bits at once! */ +/* Feedback mask = 0x2d = 101101 binary */ +/* Because pattern 101 is repeated, only 2 shifts are neccessary! */ +/* Feedback ^= Feedback >> 3; folds 101 101 to 101 */ +/* Feedback ^= Feedback >> 2; folds 101 => 1 */ +/* With these two lines not only bit 0 is calculated, */ +/* but all the bits which do no overlap with the feedback! */ +/* I.e. the 10 leading zeros in the feedback mask bits */ +/* gives us a total of 11 valid feedback bits! */ +/* The ClockCount for the PRNG is always multiple of 32! */ +/* Up tp 11 Bits can be calculated at once */ +/* Split into chunks of 11+11+10 = 32 bits */ +/* This avoids a calculated number of shifts */ +void Crypto1PRNG(uint8_t State[4], uint8_t ClockCount) { - while(ClockCount--) { - /* Actually, the PRNG is a 32 bit register with the upper 16 bit - * used as a LFSR. Furthermore only mask-byte 2 contains feedback at all. - * We rely on the compiler to optimize this for us here. - * XOR all tapped bits to a single feedback bit. */ - uint8_t Feedback = 0; - - Feedback ^= State[0] & (uint8_t) (PRNG_MASK >> 0); - Feedback ^= State[1] & (uint8_t) (PRNG_MASK >> 8); - Feedback ^= State[2] & (uint8_t) (PRNG_MASK >> 16); - Feedback ^= State[3] & (uint8_t) (PRNG_MASK >> 24); - - Feedback ^= Feedback >> 4; - Feedback ^= Feedback >> 2; - Feedback ^= Feedback >> 1; - - /* For ease of processing convert the state into a 32 bit integer first */ - uint32_t Temp = 0; - - Temp |= (uint32_t) State[0] << 0; - Temp |= (uint32_t) State[1] << 8; - Temp |= (uint32_t) State[2] << 16; - Temp |= (uint32_t) State[3] << 24; - + /* For ease of processing convert the state into a 32 bit integer first */ + uint32_t Temp; + uint16_t Feedback; + + Temp = (uint32_t) State[0] << 0; + Temp |= (uint32_t) State[1] << 8; + Temp |= (uint32_t) State[2] << 16; + Temp |= (uint32_t) State[3] << 24; + + /* PRNG is always a multiple of 32! */ + /* Up tp 11 Bits can be calculated at once */ + /* Split into chunks of 11+11+10 = 32 bits */ + while(ClockCount >= 32) { + Feedback = (uint16_t)(Temp>>16); + Feedback ^= Feedback >> 3; /* 2d = 101101, fold 101 101 => 101 */ + Feedback ^= Feedback >> 2; /* fold 101 => 1 */ /* Cycle LFSR and feed back. */ - Temp >>= 1; - - if (Feedback & 0x01) { - Temp |= (uint32_t) 1 << (8 * PRNG_SIZE - 1); - } - - /* Store back state */ - State[0] = (uint8_t) (Temp >> 0); - State[1] = (uint8_t) (Temp >> 8); - State[2] = (uint8_t) (Temp >> 16); - State[3] = (uint8_t) (Temp >> 24); + Temp = (Temp >> 11) | (((uint32_t)Feedback) << (32-11)); + + /* Same for the next 11 Bits */ + Feedback = (uint16_t)(Temp>>16); + Feedback ^= Feedback >> 3; /* 2d = 101101, fold 101 101 => 101 */ + Feedback ^= Feedback >> 2; /* fold 101 => 1 */ + Temp = (Temp >> 11) | (((uint32_t)Feedback) << (32-11)); + + /* Remaining 10 bits */ + Feedback = (uint16_t)(Temp>>16); + Feedback ^= Feedback >> 3; /* 2d = 101101, fold 101 101 => 101 */ + Feedback ^= Feedback >> 2; /* fold 101 => 1 */ + Temp = (Temp >> 10) | (((uint32_t)Feedback) << (32-10)); + + /* Now 32 bits are fed back */ + ClockCount -= 32; } - + /* Store back state */ + State[0] = (uint8_t) (Temp >> 0); + State[1] = (uint8_t) (Temp >> 8); + State[2] = (uint8_t) (Temp >> 16); + State[3] = (uint8_t) (Temp >> 24); } -void Crypto1EncryptWithParity(uint8_t * Buffer, uint16_t BitCount) +void Crypto1EncryptWithParity(uint8_t * Buffer, uint8_t BitCount) { uint8_t i = 0; while (i < BitCount) { - Buffer[i/8] ^= Crypto1FilterOutput() << (i % 8); + Buffer[i/8] ^= + CRYPTO1_FILTER_OUTPUT_B0_24(State.Odd[0], State.Odd[1], State.Odd[2]) + << (i % 8); if (++i % 9 != 0) // only shift, if this was no parity bit Crypto1LFSR(0); } @@ -403,7 +1078,9 @@ void Crypto1ReaderAuthWithParity(uint8_t PlainReaderAnswerWithParityBits[9]) while (i < 72) { feedback = PlainReaderAnswerWithParityBits[i/8] >> (i % 8); - PlainReaderAnswerWithParityBits[i/8] ^= Crypto1FilterOutput() << (i % 8); + PlainReaderAnswerWithParityBits[i/8] ^= + CRYPTO1_FILTER_OUTPUT_B0_24(State.Odd[0], State.Odd[1], State.Odd[2]) + << (i % 8); if (++i % 9 != 0) // only shift, if this was no parity bit { if (i <= 36) diff --git a/Firmware/Chameleon-Mini/Application/Crypto1.h b/Firmware/Chameleon-Mini/Application/Crypto1.h index dd4dfbcc..e11fb1e4 100644 --- a/Firmware/Chameleon-Mini/Application/Crypto1.h +++ b/Firmware/Chameleon-Mini/Application/Crypto1.h @@ -4,18 +4,16 @@ #include #include +void Crypto1GetState(uint8_t* pEven, uint8_t* pOdd); + /* Gets the current keystream-bit, without shifting the internal LFSR */ uint8_t Crypto1FilterOutput(void); /* Set up Crypto1 cipher using the given Key, Uid and CardNonce. Also encrypts * the CardNonce in-place while in non-linear mode. */ void Crypto1Setup(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[4]); - -/* Set up Crypto1 cipher using the given Key, Uid and CardNonce. Nested indicates - * whether the CardNonce is encrypted (true) or not (false). - * If the CardNonce is encrypted, it will we decrypted in-place. If not, it will - * be fed into the LFSR, but remains unchanged. */ -void Crypto1SetupReader(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[4], bool Nested); +/* Same for nested auth. CardNonce[4]..[7] will contain the parity bits after return */ +void Crypto1SetupNested(uint8_t Key[6], uint8_t Uid[4], uint8_t CardNonce[8], bool Decrypt); /* Load the decrypted ReaderNonce into the Crypto1 state LFSR */ void Crypto1Auth(uint8_t EncryptedReaderNonce[4]); @@ -23,14 +21,18 @@ void Crypto1Auth(uint8_t EncryptedReaderNonce[4]); /* Generate 8 Bits of key stream */ uint8_t Crypto1Byte(void); +/* Encrypt/Decrypt array */ +void Crypto1ByteArray(uint8_t* Buffer, uint8_t Count); +void Crypto1ByteArrayWithParity(uint8_t* Buffer, uint8_t Count); + /* Generate 4 Bits of key stream */ uint8_t Crypto1Nibble(void); /* Execute 'ClockCount' cycles on the PRNG state 'State' */ -void Crypto1PRNG(uint8_t State[4], uint16_t ClockCount); +void Crypto1PRNG(uint8_t State[4], uint8_t ClockCount); /* Encrypts buffer with consideration of parity bits */ -void Crypto1EncryptWithParity(uint8_t * Buffer, uint16_t BitCount); +void Crypto1EncryptWithParity(uint8_t * Buffer, uint8_t BitCount); /* Encrypts buffer with LFSR feedback within reader nonce and considers parity bits */ void Crypto1ReaderAuthWithParity(uint8_t PlainReaderAnswerWithParityBits[9]); diff --git a/Firmware/Chameleon-Mini/Application/MifareClassic.c b/Firmware/Chameleon-Mini/Application/MifareClassic.c index 63fccac1..e843b307 100644 --- a/Firmware/Chameleon-Mini/Application/MifareClassic.c +++ b/Firmware/Chameleon-Mini/Application/MifareClassic.c @@ -31,10 +31,16 @@ #define MEM_KEY_B_OFFSET 58 /* Bytes */ #define MEM_KEY_BIGSECTOR_OFFSET 192 #define MEM_KEY_SIZE 6 /* Bytes */ +#define MEM_ACC_GPB_SIZE 4 /* Bytes */ #define MEM_SECTOR_ADDR_MASK 0xFC #define MEM_BYTES_PER_BLOCK 16 /* Bytes */ #define MEM_VALUE_SIZE 4 /* Bytes */ +/* NXP Originality check */ +/* Sector 18/Block 68..71 is used to store signature data for NXP originality check */ +#define MEM_EV1_SIGNATURE_BLOCK 68 +#define MEM_EV1_SIGNATURE_TRAILOR ((MEM_EV1_SIGNATURE_BLOCK + 3 ) * MEM_BYTES_PER_BLOCK) + #define ACK_NAK_FRAME_SIZE 4 /* Bits */ #define ACK_VALUE 0x0A #define NAK_INVALID_ARG 0x00 @@ -62,19 +68,201 @@ #define CMD_INCREMENT_FRAME_SIZE 2 /* Bytes without CRCA */ #define CMD_RESTORE 0xC2 #define CMD_RESTORE_FRAME_SIZE 2 /* Bytes without CRCA */ +#define CMD_SIG_READ 0xC2 +#define CMD_SIG_READ_FRAME_SIZE 1 /* Bytes without CRCA */ #define CMD_TRANSFER 0xB0 #define CMD_TRANSFER_FRAME_SIZE 2 /* Bytes without CRCA */ #define CMD_CHINESE_UNLOCK 0x40 #define CMD_CHINESE_WIPE 0x41 #define CMD_CHINESE_UNLOCK_RW 0x43 + + + +/* +Source: NXP: MF1S50YYX Product data sheet + +Access conditions for the sector trailer + +Access bits Access condition for Remark + KEYA Access bits KEYB +C1 C2 C3 read write read write read write +0 0 0 never key A key A never key A key A Key B may be read[1] +0 1 0 never never key A never key A never Key B may be read[1] +1 0 0 never key B keyA|B never never key B +1 1 0 never never keyA|B never never never +0 0 1 never key A key A key A key A key A Key B may be read, + transport configuration[1] +0 1 1 never key B keyA|B key B never key B +1 0 1 never never keyA|B key B never never +1 1 1 never never keyA|B never never never + +[1] For this access condition key B is readable and may be used for data +*/ +#define ACC_TRAILOR_READ_KEYA 0x01 +#define ACC_TRAILOR_WRITE_KEYA 0x02 +#define ACC_TRAILOR_READ_ACC 0x04 +#define ACC_TRAILOR_WRITE_ACC 0x08 +#define ACC_TRAILOR_READ_KEYB 0x10 +#define ACC_TRAILOR_WRITE_KEYB 0x20 + + + +/* +Access conditions for data blocks +Access bits Access condition for Application +C1 C2 C3 read write increment decrement, + transfer, + restore + +0 0 0 key A|B key A|B key A|B key A|B transport configuration +0 1 0 key A|B never never never read/write block +1 0 0 key A|B key B never never read/write block +1 1 0 key A|B key B key B key A|B value block +0 0 1 key A|B never never key A|B value block +0 1 1 key B key B never never read/write block +1 0 1 key B never never never read/write block +1 1 1 never never never never read/write block + +*/ +#define ACC_BLOCK_READ 0x01 +#define ACC_BLOCK_WRITE 0x02 +#define ACC_BLOCK_INCREMENT 0x04 +#define ACC_BLOCK_DECREMENT 0x08 + +#define KEY_A 0 +#define KEY_B 1 + +/* Decoding table for Access conditions of a data block */ +static const uint8_t abBlockAccessConditions[8][2] = +{ + /*C1C2C3 */ + /* 0 0 0 R:key A|B W: key A|B I:key A|B D:key A|B transport configuration */ + { + /* Access with Key A */ + ACC_BLOCK_READ | ACC_BLOCK_WRITE | ACC_BLOCK_INCREMENT | ACC_BLOCK_DECREMENT, + /* Access with Key B */ + ACC_BLOCK_READ | ACC_BLOCK_WRITE | ACC_BLOCK_INCREMENT | ACC_BLOCK_DECREMENT + }, + /* 1 0 0 R:key A|B W:key B I:never D:never read/write block */ + { + /* Access with Key A */ + ACC_BLOCK_READ, + /* Access with Key B */ + ACC_BLOCK_READ | ACC_BLOCK_WRITE + }, + /* 0 1 0 R:key A|B W:never I:never D:never read/write block */ + { + /* Access with Key A */ + ACC_BLOCK_READ, + /* Access with Key B */ + ACC_BLOCK_READ + }, + /* 1 1 0 R:key A|B W:key B I:key B D:key A|B value block */ + { + /* Access with Key A */ + ACC_BLOCK_READ | ACC_BLOCK_DECREMENT, + /* Access with Key B */ + ACC_BLOCK_READ | ACC_BLOCK_WRITE | ACC_BLOCK_INCREMENT | ACC_BLOCK_DECREMENT + }, + /* 0 0 1 R:key A|B W:never I:never D:key A|B value block */ + { + /* Access with Key A */ + ACC_BLOCK_READ | ACC_BLOCK_DECREMENT, + /* Access with Key B */ + ACC_BLOCK_READ | ACC_BLOCK_DECREMENT + }, + /* 1 0 1 R:key B W:never I:never D:never read/write block */ + { + /* Access with Key A */ + 0, + /* Access with Key B */ + ACC_BLOCK_READ + }, + /* 0 1 1 R:key B W:key B I:never D:never read/write block */ + { + /* Access with Key A */ + 0, + /* Access with Key B */ + ACC_BLOCK_READ | ACC_BLOCK_WRITE + }, + /* 1 1 1 R:never W:never I:never D:never read/write block */ + { + /* Access with Key A */ + 0, + /* Access with Key B */ + 0 + } + +}; +/* Decoding table for Access conditions of the sector trailor */ +static const uint8_t abTrailorAccessConditions[8][2] = +{ + /* 0 0 0 RdKA:never WrKA:key A RdAcc:key A WrAcc:never RdKB:key A WrKB:key A Key B may be read[1] */ + { + /* Access with Key A */ + ACC_TRAILOR_WRITE_KEYA | ACC_TRAILOR_READ_ACC | ACC_TRAILOR_WRITE_ACC | ACC_TRAILOR_READ_KEYB | ACC_TRAILOR_WRITE_KEYB, + /* Access with Key B */ + 0 + }, + /* 1 0 0 RdKA:never WrKA:key B RdAcc:keyA|B WrAcc:never RdKB:never WrKB:key B */ + { + /* Access with Key A */ + ACC_TRAILOR_READ_ACC, + /* Access with Key B */ + ACC_TRAILOR_WRITE_KEYA | ACC_TRAILOR_READ_ACC | ACC_TRAILOR_WRITE_KEYB + }, + /* 0 1 0 RdKA:never WrKA:never RdAcc:key A WrAcc:never RdKB:key A WrKB:never Key B may be read[1] */ + { + /* Access with Key A */ + ACC_TRAILOR_READ_ACC | ACC_TRAILOR_READ_KEYB, + /* Access with Key B */ + 0 + }, + /* 1 1 0 never never keyA|B never never never */ + { + /* Access with Key A */ + ACC_TRAILOR_READ_ACC, + /* Access with Key B */ + ACC_TRAILOR_READ_ACC + }, + /* 0 0 1 never key A key A key A key A key A Key B may be read,transport configuration[1] */ + { + /* Access with Key A */ + ACC_TRAILOR_WRITE_KEYA | ACC_TRAILOR_READ_ACC | ACC_TRAILOR_WRITE_ACC | ACC_TRAILOR_READ_KEYB | ACC_TRAILOR_WRITE_KEYB, + /* Access with Key B */ + 0 + }, + /* 0 1 1 never key B keyA|B key B never key B */ + { + /* Access with Key A */ + ACC_TRAILOR_READ_ACC, + /* Access with Key B */ + ACC_TRAILOR_WRITE_KEYA | ACC_TRAILOR_READ_ACC | ACC_TRAILOR_WRITE_ACC | ACC_TRAILOR_WRITE_KEYB + }, + /* 1 0 1 never never keyA|B key B never never */ + { + /* Access with Key A */ + ACC_TRAILOR_READ_ACC, + /* Access with Key B */ + ACC_TRAILOR_READ_ACC | ACC_TRAILOR_WRITE_ACC + }, + /* 1 1 1 never never keyA|B never never never */ + { + /* Access with Key A */ + ACC_TRAILOR_READ_ACC, + /* Access with Key B */ + ACC_TRAILOR_READ_ACC + }, +}; + static enum { STATE_HALT, STATE_IDLE, STATE_CHINESE_IDLE, STATE_CHINESE_WRITE, STATE_READY1, - STATE_READY2, + STATE_READY2, STATE_ACTIVE, STATE_AUTHING, STATE_AUTHED_IDLE, @@ -87,11 +275,56 @@ static enum { static uint8_t CardResponse[4]; static uint8_t ReaderResponse[4]; static uint8_t CurrentAddress; +static uint8_t KeyInUse; static uint8_t BlockBuffer[MEM_BYTES_PER_BLOCK]; +static uint8_t AccessConditions[MEM_ACC_GPB_SIZE]; /* Access Conditions + General purpose Byte */ +static uint8_t AccessAddress; static uint16_t CardATQAValue; static uint8_t CardSAKValue; static bool FromHalt = false; +#define BYTE_SWAP(x) (((uint8_t)(x)>>4)|((uint8_t)(x)<<4)) +#define NO_ACCESS 0x07 + +/* decode Access conditions for a block */ +INLINE uint8_t GetAccessCondition(uint8_t Block) +{ + uint8_t InvSAcc0; + uint8_t InvSAcc1; + uint8_t Acc0 = AccessConditions[0]; + uint8_t Acc1 = AccessConditions[1]; + uint8_t Acc2 = AccessConditions[2]; + uint8_t ResultForBlock = 0; + + InvSAcc0 = ~BYTE_SWAP(Acc0); + InvSAcc1 = ~BYTE_SWAP(Acc1); + + /* Check */ + if ( ((InvSAcc0 ^ Acc1) & 0xf0) || /* C1x */ + ((InvSAcc0 ^ Acc2) & 0x0f) || /* C2x */ + ((InvSAcc1 ^ Acc2) & 0xf0) || /* C1x */ + ((InvSAcc1 ^ Acc0) & 0x0f)) /* C3x */ + { + return(NO_ACCESS); + } + Block &= 3; + + Acc0 = ~Acc0; /* C1x Bits to bit 0..3 */ + Acc1 = Acc2; /* C2x Bits to bit 0..3 */ + Acc2 = Acc2 >> 4; /* C3x Bits to bit 0..3 */ + + if(Block) + { + Acc0 >>= Block; + Acc1 >>= Block; + Acc2 >>= Block; + } + /* combine the bits */ + ResultForBlock = ((Acc2 & 1) << 2) | + ((Acc1 & 1) << 1) | + (Acc0 & 1); + return(ResultForBlock); +} INLINE bool CheckValueIntegrity(uint8_t* Block) { @@ -163,6 +396,19 @@ void MifareClassicAppTask(void) uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount) { + /* Wakeup and Request may occure in all states */ + if ( (BitCount == 7) && + /* precheck of WUP/REQ because ISO14443AWakeUp destroys BitCount */ + (((State != STATE_HALT) && (Buffer[0] == ISO14443A_CMD_REQA)) || + (Buffer[0] == ISO14443A_CMD_WUPA) )){ + FromHalt = State == STATE_HALT; + if (ISO14443AWakeUp(Buffer, &BitCount, CardATQAValue, FromHalt)) { + AccessAddress = 0xff; + State = STATE_READY1; + return BitCount; + } + } + switch(State) { case STATE_IDLE: case STATE_HALT: @@ -194,7 +440,7 @@ uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount) } else if (Buffer[0] == CMD_READ) { if (ISO14443ACheckCRCA(Buffer, CMD_READ_FRAME_SIZE)) { /* Read command. Read data from memory and append CRCA. */ - MemoryReadBlock(Buffer, (uint16_t) Buffer[1] * MEM_BYTES_PER_BLOCK, MEM_BYTES_PER_BLOCK); + MemoryReadBlock(Buffer, (uint16_t)Buffer[1] * MEM_BYTES_PER_BLOCK, MEM_BYTES_PER_BLOCK); ISO14443AAppendCRCA(Buffer, MEM_BYTES_PER_BLOCK); return (CMD_READ_RESPONSE_FRAME_SIZE + ISO14443A_CRCA_SIZE ) @@ -268,10 +514,12 @@ uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount) UidCL1[0] = ISO14443A_UID0_CT; if (ISO14443ASelect(Buffer, &BitCount, UidCL1, SAK_UID_NOT_FINISHED)) State = STATE_READY2; - } else { + } else { MemoryReadBlock(UidCL1, MEM_UID_CL1_ADDRESS, MEM_UID_CL1_SIZE); - if (ISO14443ASelect(Buffer, &BitCount, UidCL1, CardSAKValue)) - State = STATE_ACTIVE; + if (ISO14443ASelect(Buffer, &BitCount, UidCL1, CardSAKValue)) { + AccessAddress = 0xff; /* invalid, force reload */ + State = STATE_ACTIVE; + } } return BitCount; @@ -291,6 +539,7 @@ uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount) MemoryReadBlock(UidCL2, MEM_UID_CL2_ADDRESS, MEM_UID_CL2_SIZE); if (ISO14443ASelect(Buffer, &BitCount, UidCL2, CardSAKValue)) { + AccessAddress = 0xff; /* invalid, force reload */ State = STATE_ACTIVE; } @@ -324,40 +573,53 @@ uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount) return ACK_NAK_FRAME_SIZE; } } else if ( (Buffer[0] == CMD_AUTH_A) || (Buffer[0] == CMD_AUTH_B)) { - if (ISO14443ACheckCRCA(Buffer, CMD_AUTH_FRAME_SIZE)) - { - /* Fix for MFClassic 4k cards */ + if (ISO14443ACheckCRCA(Buffer, CMD_AUTH_FRAME_SIZE)) { + uint16_t SectorAddress = Buffer[1] & MEM_SECTOR_ADDR_MASK; uint16_t KeyOffset = (Buffer[0] == CMD_AUTH_A ? MEM_KEY_A_OFFSET : MEM_KEY_B_OFFSET); - uint16_t KeyAddress = (uint16_t) SectorAddress * MEM_BYTES_PER_BLOCK + KeyOffset; - - if(Buffer[1] >= 128) - { - KeyAddress = (Buffer[1] & 0xF0) * 16 + KeyOffset + MEM_KEY_BIGSECTOR_OFFSET; - } + uint16_t SectorStartAddress = (uint16_t)SectorAddress * MEM_BYTES_PER_BLOCK; uint8_t Key[6]; uint8_t Uid[4]; uint8_t CardNonce[4]; + /* Fix for MFClassic 4k cards */ + if(Buffer[1] >= 128) + { + SectorStartAddress += MEM_KEY_BIGSECTOR_OFFSET; + } + LogEntry(LOG_INFO_APP_CMD_AUTH, Buffer, 2); + KeyInUse = Buffer[0] & 1; + CurrentAddress = Buffer[1] & MEM_SECTOR_ADDR_MASK; + if (!AccessConditions[MEM_ACC_GPB_SIZE-1] ||(CurrentAddress != AccessAddress)) { + /* Get access conditions from the sector trailor */ + MemoryReadBlock(AccessConditions, SectorStartAddress + MEM_KEY_A_OFFSET + MEM_KEY_SIZE, MEM_ACC_GPB_SIZE); + AccessAddress = CurrentAddress; + } + /* Generate a random nonce and read UID and key from memory */ RandomGetBuffer(CardNonce, sizeof(CardNonce)); if (ActiveConfiguration.UidSize == 7) MemoryReadBlock(Uid, MEM_UID_CL2_ADDRESS, MEM_UID_CL2_SIZE); else MemoryReadBlock(Uid, MEM_UID_CL1_ADDRESS, MEM_UID_CL1_SIZE); - MemoryReadBlock(Key, KeyAddress, MEM_KEY_SIZE); - /* Precalculate the reader response from card-nonce */ - for (uint8_t i=0; i= 128) - { - KeyAddress = (Buffer[1] & 0xF0) * 16 + KeyOffset + MEM_KEY_BIGSECTOR_OFFSET; - } - + uint16_t SectorStartAddress = (uint16_t) SectorAddress * MEM_BYTES_PER_BLOCK; uint8_t Key[6]; uint8_t Uid[4]; - uint8_t CardNonce[4]; + uint8_t CardNonce[8]; + + /* Fix for MFClassic 4k cards */ + if(Buffer[1] >= 128) + { + SectorStartAddress += MEM_KEY_BIGSECTOR_OFFSET; + } LogEntry(LOG_INFO_APP_CMD_AUTH, Buffer, 2); + KeyInUse = Buffer[0] & 1; + CurrentAddress = Buffer[1] & MEM_SECTOR_ADDR_MASK; + if (CurrentAddress != AccessAddress) { + /* Get access conditions from the sector trailor */ + MemoryReadBlock(AccessConditions, SectorStartAddress + MEM_KEY_A_OFFSET + MEM_KEY_SIZE, MEM_ACC_GPB_SIZE); + AccessAddress = CurrentAddress; + } + /* Generate a random nonce and read UID and key from memory */ RandomGetBuffer(CardNonce, sizeof(CardNonce)); if (ActiveConfiguration.UidSize == 7) MemoryReadBlock(Uid, MEM_UID_CL2_ADDRESS, MEM_UID_CL2_SIZE); else MemoryReadBlock(Uid, MEM_UID_CL1_ADDRESS, MEM_UID_CL1_SIZE); - MemoryReadBlock(Key, KeyAddress, MEM_KEY_SIZE); + MemoryReadBlock(Key, SectorStartAddress + KeyOffset, MEM_KEY_SIZE); /* Precalculate the reader response from card-nonce */ for (uint8_t i=0; i