mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
324 lines
11 KiB
C
324 lines
11 KiB
C
#include "Crypto1.h"
|
|
|
|
#define PRNG_MASK 0x002D0000UL
|
|
/* x^16 + x^14 + x^13 + x^11 + 1 */
|
|
|
|
#define PRNG_SIZE 4 /* Bytes */
|
|
|
|
#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 */
|
|
|
|
#define LFSR_SIZE 6 /* Bytes */
|
|
|
|
/* Functions fa, fb and fc in filter output network. Definitions taken
|
|
* from Timo Kasper's thesis */
|
|
#define FA(x3, x2, x1, x0) ( \
|
|
( (x0 | x1) ^ (x0 & x3) ) ^ ( x2 & ( (x0 ^ x1) | x3 ) ) \
|
|
)
|
|
|
|
#define FB(x3, x2, x1, x0) ( \
|
|
( (x0 & x1) | x2 ) ^ ( (x0 ^ x1) & (x2 | x3) ) \
|
|
)
|
|
|
|
#define FC(x4, x3, x2, x1, x0) ( \
|
|
( x0 | ( (x1 | x4) & (x3 ^ x4) ) ) ^ ( ( x0 ^ (x1 & x3) ) & ( (x2 ^ x3) | (x1 & x4) ) ) \
|
|
)
|
|
|
|
/* 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,
|
|
},
|
|
{ /* 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,
|
|
},
|
|
{ /* 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,
|
|
}
|
|
};
|
|
|
|
static const uint8_t TableC[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),
|
|
FC(0,1,0,0,0), FC(0,1,0,0,1), FC(0,1,0,1,0), FC(0,1,0,1,1),
|
|
FC(0,1,1,0,0), FC(0,1,1,0,1), FC(0,1,1,1,0), FC(0,1,1,1,1),
|
|
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),
|
|
};
|
|
|
|
/* 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};
|
|
|
|
/* Proceed LFSR by one clock cycle */
|
|
static void Crypto1LFSR(uint8_t In) {
|
|
uint8_t Feedback = 0;
|
|
|
|
/* 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);
|
|
|
|
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 ^= Feedback >> 4;
|
|
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);
|
|
}
|
|
|
|
/* 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];
|
|
|
|
StateOdd[0] = (uint8_t) (Temp >> 0);
|
|
StateOdd[1] = (uint8_t) (Temp >> 8);
|
|
StateOdd[2] = (uint8_t) (Temp >> 16);
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
void Crypto1Auth(uint8_t EncryptedReaderNonce[4])
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
uint8_t Crypto1Byte(void)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
return KeyStream;
|
|
}
|
|
|
|
uint8_t Crypto1Nibble(void)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
return KeyStream;
|
|
}
|
|
|
|
void Crypto1PRNG(uint8_t State[4], uint16_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;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
|
|
}
|