mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
RevE-light development version
WARNING: The files in this commit are our current development version. The hardware and software is not functional yet - so do not use it. We will not answer any support inquiry regarding this version at the moment.
This commit is contained in:
@@ -34,3 +34,4 @@ The code repository contains
|
||||
* Firmware: The complete firmware including a modified Atmel DFU bootloader and LUFA
|
||||
* Software: Contains a python tool for an easy configuration (and more) of the ChameleonMini, Note that this is currently under construction
|
||||
* RevE: Contains the whole contents of the discontinued RevE repository.
|
||||
* RevE-light: Contains our development files for the RevE-light - **WARNING:** currently not supported / not functional
|
||||
|
||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
WARNING: The files in this folder are our current development version. The hardware and software is not functional yet - so do not use it. We will not answer any support inquiry regarding this version at the moment.
|
||||
@@ -0,0 +1 @@
|
||||
/.metadata
|
||||
@@ -0,0 +1 @@
|
||||
/Bin
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* AntennaLevel.h
|
||||
*
|
||||
* Created on: 24.11.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#ifndef ANTENNALEVEL_H_
|
||||
#define ANTENNALEVEL_H_
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#define ANTENNA_LEVEL_R1 10E3
|
||||
#define ANTENNA_LEVEL_R2 220E0
|
||||
#define ANTENNA_LEVEL_VREF 1.0
|
||||
#define ANTENNA_LEVEL_RES 4096
|
||||
#define ANTENNA_LEVEL_OFFSET 190 /* LSB */
|
||||
|
||||
#define ANTENNA_LEVEL_MILLIVOLT 1E3
|
||||
#define ANTENNA_LEVEL_FACTOR (ANTENNA_LEVEL_VREF * (ANTENNA_LEVEL_R1 + ANTENNA_LEVEL_R2) / (ANTENNA_LEVEL_RES * ANTENNA_LEVEL_R2) )
|
||||
#define ANTENNA_LEVEL_SCALE ((uint32_t) 1<<16)
|
||||
#define ANTENNA_LEVEL_NUMERATOR ((uint32_t) (ANTENNA_LEVEL_MILLIVOLT * ANTENNA_LEVEL_FACTOR * ANTENNA_LEVEL_SCALE + .5))
|
||||
#define ANTENNA_LEVEL_DENOMINATOR (ANTENNA_LEVEL_SCALE)
|
||||
|
||||
static inline
|
||||
void AntennaLevelInit(void)
|
||||
{
|
||||
ADCA.CTRLA = ADC_ENABLE_bm;
|
||||
ADCA.CTRLB = ADC_RESOLUTION_12BIT_gc;
|
||||
ADCA.REFCTRL = ADC_REFSEL_INT1V_gc | ADC_BANDGAP_bm;
|
||||
ADCA.PRESCALER = ADC_PRESCALER_DIV32_gc;
|
||||
ADCA.CH0.CTRL = ADC_CH_INPUTMODE_SINGLEENDED_gc;
|
||||
ADCA.CH0.MUXCTRL = ADC_CH_MUXPOS_PIN7_gc;
|
||||
|
||||
}
|
||||
|
||||
static inline
|
||||
uint16_t AntennaLevelGet(void)
|
||||
{
|
||||
ADCA.CH0.CTRL |= ADC_CH_START_bm;
|
||||
while( !(ADCA.CH0.INTFLAGS & ADC_CH_CHIF_bm) );
|
||||
|
||||
ADCA.CH0.INTFLAGS = ADC_CH_CHIF_bm;
|
||||
|
||||
int16_t Result = ADCA.CH0RES - ANTENNA_LEVEL_OFFSET;
|
||||
if (Result < 0) Result = 0;
|
||||
|
||||
return (uint16_t) (((uint32_t) Result * ANTENNA_LEVEL_NUMERATOR) / ANTENNA_LEVEL_DENOMINATOR);
|
||||
}
|
||||
|
||||
#endif /* ANTENNALEVEL_H_ */
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Application.h
|
||||
*
|
||||
* Created on: 18.02.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#ifndef APPLICATION_H_
|
||||
#define APPLICATION_H_
|
||||
|
||||
#include "../Common.h"
|
||||
#include "../Configuration.h"
|
||||
#include "../Log.h"
|
||||
|
||||
/* Applications */
|
||||
#include "MifareUltralight.h"
|
||||
#include "MifareClassic.h"
|
||||
|
||||
|
||||
/* Function wrappers */
|
||||
INLINE void ApplicationInit(void) {
|
||||
ActiveConfiguration.ApplicationInitFunc();
|
||||
}
|
||||
|
||||
INLINE void ApplicationTask(void) {
|
||||
ActiveConfiguration.ApplicationTaskFunc();
|
||||
}
|
||||
|
||||
INLINE uint16_t ApplicationProcess(uint8_t* ByteBuffer, uint16_t ByteCount) {
|
||||
return ActiveConfiguration.ApplicationProcessFunc(ByteBuffer, ByteCount);
|
||||
}
|
||||
|
||||
INLINE void ApplicationTick(void) {
|
||||
ActiveConfiguration.ApplicationTickFunc();
|
||||
}
|
||||
|
||||
INLINE void ApplicationReset(void) {
|
||||
ActiveConfiguration.ApplicationResetFunc();
|
||||
//LogEntry(LOG_INFO_APP_RESET, NULL, 0);
|
||||
}
|
||||
|
||||
INLINE void ApplicationGetUid(ConfigurationUidType Uid) {
|
||||
ActiveConfiguration.ApplicationGetUidFunc(Uid);
|
||||
}
|
||||
|
||||
INLINE void ApplicationSetUid(ConfigurationUidType Uid) {
|
||||
ActiveConfiguration.ApplicationSetUidFunc(Uid);
|
||||
LogEntry(LOG_INFO_UID_SET, Uid, ActiveConfiguration.UidSize);
|
||||
}
|
||||
|
||||
#endif /* APPLICATION_H_ */
|
||||
@@ -0,0 +1,323 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef CRYPTO1_H
|
||||
#define CRYPTO1_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* 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]);
|
||||
|
||||
/* Load the decrypted ReaderNonce into the Crypto1 state LFSR */
|
||||
void Crypto1Auth(uint8_t EncryptedReaderNonce[4]);
|
||||
|
||||
/* Generate 8 Bits of key stream */
|
||||
uint8_t Crypto1Byte(void);
|
||||
|
||||
/* 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);
|
||||
|
||||
#endif //CRYPTO1_H
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* ISO14443A.c
|
||||
*
|
||||
* Created on: 19.03.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#include "ISO14443-3A.h"
|
||||
|
||||
void ISO14443AAppendCRCA(void* Buffer, uint16_t ByteCount) {
|
||||
uint16_t Checksum = 0x6363;
|
||||
uint8_t* DataPtr = (uint8_t*) Buffer;
|
||||
|
||||
while(ByteCount--) {
|
||||
uint8_t Byte = *DataPtr++;
|
||||
|
||||
Byte ^= (uint8_t) (Checksum & 0x00FF);
|
||||
Byte ^= Byte << 4;
|
||||
|
||||
Checksum = (Checksum >> 8) ^ ( (uint16_t) Byte << 8 ) ^
|
||||
( (uint16_t) Byte << 3 ) ^ ( (uint16_t) Byte >> 4 );
|
||||
}
|
||||
|
||||
*DataPtr++ = (Checksum >> 0) & 0x00FF;
|
||||
*DataPtr = (Checksum >> 8) & 0x00FF;
|
||||
}
|
||||
|
||||
bool ISO14443ACheckCRCA(void* Buffer, uint16_t ByteCount)
|
||||
{
|
||||
uint16_t Checksum = 0x6363;
|
||||
uint8_t* DataPtr = (uint8_t*) Buffer;
|
||||
|
||||
while(ByteCount--) {
|
||||
uint8_t Byte = *DataPtr++;
|
||||
|
||||
Byte ^= (uint8_t) (Checksum & 0x00FF);
|
||||
Byte ^= Byte << 4;
|
||||
|
||||
Checksum = (Checksum >> 8) ^ ( (uint16_t) Byte << 8 ) ^
|
||||
( (uint16_t) Byte << 3 ) ^ ( (uint16_t) Byte >> 4 );
|
||||
}
|
||||
|
||||
return (DataPtr[0] == ((Checksum >> 0) & 0xFF)) && (DataPtr[1] == ((Checksum >> 8) & 0xFF));
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue)
|
||||
{
|
||||
uint8_t* DataPtr = (uint8_t*) Buffer;
|
||||
uint8_t NVB = DataPtr[1];
|
||||
//uint8_t CollisionByteCount = (NVB >> 4) & 0x0F;
|
||||
//uint8_t CollisionBitCount = (NVB >> 0) & 0x0F;
|
||||
|
||||
switch (NVB) {
|
||||
case ISO14443A_NVB_AC_START:
|
||||
/* Start of anticollision procedure.
|
||||
* Send whole UID CLn + BCC */
|
||||
DataPtr[0] = UidCL[0];
|
||||
DataPtr[1] = UidCL[1];
|
||||
DataPtr[2] = UidCL[2];
|
||||
DataPtr[3] = UidCL[3];
|
||||
DataPtr[4] = ISO14443A_CALC_BCC(DataPtr);
|
||||
|
||||
*BitCount = ISO14443A_CL_FRAME_SIZE;
|
||||
|
||||
return false;
|
||||
|
||||
case ISO14443A_NVB_AC_END:
|
||||
/* End of anticollision procedure.
|
||||
* Send SAK CLn if we are selected. */
|
||||
if ( (DataPtr[2] == UidCL[0]) &&
|
||||
(DataPtr[3] == UidCL[1]) &&
|
||||
(DataPtr[4] == UidCL[2]) &&
|
||||
(DataPtr[5] == UidCL[3]) ) {
|
||||
|
||||
DataPtr[0] = SAKValue;
|
||||
ISO14443AAppendCRCA(Buffer, 1);
|
||||
|
||||
*BitCount = ISO14443A_SAK_FRAME_SIZE;
|
||||
return true;
|
||||
} else {
|
||||
/* We have not been selected. Don't send anything. */
|
||||
*BitCount = 0;
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
/* TODO: No anticollision supported */
|
||||
*BitCount = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue)
|
||||
{
|
||||
uint8_t* DataPtr = (uint8_t*) Buffer;
|
||||
|
||||
if ( (DataPtr[0] == ISO14443A_CMD_REQA) || (DataPtr[0] == ISO14443A_CMD_WUPA) ){
|
||||
DataPtr[0] = (ATQAValue >> 0) & 0x00FF;
|
||||
DataPtr[1] = (ATQAValue >> 8) & 0x00FF;
|
||||
|
||||
*BitCount = ISO14443A_ATQA_FRAME_SIZE;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* ISO14443-2A.h
|
||||
*
|
||||
* Created on: 19.03.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#ifndef ISO14443_3A_H_
|
||||
#define ISO14443_3A_H_
|
||||
|
||||
#include "../Common.h"
|
||||
|
||||
#define ISO14443A_UID_SIZE_SINGLE 4 /* bytes */
|
||||
#define ISO14443A_UID_SIZE_DOUBLE 7
|
||||
#define ISO14443A_UID_SIZE_TRIPLE 10
|
||||
|
||||
#define ISO14443A_CMD_REQA 0x26
|
||||
#define ISO14443A_CMD_WUPA 0x52
|
||||
#define ISO14443A_CMD_SELECT_CL1 0x93
|
||||
#define ISO14443A_CMD_SELECT_CL2 0x95
|
||||
#define ISO14443A_CMD_SELECT_CL3 0x97
|
||||
#define ISO14443A_CMD_HLTA 0x50
|
||||
|
||||
#define ISO14443A_NVB_AC_START 0x20
|
||||
#define ISO14443A_NVB_AC_END 0x70
|
||||
|
||||
#define ISO14443A_CL_UID_OFFSET 0
|
||||
#define ISO14443A_CL_UID_SIZE 4
|
||||
#define ISO14443A_CL_BCC_OFFSET 4
|
||||
#define ISO14443A_CL_BCC_SIZE 1 /* Byte */
|
||||
#define ISO14443A_CL_FRAME_SIZE ((ISO14443A_CL_UID_SIZE + ISO14443A_CL_BCC_SIZE) * 8) /* UID[N...N+3] || BCCN */
|
||||
#define ISO14443A_SAK_INCOMPLETE 0x04
|
||||
#define ISO14443A_SAK_COMPLETE_COMPLIANT 0x20
|
||||
#define ISO14443A_SAK_COMPLETE_NOT_COMPLIANT 0x00
|
||||
|
||||
#define ISO14443A_ATQA_FRAME_SIZE (2 * 8) /* Bit */
|
||||
#define ISO14443A_SAK_FRAME_SIZE (3 * 8) /* Bit */
|
||||
|
||||
#define ISO14443A_UID0_RANDOM 0x08
|
||||
#define ISO14443A_UID0_CT 0x88
|
||||
|
||||
#define ISO14443A_CRCA_SIZE 2
|
||||
|
||||
#define ISO14443A_CALC_BCC(ByteBuffer) \
|
||||
( ByteBuffer[0] ^ ByteBuffer[1] ^ ByteBuffer[2] ^ ByteBuffer[3] )
|
||||
|
||||
void ISO14443AAppendCRCA(void* Buffer, uint16_t ByteCount);
|
||||
bool ISO14443ACheckCRCA(void* Buffer, uint16_t ByteCount);
|
||||
|
||||
INLINE bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue);
|
||||
INLINE bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue);
|
||||
|
||||
INLINE
|
||||
bool ISO14443ASelect(void* Buffer, uint16_t* BitCount, uint8_t* UidCL, uint8_t SAKValue)
|
||||
{
|
||||
uint8_t* DataPtr = (uint8_t*) Buffer;
|
||||
uint8_t NVB = DataPtr[1];
|
||||
//uint8_t CollisionByteCount = (NVB >> 4) & 0x0F;
|
||||
//uint8_t CollisionBitCount = (NVB >> 0) & 0x0F;
|
||||
|
||||
switch (NVB) {
|
||||
case ISO14443A_NVB_AC_START:
|
||||
/* Start of anticollision procedure.
|
||||
* Send whole UID CLn + BCC */
|
||||
DataPtr[0] = UidCL[0];
|
||||
DataPtr[1] = UidCL[1];
|
||||
DataPtr[2] = UidCL[2];
|
||||
DataPtr[3] = UidCL[3];
|
||||
DataPtr[4] = ISO14443A_CALC_BCC(DataPtr);
|
||||
|
||||
*BitCount = ISO14443A_CL_FRAME_SIZE;
|
||||
|
||||
return false;
|
||||
|
||||
case ISO14443A_NVB_AC_END:
|
||||
/* End of anticollision procedure.
|
||||
* Send SAK CLn if we are selected. */
|
||||
if ( (DataPtr[2] == UidCL[0]) &&
|
||||
(DataPtr[3] == UidCL[1]) &&
|
||||
(DataPtr[4] == UidCL[2]) &&
|
||||
(DataPtr[5] == UidCL[3]) ) {
|
||||
|
||||
DataPtr[0] = SAKValue;
|
||||
ISO14443AAppendCRCA(Buffer, 1);
|
||||
|
||||
*BitCount = ISO14443A_SAK_FRAME_SIZE;
|
||||
return true;
|
||||
} else {
|
||||
/* We have not been selected. Don't send anything. */
|
||||
*BitCount = 0;
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
/* TODO: No anticollision supported */
|
||||
*BitCount = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE
|
||||
bool ISO14443AWakeUp(void* Buffer, uint16_t* BitCount, uint16_t ATQAValue)
|
||||
{
|
||||
uint8_t* DataPtr = (uint8_t*) Buffer;
|
||||
|
||||
if ( (DataPtr[0] == ISO14443A_CMD_REQA) || (DataPtr[0] == ISO14443A_CMD_WUPA) ){
|
||||
DataPtr[0] = (ATQAValue >> 0) & 0x00FF;
|
||||
DataPtr[1] = (ATQAValue >> 8) & 0x00FF;
|
||||
|
||||
*BitCount = ISO14443A_ATQA_FRAME_SIZE;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* MifareClassic.h
|
||||
*
|
||||
* Created on: 13.05.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#ifndef MIFARECLASSIC_H_
|
||||
#define MIFARECLASSIC_H_
|
||||
|
||||
#include "Application.h"
|
||||
#include "ISO14443-3A.h"
|
||||
|
||||
#define MIFARE_CLASSIC_UID_SIZE ISO14443A_UID_SIZE_SINGLE
|
||||
#define MIFARE_CLASSIC_1K_MEM_SIZE 1024
|
||||
#define MIFARE_CLASSIC_4K_MEM_SIZE 4096
|
||||
|
||||
void MifareClassicAppInit1K(void);
|
||||
void MifarePlus1kAppInit_7B(void);
|
||||
void MifareClassicAppInit4K(void);
|
||||
void MifareClassicAppReset(void);
|
||||
void MifareClassicAppTask(void);
|
||||
|
||||
uint16_t MifareClassicAppProcess(uint8_t* Buffer, uint16_t BitCount);
|
||||
|
||||
void MifareClassicGetUid(ConfigurationUidType Uid);
|
||||
void MifareClassicSetUid(ConfigurationUidType Uid);
|
||||
|
||||
|
||||
#endif /* MIFARECLASSIC_H_ */
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* MifareUltralight.c
|
||||
*
|
||||
* Created on: 20.03.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#include "MifareUltralight.h"
|
||||
#include "ISO14443-3A.h"
|
||||
#include "../Codec/ISO14443-2A.h"
|
||||
#include "../Memory.h"
|
||||
|
||||
|
||||
#define ATQA_VALUE 0x0044
|
||||
#define SAK_CL1_VALUE ISO14443A_SAK_INCOMPLETE
|
||||
#define SAK_CL2_VALUE ISO14443A_SAK_COMPLETE_NOT_COMPLIANT
|
||||
|
||||
#define ACK_VALUE 0x0A
|
||||
#define ACK_FRAME_SIZE 4 /* Bits */
|
||||
#define NAK_INVALID_ARG 0x00
|
||||
#define NAK_CRC_ERROR 0x01
|
||||
#define NAK_EEPROM_ERROR 0x05
|
||||
#define NAK_OTHER_ERROR 0x06
|
||||
#define NAK_FRAME_SIZE 4
|
||||
|
||||
#define CMD_READ 0x30
|
||||
#define CMD_READ_FRAME_SIZE 2 /* without CRC bytes */
|
||||
#define CMD_WRITE 0xA2
|
||||
#define CMD_WRITE_FRAME_SIZE 6 /* without CRC bytes */
|
||||
#define CMD_COMPAT_WRITE 0xA0
|
||||
#define CMD_COMPAT_WRITE_FRAME_SIZE 2
|
||||
#define CMD_HALT 0x50
|
||||
|
||||
#define UID_CL1_ADDRESS 0x00 /* In Card Memory */
|
||||
#define UID_CL1_SIZE 3 /* In Bytes */
|
||||
#define UID_BCC1_ADDRESS 0x03
|
||||
#define UID_CL2_ADDRESS 0x04
|
||||
#define UID_CL2_SIZE 4
|
||||
#define UID_BCC2_ADDRESS 0x08
|
||||
|
||||
#define BYTES_PER_PAGE 4
|
||||
#define PAGE_ADDRESS_MASK 0x0F
|
||||
|
||||
#define BYTES_PER_READ 16
|
||||
#define PAGE_READ_MIN 0x00
|
||||
#define PAGE_READ_MAX 0x0F
|
||||
|
||||
#define BYTES_PER_WRITE 4
|
||||
#define PAGE_WRITE_MIN 0x02
|
||||
#define PAGE_WRITE_MAX 0x0F
|
||||
|
||||
#define BYTES_PER_COMPAT_WRITE 16
|
||||
|
||||
static enum {
|
||||
STATE_HALT,
|
||||
STATE_IDLE,
|
||||
STATE_READY1,
|
||||
STATE_READY2,
|
||||
STATE_ACTIVE,
|
||||
STATE_COMPAT_WRITE
|
||||
} State;
|
||||
|
||||
static uint8_t CompatWritePageAddress;
|
||||
|
||||
void MifareUltralightAppInit(void)
|
||||
{
|
||||
State = STATE_IDLE;
|
||||
}
|
||||
|
||||
void MifareUltralightAppReset(void)
|
||||
{
|
||||
State = STATE_IDLE;
|
||||
}
|
||||
|
||||
void MifareUltralightAppTask(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount)
|
||||
{
|
||||
uint8_t Cmd = Buffer[0];
|
||||
|
||||
switch(State) {
|
||||
case STATE_IDLE:
|
||||
case STATE_HALT:
|
||||
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
|
||||
/* We received a REQA or WUPA command, so wake up. */
|
||||
State = STATE_READY1;
|
||||
return BitCount;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READY1:
|
||||
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
|
||||
State = STATE_READY1;
|
||||
return BitCount;
|
||||
} else if (Cmd == ISO14443A_CMD_SELECT_CL1) {
|
||||
/* Load UID CL1 and perform anticollision. Since
|
||||
* MF Ultralight use a double-sized UID, the first byte
|
||||
* of CL1 has to be the cascade-tag byte. */
|
||||
uint8_t UidCL1[ISO14443A_CL_UID_SIZE] = { [0] = ISO14443A_UID0_CT };
|
||||
|
||||
MemoryReadBlock(&UidCL1[1], UID_CL1_ADDRESS, UID_CL1_SIZE);
|
||||
|
||||
if (ISO14443ASelect(Buffer, &BitCount, UidCL1, SAK_CL1_VALUE)) {
|
||||
/* CL1 stage has ended successfully */
|
||||
State = STATE_READY2;
|
||||
}
|
||||
|
||||
return BitCount;
|
||||
} else {
|
||||
/* Unknown command. Enter halt state */
|
||||
State = STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_READY2:
|
||||
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
|
||||
State = STATE_READY1;
|
||||
return BitCount;
|
||||
} else if (Cmd == ISO14443A_CMD_SELECT_CL2) {
|
||||
/* Load UID CL2 and perform anticollision */
|
||||
uint8_t UidCL2[ISO14443A_CL_UID_SIZE];
|
||||
|
||||
MemoryReadBlock(UidCL2, UID_CL2_ADDRESS, UID_CL2_SIZE);
|
||||
|
||||
if (ISO14443ASelect(Buffer, &BitCount, UidCL2, SAK_CL2_VALUE)) {
|
||||
/* CL2 stage has ended successfully. This means
|
||||
* our complete UID has been sent to the reader. */
|
||||
State = STATE_ACTIVE;
|
||||
}
|
||||
|
||||
return BitCount;
|
||||
} else {
|
||||
/* Unknown command. Enter halt state */
|
||||
State = STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_ACTIVE:
|
||||
if (ISO14443AWakeUp(Buffer, &BitCount, ATQA_VALUE)) {
|
||||
State = STATE_READY1;
|
||||
return BitCount;
|
||||
} else if (Cmd == CMD_READ) {
|
||||
uint8_t PageAddress = Buffer[1];
|
||||
|
||||
if (ISO14443ACheckCRCA(Buffer, CMD_READ_FRAME_SIZE)) {
|
||||
if ( (PageAddress >= PAGE_READ_MIN)
|
||||
&& (PageAddress <= PAGE_READ_MAX) ) {
|
||||
/* TODO: Missing address wrap around behaviour.
|
||||
* Implement using a for-loop copying 4 bytes each iteration
|
||||
* and mask pageaddress */
|
||||
MemoryReadBlock(Buffer, PageAddress * BYTES_PER_PAGE, BYTES_PER_READ);
|
||||
ISO14443AAppendCRCA(Buffer, BYTES_PER_READ);
|
||||
return (BYTES_PER_READ + ISO14443A_CRCA_SIZE) * 8;
|
||||
} else {
|
||||
Buffer[0] = NAK_INVALID_ARG;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else {
|
||||
Buffer[0] = NAK_CRC_ERROR;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else if (Cmd == CMD_WRITE) {
|
||||
/* This is a write command containing 4 bytes of data that
|
||||
* should be written to the given page address. */
|
||||
uint8_t PageAddress = Buffer[1];
|
||||
if (ISO14443ACheckCRCA(Buffer, CMD_WRITE_FRAME_SIZE)) {
|
||||
/* CRC check passed */
|
||||
if ( (PageAddress >= PAGE_WRITE_MIN)
|
||||
&& (PageAddress <= PAGE_WRITE_MAX) ) {
|
||||
/* PageAddress is within bounds. */
|
||||
|
||||
if (!ActiveConfiguration.ReadOnly) {
|
||||
MemoryWriteBlock(&Buffer[2], PageAddress * BYTES_PER_PAGE, BYTES_PER_WRITE);
|
||||
} else {
|
||||
/* If the chameleon is in read only mode, it silently
|
||||
* ignores any attempt to write data. */
|
||||
}
|
||||
|
||||
Buffer[0] = ACK_VALUE;
|
||||
return ACK_FRAME_SIZE;
|
||||
|
||||
} else {
|
||||
Buffer[0] = NAK_INVALID_ARG;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else {
|
||||
Buffer[0] = NAK_CRC_ERROR;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else if (Cmd == CMD_COMPAT_WRITE) {
|
||||
/* The Mifare compatbility write command is a 2-frame command.
|
||||
* The first frame contains the page-address and the second frame
|
||||
* holds the data. */
|
||||
uint8_t PageAddress = Buffer[1];
|
||||
|
||||
if (ISO14443ACheckCRCA(Buffer, CMD_COMPAT_WRITE_FRAME_SIZE)) {
|
||||
if ( (PageAddress >= PAGE_WRITE_MIN)
|
||||
&& (PageAddress <= PAGE_WRITE_MAX) ) {
|
||||
/* CRC check passed and page-address is within bounds.
|
||||
* Store address and proceed to receiving the data. */
|
||||
CompatWritePageAddress = PageAddress;
|
||||
State = STATE_COMPAT_WRITE;
|
||||
|
||||
Buffer[0] = ACK_VALUE;
|
||||
return ACK_FRAME_SIZE;
|
||||
} else {
|
||||
Buffer[0] = NAK_INVALID_ARG;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else {
|
||||
Buffer[0] = NAK_CRC_ERROR;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else if (Cmd == CMD_HALT) {
|
||||
/* Halts the tag. According to the ISO14443, the second
|
||||
* byte is supposed to be 0. */
|
||||
if (Buffer[1] == 0) {
|
||||
if (ISO14443ACheckCRCA(Buffer, 2)) {
|
||||
/* According to ISO14443, we must not send anything
|
||||
* in order to acknowledge the HALT command. */
|
||||
State = STATE_HALT;
|
||||
return ISO14443A_APP_NO_RESPONSE;
|
||||
} else {
|
||||
Buffer[0] = NAK_CRC_ERROR;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else {
|
||||
Buffer[0] = NAK_INVALID_ARG;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
} else {
|
||||
/* Unknown command. Enter halt state */
|
||||
State = STATE_IDLE;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_COMPAT_WRITE:
|
||||
/* Compatibility write. Receiving 16 bytes of data of which 4 bytes are valid. */
|
||||
if (ISO14443ACheckCRCA(Buffer, BYTES_PER_COMPAT_WRITE)) {
|
||||
/* We don't perform any checks here. You will be able to program the
|
||||
* whole memory. Also there is no OTP behaviour. */
|
||||
if (!ActiveConfiguration.ReadOnly) {
|
||||
MemoryWriteBlock(Buffer, CompatWritePageAddress * BYTES_PER_PAGE, BYTES_PER_WRITE);
|
||||
} else {
|
||||
/* If we are told to be read only, we silently ignore the write command
|
||||
* and pretend to have written data. */
|
||||
}
|
||||
|
||||
State = STATE_ACTIVE;
|
||||
Buffer[0] = ACK_VALUE;
|
||||
return ACK_FRAME_SIZE;
|
||||
} else {
|
||||
State = STATE_ACTIVE;
|
||||
Buffer[0] = NAK_CRC_ERROR;
|
||||
return NAK_FRAME_SIZE;
|
||||
}
|
||||
|
||||
default:
|
||||
/* Unknown state? Should never happen. */
|
||||
break;
|
||||
}
|
||||
|
||||
/* No response has been sent, when we reach here */
|
||||
return ISO14443A_APP_NO_RESPONSE;
|
||||
}
|
||||
|
||||
void MifareUltralightGetUid(ConfigurationUidType Uid)
|
||||
{
|
||||
/* Read UID from memory */
|
||||
MemoryReadBlock(&Uid[0], UID_CL1_ADDRESS, UID_CL1_SIZE);
|
||||
MemoryReadBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE);
|
||||
}
|
||||
|
||||
void MifareUltralightSetUid(ConfigurationUidType Uid)
|
||||
{
|
||||
/* Calculate check bytes and write everything into memory */
|
||||
uint8_t BCC1 = ISO14443A_UID0_CT ^ Uid[0] ^ Uid[1] ^ Uid[2];
|
||||
uint8_t BCC2 = Uid[3] ^ Uid[4] ^ Uid[5] ^ Uid[6];
|
||||
|
||||
MemoryWriteBlock(&Uid[0], UID_CL1_ADDRESS, UID_CL1_SIZE);
|
||||
MemoryWriteBlock(&BCC1, UID_BCC1_ADDRESS, ISO14443A_CL_BCC_SIZE);
|
||||
MemoryWriteBlock(&Uid[UID_CL1_SIZE], UID_CL2_ADDRESS, UID_CL2_SIZE);
|
||||
MemoryWriteBlock(&BCC2, UID_BCC2_ADDRESS, ISO14443A_CL_BCC_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* MifareUltralight.h
|
||||
*
|
||||
* Created on: 20.03.2013
|
||||
* Author: skuser
|
||||
*/
|
||||
|
||||
#ifndef MIFAREULTRALIGHT_H_
|
||||
#define MIFAREULTRALIGHT_H_
|
||||
|
||||
#include "Application.h"
|
||||
#include "ISO14443-3A.h"
|
||||
|
||||
#define MIFARE_ULTRALIGHT_UID_SIZE ISO14443A_UID_SIZE_DOUBLE
|
||||
#define MIFARE_ULTRALIGHT_MEM_SIZE 64
|
||||
|
||||
void MifareUltralightAppInit(void);
|
||||
void MifareUltralightAppReset(void);
|
||||
void MifareUltralightAppTask(void);
|
||||
|
||||
uint16_t MifareUltralightAppProcess(uint8_t* Buffer, uint16_t BitCount);
|
||||
|
||||
void MifareUltralightGetUid(ConfigurationUidType Uid);
|
||||
void MifareUltralightSetUid(ConfigurationUidType Uid);
|
||||
|
||||
|
||||
|
||||
#endif /* MIFAREULTRALIGHT_H_ */
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Battery.h
|
||||
*
|
||||
* Created on: 20.08.2014
|
||||
* Author: sk
|
||||
*/
|
||||
|
||||
#ifndef BATTERY_H_
|
||||
#define BATTERY_H_
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#define BATTERY_PORT PORTD
|
||||
#define BATTERY_STAT_PIN PIN4_bm
|
||||
#define BATTERY_STAT_PINCTRL PIN4CTRL
|
||||
#define BATTERY_PORT_MASK (BATTERY_STAT_PIN)
|
||||
|
||||
INLINE void BatteryInit(void)
|
||||
{
|
||||
BATTERY_PORT.DIRCLR = BATTERY_PORT_MASK;
|
||||
BATTERY_PORT.BATTERY_STAT_PINCTRL = PORT_OPC_PULLUP_gc;
|
||||
}
|
||||
|
||||
INLINE bool BatteryIsCharging(void)
|
||||
{
|
||||
if (!(BATTERY_PORT.IN & BATTERY_STAT_PIN))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* BATTERY_H_ */
|
||||
@@ -0,0 +1,217 @@
|
||||
#include "Button.h"
|
||||
#include "Random.h"
|
||||
#include "Common.h"
|
||||
#include "Settings.h"
|
||||
#include "Memory.h"
|
||||
#include "Map.h"
|
||||
#include "Application/Application.h"
|
||||
|
||||
#define BUTTON_PORT PORTA
|
||||
#define BUTTON_L PIN3_bm
|
||||
#define BUTTON_R PIN6_bm
|
||||
#define BUTTON_L_PINCTRL PIN3CTRL
|
||||
#define BUTTON_R_PINCTRL PIN6CTRL
|
||||
#define BUTTON_MASK (BUTTON_L | BUTTON_R)
|
||||
|
||||
#define LONG_PRESS_TICK_COUNT 10
|
||||
|
||||
static const MapEntryType PROGMEM ButtonActionMap[] = {
|
||||
{ .Id = BUTTON_ACTION_NONE, .Text = "NONE" },
|
||||
{ .Id = BUTTON_ACTION_UID_RANDOM, .Text = "UID_RANDOM" },
|
||||
{ .Id = BUTTON_ACTION_UID_LEFT_INCREMENT, .Text = "UID_LEFT_INCREMENT" },
|
||||
{ .Id = BUTTON_ACTION_UID_RIGHT_INCREMENT, .Text = "UID_RIGHT_INCREMENT" },
|
||||
{ .Id = BUTTON_ACTION_UID_LEFT_DECREMENT, .Text = "UID_LEFT_DECREMENT" },
|
||||
{ .Id = BUTTON_ACTION_UID_RIGHT_DECREMENT, .Text = "UID_RIGHT_DECREMENT" },
|
||||
{ .Id = BUTTON_ACTION_CYCLE_SETTINGS, .Text = "CYCLE_SETTINGS" },
|
||||
{ .Id = BUTTON_ACTION_STORE_MEM, .Text = "STORE_MEM" },
|
||||
{ .Id = BUTTON_ACTION_RECALL_MEM, .Text = "RECALL_MEM" },
|
||||
};
|
||||
|
||||
static void ExecuteButtonAction(ButtonActionEnum ButtonAction)
|
||||
{
|
||||
uint8_t UidBuffer[32];
|
||||
|
||||
if (ButtonAction == BUTTON_ACTION_UID_RANDOM) {
|
||||
for (uint8_t i=0; i<ActiveConfiguration.UidSize; i++) {
|
||||
UidBuffer[i] = RandomGetByte();
|
||||
}
|
||||
|
||||
ApplicationSetUid(UidBuffer);
|
||||
} else if (ButtonAction == BUTTON_ACTION_UID_LEFT_INCREMENT) {
|
||||
ApplicationGetUid(UidBuffer);
|
||||
bool Carry = 1;
|
||||
uint8_t i;
|
||||
|
||||
for (i=0; i<ActiveConfiguration.UidSize; i++) {
|
||||
if (Carry) {
|
||||
if (UidBuffer[i] == 0xFF) {
|
||||
Carry = 1;
|
||||
} else {
|
||||
Carry = 0;
|
||||
}
|
||||
|
||||
UidBuffer[i] = (UidBuffer[i] + 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationSetUid(UidBuffer);
|
||||
} else if (ButtonAction == BUTTON_ACTION_UID_RIGHT_INCREMENT) {
|
||||
ApplicationGetUid(UidBuffer);
|
||||
bool Carry = 1;
|
||||
uint8_t i = ActiveConfiguration.UidSize;
|
||||
|
||||
while(i-- > 0) {
|
||||
if (Carry) {
|
||||
if (UidBuffer[i] == 0xFF) {
|
||||
Carry = 1;
|
||||
} else {
|
||||
Carry = 0;
|
||||
}
|
||||
|
||||
UidBuffer[i] = (UidBuffer[i] + 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationSetUid(UidBuffer);
|
||||
} else if (ButtonAction == BUTTON_ACTION_UID_LEFT_DECREMENT) {
|
||||
ApplicationGetUid(UidBuffer);
|
||||
bool Carry = 1;
|
||||
uint8_t i;
|
||||
|
||||
for (i=0; i<ActiveConfiguration.UidSize; i++) {
|
||||
if (Carry) {
|
||||
if (UidBuffer[i] == 0x00) {
|
||||
Carry = 1;
|
||||
} else {
|
||||
Carry = 0;
|
||||
}
|
||||
|
||||
UidBuffer[i] = (UidBuffer[i] - 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationSetUid(UidBuffer);
|
||||
} else if (ButtonAction == BUTTON_ACTION_UID_RIGHT_DECREMENT) {
|
||||
ApplicationGetUid(UidBuffer);
|
||||
bool Carry = 1;
|
||||
uint8_t i = ActiveConfiguration.UidSize;
|
||||
|
||||
while(i-- > 0) {
|
||||
if (Carry) {
|
||||
if (UidBuffer[i] == 0x00) {
|
||||
Carry = 1;
|
||||
} else {
|
||||
Carry = 0;
|
||||
}
|
||||
|
||||
UidBuffer[i] = (UidBuffer[i] - 1) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
ApplicationSetUid(UidBuffer);
|
||||
} else if (ButtonAction == BUTTON_ACTION_CYCLE_SETTINGS) {
|
||||
SettingsCycle();
|
||||
} else if (ButtonAction == BUTTON_ACTION_STORE_MEM) {
|
||||
MemoryStore();
|
||||
} else if (ButtonAction == BUTTON_ACTION_RECALL_MEM) {
|
||||
MemoryRecall();
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonInit(void)
|
||||
{
|
||||
BUTTON_PORT.DIRCLR = BUTTON_MASK;
|
||||
BUTTON_PORT.BUTTON_R_PINCTRL = PORT_OPC_PULLUP_gc;
|
||||
BUTTON_PORT.BUTTON_L_PINCTRL = PORT_OPC_PULLUP_gc;
|
||||
}
|
||||
|
||||
void ButtonTick(void)
|
||||
{
|
||||
static uint8_t ButtonRPressTick = 0;
|
||||
static uint8_t ButtonLPressTick = 0;
|
||||
uint8_t ThisButtonState = ~BUTTON_PORT.IN;
|
||||
|
||||
if (ThisButtonState & BUTTON_R) {
|
||||
/* Button is currently pressed */
|
||||
if (ButtonRPressTick < LONG_PRESS_TICK_COUNT) {
|
||||
/* Count ticks while button is being pressed */
|
||||
ButtonRPressTick++;
|
||||
} else if (ButtonRPressTick == LONG_PRESS_TICK_COUNT) {
|
||||
/* Long button press detected execute button action and advance PressTickCounter
|
||||
* to an invalid state. */
|
||||
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_R_PRESS_LONG]);
|
||||
ButtonRPressTick++;
|
||||
} else {
|
||||
/* Button is still pressed, ignore */
|
||||
}
|
||||
} else if (!(ThisButtonState & BUTTON_MASK)) {
|
||||
/* Button is currently not being pressed. Check if PressTickCounter contains
|
||||
* a recent short button press. */
|
||||
if ( (ButtonRPressTick > 0) && (ButtonRPressTick <= LONG_PRESS_TICK_COUNT) ) {
|
||||
/* We have a short button press */
|
||||
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_R_PRESS_SHORT]);
|
||||
}
|
||||
|
||||
ButtonRPressTick = 0;
|
||||
}
|
||||
|
||||
if (ThisButtonState & BUTTON_L) {
|
||||
/* Button is currently pressed */
|
||||
if (ButtonLPressTick < LONG_PRESS_TICK_COUNT) {
|
||||
/* Count ticks while button is being pressed */
|
||||
ButtonLPressTick++;
|
||||
} else if (ButtonLPressTick == LONG_PRESS_TICK_COUNT) {
|
||||
/* Long button press detected execute button action and advance PressTickCounter
|
||||
* to an invalid state. */
|
||||
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_L_PRESS_LONG]);
|
||||
ButtonLPressTick++;
|
||||
} else {
|
||||
/* Button is still pressed, ignore */
|
||||
}
|
||||
} else if (!(ThisButtonState & BUTTON_MASK)) {
|
||||
/* Button is currently not being pressed. Check if PressTickCounter contains
|
||||
* a recent short button press. */
|
||||
if ( (ButtonLPressTick > 0) && (ButtonLPressTick <= LONG_PRESS_TICK_COUNT) ) {
|
||||
/* We have a short button press */
|
||||
ExecuteButtonAction(GlobalSettings.ActiveSettingPtr->ButtonActions[BUTTON_L_PRESS_SHORT]);
|
||||
}
|
||||
|
||||
ButtonLPressTick = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ButtonGetActionList(char* List, uint16_t BufferSize)
|
||||
{
|
||||
MapToString(ButtonActionMap, sizeof(ButtonActionMap)/sizeof(*ButtonActionMap), List, BufferSize);
|
||||
}
|
||||
|
||||
void ButtonSetActionById(ButtonTypeEnum Type, ButtonActionEnum Action)
|
||||
{
|
||||
#ifndef BUTTON_SETTING_GLOBAL
|
||||
GlobalSettings.ActiveSettingPtr->ButtonActions[Type] = Action;
|
||||
#else
|
||||
/* Write button action to all settings when using global settings */
|
||||
for (uint8_t i=0; i<SETTINGS_COUNT; i++) {
|
||||
GlobalSettings.Settings[i].ButtonActions[Type] = Action;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ButtonGetActionByName(ButtonTypeEnum Type, char* Action, uint16_t BufferSize)
|
||||
{
|
||||
MapIdToText(ButtonActionMap, sizeof(ButtonActionMap)/sizeof(*ButtonActionMap),
|
||||
GlobalSettings.ActiveSettingPtr->ButtonActions[Type], Action, BufferSize);
|
||||
}
|
||||
|
||||
bool ButtonSetActionByName(ButtonTypeEnum Type, const char* Action)
|
||||
{
|
||||
MapIdType Id;
|
||||
|
||||
if (MapTextToId(ButtonActionMap, sizeof(ButtonActionMap)/sizeof(*ButtonActionMap),
|
||||
Action, &Id)) {
|
||||
ButtonSetActionById(Type, Id);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user