mirror of
https://github.com/RfidResearchGroup/ChameleonMini.git
synced 2026-05-12 11:20:37 -07:00
Merge branch 'master' into ISO15693_base
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
#include "Reader14443A.h"
|
||||
#include "Vicinity.h"
|
||||
#include "Sl2s2002.h"
|
||||
|
||||
#include "Sniff14443A.h"
|
||||
|
||||
/* Function wrappers */
|
||||
INLINE void ApplicationInit(void) {
|
||||
|
||||
@@ -141,6 +141,7 @@ uint16_t addParityBits(uint8_t * Buffer, uint16_t BitCount)
|
||||
|
||||
uint16_t removeParityBits(uint8_t * Buffer, uint16_t BitCount)
|
||||
{
|
||||
// Short frame, no parity bit is added
|
||||
if (BitCount == 7)
|
||||
return 7;
|
||||
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
//
|
||||
// Created by Zitai Chen on 25/07/2018.
|
||||
// Application layer for sniffing
|
||||
// Currently only support Autocalibrate
|
||||
//
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <LED.h>
|
||||
#include "Sniff14443A.h"
|
||||
#include "Codec/SniffISO14443-2A.h"
|
||||
extern bool checkParityBits(uint8_t * Buffer, uint16_t BitCount);
|
||||
|
||||
Sniff14443Command Sniff14443CurrentCommand = Sniff14443_Do_Nothing;
|
||||
//bool selected = false;
|
||||
static enum {
|
||||
STATE_IDLE,
|
||||
STATE_REQA,
|
||||
STATE_ATQA,
|
||||
STATE_ANTICOLLI,
|
||||
STATE_SELECT,
|
||||
STATE_UID,
|
||||
STATE_SAK,
|
||||
} SniffState = STATE_IDLE;
|
||||
|
||||
static uint16_t tmp_th = CODEC_THRESHOLD_CALIBRATE_MIN;
|
||||
static uint8_t Thresholds[(CODEC_THRESHOLD_CALIBRATE_MAX - CODEC_THRESHOLD_CALIBRATE_MIN) /
|
||||
CODEC_THRESHOLD_CALIBRATE_STEPS] = {0};
|
||||
|
||||
void Sniff14443AAppInit(void){
|
||||
SniffState = STATE_REQA;
|
||||
// Get current threshold and continue searching from here
|
||||
tmp_th = GlobalSettings.ActiveSettingPtr->ReaderThreshold;
|
||||
}
|
||||
|
||||
void Sniff14443AAppReset(void){
|
||||
SniffState = STATE_IDLE;
|
||||
|
||||
Sniff14443CurrentCommand = Sniff14443_Do_Nothing;
|
||||
}
|
||||
// Currently APPTask and AppTick is not being used
|
||||
void Sniff14443AAppTask(void){/* Empty */}
|
||||
void Sniff14443AAppTick(void){/* Empty */}
|
||||
|
||||
void Sniff14443AAppTimeout(void){
|
||||
Sniff14443AAppReset();
|
||||
}
|
||||
|
||||
INLINE void reset2REQA(void){
|
||||
SniffState = STATE_REQA;
|
||||
LED_PORT.OUTCLR = LED_RED;
|
||||
|
||||
// Mark the current threshold as fail and continue
|
||||
if(tmp_th < CODEC_THRESHOLD_CALIBRATE_MAX){
|
||||
Thresholds[(tmp_th - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] = 0;
|
||||
tmp_th = CodecThresholdIncrement();
|
||||
} else{
|
||||
// mark finish
|
||||
CommandLinePendingTaskFinished(COMMAND_INFO_FALSE, NULL);
|
||||
}
|
||||
}
|
||||
uint16_t Sniff14443AAppProcess(uint8_t* Buffer, uint16_t BitCount){
|
||||
switch (Sniff14443CurrentCommand){
|
||||
case Sniff14443_Do_Nothing: {
|
||||
return 0;
|
||||
}
|
||||
case Sniff14443_Autocalibrate: {
|
||||
|
||||
switch (SniffState) {
|
||||
case STATE_REQA:
|
||||
LED_PORT.OUTCLR = LED_RED;
|
||||
// If received Reader REQA or WUPA
|
||||
if (TrafficSource == TRAFFIC_READER &&
|
||||
(Buffer[0] == 0x26 || Buffer[0] == 0x52)) {
|
||||
SniffState = STATE_ATQA;
|
||||
} else {
|
||||
// Stay in this state, do noting
|
||||
}
|
||||
break;
|
||||
case STATE_ATQA:
|
||||
// ATQA: P RRRR XXXX P XXRX XXXX
|
||||
if (TrafficSource == TRAFFIC_CARD &&
|
||||
BitCount == 2 * 9 &&
|
||||
(Buffer[0] & 0x20) == 0x00 && // Bit6 RFU shall be 0
|
||||
(Buffer[1] & 0xE0) == 0x00 && // bit13-16 RFU shall be 0
|
||||
(Buffer[2] & 0x01) == 0x00 &&
|
||||
checkParityBits(Buffer, BitCount)) {
|
||||
// Assume this is a good ATQA
|
||||
SniffState = STATE_ANTICOLLI;
|
||||
} else {
|
||||
// If not ATQA, but REQA, then stay on this state,
|
||||
// Reset to REQA, save the counter and reset the counter
|
||||
if (TrafficSource == TRAFFIC_READER &&
|
||||
(Buffer[0] == 0x26 || Buffer[0] == 0x52)) {
|
||||
} else {
|
||||
// If not ATQA and not REQA then reset to REQA
|
||||
reset2REQA();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_ANTICOLLI:
|
||||
// SEL: 93/95/97
|
||||
if (TrafficSource == TRAFFIC_READER &&
|
||||
BitCount == 2 * 8 &&
|
||||
(Buffer[0] & 0xf0) == 0x90 &&
|
||||
(Buffer[0] & 0x09) == 0x01) {
|
||||
SniffState = STATE_UID;
|
||||
|
||||
} else {
|
||||
reset2REQA();
|
||||
}
|
||||
break;
|
||||
case STATE_UID:
|
||||
if (TrafficSource == TRAFFIC_CARD &&
|
||||
BitCount == 5 * 9 &&
|
||||
checkParityBits(Buffer, BitCount)) {
|
||||
SniffState = STATE_SELECT;
|
||||
} else {
|
||||
reset2REQA();
|
||||
}
|
||||
break;
|
||||
case STATE_SELECT:
|
||||
|
||||
// SELECT: 9 bytes, SEL = 93/95/97, NVB=70
|
||||
if (TrafficSource == TRAFFIC_READER &&
|
||||
BitCount == 9 * 8 &&
|
||||
(Buffer[0] & 0xf0) == 0x90 &&
|
||||
(Buffer[0] & 0x09) == 0x01 &&
|
||||
Buffer[1] == 0x70) {
|
||||
SniffState = STATE_SAK;
|
||||
} else {
|
||||
// Not valid, reset
|
||||
reset2REQA();
|
||||
}
|
||||
break;
|
||||
case STATE_SAK:
|
||||
// SAK: 1Byte SAK + CRC
|
||||
if (TrafficSource == TRAFFIC_CARD &&
|
||||
BitCount == 3 * 9 &&
|
||||
checkParityBits(Buffer, BitCount)) {
|
||||
if ((Buffer[0] & 0x04) == 0x00) {
|
||||
// UID complete, success SELECTED,
|
||||
// Mark the current threshold as ok and finish
|
||||
// reset
|
||||
SniffState = STATE_REQA;
|
||||
LED_PORT.OUTSET = LED_RED;
|
||||
Thresholds[(tmp_th - CODEC_THRESHOLD_CALIBRATE_MIN) / CODEC_THRESHOLD_CALIBRATE_STEPS] += 1;
|
||||
CommandLinePendingTaskFinished(COMMAND_INFO_OK_WITH_TEXT_ID, NULL);
|
||||
// Send this threshold to terminal
|
||||
char tmpBuf[10];
|
||||
snprintf(tmpBuf, 10, "%4" PRIu16 ": ", tmp_th);
|
||||
TerminalSendString(tmpBuf);
|
||||
|
||||
// Save value to EEPROM
|
||||
SETTING_UPDATE(GlobalSettings.ActiveSettingPtr->ReaderThreshold);
|
||||
|
||||
Sniff14443AAppReset();
|
||||
} else {
|
||||
// UID not complete, goto ANTICOLLI
|
||||
SniffState = STATE_ANTICOLLI;
|
||||
}
|
||||
} else {
|
||||
reset2REQA();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by Zitai Chen on 25/07/2018.
|
||||
//
|
||||
|
||||
#ifndef CHAMELEON_MINI_SNIFF14443A_H
|
||||
#define CHAMELEON_MINI_SNIFF14443A_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void Sniff14443AAppInit(void);
|
||||
void Sniff14443AAppReset(void);
|
||||
void Sniff14443AAppTask(void);
|
||||
void Sniff14443AAppTick(void);
|
||||
void Sniff14443AAppTimeout(void);
|
||||
|
||||
uint16_t Sniff14443AAppProcess(uint8_t* Buffer, uint16_t BitCount);
|
||||
|
||||
typedef enum {
|
||||
Sniff14443_Do_Nothing,
|
||||
Sniff14443_Autocalibrate,
|
||||
} Sniff14443Command;
|
||||
|
||||
#endif //CHAMELEON_MINI_SNIFF14443A_H
|
||||
|
||||
@@ -23,7 +23,7 @@ static volatile struct {
|
||||
} ReaderFieldFlags = { false };
|
||||
|
||||
uint8_t CodecBuffer[CODEC_BUFFER_SIZE];
|
||||
|
||||
uint8_t CodecBuffer2[CODEC_BUFFER_SIZE];
|
||||
// the following three functions prevent sending data directly after turning on the reader field
|
||||
void CodecReaderFieldStart(void) // DO NOT CALL THIS FUNCTION INSIDE APPLICATION!
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "ISO14443-2A.h"
|
||||
#include "Reader14443-2A.h"
|
||||
#include "SniffISO14443-2A.h"
|
||||
#include "ISO15693.h"
|
||||
|
||||
/* Timing definitions for ISO14443A */
|
||||
@@ -25,7 +26,7 @@
|
||||
#define ISO14443A_BIT_RATE_CYCLES 128
|
||||
#define ISO14443A_FRAME_DELAY_PREV1 1236
|
||||
#define ISO14443A_FRAME_DELAY_PREV0 1172
|
||||
#define ISO14443A_RX_PENDING_TIMEOUT 1 // ms
|
||||
#define ISO14443A_RX_PENDING_TIMEOUT 4 // ms
|
||||
|
||||
/* Peripheral definitions */
|
||||
#define CODEC_DEMOD_POWER_PORT PORTB
|
||||
@@ -38,8 +39,8 @@
|
||||
#define CODEC_DEMOD_IN_PINCTRL1 PIN2CTRL
|
||||
#define CODEC_DEMOD_IN_EVMUX0 EVSYS_CHMUX_PORTB_PIN1_gc
|
||||
#define CODEC_DEMOD_IN_EVMUX1 EVSYS_CHMUX_PORTB_PIN2_gc
|
||||
#define CODEC_DEMOD_IN_INT0_VECT PORTB_INT0_vect
|
||||
#define CODEC_DEMOD_IN_INT1_VECT PORTB_INT1_vect
|
||||
#define CODEC_DEMOD_IN_INT0_VECT PORTB_INT0_vect
|
||||
#define CODEC_DEMOD_IN_INT1_VECT PORTB_INT1_vect
|
||||
#define CODEC_LOADMOD_PORT PORTC
|
||||
#define CODEC_LOADMOD_MASK PIN6_bm
|
||||
#define CODEC_CARRIER_IN_PORT PORTC
|
||||
@@ -58,8 +59,9 @@
|
||||
#define CODEC_SUBCARRIER_CCEN_OOK TC1_CCBEN_bm
|
||||
#define CODEC_TIMER_SAMPLING TCD0
|
||||
#define CODEC_TIMER_SAMPLING_CCA_VECT TCD0_CCA_vect
|
||||
#define CODEC_TIMER_SAMPLING_CCB_VECT TCD0_CCB_vect
|
||||
#define CODEC_TIMER_SAMPLING_CCB_VECT TCD0_CCB_vect
|
||||
#define CODEC_TIMER_SAMPLING_CCC_VECT TCD0_CCC_vect
|
||||
#define CODEC_TIMER_SAMPLING_CCD_VECT TCD0_CCD_vect
|
||||
#define CODEC_TIMER_LOADMOD TCE0
|
||||
#define CODEC_TIMER_LOADMOD_OVF_VECT TCE0_OVF_vect
|
||||
#define CODEC_TIMER_LOADMOD_CCA_VECT TCE0_CCA_vect
|
||||
@@ -83,6 +85,8 @@
|
||||
#define CODEC_THRESHOLD_CALIBRATE_STEPS 16
|
||||
#define CODEC_TIMER_TIMESTAMPS TCD1
|
||||
#define CODEC_TIMER_TIMESTAMPS_CCA_VECT TCD1_CCA_vect
|
||||
#define CODEC_TIMER_TIMESTAMPS_CCB_VECT TCD1_CCB_vect
|
||||
|
||||
|
||||
#define CODEC_BUFFER_SIZE 256
|
||||
|
||||
@@ -93,9 +97,11 @@
|
||||
#define Codec8Reg2 GPIOR2
|
||||
#define Codec8Reg3 GPIOR3
|
||||
#define CodecCount16Register1 (*((volatile uint16_t*) &GPIOR4)) /* GPIOR4 & GPIOR5 */
|
||||
#define CodecCount16Register2 (*((volatile uint16_t*) &GPIOR6)) /* GPIOR4 & GPIOR5 */
|
||||
#define CodecCount16Register2 (*((volatile uint16_t*) &GPIOR6)) /* GPIOR6 & GPIOR7 */
|
||||
#define CodecPtrRegister1 (*((volatile uint8_t**) &GPIOR8))
|
||||
#define CodecPtrRegister2 (*((volatile uint8_t**) &GPIORA))
|
||||
#define CodecPtrRegister3 (*((volatile uint8_t**) &GPIORC))
|
||||
|
||||
|
||||
extern uint16_t Reader_FWT;
|
||||
|
||||
@@ -107,6 +113,7 @@ typedef enum {
|
||||
} SubcarrierModType;
|
||||
|
||||
extern uint8_t CodecBuffer[CODEC_BUFFER_SIZE];
|
||||
extern uint8_t CodecBuffer2[CODEC_BUFFER_SIZE];
|
||||
|
||||
volatile void (*isr_func_TCD0_CCC_vect)(void);
|
||||
void isr_Reader14443_2A_TCD0_CCC_vect(void);
|
||||
@@ -150,10 +157,14 @@ INLINE void CodecInitCommon(void)
|
||||
CODEC_DEMOD_IN_PORT.CODEC_DEMOD_IN_PINCTRL0 = PORT_ISC_RISING_gc;
|
||||
CODEC_DEMOD_IN_PORT.CODEC_DEMOD_IN_PINCTRL1 = PORT_ISC_FALLING_gc;
|
||||
CODEC_DEMOD_IN_PORT.INT0MASK = 0;
|
||||
CODEC_DEMOD_IN_PORT.INT1MASK = 0;
|
||||
CODEC_DEMOD_IN_PORT.INTCTRL = PORT_INT0LVL_HI_gc | PORT_INT1LVL_HI_gc;
|
||||
EVSYS.CH0MUX = CODEC_DEMOD_IN_EVMUX0;
|
||||
EVSYS.CH1MUX = CODEC_DEMOD_IN_EVMUX1;
|
||||
|
||||
EVSYS.CH2MUX = CODEC_DEMOD_IN_EVMUX0;
|
||||
|
||||
|
||||
/* Configure loadmod pin configuration and use a virtual port configuration
|
||||
* for single instruction cycle access */
|
||||
CODEC_LOADMOD_PORT.DIRSET = CODEC_LOADMOD_MASK;
|
||||
@@ -246,6 +257,7 @@ INLINE void CodecSetLoadmodState(bool bOnOff) {
|
||||
}
|
||||
}
|
||||
|
||||
// Turn on and off the codec Reader field
|
||||
INLINE void CodecSetReaderField(bool bOnOff) { // this is the function for turning on/off the reader field dumbly; before using this function, please consider to use CodecReaderField{Start,Stop}
|
||||
|
||||
if (bOnOff) {
|
||||
@@ -259,6 +271,7 @@ INLINE void CodecSetReaderField(bool bOnOff) { // this is the function for turni
|
||||
}
|
||||
}
|
||||
|
||||
// Get the status of the reader field
|
||||
INLINE bool CodecGetReaderField(void) {
|
||||
return (CODEC_READER_TIMER.CTRLA == TC_CLKSEL_DIV1_gc) && (AWEXC.OUTOVEN == CODEC_READER_MASK);
|
||||
}
|
||||
|
||||
@@ -66,8 +66,8 @@ static void StartDemod(void) {
|
||||
StateRegister = DEMOD_DATA_BIT;
|
||||
|
||||
/* Configure sampling-timer free running and sync to first modulation-pause. */
|
||||
CODEC_TIMER_SAMPLING.CNT = 0;
|
||||
CODEC_TIMER_SAMPLING.PER = SAMPLE_RATE_SYSTEM_CYCLES - 1;
|
||||
CODEC_TIMER_SAMPLING.CNT = 0; // Reset the timer count
|
||||
CODEC_TIMER_SAMPLING.PER = SAMPLE_RATE_SYSTEM_CYCLES - 1; // Set Period regisiter
|
||||
CODEC_TIMER_SAMPLING.CCA = 0xFFFF; /* CCA Interrupt is not active! */
|
||||
CODEC_TIMER_SAMPLING.CTRLA = TC_CLKSEL_DIV1_gc;
|
||||
CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL;
|
||||
@@ -75,7 +75,7 @@ static void StartDemod(void) {
|
||||
CODEC_TIMER_SAMPLING.INTCTRLB = TC_CCAINTLVL_HI_gc;
|
||||
|
||||
/* Start looking out for modulation pause via interrupt. */
|
||||
CODEC_DEMOD_IN_PORT.INTFLAGS = 0x03;
|
||||
CODEC_DEMOD_IN_PORT.INTFLAGS = PORT_INT0IF_bm;
|
||||
CODEC_DEMOD_IN_PORT.INT0MASK = CODEC_DEMOD_IN_MASK0;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,7 @@ ISR (CODEC_DEMOD_IN_INT0_VECT)
|
||||
}
|
||||
|
||||
// ISR(CODEC_DEMOD_IN_INT0_VECT)
|
||||
// Find first pause and start sampling
|
||||
void isr_ISO14443_2A_TCD0_CCC_vect(void)
|
||||
{
|
||||
/* This is the first edge of the first modulation-pause after StartDemod.
|
||||
@@ -119,6 +120,7 @@ void isr_ISO14443_2A_TCD0_CCC_vect(void)
|
||||
CODEC_DEMOD_IN_PORT.INT0MASK = 0;
|
||||
}
|
||||
|
||||
// Sampling with timer and demod
|
||||
ISR(CODEC_TIMER_SAMPLING_CCA_VECT) {
|
||||
/* This interrupt gets called twice for every bit to sample it. */
|
||||
uint8_t SamplePin = CODEC_DEMOD_IN_PORT.IN & CODEC_DEMOD_IN_MASK;
|
||||
@@ -227,6 +229,7 @@ ISR(CODEC_TIMER_SAMPLING_CCA_VECT) {
|
||||
CODEC_TIMER_SAMPLING.CTRLD = TC_EVACT_RESTART_gc | CODEC_TIMER_MODSTART_EVSEL;
|
||||
}
|
||||
|
||||
// Enumulate as a card to send card responds
|
||||
ISR(CODEC_TIMER_LOADMOD_OVF_VECT) {
|
||||
/* Bit rate timer. Output a half bit on the output. */
|
||||
|
||||
@@ -427,6 +430,7 @@ void ISO14443ACodecTask(void) {
|
||||
uint16_t AnswerBitCount = ISO14443A_APP_NO_RESPONSE;
|
||||
|
||||
if (DemodBitCount >= ISO14443A_MIN_BITS_PER_FRAME) {
|
||||
// For logging data
|
||||
LogEntry(LOG_INFO_CODEC_RX_DATA, CodecBuffer, (DemodBitCount+7)/8);
|
||||
LEDHook(LED_CODEC_RX, LED_PULSE);
|
||||
|
||||
|
||||
@@ -95,6 +95,8 @@ INLINE void Insert1(void)
|
||||
*CodecBufferPtr++ = SampleRegister;
|
||||
}
|
||||
|
||||
|
||||
// End of Card-> reader communication and enter frame delay time
|
||||
INLINE void Reader14443A_EOC(void)
|
||||
{
|
||||
CODEC_TIMER_LOADMOD.INTCTRLB = 0;
|
||||
@@ -139,6 +141,7 @@ INLINE void BufferToSequence(void)
|
||||
uint8_t * Buffer = CodecBuffer + CODEC_BUFFER_SIZE / 2;
|
||||
CodecBufferPtr = CodecBuffer;
|
||||
|
||||
// Modified Miller Coding ISO14443-2 8.1.3
|
||||
Insert1(); // SOC
|
||||
Insert0();
|
||||
|
||||
@@ -176,8 +179,8 @@ INLINE void BufferToSequence(void)
|
||||
if (BitCount % 8)
|
||||
CodecBuffer[BitCount / 8] = SampleRegister >> (8 - (BitCount % 8));
|
||||
}
|
||||
|
||||
ISR (TCD0_CCC_vect)
|
||||
// Frame Delay Time PCD to PICC ends
|
||||
ISR (CODEC_TIMER_SAMPLING_CCC_VECT)
|
||||
{
|
||||
isr_func_TCD0_CCC_vect();
|
||||
}
|
||||
@@ -209,6 +212,8 @@ void isr_Reader14443_2A_TCD0_CCC_vect(void)
|
||||
PORTE.OUTTGL = PIN3_bm;
|
||||
}
|
||||
|
||||
// Reader -> card send bits finished
|
||||
// Start Frame delay time PCD to PICC
|
||||
void Reader14443AMillerEOC(void)
|
||||
{
|
||||
CODEC_TIMER_SAMPLING.PER = 5*SAMPLE_RATE_SYSTEM_CYCLES - 1;
|
||||
@@ -218,11 +223,13 @@ void Reader14443AMillerEOC(void)
|
||||
PORTE.OUTTGL = PIN3_bm;
|
||||
}
|
||||
|
||||
// EOC of Card->Reader found
|
||||
ISR(CODEC_TIMER_TIMESTAMPS_CCA_VECT) // EOC found
|
||||
{
|
||||
Reader14443A_EOC();
|
||||
}
|
||||
|
||||
// This interrupt find Card -> Reader SOC
|
||||
ISR(ACA_AC1_vect) // this interrupt either finds the SOC or gets triggered before
|
||||
{
|
||||
ACA.AC1CTRL &= ~AC_INTLVL_HI_gc; // disable this interrupt
|
||||
@@ -231,6 +238,10 @@ ISR(ACA_AC1_vect) // this interrupt either finds the SOC or gets triggered befor
|
||||
CODEC_TIMER_LOADMOD.CTRLA = TC_CLKSEL_DIV1_gc;
|
||||
}
|
||||
|
||||
// Decode the Card -> Reader signal
|
||||
// according to the pause and modulated period
|
||||
// if the half bit duration is modulated, then add 1 to buffer
|
||||
// if the half bit duration is not modulated, then add 0 to buffer
|
||||
ISR(CODEC_TIMER_LOADMOD_CCA_VECT) // pause found
|
||||
{
|
||||
uint8_t tmp = CODEC_TIMER_TIMESTAMPS.CNTL;
|
||||
@@ -303,6 +314,8 @@ void Reader14443ACodecTask(void)
|
||||
|
||||
bool breakflag = false;
|
||||
TmpCodecBuffer[0] >>= 2; // with this (and BitCountTmp = 2), the SOC is ignored
|
||||
|
||||
// Manchester Code ISO14443-2 8.2.5
|
||||
while (!breakflag && BitCountTmp < TotalBitCount)
|
||||
{
|
||||
uint8_t Bit = TmpCodecBuffer[BitCountTmp / 8] & 0x03;
|
||||
@@ -382,6 +395,7 @@ void Reader14443ACodecTask(void)
|
||||
LogEntry(LOG_INFO_CODEC_TX_DATA_W_PARITY, CodecBuffer, (BitCount + 7) / 8);
|
||||
|
||||
/* Set state and start timer for Miller encoding. */
|
||||
// Send bits to card using TCD0_CCB interrupt (See Reader14443-ISR.S)
|
||||
BufferToSequence();
|
||||
State = STATE_MILLER_SEND;
|
||||
CodecBufferPtr = CodecBuffer;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define AWEXC__OUTOVEN 0x088C
|
||||
#define CODEC_READER_TIMER__CTRLA 0x0800
|
||||
|
||||
; For sending reader bits to cards
|
||||
.global TCD0_CCB_vect, Reader14443AMillerEOC
|
||||
TCD0_CCB_vect:
|
||||
push Zero ; 1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Created by Zitai Chen on 05/07/2018.
|
||||
//
|
||||
|
||||
#ifndef CHAMELEON_MINI_SNIFFISO14443_2A_H
|
||||
#define CHAMELEON_MINI_SNIFFISO14443_2A_H
|
||||
|
||||
|
||||
#include "Codec.h"
|
||||
#include "Terminal/CommandLine.h"
|
||||
|
||||
enum RCTraffic {TRAFFIC_READER, TRAFFIC_CARD} TrafficSource;
|
||||
/* Codec Interface */
|
||||
void Sniff14443ACodecInit(void);
|
||||
void Sniff14443ACodecDeInit(void);
|
||||
void Sniff14443ACodecTask(void);
|
||||
|
||||
|
||||
|
||||
#endif //CHAMELEON_MINI_SNIFFISO14443_2A_H
|
||||
@@ -197,14 +197,14 @@ static const PROGMEM ConfigurationType ConfigurationTable[] = {
|
||||
#endif
|
||||
#ifdef CONFIG_ISO14443A_SNIFF_SUPPORT
|
||||
[CONFIG_ISO14443A_SNIFF] = {
|
||||
.CodecInitFunc = ISO14443ACodecInit,
|
||||
.CodecDeInitFunc = ISO14443ACodecDeInit,
|
||||
.CodecTaskFunc = ISO14443ACodecTask,
|
||||
.ApplicationInitFunc = ApplicationInitDummy,
|
||||
.ApplicationResetFunc = ApplicationResetDummy,
|
||||
.ApplicationTaskFunc = ApplicationTaskDummy,
|
||||
.ApplicationTickFunc = ApplicationTickDummy,
|
||||
.ApplicationProcessFunc = ApplicationProcessDummy,
|
||||
.CodecInitFunc = Sniff14443ACodecInit,
|
||||
.CodecDeInitFunc = Sniff14443ACodecDeInit,
|
||||
.CodecTaskFunc = Sniff14443ACodecTask,
|
||||
.ApplicationInitFunc = Sniff14443AAppInit,
|
||||
.ApplicationResetFunc = Sniff14443AAppReset,
|
||||
.ApplicationTaskFunc = Sniff14443AAppTask,
|
||||
.ApplicationTickFunc = Sniff14443AAppTick,
|
||||
.ApplicationProcessFunc = Sniff14443AAppProcess,
|
||||
.ApplicationGetUidFunc = ApplicationGetUidDummy,
|
||||
.ApplicationSetUidFunc = ApplicationSetUidDummy,
|
||||
.UidSize = 0,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
:100000000000B32E0606080800000401320090012B
|
||||
:100000000000402F0606080800000401320090019D
|
||||
:1000100006060808000004013200900106060808E0
|
||||
:1000200000000401320090010606080800000401E7
|
||||
:100030003200900106060808000004013200900119
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,14 @@ typedef enum {
|
||||
LOG_INFO_CODEC_RX_DATA_W_PARITY = 0x42, ///< Currently active codec received data.
|
||||
LOG_INFO_CODEC_TX_DATA_W_PARITY = 0x43, ///< Currently active codec sent data.
|
||||
|
||||
LOG_INFO_CODEC_SNI_READER_DATA = 0x44, //< Sniffing codec receive data from reader
|
||||
LOG_INFO_CODEC_SNI_READER_DATA_W_PARITY = 0x45, //< Sniffing codec receive data from reader
|
||||
|
||||
LOG_INFO_CODEC_SNI_CARD_DATA = 0x46, //< Sniffing codec receive data from card
|
||||
LOG_INFO_CODEC_SNI_CARD_DATA_W_PARITY = 0x47, //< Sniffing codec receive data from card
|
||||
|
||||
|
||||
|
||||
/* App */
|
||||
LOG_INFO_APP_CMD_READ = 0x80, ///< Application processed read command.
|
||||
LOG_INFO_APP_CMD_WRITE = 0x81, ///< Application processed write command.
|
||||
|
||||
@@ -95,8 +95,10 @@ TARGET = Chameleon-Mini
|
||||
OPTIMIZATION = s
|
||||
SRC += $(TARGET).c LUFADescriptors.c System.c Configuration.c Random.c Common.c Memory.c MemoryAsm.S Button.c Log.c Settings.c LED.c Map.c AntennaLevel.c
|
||||
SRC += Terminal/Terminal.c Terminal/Commands.c Terminal/XModem.c Terminal/CommandLine.c
|
||||
SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/Reader14443-ISR.S Codec/ISO15693.c
|
||||
SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Vicinity.c Application/Sl2s2002.c Application/ISO15693-A.c
|
||||
SRC += Codec/Codec.c Codec/ISO14443-2A.c Codec/Reader14443-2A.c Codec/SniffISO14443-2A.c Codec/Reader14443-ISR.S
|
||||
SRC += Application/MifareUltralight.c Application/MifareClassic.c Application/ISO14443-3A.c Application/Crypto1.c Application/Reader14443A.c Application/Sniff14443A.c
|
||||
SRC += Codec/ISO15693.c
|
||||
SRC += Application/Vicinity.c Application/Sl2s2002.c Application/ISO15693-A.c
|
||||
SRC += $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
|
||||
LUFA_PATH = ../LUFA
|
||||
CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -DFLASH_DATA_ADDR=$(FLASH_DATA_ADDR) -DFLASH_DATA_SIZE=$(FLASH_DATA_SIZE) -DSPM_HELPER_ADDR=$(SPM_HELPER_ADDR) -DBUILD_DATE=$(BUILD_DATE) -DCOMMIT_ID=\"$(COMMIT_ID)\" $(SETTINGS)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "Commands.h"
|
||||
#include <stdio.h>
|
||||
#include <avr/pgmspace.h>
|
||||
#include <Settings.h>
|
||||
#include "XModem.h"
|
||||
#include "../Settings.h"
|
||||
#include "../Chameleon-Mini.h"
|
||||
@@ -16,6 +17,7 @@
|
||||
#include "../Codec/Codec.h"
|
||||
|
||||
extern Reader14443Command Reader14443CurrentCommand;
|
||||
extern Sniff14443Command Sniff14443CurrentCommand;
|
||||
|
||||
extern const PROGMEM CommandEntryType CommandTable[];
|
||||
|
||||
@@ -629,15 +631,27 @@ CommandStatusIdType CommandGetField(char* OutMessage)
|
||||
|
||||
CommandStatusIdType CommandExecAutocalibrate(char* OutMessage)
|
||||
{
|
||||
if (GlobalSettings.ActiveSettingPtr->Configuration != CONFIG_ISO14443A_READER)
|
||||
if (GlobalSettings.ActiveSettingPtr->Configuration == CONFIG_ISO14443A_READER){
|
||||
ApplicationReset();
|
||||
|
||||
Reader14443CurrentCommand = Reader14443_Autocalibrate;
|
||||
Reader14443AAppInit();
|
||||
Reader14443ACodecStart();
|
||||
CommandLinePendingTaskTimeout = &Reader14443AAppTimeout;
|
||||
return TIMEOUT_COMMAND;
|
||||
}
|
||||
else if (GlobalSettings.ActiveSettingPtr->Configuration == CONFIG_ISO14443A_SNIFF){
|
||||
ApplicationReset();
|
||||
|
||||
Sniff14443CurrentCommand = Sniff14443_Autocalibrate;
|
||||
Sniff14443AAppInit();
|
||||
CommandLinePendingTaskTimeout = &Sniff14443AAppTimeout;
|
||||
return TIMEOUT_COMMAND;
|
||||
}
|
||||
else {
|
||||
return COMMAND_ERR_INVALID_USAGE_ID;
|
||||
ApplicationReset();
|
||||
}
|
||||
|
||||
Reader14443CurrentCommand = Reader14443_Autocalibrate;
|
||||
Reader14443AAppInit();
|
||||
Reader14443ACodecStart();
|
||||
CommandLinePendingTaskTimeout = &Reader14443AAppTimeout;
|
||||
return TIMEOUT_COMMAND;
|
||||
}
|
||||
|
||||
CommandStatusIdType CommandExecClone(char *OutMessage)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/python3
|
||||
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
@@ -13,15 +13,22 @@ class Device:
|
||||
COMMAND_DOWNLOAD = "DOWNLOAD"
|
||||
COMMAND_SETTING = "SETTING"
|
||||
COMMAND_UID = "UID"
|
||||
COMMAND_GETUID = "GETUID"
|
||||
COMMAND_IDENTIFY = "IDENTIFY"
|
||||
COMMAND_DUMPMFU = "DUMP_MFU"
|
||||
COMMAND_CONFIG = "CONFIG"
|
||||
COMMAND_LOG_DOWNLOAD = "LOGDOWNLOAD"
|
||||
COMMAND_LOG_CLEAR = "LOGCLEAR"
|
||||
COMMAND_LOGMODE = "LOGMODE"
|
||||
COMMAND_LBUTTON = "LBUTTON"
|
||||
COMMAND_LBUTTONLONG = "LBUTTON_LONG"
|
||||
COMMAND_RBUTTON = "RBUTTON"
|
||||
COMMAND_RBUTTONLONG = "RBUTTON_LONG"
|
||||
COMMAND_GREEN_LED = "LEDGREEN"
|
||||
COMMAND_RED_LED = "LEDRED"
|
||||
|
||||
COMMAND_THRESHOLD = "THRESHOLD"
|
||||
COMMAND_UPGRADE = "upgrade"
|
||||
|
||||
STATUS_CODE_OK = 100
|
||||
STATUS_CODE_OK_WITH_TEXT = 101
|
||||
STATUS_CODE_WAITING_FOR_XMODEM = 110
|
||||
@@ -49,33 +56,32 @@ class Device:
|
||||
SUGGEST_CHAR = "?"
|
||||
SET_CHAR = "="
|
||||
GET_CHAR = "?"
|
||||
|
||||
|
||||
def __init__(self, verboseFunc = None):
|
||||
self.verboseFunc = verboseFunc
|
||||
self.serial = serial.Serial(None, 9600, timeout=5.0)
|
||||
self.versionString = ""
|
||||
self.supportedConfs = []
|
||||
|
||||
|
||||
def verboseLog(self, text):
|
||||
if (self.verboseFunc):
|
||||
self.verboseFunc(text)
|
||||
|
||||
|
||||
def listDevices():
|
||||
devices = []
|
||||
|
||||
for port in serial.tools.list_ports.grep("({0:04x}:{1:04x})|({0:04X}:{1:04X})".format(Chameleon.USB_VID, Chameleon.USB_PID)):
|
||||
devices.append(port[0])
|
||||
|
||||
return devices
|
||||
|
||||
def connect(self, comport):
|
||||
return devices
|
||||
|
||||
def connect(self, comport):
|
||||
self.serial.port = comport
|
||||
try:
|
||||
self.serial.open()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if (self.serial.isOpen()):
|
||||
# Send escape key to force clearing the Chameleon's input buffer
|
||||
self.serial.write(b"\x1B")
|
||||
@@ -94,20 +100,20 @@ class Device:
|
||||
return False
|
||||
|
||||
result = self.getCmdSuggestions(self.COMMAND_CONFIG)
|
||||
|
||||
|
||||
if (result['statusCode'] == self.STATUS_CODE_OK_WITH_TEXT):
|
||||
self.supportedConfs = result['response'].split(",")
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def disconnect(self):
|
||||
self.verboseLog("Closing serial port")
|
||||
self.serial.close()
|
||||
|
||||
|
||||
def isConnected(self):
|
||||
return self.serial.isOpen()
|
||||
|
||||
@@ -116,7 +122,7 @@ class Device:
|
||||
data = self.serial.read(size)
|
||||
self.serial.timeout = 5.0
|
||||
return data
|
||||
|
||||
|
||||
def writeCmd(self, cmd):
|
||||
# Execute command
|
||||
cmdLine = cmd + self.LINE_ENDING
|
||||
@@ -130,19 +136,19 @@ class Device:
|
||||
return None
|
||||
else:
|
||||
self.verboseLog("Executing <{}>: {}".format(cmd, status))
|
||||
|
||||
|
||||
statusCode, statusText = status.split(":")
|
||||
statusCode = int(statusCode)
|
||||
|
||||
|
||||
result = {'statusCode': statusCode, 'statusText': statusText, 'response': None}
|
||||
|
||||
|
||||
if (statusCode == self.STATUS_CODE_OK_WITH_TEXT):
|
||||
result['response'] = self.readResponse()
|
||||
elif (statusCode == self.STATUS_CODE_TRUE):
|
||||
result['response'] = True
|
||||
elif (statusCode == self.STATUS_CODE_FALSE):
|
||||
result['response'] = False
|
||||
|
||||
|
||||
return result
|
||||
|
||||
def readResponse(self):
|
||||
@@ -150,27 +156,30 @@ class Device:
|
||||
response = self.serial.readline().decode('ascii').rstrip()
|
||||
self.verboseLog("Response: {}".format(response))
|
||||
return response
|
||||
|
||||
|
||||
def execCmd(self, cmd, args=None):
|
||||
if (args is None):
|
||||
return self.writeCmd("{}".format(cmd))
|
||||
else:
|
||||
return self.writeCmd("{} {}".format(cmd, args))
|
||||
|
||||
|
||||
def getSetCmd(self, cmd, arg=None):
|
||||
# Determine if set or get mode
|
||||
if (arg is None):
|
||||
return self.writeCmd("{}{}".format(cmd, self.GET_CHAR))
|
||||
else:
|
||||
return self.writeCmd("{}{}{}".format(cmd, self.SET_CHAR, arg))
|
||||
|
||||
|
||||
def returnCmd(self, cmd, arg=None):
|
||||
return self.writeCmd("{}".format(cmd))
|
||||
|
||||
def getCmdSuggestions(self, cmd):
|
||||
result = self.getSetCmd(cmd, self.SUGGEST_CHAR)
|
||||
if (result['response'] is not None):
|
||||
result['suggestions'] = result['response'].split(",")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def cmdUploadDump(self, dataStream):
|
||||
if (self.execCmd(self.COMMAND_UPLOAD)['statusCode'] == self.STATUS_CODE_WAITING_FOR_XMODEM):
|
||||
# XMODEM started
|
||||
@@ -201,7 +210,7 @@ class Device:
|
||||
|
||||
def cmdLogMode(self, newLogMode):
|
||||
return self.getSetCmd(self.COMMAND_LOGMODE, newLogMode)
|
||||
|
||||
|
||||
def cmdVersion(self):
|
||||
return self.getSetCmd(self.COMMAND_VERSION)
|
||||
|
||||
@@ -211,6 +220,15 @@ class Device:
|
||||
def cmdUID(self, newUID = None):
|
||||
return self.getSetCmd(self.COMMAND_UID, newUID)
|
||||
|
||||
def cmdGetUID(self):
|
||||
return self.returnCmd(self.COMMAND_GETUID)
|
||||
|
||||
def cmdIdentify(self):
|
||||
return self.returnCmd(self.COMMAND_IDENTIFY)
|
||||
|
||||
def cmdDumpMFU(self):
|
||||
return self.returnCmd(self.COMMAND_DUMPMFU)
|
||||
|
||||
def cmdConfig(self, newConfig = None):
|
||||
if (newConfig == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_CONFIG)
|
||||
@@ -223,20 +241,44 @@ class Device:
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_LBUTTON, newAction)
|
||||
|
||||
def cmdLButtonLong(self, newAction = None):
|
||||
if (newAction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_LBUTTONLONG)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_LBUTTONLONG, newAction)
|
||||
|
||||
def cmdRButton(self, newAction = None):
|
||||
if (newAction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_RBUTTON)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_RBUTTON, newAction)
|
||||
|
||||
def cmdRButtonLong(self, newAction = None):
|
||||
if (newAction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_RBUTTONLONG)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_RBUTTONLONG, newAction)
|
||||
|
||||
def cmdGreenLED(self, newFunction = None):
|
||||
if (newFunction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_GREEN_LED)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_GREEN_LED, newFunction)
|
||||
|
||||
|
||||
def cmdRedLED(self, newFunction = None):
|
||||
if (newFunction == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_RED_LED)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_RED_LED, newFunction)
|
||||
|
||||
def cmdThreshold(self, value):
|
||||
if(value == self.SUGGEST_CHAR):
|
||||
return self.getCmdSuggestions(self.COMMAND_THRESHOLD)
|
||||
else:
|
||||
return self.getSetCmd(self.COMMAND_THRESHOLD, value)
|
||||
|
||||
def cmdUpgrade(self):
|
||||
# Execute command
|
||||
cmdLine = self.COMMAND_UPGRADE + self.LINE_ENDING
|
||||
self.serial.write(cmdLine.encode('ascii'))
|
||||
return 0
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
import binascii
|
||||
import crcmod
|
||||
from enum import Enum
|
||||
|
||||
from Chameleon.MFDESFire import MFDESFireDecode
|
||||
from Chameleon.utils import TrafficSource
|
||||
|
||||
# Parameters for CRC_A
|
||||
CRC_INIT = 0x6363
|
||||
POLY = 0x11021
|
||||
CRC_A_func = crcmod.mkCrcFun(POLY, initCrc=CRC_INIT, xorOut=0)
|
||||
|
||||
|
||||
class ReaderCMD(Enum):
|
||||
NONE = 0
|
||||
SELECT = 1
|
||||
RATS = 2
|
||||
PPS = 3
|
||||
|
||||
|
||||
readerCMD = ReaderCMD.NONE
|
||||
|
||||
# Map card types string to decoder
|
||||
def DummyCardDecoder(data, source):
|
||||
return ""
|
||||
|
||||
CardTypesMap = {
|
||||
"None": {"ApplicationDecoder": DummyCardDecoder},
|
||||
"MFDESFire": {"ApplicationDecoder": MFDESFireDecode},
|
||||
|
||||
}
|
||||
|
||||
|
||||
class BlockData:
|
||||
@staticmethod
|
||||
def isBlockData(byteCount, data):
|
||||
if(byteCount >= 3 and (data[0]& 0xE6) in ReaderTrafficTypes["PCB"]):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def __init__(self, byteCount, data, source, Cardtype):
|
||||
self.byteCount = byteCount
|
||||
self.data = data
|
||||
self.PCB = data[0]
|
||||
self.type = ReaderTrafficTypes["PCB"][self.PCB & 0xE6]
|
||||
self.CID = None
|
||||
self.NAD = None
|
||||
self.INF = None
|
||||
self.source = source
|
||||
self.CRCChecked = CRC_A_check(data)
|
||||
self.CardApplicationDecoder = CardTypesMap[Cardtype]["ApplicationDecoder"]
|
||||
|
||||
if self.CRCChecked:
|
||||
|
||||
hasCID = self.PCB & 0x08 # PCB b4 indicate CID
|
||||
hasNAD = 0
|
||||
|
||||
if (self.type == "IBlock"):
|
||||
hasNAD = self.PCB & 0x04 # IBlock PCB b2 indicate NAD
|
||||
|
||||
byteNext = 1
|
||||
# CID
|
||||
if (hasCID):
|
||||
self.CID = self.data[byteNext]
|
||||
byteNext += 1
|
||||
|
||||
# NAD
|
||||
if (hasNAD):
|
||||
self.NAD = self.data[byteNext]
|
||||
byteNext += 1
|
||||
|
||||
# INF field not empty
|
||||
if(byteNext < byteCount -2):
|
||||
self.INF = self.data[byteNext: byteCount-2]
|
||||
|
||||
|
||||
def decode(self):
|
||||
note = ""
|
||||
# Prologue
|
||||
# PCB
|
||||
note += self.type + " "
|
||||
|
||||
# Block number
|
||||
if ((self.type == "IBlock" or self.type == "RBlock") and self.PCB & 0x01):
|
||||
note += "BlkNo:1 "
|
||||
|
||||
# Chaining?
|
||||
if (self.type == "IBlock" and self.PCB & 0x10):
|
||||
note += "Chaining "
|
||||
|
||||
# ACK/NAK? for R-Block
|
||||
if (self.type == "RBlock"):
|
||||
if (self.PCB & 0x10):
|
||||
note += "NAK "
|
||||
else:
|
||||
note += "ACK "
|
||||
|
||||
# DESEL/WTX for SBlock
|
||||
if (self.type == "SBlock"):
|
||||
if (self.PCB & 0x30 == 0x00):
|
||||
note += "DESEL "
|
||||
elif (self.PCB & 0x30 == 0x30):
|
||||
note += "WTX"
|
||||
|
||||
# CID
|
||||
if (self.CID != None):
|
||||
note += "CID:" + hex(self.CID) + " "
|
||||
|
||||
# NAD
|
||||
if (self.NAD != None):
|
||||
note += "NAD:" + hex(self.NAD) + " "
|
||||
|
||||
# INF
|
||||
if (self.INF != None):
|
||||
note += self.CardApplicationDecoder(self.INF, self.source)
|
||||
# EDC CRC check
|
||||
if not self.CRCChecked:
|
||||
note += " WRONG CRC "
|
||||
|
||||
|
||||
return note
|
||||
|
||||
|
||||
ReaderTrafficTypes = {
|
||||
"SEL":{
|
||||
0x93: "SEL_CL1 ",
|
||||
0x95: "SEL_CL2 ",
|
||||
0x97: "SEL_CL3 ",
|
||||
},
|
||||
# 1 byte commands
|
||||
"SHORTFRAME": {
|
||||
0x26: "REQA",
|
||||
0x52: "WUPA",
|
||||
0x35: "Optional Timeslot Method",
|
||||
# 40 - 4F Proprietary
|
||||
# 78 - 7F proprietary
|
||||
# Other RFU
|
||||
},
|
||||
"FSDI":{
|
||||
0x0: "FSD:16 ",
|
||||
0x1: "FSD:24 ",
|
||||
0x2: "FSD:32 ",
|
||||
0x3: "FSD:40 ",
|
||||
0x4: "FSD:48 ",
|
||||
0x5: "FSD:64 ",
|
||||
0x6: "FSD:96 ",
|
||||
0x7: "FSD:128 ",
|
||||
0x8: "FSD:256 "
|
||||
},
|
||||
"PCB":{
|
||||
# IBlock 000X XX1X
|
||||
0x02: "IBlock", # 000X X01X
|
||||
0x08: "IBlock", # 000X X11X
|
||||
# RBlock 101X X01X
|
||||
0xA2: "RBlock", # 101X X01X
|
||||
# SBlock 11XX X010
|
||||
0xC2: "SBlock", # 110X X010
|
||||
0xE2: "SBlock" # 111X X010
|
||||
}
|
||||
}
|
||||
|
||||
CardTrafficTypes = {
|
||||
"SAK":{
|
||||
0x04: "UID NOT Complete ",
|
||||
0x24: "UID NOT Complete, PICC compliant with 14443-4",
|
||||
0x20: "UID complete, PICC compliant with 14443-4 ",
|
||||
0x00: "UID complete, PICC NOT compliant with 14443-4"
|
||||
},
|
||||
"FSCI": {
|
||||
0x0: "FSC:16 ",
|
||||
0x1: "FSC:24 ",
|
||||
0x2: "FSC:32 ",
|
||||
0x3: "FSC:40 ",
|
||||
0x4: "FSC:48 ",
|
||||
0x5: "FSC:64 ",
|
||||
0x6: "FSC:96 ",
|
||||
0x7: "FSC:128 ",
|
||||
0x8: "FSC:256 "
|
||||
}
|
||||
}
|
||||
|
||||
def CRC_A(data):
|
||||
return CRC_A_func(data)
|
||||
|
||||
def CRC_A_check(data):
|
||||
datalen = len(data)
|
||||
# Short frame/SAK or no space for CRC skip check
|
||||
if(datalen < 3 ):
|
||||
return True
|
||||
|
||||
crc = CRC_A(bytearray(data[0:datalen-2])).to_bytes(2,'little')
|
||||
if (data[datalen-2:datalen] == crc):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def parseReader_3(data):
|
||||
global readerCMD
|
||||
byteCount = len(data)
|
||||
note = ""
|
||||
|
||||
# short frame commands
|
||||
if (byteCount == 1 and data[0] in ReaderTrafficTypes["SHORTFRAME"]):
|
||||
note += ReaderTrafficTypes["SHORTFRAME"][data[0]]
|
||||
|
||||
# ANTICOLLISION command
|
||||
elif (byteCount < 9 and byteCount > 1 and data[0] in ReaderTrafficTypes["SEL"] and data[1] & 0x88 == 0):
|
||||
note += "ATCOLI - "
|
||||
note += ReaderTrafficTypes["SEL"][data[0]]
|
||||
# note += "UID_CLn:" + binascii.hexlify(data[2:7]).decode() + " "
|
||||
# note += str((data[1] >> 4) & 0x0f) + "bytes + " + str(data[1] & 0x0f) + "bits "
|
||||
|
||||
# SELECT Command
|
||||
elif (byteCount == 9 and data[0] in ReaderTrafficTypes["SEL"] and data[1] == 0x70):
|
||||
readerCMD = ReaderCMD.SELECT
|
||||
note += "SELECT - "
|
||||
note += ReaderTrafficTypes["SEL"][data[0]]
|
||||
note += "UID_CLn:" + binascii.hexlify(data[2:6]).decode() + " "
|
||||
# Check CRC for SELECT
|
||||
if (not CRC_A_check(data)):
|
||||
note += " WRONG CRC "
|
||||
# note += "7bytes + 0bits "
|
||||
# note += "CRC_A:"+data[7:9]
|
||||
# HALT Command
|
||||
elif (byteCount == 4 and data[0] == 0x50 and data[1] == 0x00):
|
||||
note += "HALT"
|
||||
|
||||
return note
|
||||
|
||||
def parseReader_4(data, Cardtype):
|
||||
global readerCMD
|
||||
byteCount = len(data)
|
||||
note = ""
|
||||
|
||||
# RATS
|
||||
if (byteCount == 4 and data[0] == 0xe0 and ((data[1] & 0x0f) < 15) and (
|
||||
(data[1] & 0xf0 >> 8) in ReaderTrafficTypes["FSDI"])):
|
||||
note += "RATS - "
|
||||
note += ReaderTrafficTypes["FSDI"][data[1] >> 8]
|
||||
note += "CID:" + hex(data[1] & 0x0f) + " "
|
||||
if (not CRC_A_check(data)):
|
||||
note += " WRONG CRC "
|
||||
else:
|
||||
readerCMD = ReaderCMD.RATS
|
||||
|
||||
# PPS Protocol and parameter selection request
|
||||
# PSS0 only
|
||||
elif (byteCount == 4 and (data[0] & 0xf0 == 0xd0) and (data[1] == 0x01)):
|
||||
note += "PSS0 - "
|
||||
note += "CID:" + str(data[1] & 0x0f) + " "
|
||||
readerCMD = ReaderCMD.PPS
|
||||
# PSS0+1
|
||||
elif (byteCount == 5 and (data[0] & 0xf0 == 0xd0) and (data[1] == 0x11) and (data[2] & 0xf0 == 0x00)):
|
||||
note += "PSS0+1 - "
|
||||
note += "CID:" + str(data[1] & 0x0f) + " "
|
||||
note += "DSI:" + str(pow(2, (data[2] >> 2) & 0x03)) + " "
|
||||
note += "DRI:" + str(pow(2, data[2] & 0x03)) + " "
|
||||
|
||||
# Half-duplex block transmission
|
||||
# PCB bit mask: 0b11100110
|
||||
elif (BlockData.isBlockData(byteCount, data)):
|
||||
blockData = BlockData(byteCount,data, TrafficSource.Reader, Cardtype)
|
||||
note = blockData.decode()
|
||||
|
||||
return note
|
||||
|
||||
def parseCard_3(data):
|
||||
byteCount = len(data)
|
||||
note = ""
|
||||
|
||||
# ATQA: RRRR XXXX XXRX XXXX
|
||||
if(byteCount == 2 and (data[0] & 0x20 == 0x00) and (data[1] & 0xf0 == 0x00)):
|
||||
note += "ATQA - "
|
||||
note += binascii.hexlify(data).decode()
|
||||
# SAK
|
||||
elif (byteCount == 3 and readerCMD == ReaderCMD.SELECT and ((data[0] & (0x24)) in CardTrafficTypes["SAK"])):
|
||||
note += "SAK - "
|
||||
note += CardTrafficTypes["SAK"][(data[2] & 0x24)]
|
||||
if not CRC_A_check(data):
|
||||
note += " WRONG CRC "
|
||||
# UID
|
||||
elif (byteCount == 5 and (data[0] ^ data[1] ^ data[2] ^ data[3]) == data[4] ):
|
||||
note += "UID Resp - CLn "
|
||||
|
||||
return note
|
||||
|
||||
def parseCard_4(data, Cardtype):
|
||||
global readerCMD
|
||||
byteCount = len(data)
|
||||
note = ""
|
||||
|
||||
# ATS
|
||||
# TL + T0 + TA + TB + TC + T1 ... + CRC
|
||||
# TL: length without CRC
|
||||
# ATS without data
|
||||
if(byteCount == 3 and readerCMD == ReaderCMD.RATS and data[0] == (byteCount-2)):
|
||||
note += "ATS - NO DATA"
|
||||
# ATS with data mush have T0, T0 b8=0
|
||||
elif (byteCount > 3 and readerCMD == ReaderCMD.RATS and data[0] == (byteCount-2)
|
||||
and data[1] & 0x80 == 0x00
|
||||
and data[1] & 0x0f in CardTrafficTypes["FSCI"]):
|
||||
note += "ATS - "
|
||||
# Decode T0
|
||||
hasTA = data[1] & 0x10
|
||||
hasTB = data[1] & 0x20
|
||||
hasTC = data[1] & 0x40
|
||||
note += CardTrafficTypes["FSCI"][data[1] & 0x0f]
|
||||
|
||||
# Which byte to decode next
|
||||
byteNext = 2 # T0 Decoded, next is TA/TB/TC
|
||||
# TA b4=0
|
||||
if (hasTA and data[byteNext] & 0x08 == 0x00):
|
||||
note += "TA:" + hex(data[byteNext]) + " "
|
||||
byteNext += 1
|
||||
|
||||
if (hasTB):
|
||||
note += "TB:" + hex(data[byteNext]) + " "
|
||||
byteNext += 1
|
||||
|
||||
# TC b3-8=0
|
||||
if (hasTC and data[byteNext] & 0xFC == 0x00):
|
||||
note += "TC:" + hex(data[byteNext]) + " "
|
||||
byteNext += 1
|
||||
|
||||
# Check CRC_A
|
||||
if not CRC_A_check(data):
|
||||
note += " WRONG CRC "
|
||||
# Application Data
|
||||
elif (BlockData.isBlockData(byteCount, data)):
|
||||
blockData = BlockData(byteCount,data, TrafficSource.Card, Cardtype)
|
||||
note = blockData.decode()
|
||||
|
||||
readerCMD = ReaderCMD.NONE
|
||||
|
||||
return note
|
||||
|
||||
def parseReader(data, Cardtype):
|
||||
return parseReader_3(data) + parseReader_4(data, Cardtype)
|
||||
|
||||
def parseCard(data, Cardtype):
|
||||
return parseCard_3(data) + parseCard_4(data, Cardtype)
|
||||
@@ -2,6 +2,41 @@
|
||||
|
||||
import struct
|
||||
import binascii
|
||||
import math
|
||||
import Chameleon.ISO14443 as iso14443_3
|
||||
|
||||
def checkParityBit(data):
|
||||
byteCount = len(data)
|
||||
# Short frame, no parityBit
|
||||
if (byteCount == 1):
|
||||
return (True, data)
|
||||
|
||||
# 9 bit is a group, validate bit count is calculated below
|
||||
bitCount = int((byteCount*8)/9) * 9
|
||||
parsedData = bytearray(int(bitCount/9))
|
||||
|
||||
oneCounter = 0 # Counter for count ones in a byte
|
||||
for i in range(0, bitCount):
|
||||
# Get bit i in data
|
||||
byteIndex = math.floor(i/8)
|
||||
bitIndex = i % 8
|
||||
bit = (data[byteIndex] >> bitIndex) & 0x01
|
||||
|
||||
# Check parityBit
|
||||
# Current bit is parityBit
|
||||
if(i % 9 == 8):
|
||||
# Even number of ones in current byte
|
||||
if(oneCounter % 2 and bit == 1):
|
||||
return (False, data)
|
||||
# Odd number of ones in current byte
|
||||
elif((not oneCounter % 2) and bit == 0):
|
||||
return (False, data)
|
||||
oneCounter = 0
|
||||
# Current bit is normal bit
|
||||
else:
|
||||
oneCounter += bit
|
||||
parsedData[int(i/9)] |= bit << (i%9)
|
||||
return (True, parsedData)
|
||||
|
||||
def noDecoder(data):
|
||||
return ""
|
||||
@@ -12,6 +47,13 @@ def textDecoder(data):
|
||||
def binaryDecoder(data):
|
||||
return binascii.hexlify(data).decode()
|
||||
|
||||
def binaryParityDecoder(data):
|
||||
isValid, checkedData = checkParityBit(data)
|
||||
if(isValid):
|
||||
return binascii.hexlify(checkedData).decode()
|
||||
else:
|
||||
return binascii.hexlify(checkedData).decode()+"!"
|
||||
|
||||
eventTypes = {
|
||||
0x00: { 'name': 'EMPTY', 'decoder': noDecoder },
|
||||
0x10: { 'name': 'GENERIC', 'decoder': textDecoder },
|
||||
@@ -25,6 +67,12 @@ eventTypes = {
|
||||
0x42: { 'name': 'CODEC RX W/PARITY', 'decoder': binaryDecoder },
|
||||
0x43: { 'name': 'CODEC TX W/PARITY', 'decoder': binaryDecoder },
|
||||
|
||||
0x44: { 'name': 'CODEC RX SNI READER', 'decoder': binaryDecoder },
|
||||
0x45: { 'name': 'CODEC RX SNI READER W/PARITY', 'decoder': binaryParityDecoder },
|
||||
0x46: { 'name': 'CODEC RX SNI CARD', 'decoder': binaryDecoder },
|
||||
0x47: { 'name': 'CODEC RX SNI CARD W/PARITY', 'decoder': binaryParityDecoder },
|
||||
|
||||
|
||||
0x80: { 'name': 'APP READ', 'decoder': binaryDecoder },
|
||||
0x81: { 'name': 'APP WRITE', 'decoder': binaryDecoder },
|
||||
0x84: { 'name': 'APP INC', 'decoder': binaryDecoder },
|
||||
@@ -49,7 +97,7 @@ eventTypes = {
|
||||
TIMESTAMP_MAX = 65536
|
||||
eventTypes = { i : ({'name': 'UNKNOWN', 'decoder': binaryDecoder} if i not in eventTypes.keys() else eventTypes[i]) for i in range(256) }
|
||||
|
||||
def parseBinary(binaryStream):
|
||||
def parseBinary(binaryStream, decoder=None):
|
||||
log = []
|
||||
|
||||
# Completely read file contents and process them byte by byte
|
||||
@@ -88,13 +136,23 @@ def parseBinary(binaryStream):
|
||||
if (deltaTimestamp < 0):
|
||||
deltaTimestamp += TIMESTAMP_MAX;
|
||||
|
||||
note = ""
|
||||
# If we need to decode the data and paritybit check success
|
||||
if (decoder!=None and len(logData) >0 and logData[-1] != '!'):
|
||||
# Decode the data from Reader
|
||||
if(event == 0x44 or event == 0x45):
|
||||
note = iso14443_3.parseReader(binascii.a2b_hex(logData), decoder)
|
||||
elif (event == 0x46 or event == 0x47):
|
||||
note = iso14443_3.parseCard(binascii.a2b_hex(logData), decoder)
|
||||
|
||||
# Create log entry as dict and append it to event list
|
||||
logEntry = {
|
||||
'eventName': eventTypes[event]['name'],
|
||||
'dataLength': dataLength,
|
||||
'timestamp': timestamp,
|
||||
'deltaTimestamp': deltaTimestamp,
|
||||
'data': logData
|
||||
'data': logData,
|
||||
'note': note
|
||||
}
|
||||
|
||||
log.append(logEntry)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user