mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-08-14 02:26:59 -07:00
Merge pull request #9 from Proxmark/master
Pulling in most recent changes in official code tree
This commit is contained in:
commit
18a3bf6119
18 changed files with 2153 additions and 422 deletions
|
@ -17,12 +17,14 @@ This project uses the changelog in accordance with [keepchangelog](http://keepac
|
|||
- Changed `hf 14a reader` to just reqest-anticilission-select sequence (Merlok)
|
||||
- Changed `hf 14a raw` - works with LED's and some exchange logic (Merlok)
|
||||
- Changed TLV parser messages to more convenient (Merlok)
|
||||
- Rewritten Legic Prime reader (`hf legic reader`, `write` and `fill`) - it is using xcorrelation now (AntiCat)
|
||||
|
||||
### Fixed
|
||||
- Changed start sequence in Qt mode (fix: short commands hangs main Qt thread) (Merlok)
|
||||
- Changed driver file proxmark3.inf to support both old and new Product/Vendor IDs (piwi)
|
||||
|
||||
### Added
|
||||
- Added `sc` smartcard (contact card) commands - reader, info, raw, upgrade, setclock, list (hardware version RDV4.0 only) must turn option on in makefile options (Willok, Iceman, marshmellow)
|
||||
- Added a bitbang mode to `lf cmdread` if delay is 0 the cmd bits turn off and on the antenna with 0 and 1 respectively (marshmellow)
|
||||
- Added PAC/Stanley detection to lf search (marshmellow)
|
||||
- Added lf pac demod and lf pac read - extracts the raw blocks from a PAC/Stanley tag (marshmellow)
|
||||
|
|
|
@ -15,18 +15,22 @@ APP_CFLAGS = -DON_DEVICE \
|
|||
|
||||
include ../common/Makefile_Enabled_Options.common
|
||||
|
||||
ifneq (,$(findstring LCD,$(APP_CFLAGS)))
|
||||
ifneq (,$(findstring WITH_LCD,$(APP_CFLAGS)))
|
||||
SRC_LCD = fonts.c LCD.c
|
||||
else
|
||||
SRC_LCD =
|
||||
endif
|
||||
#SRC_LCD = fonts.c LCD.c
|
||||
SRC_LF = lfops.c hitag2.c hitagS.c lfsampling.c pcf7931.c lfdemod.c protocols.c
|
||||
SRC_ISO15693 = iso15693.c iso15693tools.c
|
||||
SRC_ISO14443a = epa.c iso14443a.c mifareutil.c mifarecmd.c mifaresniff.c mifaresim.c
|
||||
SRC_ISO14443b = iso14443b.c
|
||||
SRC_CRAPTO1 = crypto1.c des.c
|
||||
SRC_CRC = iso14443crc.c crc.c crc16.c crc32.c parity.c
|
||||
ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS)))
|
||||
SRC_SMARTCARD = i2c.c
|
||||
else
|
||||
SRC_SMARTCARD =
|
||||
endif
|
||||
#the FPGA bitstream files. Note: order matters!
|
||||
FPGA_BITSTREAMS = fpga_lf.bit fpga_hf.bit
|
||||
|
||||
|
@ -44,6 +48,7 @@ THUMBSRC = start.c \
|
|||
$(SRC_ISO15693) \
|
||||
$(SRC_LF) \
|
||||
$(SRC_ZLIB) \
|
||||
$(SRC_SMARTCARD) \
|
||||
appmain.c \
|
||||
printf.c \
|
||||
util.c \
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
#ifdef WITH_LCD
|
||||
#include "LCD.h"
|
||||
#endif
|
||||
#ifdef WITH_SMARTCARD
|
||||
#include "i2c.h"
|
||||
#endif
|
||||
|
||||
|
||||
// Craig Young - 14a stand-alone code
|
||||
#ifdef WITH_ISO14443a
|
||||
|
@ -357,12 +361,15 @@ void SendStatus(void)
|
|||
{
|
||||
BigBuf_print_status();
|
||||
Fpga_print_status();
|
||||
#ifdef WITH_SMARTCARD
|
||||
I2C_print_status();
|
||||
#endif
|
||||
printConfig(); //LF Sampling config
|
||||
printUSBSpeed();
|
||||
Dbprintf("Various");
|
||||
Dbprintf(" MF_DBGLEVEL......%d", MF_DBGLEVEL);
|
||||
Dbprintf(" ToSendMax........%d",ToSendMax);
|
||||
Dbprintf(" ToSendBit........%d",ToSendBit);
|
||||
Dbprintf(" MF_DBGLEVEL........%d", MF_DBGLEVEL);
|
||||
Dbprintf(" ToSendMax..........%d", ToSendMax);
|
||||
Dbprintf(" ToSendBit..........%d", ToSendBit);
|
||||
|
||||
cmd_send(CMD_ACK,1,0,0,0,0);
|
||||
}
|
||||
|
@ -1253,6 +1260,31 @@ void UsbPacketReceived(uint8_t *packet, int len)
|
|||
HfSnoop(c->arg[0], c->arg[1]);
|
||||
break;
|
||||
#endif
|
||||
#ifdef WITH_SMARTCARD
|
||||
case CMD_SMART_ATR: {
|
||||
SmartCardAtr();
|
||||
break;
|
||||
}
|
||||
case CMD_SMART_SETCLOCK:{
|
||||
SmartCardSetClock(c->arg[0]);
|
||||
break;
|
||||
}
|
||||
case CMD_SMART_RAW: {
|
||||
SmartCardRaw(c->arg[0], c->arg[1], c->d.asBytes);
|
||||
break;
|
||||
}
|
||||
case CMD_SMART_UPLOAD: {
|
||||
// upload file from client
|
||||
uint8_t *mem = BigBuf_get_addr();
|
||||
memcpy( mem + c->arg[0], c->d.asBytes, USB_CMD_DATA_SIZE);
|
||||
cmd_send(CMD_ACK,1,0,0,0,0);
|
||||
break;
|
||||
}
|
||||
case CMD_SMART_UPGRADE: {
|
||||
SmartCardUpgrade(c->arg[0]);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
case CMD_BUFF_CLEAR:
|
||||
BigBuf_Clear();
|
||||
|
|
720
armsrc/i2c.c
Normal file
720
armsrc/i2c.c
Normal file
|
@ -0,0 +1,720 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Willok, June 2018
|
||||
// Edits by Iceman, July 2018
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// The main i2c code, for communications with smart card module
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "i2c.h"
|
||||
#include "mifareutil.h" //for mf_dbglevel
|
||||
#include "string.h" //for memset memcmp
|
||||
|
||||
// 定义连接引脚
|
||||
#define GPIO_RST AT91C_PIO_PA1
|
||||
#define GPIO_SCL AT91C_PIO_PA5
|
||||
#define GPIO_SDA AT91C_PIO_PA7
|
||||
|
||||
#define SCL_H HIGH(GPIO_SCL)
|
||||
#define SCL_L LOW(GPIO_SCL)
|
||||
#define SDA_H HIGH(GPIO_SDA)
|
||||
#define SDA_L LOW(GPIO_SDA)
|
||||
|
||||
#define SCL_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SCL)
|
||||
#define SDA_read (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SDA)
|
||||
|
||||
#define I2C_ERROR "I2C_WaitAck Error"
|
||||
|
||||
volatile unsigned long c;
|
||||
|
||||
// 直接使用循环来延时,一个循环 6 条指令,48M, Delay=1 大概为 200kbps
|
||||
// timer.
|
||||
// I2CSpinDelayClk(4) = 12.31us
|
||||
// I2CSpinDelayClk(1) = 3.07us
|
||||
void __attribute__((optimize("O0"))) I2CSpinDelayClk(uint16_t delay) {
|
||||
for (c = delay * 2; c; c--) {};
|
||||
}
|
||||
|
||||
// 通讯延迟函数 communication delay function
|
||||
#define I2C_DELAY_1CLK I2CSpinDelayClk(1)
|
||||
#define I2C_DELAY_2CLK I2CSpinDelayClk(2)
|
||||
#define I2C_DELAY_XCLK(x) I2CSpinDelayClk((x))
|
||||
|
||||
|
||||
#define ISO7618_MAX_FRAME 255
|
||||
|
||||
void I2C_init(void) {
|
||||
// 配置复位引脚,关闭上拉,推挽输出,默认高
|
||||
// Configure reset pin, close up pull up, push-pull output, default high
|
||||
AT91C_BASE_PIOA->PIO_PPUDR = GPIO_RST;
|
||||
AT91C_BASE_PIOA->PIO_MDDR = GPIO_RST;
|
||||
|
||||
// 配置 I2C 引脚,开启上拉,开漏输出
|
||||
// Configure I2C pin, open up, open leakage
|
||||
AT91C_BASE_PIOA->PIO_PPUER |= (GPIO_SCL | GPIO_SDA); // 打开上拉 Open up the pull up
|
||||
AT91C_BASE_PIOA->PIO_MDER |= (GPIO_SCL | GPIO_SDA);
|
||||
|
||||
// 默认三根线全部拉高
|
||||
// default three lines all pull up
|
||||
AT91C_BASE_PIOA->PIO_SODR |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
|
||||
|
||||
// 允许输出
|
||||
// allow output
|
||||
AT91C_BASE_PIOA->PIO_OER |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
|
||||
AT91C_BASE_PIOA->PIO_PER |= (GPIO_SCL | GPIO_SDA | GPIO_RST);
|
||||
}
|
||||
|
||||
|
||||
// 设置复位状态
|
||||
// set the reset state
|
||||
void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA) {
|
||||
if (LineRST)
|
||||
HIGH(GPIO_RST);
|
||||
else
|
||||
LOW(GPIO_RST);
|
||||
|
||||
if (LineSCK)
|
||||
HIGH(GPIO_SCL);
|
||||
else
|
||||
LOW(GPIO_SCL);
|
||||
|
||||
if (LineSDA)
|
||||
HIGH(GPIO_SDA);
|
||||
else
|
||||
LOW(GPIO_SDA);
|
||||
}
|
||||
|
||||
// 复位进入主程序
|
||||
// Reset the SIM_Adapter, then enter the main program
|
||||
// Note: the SIM_Adapter will not enter the main program after power up. Please run this function before use SIM_Adapter.
|
||||
void I2C_Reset_EnterMainProgram(void) {
|
||||
I2C_SetResetStatus(0, 0, 0); // 拉低复位线
|
||||
SpinDelay(30);
|
||||
I2C_SetResetStatus(1, 0, 0); // 解除复位
|
||||
SpinDelay(30);
|
||||
I2C_SetResetStatus(1, 1, 1); // 拉高数据线
|
||||
SpinDelay(10);
|
||||
}
|
||||
|
||||
// 复位进入引导模式
|
||||
// Reset the SIM_Adapter, then enter the bootloader program
|
||||
// Reserve:For firmware update.
|
||||
void I2C_Reset_EnterBootloader(void) {
|
||||
I2C_SetResetStatus(0, 1, 1); // 拉低复位线
|
||||
SpinDelay(100);
|
||||
I2C_SetResetStatus(1, 1, 1); // 解除复位
|
||||
SpinDelay(10);
|
||||
}
|
||||
|
||||
// 等待时钟变高
|
||||
// Wait for the clock to go High.
|
||||
bool WaitSCL_H_delay(uint32_t delay) {
|
||||
while (delay--) {
|
||||
if (SCL_read) {
|
||||
return true;
|
||||
}
|
||||
I2C_DELAY_1CLK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 5000 * 3.07us = 15350us. 15.35ms
|
||||
bool WaitSCL_H(void) {
|
||||
return WaitSCL_H_delay(5000);
|
||||
}
|
||||
|
||||
// Wait max 300ms or until SCL goes LOW.
|
||||
// Which ever comes first
|
||||
bool WaitSCL_L_300ms(void) {
|
||||
volatile uint16_t delay = 300;
|
||||
while ( delay-- ) {
|
||||
// exit on SCL LOW
|
||||
if (!SCL_read)
|
||||
return true;
|
||||
|
||||
SpinDelay(1);
|
||||
}
|
||||
return (delay == 0);
|
||||
}
|
||||
|
||||
bool I2C_Start(void) {
|
||||
|
||||
I2C_DELAY_XCLK(4);
|
||||
SDA_H; I2C_DELAY_1CLK;
|
||||
SCL_H;
|
||||
if (!WaitSCL_H()) return false;
|
||||
|
||||
I2C_DELAY_2CLK;
|
||||
|
||||
if (!SCL_read) return false;
|
||||
if (!SDA_read) return false;
|
||||
|
||||
SDA_L; I2C_DELAY_2CLK;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool I2C_WaitForSim() {
|
||||
// variable delay here.
|
||||
if (!WaitSCL_L_300ms())
|
||||
return false;
|
||||
|
||||
// 8051 speaks with smart card.
|
||||
// 1000*50*3.07 = 153.5ms
|
||||
// 1byte transfer == 1ms
|
||||
if (!WaitSCL_H_delay(2000*50) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// send i2c STOP
|
||||
void I2C_Stop(void) {
|
||||
SCL_L; I2C_DELAY_2CLK;
|
||||
SDA_L; I2C_DELAY_2CLK;
|
||||
SCL_H; I2C_DELAY_2CLK;
|
||||
if (!WaitSCL_H()) return;
|
||||
SDA_H;
|
||||
I2C_DELAY_XCLK(8);
|
||||
}
|
||||
|
||||
// Send i2c ACK
|
||||
void I2C_Ack(void) {
|
||||
SCL_L; I2C_DELAY_2CLK;
|
||||
SDA_L; I2C_DELAY_2CLK;
|
||||
SCL_H; I2C_DELAY_2CLK;
|
||||
SCL_L; I2C_DELAY_2CLK;
|
||||
}
|
||||
|
||||
// Send i2c NACK
|
||||
void I2C_NoAck(void) {
|
||||
SCL_L; I2C_DELAY_2CLK;
|
||||
SDA_H; I2C_DELAY_2CLK;
|
||||
SCL_H; I2C_DELAY_2CLK;
|
||||
SCL_L; I2C_DELAY_2CLK;
|
||||
}
|
||||
|
||||
bool I2C_WaitAck(void) {
|
||||
SCL_L; I2C_DELAY_1CLK;
|
||||
SDA_H; I2C_DELAY_1CLK;
|
||||
SCL_H;
|
||||
if (!WaitSCL_H())
|
||||
return false;
|
||||
|
||||
I2C_DELAY_2CLK;
|
||||
if (SDA_read) {
|
||||
SCL_L;
|
||||
return false;
|
||||
}
|
||||
SCL_L;
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2C_SendByte(uint8_t data) {
|
||||
uint8_t i = 8;
|
||||
|
||||
while (i--) {
|
||||
SCL_L; I2C_DELAY_1CLK;
|
||||
|
||||
if (data & 0x80)
|
||||
SDA_H;
|
||||
else
|
||||
SDA_L;
|
||||
|
||||
data <<= 1;
|
||||
I2C_DELAY_1CLK;
|
||||
|
||||
SCL_H;
|
||||
if (!WaitSCL_H())
|
||||
return;
|
||||
|
||||
I2C_DELAY_2CLK;
|
||||
}
|
||||
SCL_L;
|
||||
}
|
||||
|
||||
uint8_t I2C_ReadByte(void) {
|
||||
uint8_t i = 8, b = 0;
|
||||
|
||||
SDA_H;
|
||||
while (i--) {
|
||||
b <<= 1;
|
||||
SCL_L; I2C_DELAY_2CLK;
|
||||
SCL_H;
|
||||
if (!WaitSCL_H())
|
||||
return 0;
|
||||
|
||||
I2C_DELAY_2CLK;
|
||||
if (SDA_read)
|
||||
b |= 0x01;
|
||||
}
|
||||
SCL_L;
|
||||
return b;
|
||||
}
|
||||
|
||||
// Sends one byte ( command to be written, SlaveDevice address)
|
||||
bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address) {
|
||||
bool bBreak = true;
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return false;
|
||||
//[C0]
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
I2C_SendByte(device_cmd);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
bBreak = false;
|
||||
} while (false);
|
||||
|
||||
I2C_Stop();
|
||||
if (bBreak) {
|
||||
if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 写入1字节数据 (待写入数据,待写入地址,器件类型)
|
||||
// Sends 1 byte data (Data to be written, command to be written , SlaveDevice address ).
|
||||
bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address) {
|
||||
bool bBreak = true;
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return false;
|
||||
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
I2C_SendByte(device_cmd);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
I2C_SendByte(data);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
bBreak = false;
|
||||
} while (false);
|
||||
|
||||
I2C_Stop();
|
||||
if (bBreak) {
|
||||
if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 写入1串数据(待写入数组地址,待写入长度,待写入地址,器件类型)
|
||||
//Sends a string of data (Array, length, command to be written , SlaveDevice address ).
|
||||
// len = uint8 (max buffer to write 256bytes)
|
||||
bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
|
||||
bool bBreak = true;
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return false;
|
||||
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
I2C_SendByte(device_cmd);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
while (len) {
|
||||
|
||||
I2C_SendByte(*data);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
len--;
|
||||
data++;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
bBreak = false;
|
||||
} while (false);
|
||||
|
||||
I2C_Stop();
|
||||
if (bBreak) {
|
||||
if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 读出1串数据(存放读出数据,待读出长度,带读出地址,器件类型)
|
||||
// read 1 strings of data (Data array, Readout length, command to be written , SlaveDevice address ).
|
||||
// len = uint8 (max buffer to read 256bytes)
|
||||
uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address) {
|
||||
|
||||
if ( !data || len == 0 )
|
||||
return 0;
|
||||
|
||||
// extra wait 500us (514us measured)
|
||||
// 200us (xx measured)
|
||||
SpinDelayUs(200);
|
||||
bool bBreak = true;
|
||||
uint8_t readcount = 0;
|
||||
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return 0;
|
||||
|
||||
// 0xB0 / 0xC0 == i2c write
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
I2C_SendByte(device_cmd);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// 0xB1 / 0xC1 == i2c read
|
||||
I2C_Start();
|
||||
I2C_SendByte(device_address | 1);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
bBreak = false;
|
||||
} while (false);
|
||||
|
||||
if (bBreak) {
|
||||
I2C_Stop();
|
||||
if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reading
|
||||
while (len) {
|
||||
|
||||
*data = I2C_ReadByte();
|
||||
|
||||
len--;
|
||||
|
||||
// 读取的第一个字节为后续长度
|
||||
// The first byte in response is the message length
|
||||
if (!readcount && (len > *data)) {
|
||||
len = *data;
|
||||
} else {
|
||||
data++;
|
||||
}
|
||||
readcount++;
|
||||
|
||||
// acknowledgements. After last byte send NACK.
|
||||
if (len == 0)
|
||||
I2C_NoAck();
|
||||
else
|
||||
I2C_Ack();
|
||||
}
|
||||
|
||||
I2C_Stop();
|
||||
// return bytecount - first byte (which is length byte)
|
||||
return (readcount) ? --readcount : 0;
|
||||
}
|
||||
|
||||
uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) {
|
||||
//START, 0xB0, 0x00, 0x00, START, 0xB1, xx, yy, zz, ......, STOP
|
||||
bool bBreak = true;
|
||||
uint8_t readcount = 0;
|
||||
|
||||
// sending
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return 0;
|
||||
|
||||
// 0xB0 / 0xC0 i2c write
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// msb
|
||||
I2C_SendByte(msb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// lsb
|
||||
I2C_SendByte(lsb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// 0xB1 / 0xC1 i2c read
|
||||
I2C_Start();
|
||||
I2C_SendByte(device_address | 1);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
bBreak = false;
|
||||
} while (false);
|
||||
|
||||
if (bBreak) {
|
||||
I2C_Stop();
|
||||
if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// reading
|
||||
while (len) {
|
||||
*data = I2C_ReadByte();
|
||||
|
||||
data++;
|
||||
readcount++;
|
||||
len--;
|
||||
|
||||
// acknowledgements. After last byte send NACK.
|
||||
if (len == 0)
|
||||
I2C_NoAck();
|
||||
else
|
||||
I2C_Ack();
|
||||
}
|
||||
|
||||
I2C_Stop();
|
||||
return readcount;
|
||||
}
|
||||
|
||||
bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address) {
|
||||
//START, 0xB0, 0x00, 0x00, xx, yy, zz, ......, STOP
|
||||
bool bBreak = true;
|
||||
|
||||
do {
|
||||
if (!I2C_Start())
|
||||
return false;
|
||||
|
||||
// 0xB0 == i2c write
|
||||
I2C_SendByte(device_address & 0xFE);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// msb
|
||||
I2C_SendByte(msb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
// lsb
|
||||
I2C_SendByte(lsb);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
while (len) {
|
||||
I2C_SendByte(*data);
|
||||
if (!I2C_WaitAck())
|
||||
break;
|
||||
|
||||
len--;
|
||||
data++;
|
||||
}
|
||||
|
||||
if (len == 0)
|
||||
bBreak = false;
|
||||
} while (false);
|
||||
|
||||
I2C_Stop();
|
||||
if (bBreak) {
|
||||
if ( MF_DBGLEVEL > 3 ) DbpString(I2C_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2C_print_status(void) {
|
||||
DbpString("Smart card module (ISO 7816)");
|
||||
uint8_t resp[] = {0,0,0,0};
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
uint8_t len = I2C_BufferRead(resp, sizeof(resp), I2C_DEVICE_CMD_GETVERSION, I2C_DEVICE_ADDRESS_MAIN);
|
||||
if ( len > 0 )
|
||||
Dbprintf(" version.................v%x.%02x", resp[0], resp[1]);
|
||||
else
|
||||
DbpString(" version.................FAILED");
|
||||
}
|
||||
|
||||
bool GetATR(smart_card_atr_t *card_ptr) {
|
||||
|
||||
// clear
|
||||
if ( card_ptr ) {
|
||||
card_ptr->atr_len = 0;
|
||||
memset(card_ptr->atr, 0, sizeof(card_ptr->atr));
|
||||
}
|
||||
|
||||
// Send ATR
|
||||
// start [C0 01] stop start C1 len aa bb cc stop]
|
||||
I2C_WriteCmd(I2C_DEVICE_CMD_GENERATE_ATR, I2C_DEVICE_ADDRESS_MAIN);
|
||||
uint8_t cmd[1] = {1};
|
||||
LogTrace(cmd, 1, 0, 0, NULL, true);
|
||||
|
||||
//wait for sim card to answer.
|
||||
if (!I2C_WaitForSim())
|
||||
return false;
|
||||
|
||||
// read answer
|
||||
uint8_t len = I2C_BufferRead(card_ptr->atr, sizeof(card_ptr->atr), I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
|
||||
|
||||
if ( len == 0 )
|
||||
return false;
|
||||
|
||||
// for some reason we only get first byte of atr, if that is so, send dummy command to retrieve the rest of the atr
|
||||
if (len == 1) {
|
||||
|
||||
uint8_t data[1] = {0};
|
||||
I2C_BufferWrite(data, len, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
|
||||
|
||||
if ( !I2C_WaitForSim() )
|
||||
return false;
|
||||
|
||||
uint8_t len2 = I2C_BufferRead(card_ptr->atr + len, sizeof(card_ptr->atr) - len, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
|
||||
len = len + len2;
|
||||
}
|
||||
|
||||
if ( card_ptr ) {
|
||||
card_ptr->atr_len = len;
|
||||
LogTrace(card_ptr->atr, card_ptr->atr_len, 0, 0, NULL, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmartCardAtr(void) {
|
||||
smart_card_atr_t card;
|
||||
LED_D_ON();
|
||||
clear_trace();
|
||||
set_tracing(true);
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
bool isOK = GetATR( &card );
|
||||
cmd_send(CMD_ACK, isOK, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
|
||||
set_tracing(false);
|
||||
LEDsoff();
|
||||
}
|
||||
|
||||
void SmartCardRaw( uint64_t arg0, uint64_t arg1, uint8_t *data ) {
|
||||
|
||||
LED_D_ON();
|
||||
|
||||
uint8_t len = 0;
|
||||
uint8_t *resp = BigBuf_malloc(ISO7618_MAX_FRAME);
|
||||
smartcard_command_t flags = arg0;
|
||||
|
||||
if ((flags & SC_CONNECT))
|
||||
clear_trace();
|
||||
|
||||
set_tracing(true);
|
||||
|
||||
if ((flags & SC_CONNECT)) {
|
||||
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
|
||||
if ( !(flags & SC_NO_SELECT) ) {
|
||||
smart_card_atr_t card;
|
||||
bool gotATR = GetATR( &card );
|
||||
//cmd_send(CMD_ACK, gotATR, sizeof(smart_card_atr_t), 0, &card, sizeof(smart_card_atr_t));
|
||||
if ( !gotATR )
|
||||
goto OUT;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & SC_RAW)) {
|
||||
|
||||
LogTrace(data, arg1, 0, 0, NULL, true);
|
||||
|
||||
// Send raw bytes
|
||||
// asBytes = A0 A4 00 00 02
|
||||
// arg1 = len 5
|
||||
I2C_BufferWrite(data, arg1, I2C_DEVICE_CMD_SEND, I2C_DEVICE_ADDRESS_MAIN);
|
||||
|
||||
if ( !I2C_WaitForSim() )
|
||||
goto OUT;
|
||||
|
||||
len = I2C_BufferRead(resp, ISO7618_MAX_FRAME, I2C_DEVICE_CMD_READ, I2C_DEVICE_ADDRESS_MAIN);
|
||||
LogTrace(resp, len, 0, 0, NULL, false);
|
||||
}
|
||||
OUT:
|
||||
cmd_send(CMD_ACK, len, 0, 0, resp, len);
|
||||
set_tracing(false);
|
||||
LEDsoff();
|
||||
}
|
||||
|
||||
void SmartCardUpgrade(uint64_t arg0) {
|
||||
|
||||
LED_C_ON();
|
||||
|
||||
#define I2C_BLOCK_SIZE 128
|
||||
// write. Sector0, with 11,22,33,44
|
||||
// erase is 128bytes, and takes 50ms to execute
|
||||
|
||||
I2C_init();
|
||||
I2C_Reset_EnterBootloader();
|
||||
|
||||
bool isOK = true;
|
||||
uint8_t res = 0;
|
||||
uint16_t length = arg0;
|
||||
uint16_t pos = 0;
|
||||
uint8_t *fwdata = BigBuf_get_addr();
|
||||
uint8_t *verfiydata = BigBuf_malloc(I2C_BLOCK_SIZE);
|
||||
|
||||
while (length) {
|
||||
|
||||
uint8_t msb = (pos >> 8) & 0xFF;
|
||||
uint8_t lsb = pos & 0xFF;
|
||||
|
||||
Dbprintf("FW %02X%02X", msb, lsb);
|
||||
|
||||
size_t size = MIN(I2C_BLOCK_SIZE, length);
|
||||
|
||||
// write
|
||||
res = I2C_WriteFW(fwdata+pos, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT);
|
||||
if ( !res ) {
|
||||
DbpString("Writing failed");
|
||||
isOK = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// writing takes time.
|
||||
SpinDelay(50);
|
||||
|
||||
// read
|
||||
res = I2C_ReadFW(verfiydata, size, msb, lsb, I2C_DEVICE_ADDRESS_BOOT);
|
||||
if ( res == 0) {
|
||||
DbpString("Reading back failed");
|
||||
isOK = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// cmp
|
||||
if ( 0 != memcmp(fwdata+pos, verfiydata, size)) {
|
||||
DbpString("not equal data");
|
||||
isOK = false;
|
||||
break;
|
||||
}
|
||||
|
||||
length -= size;
|
||||
pos += size;
|
||||
}
|
||||
cmd_send(CMD_ACK, isOK, pos, 0, 0, 0);
|
||||
LED_C_OFF();
|
||||
}
|
||||
|
||||
// unfinished (or not needed?)
|
||||
//void SmartCardSetBaud(uint64_t arg0) {
|
||||
//}
|
||||
|
||||
void SmartCardSetClock(uint64_t arg0) {
|
||||
LED_D_ON();
|
||||
set_tracing(true);
|
||||
I2C_init();
|
||||
I2C_Reset_EnterMainProgram();
|
||||
|
||||
// Send SIM CLC
|
||||
// start [C0 05 xx] stop
|
||||
I2C_WriteByte(arg0, I2C_DEVICE_CMD_SIM_CLC, I2C_DEVICE_ADDRESS_MAIN);
|
||||
|
||||
cmd_send(CMD_ACK, 1, 0, 0, 0, 0);
|
||||
set_tracing(false);
|
||||
LEDsoff();
|
||||
}
|
58
armsrc/i2c.h
Normal file
58
armsrc/i2c.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Willok, June 2018
|
||||
// Edits by Iceman, July 2018
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// The main i2c code, for communications with smart card module
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef __I2C_H
|
||||
#define __I2C_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "proxmark3.h"
|
||||
#include "apps.h"
|
||||
#include "util.h"
|
||||
#include "BigBuf.h"
|
||||
#include "smartcard.h"
|
||||
|
||||
#define I2C_DEVICE_ADDRESS_BOOT 0xB0
|
||||
#define I2C_DEVICE_ADDRESS_MAIN 0xC0
|
||||
|
||||
#define I2C_DEVICE_CMD_GENERATE_ATR 0x01
|
||||
#define I2C_DEVICE_CMD_SEND 0x02
|
||||
#define I2C_DEVICE_CMD_READ 0x03
|
||||
#define I2C_DEVICE_CMD_SETBAUD 0x04
|
||||
#define I2C_DEVICE_CMD_SIM_CLC 0x05
|
||||
#define I2C_DEVICE_CMD_GETVERSION 0x06
|
||||
|
||||
|
||||
void I2C_init(void);
|
||||
void I2C_Reset(void);
|
||||
void I2C_SetResetStatus(uint8_t LineRST, uint8_t LineSCK, uint8_t LineSDA);
|
||||
|
||||
void I2C_Reset_EnterMainProgram(void);
|
||||
void I2C_Reset_EnterBootloader(void);
|
||||
|
||||
bool I2C_WriteCmd(uint8_t device_cmd, uint8_t device_address);
|
||||
|
||||
bool I2C_WriteByte(uint8_t data, uint8_t device_cmd, uint8_t device_address);
|
||||
bool I2C_BufferWrite(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address);
|
||||
uint8_t I2C_BufferRead(uint8_t *data, uint8_t len, uint8_t device_cmd, uint8_t device_address);
|
||||
|
||||
// for firmware
|
||||
uint8_t I2C_ReadFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address);
|
||||
bool I2C_WriteFW(uint8_t *data, uint8_t len, uint8_t msb, uint8_t lsb, uint8_t device_address);
|
||||
|
||||
bool GetATR(smart_card_atr_t *card_ptr);
|
||||
|
||||
// generic functions
|
||||
void SmartCardAtr(void);
|
||||
void SmartCardRaw(uint64_t arg0, uint64_t arg1, uint8_t *data);
|
||||
void SmartCardUpgrade(uint64_t arg0);
|
||||
//void SmartCardSetBaud(uint64_t arg0);
|
||||
void SmartCardSetClock(uint64_t arg0);
|
||||
void I2C_print_status(void);
|
||||
#endif
|
843
armsrc/legicrf.c
843
armsrc/legicrf.c
|
@ -1,5 +1,7 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// (c) 2009 Henryk Plötz <henryk@ploetzli.ch>
|
||||
// 2016 Iceman
|
||||
// 2018 AntiCat (rwd rewritten)
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
|
@ -15,6 +17,7 @@
|
|||
|
||||
#include "legicrf.h"
|
||||
#include "legic_prng.h"
|
||||
#include "legic.h"
|
||||
#include "crc.h"
|
||||
|
||||
static struct legic_frame {
|
||||
|
@ -40,6 +43,460 @@ static int legic_reqresp_drift;
|
|||
AT91PS_TC timer;
|
||||
AT91PS_TC prng_timer;
|
||||
|
||||
static legic_card_select_t card;/* metadata of currently selected card */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Frame timing and pseudorandom number generator
|
||||
//
|
||||
// The Prng is forwarded every 100us (TAG_BIT_PERIOD), except when the reader is
|
||||
// transmitting. In that case the prng has to be forwarded every bit transmitted:
|
||||
// - 60us for a 0 (RWD_TIME_0)
|
||||
// - 100us for a 1 (RWD_TIME_1)
|
||||
//
|
||||
// The data dependent timing makes writing comprehensible code significantly
|
||||
// harder. The current aproach forwards the prng data based if there is data on
|
||||
// air and time based, using GET_TICKS, during computational and wait periodes.
|
||||
//
|
||||
// To not have the necessity to calculate/guess exection time dependend timeouts
|
||||
// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */
|
||||
|
||||
#define RWD_TIME_PAUSE 30 /* 20us */
|
||||
#define RWD_TIME_1 150 /* READER_TIME_PAUSE 20us off + 80us on = 100us */
|
||||
#define RWD_TIME_0 90 /* READER_TIME_PAUSE 20us off + 40us on = 60us */
|
||||
#define RWD_FRAME_WAIT 330 /* 220us from TAG frame end to READER frame start */
|
||||
#define TAG_FRAME_WAIT 495 /* 330us from READER frame end to TAG frame start */
|
||||
#define TAG_BIT_PERIOD 150 /* 100us */
|
||||
#define TAG_WRITE_TIMEOUT 60 /* 40 * 100us (write should take at most 3.6ms) */
|
||||
|
||||
#define SIM_DIVISOR 586 /* prng_time/DIV count prng needs to be forwared */
|
||||
#define SIM_SHIFT 900 /* prng_time+SHIFT shift of delayed start */
|
||||
#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector
|
||||
/+ hysteresis fuzz quite a bit */
|
||||
|
||||
#define LEGIC_READ 0x01 /* Read Command */
|
||||
#define LEGIC_WRITE 0x00 /* Write Command */
|
||||
|
||||
#define SESSION_IV 0x55 /* An arbitrary chose session IV, all shoud work */
|
||||
#define OFFSET_LOG 1024 /* The largest Legic Prime card is 1k */
|
||||
#define WRITE_LOWERLIMIT 4 /* UID and MCC are not writable */
|
||||
|
||||
#define INPUT_THRESHOLD 8 /* heuristically determined, lower values */
|
||||
/* lead to detecting false ack during write */
|
||||
|
||||
#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz)))
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// I/O interface abstraction (FPGA -> ARM)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static inline uint8_t rx_byte_from_fpga() {
|
||||
for(;;) {
|
||||
WDT_HIT();
|
||||
|
||||
// wait for byte be become available in rx holding register
|
||||
if(AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) {
|
||||
return AT91C_BASE_SSC->SSC_RHR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Demodulation (Reader)
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Returns a demedulated bit
|
||||
//
|
||||
// The FPGA running xcorrelation samples the subcarrier at ~13.56 MHz. The mode
|
||||
// was initialy designed to receive BSPK/2-PSK. Hance, it reports an I/Q pair
|
||||
// every 4.7us (8 bits i and 8 bits q).
|
||||
//
|
||||
// The subcarrier amplitude can be calculated using Pythagoras sqrt(i^2 + q^2).
|
||||
// To reduce CPU time the amplitude is approximated by using linear functions:
|
||||
// am = MAX(ABS(i),ABS(q)) + 1/2*MIN(ABS(i),ABSq))
|
||||
//
|
||||
// Note: The SSC receiver is never synchronized the calculation my be performed
|
||||
// on a I/Q pair from two subsequent correlations, but does not matter.
|
||||
//
|
||||
// The bit time is 99.1us (21 I/Q pairs). The receiver skips the first 5 samples
|
||||
// and averages the next (most stable) 8 samples. The final 8 samples are dropped
|
||||
// also.
|
||||
//
|
||||
// The demedulated should be alligned to the bit periode by the caller. This is
|
||||
// done in rx_bit_as_reader and rx_ack_as_reader.
|
||||
static inline bool rx_bit_as_reader() {
|
||||
int32_t cq = 0;
|
||||
int32_t ci = 0;
|
||||
|
||||
// skip first 5 I/Q pairs
|
||||
for(size_t i = 0; i<5; ++i) {
|
||||
(int8_t)rx_byte_from_fpga();
|
||||
(int8_t)rx_byte_from_fpga();
|
||||
}
|
||||
|
||||
// sample next 8 I/Q pairs
|
||||
for(size_t i = 0; i<8; ++i) {
|
||||
cq += (int8_t)rx_byte_from_fpga();
|
||||
ci += (int8_t)rx_byte_from_fpga();
|
||||
}
|
||||
|
||||
// calculate power
|
||||
int32_t power = (MAX(ABS(ci), ABS(cq)) + (MIN(ABS(ci), ABS(cq)) >> 1));
|
||||
|
||||
// compare average (power / 8) to threshold
|
||||
return ((power >> 3) > INPUT_THRESHOLD);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Modulation (Reader)
|
||||
//
|
||||
// I've tried to modulate the Legic specific pause-puls using ssc and the default
|
||||
// ssc clock of 105.4 kHz (bit periode of 9.4us) - previous commit. However,
|
||||
// the timing was not precise enough. By increasing the ssc clock this could
|
||||
// be circumvented, but the adventage over bitbang would be little.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static inline void tx_bit_as_reader(bool bit) {
|
||||
// insert pause
|
||||
LOW(GPIO_SSC_DOUT);
|
||||
last_frame_end += RWD_TIME_PAUSE;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
HIGH(GPIO_SSC_DOUT);
|
||||
|
||||
// return to high, wait for bit periode to end
|
||||
last_frame_end += (bit ? RWD_TIME_1 : RWD_TIME_0) - RWD_TIME_PAUSE;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Frame Handling (Reader)
|
||||
//
|
||||
// The LEGIC RF protocol from card to reader does not include explicit frame
|
||||
// start/stop information or length information. The reader must know beforehand
|
||||
// how many bits it wants to receive.
|
||||
// Notably: a card sending a stream of 0-bits is indistinguishable from no card
|
||||
// present.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void tx_frame_as_reader(uint32_t frame, uint8_t len) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
|
||||
|
||||
// wait for next tx timeslot
|
||||
last_frame_end += RWD_FRAME_WAIT;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
|
||||
// transmit frame, MSB first
|
||||
for(uint8_t i = 0; i < len; ++i) {
|
||||
bool bit = (frame >> i) & 0x01;
|
||||
tx_bit_as_reader(bit ^ legic_prng_get_bit());
|
||||
legic_prng_forward(1);
|
||||
};
|
||||
|
||||
// add pause to mark end of the frame
|
||||
LOW(GPIO_SSC_DOUT);
|
||||
last_frame_end += RWD_TIME_PAUSE;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
HIGH(GPIO_SSC_DOUT);
|
||||
}
|
||||
|
||||
static uint32_t rx_frame_as_reader(uint8_t len) {
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
|
||||
| FPGA_HF_READER_RX_XCORR_848_KHZ
|
||||
| FPGA_HF_READER_RX_XCORR_QUARTER_FREQ);
|
||||
|
||||
// hold sampling until card is expected to respond
|
||||
last_frame_end += TAG_FRAME_WAIT;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
|
||||
uint32_t frame = 0;
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
frame |= (rx_bit_as_reader() ^ legic_prng_get_bit()) << i;
|
||||
legic_prng_forward(1);
|
||||
|
||||
// rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD
|
||||
last_frame_end += TAG_BIT_PERIOD;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
static bool rx_ack_as_reader() {
|
||||
// change fpga into rx mode
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
|
||||
| FPGA_HF_READER_RX_XCORR_848_KHZ
|
||||
| FPGA_HF_READER_RX_XCORR_QUARTER_FREQ);
|
||||
|
||||
// hold sampling until card is expected to respond
|
||||
last_frame_end += TAG_FRAME_WAIT;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
|
||||
uint32_t ack = 0;
|
||||
for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) {
|
||||
// sample bit
|
||||
ack = rx_bit_as_reader();
|
||||
legic_prng_forward(1);
|
||||
|
||||
// rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD
|
||||
last_frame_end += TAG_BIT_PERIOD;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
|
||||
// check if it was an ACK
|
||||
if(ack) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ack;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Legic Reader
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
int init_card(uint8_t cardtype, legic_card_select_t *p_card) {
|
||||
p_card->tagtype = cardtype;
|
||||
|
||||
switch(p_card->tagtype) {
|
||||
case 0x0d:
|
||||
p_card->cmdsize = 6;
|
||||
p_card->addrsize = 5;
|
||||
p_card->cardsize = 22;
|
||||
break;
|
||||
case 0x1d:
|
||||
p_card->cmdsize = 9;
|
||||
p_card->addrsize = 8;
|
||||
p_card->cardsize = 256;
|
||||
break;
|
||||
case 0x3d:
|
||||
p_card->cmdsize = 11;
|
||||
p_card->addrsize = 10;
|
||||
p_card->cardsize = 1024;
|
||||
break;
|
||||
default:
|
||||
p_card->cmdsize = 0;
|
||||
p_card->addrsize = 0;
|
||||
p_card->cardsize = 0;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void init_reader(bool clear_mem) {
|
||||
// configure FPGA
|
||||
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
|
||||
| FPGA_HF_READER_RX_XCORR_848_KHZ
|
||||
| FPGA_HF_READER_RX_XCORR_QUARTER_FREQ);
|
||||
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
||||
LED_D_ON();
|
||||
|
||||
// configure SSC with defaults
|
||||
FpgaSetupSsc();
|
||||
|
||||
// re-claim GPIO_SSC_DOUT as GPIO and enable output
|
||||
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
|
||||
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
|
||||
HIGH(GPIO_SSC_DOUT);
|
||||
|
||||
// init crc calculator
|
||||
crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0);
|
||||
|
||||
// start us timer
|
||||
StartTicks();
|
||||
}
|
||||
|
||||
// Setup reader to card connection
|
||||
//
|
||||
// The setup consists of a three way handshake:
|
||||
// - Transmit initialisation vector 7 bits
|
||||
// - Receive card type 6 bits
|
||||
// - Acknowledge frame 6 bits
|
||||
static uint32_t setup_phase_reader(uint8_t iv) {
|
||||
// init coordination timestamp
|
||||
last_frame_end = GET_TICKS;
|
||||
|
||||
// Switch on carrier and let the card charge for 5ms.
|
||||
last_frame_end += 7500;
|
||||
while(GET_TICKS < last_frame_end) { };
|
||||
|
||||
legic_prng_init(0);
|
||||
tx_frame_as_reader(iv, 7);
|
||||
|
||||
// configure iv
|
||||
legic_prng_init(iv);
|
||||
legic_prng_forward(2);
|
||||
|
||||
// receive card type
|
||||
int32_t card_type = rx_frame_as_reader(6);
|
||||
legic_prng_forward(3);
|
||||
|
||||
// send obsfuscated acknowledgment frame
|
||||
switch (card_type) {
|
||||
case 0x0D:
|
||||
tx_frame_as_reader(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01
|
||||
break;
|
||||
case 0x1D:
|
||||
case 0x3D:
|
||||
tx_frame_as_reader(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01
|
||||
break;
|
||||
}
|
||||
|
||||
return card_type;
|
||||
}
|
||||
|
||||
static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) {
|
||||
crc_clear(&legic_crc);
|
||||
crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz);
|
||||
return crc_finish(&legic_crc);
|
||||
}
|
||||
|
||||
static int16_t read_byte(uint16_t index, uint8_t cmd_sz) {
|
||||
uint16_t cmd = (index << 1) | LEGIC_READ;
|
||||
|
||||
// read one byte
|
||||
LED_B_ON();
|
||||
legic_prng_forward(2);
|
||||
tx_frame_as_reader(cmd, cmd_sz);
|
||||
legic_prng_forward(2);
|
||||
uint32_t frame = rx_frame_as_reader(12);
|
||||
LED_B_OFF();
|
||||
|
||||
// split frame into data and crc
|
||||
uint8_t byte = BYTEx(frame, 0);
|
||||
uint8_t crc = BYTEx(frame, 1);
|
||||
|
||||
// check received against calculated crc
|
||||
uint8_t calc_crc = calc_crc4(cmd, cmd_sz, byte);
|
||||
if(calc_crc != crc) {
|
||||
Dbprintf("!!! crc mismatch: %x != %x !!!", calc_crc, crc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
legic_prng_forward(1);
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
// Transmit write command, wait until (3.6ms) the tag sends back an unencrypted
|
||||
// ACK ('1' bit) and forward the prng time based.
|
||||
bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) {
|
||||
uint32_t cmd = index << 1 | LEGIC_WRITE; // prepare command
|
||||
uint8_t crc = calc_crc4(cmd, addr_sz + 1, byte); // calculate crc
|
||||
cmd |= byte << (addr_sz + 1); // append value
|
||||
cmd |= (crc & 0xF) << (addr_sz + 1 + 8); // and crc
|
||||
|
||||
// send write command
|
||||
LED_C_ON();
|
||||
legic_prng_forward(2);
|
||||
tx_frame_as_reader(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc
|
||||
legic_prng_forward(3);
|
||||
LED_C_OFF();
|
||||
|
||||
// wait for ack
|
||||
return rx_ack_as_reader();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Command Line Interface
|
||||
//
|
||||
// Only this functions are public / called from appmain.c
|
||||
//-----------------------------------------------------------------------------
|
||||
void LegicRfReader(int offset, int bytes) {
|
||||
uint8_t *BigBuf = BigBuf_get_addr();
|
||||
memset(BigBuf, 0, 1024);
|
||||
|
||||
// configure ARM and FPGA
|
||||
init_reader(false);
|
||||
|
||||
// establish shared secret and detect card type
|
||||
DbpString("Reading card ...");
|
||||
uint8_t card_type = setup_phase_reader(SESSION_IV);
|
||||
if(init_card(card_type, &card) != 0) {
|
||||
Dbprintf("No or unknown card found, aborting");
|
||||
goto OUT;
|
||||
}
|
||||
|
||||
// if no argument is specified create full dump
|
||||
if(bytes == -1) {
|
||||
bytes = card.cardsize;
|
||||
}
|
||||
|
||||
// do not read beyond card memory
|
||||
if(bytes + offset > card.cardsize) {
|
||||
bytes = card.cardsize - offset;
|
||||
}
|
||||
|
||||
for(uint16_t i = 0; i < bytes; ++i) {
|
||||
int16_t byte = read_byte(offset + i, card.cmdsize);
|
||||
if(byte == -1) {
|
||||
Dbprintf("operation failed @ 0x%03.3x", bytes);
|
||||
goto OUT;
|
||||
}
|
||||
BigBuf[i] = byte;
|
||||
}
|
||||
|
||||
// OK
|
||||
Dbprintf("Card (MIM %i) read, use 'hf legic decode' or", card.cardsize);
|
||||
Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7);
|
||||
|
||||
OUT:
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
LED_D_OFF();
|
||||
StopTicks();
|
||||
}
|
||||
|
||||
void LegicRfWriter(int bytes, int offset) {
|
||||
uint8_t *BigBuf = BigBuf_get_addr();
|
||||
|
||||
// configure ARM and FPGA
|
||||
init_reader(false);
|
||||
|
||||
// uid is not writeable
|
||||
if(offset <= WRITE_LOWERLIMIT) {
|
||||
goto OUT;
|
||||
}
|
||||
|
||||
// establish shared secret and detect card type
|
||||
Dbprintf("Writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes);
|
||||
uint8_t card_type = setup_phase_reader(SESSION_IV);
|
||||
if(init_card(card_type, &card) != 0) {
|
||||
Dbprintf("No or unknown card found, aborting");
|
||||
goto OUT;
|
||||
}
|
||||
|
||||
// do not write beyond card memory
|
||||
if(bytes + offset > card.cardsize) {
|
||||
bytes = card.cardsize - offset;
|
||||
}
|
||||
|
||||
// write in reverse order, only then is DCF (decremental field) writable
|
||||
while(bytes-- > 0 && !BUTTON_PRESS()) {
|
||||
if(!write_byte(bytes + offset, BigBuf[bytes + offset], card.addrsize)) {
|
||||
Dbprintf("operation failed @ 0x%03.3x", bytes);
|
||||
goto OUT;
|
||||
}
|
||||
}
|
||||
|
||||
// OK
|
||||
DbpString("Write successful");
|
||||
|
||||
OUT:
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
LED_D_OFF();
|
||||
StopTicks();
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Legic Simulator
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void setup_timer(void)
|
||||
{
|
||||
/* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging
|
||||
|
@ -62,22 +519,6 @@ static void setup_timer(void)
|
|||
prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
||||
}
|
||||
|
||||
/* At TIMER_CLOCK3 (MCK/32) */
|
||||
#define RWD_TIME_1 150 /* RWD_TIME_PAUSE off, 80us on = 100us */
|
||||
#define RWD_TIME_0 90 /* RWD_TIME_PAUSE off, 40us on = 60us */
|
||||
#define RWD_TIME_PAUSE 30 /* 20us */
|
||||
#define RWD_TIME_FUZZ 20 /* rather generous 13us, since the peak detector + hysteresis fuzz quite a bit */
|
||||
#define TAG_TIME_BIT 150 /* 100us for every bit */
|
||||
#define TAG_TIME_WAIT 490 /* time from RWD frame end to tag frame start, experimentally determined */
|
||||
|
||||
#define SIM_DIVISOR 586 /* prng_time/SIM_DIVISOR count prng needs to be forwared */
|
||||
#define SIM_SHIFT 900 /* prng_time+SIM_SHIFT shift of delayed start */
|
||||
|
||||
#define SESSION_IV 0x55
|
||||
#define OFFSET_LOG 1024
|
||||
|
||||
#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz)))
|
||||
|
||||
/* Generate Keystream */
|
||||
static uint32_t get_key_stream(int skip, int count)
|
||||
{
|
||||
|
@ -138,11 +579,11 @@ static void frame_send_tag(uint16_t response, int bits, int crypt)
|
|||
}
|
||||
|
||||
/* Wait for the frame start */
|
||||
while(timer->TC_CV < (TAG_TIME_WAIT - 30)) ;
|
||||
while(timer->TC_CV < (TAG_FRAME_WAIT - 30)) ;
|
||||
|
||||
int i;
|
||||
for(i=0; i<bits; i++) {
|
||||
int nextbit = timer->TC_CV + TAG_TIME_BIT;
|
||||
int nextbit = timer->TC_CV + TAG_BIT_PERIOD;
|
||||
int bit = response & 1;
|
||||
response = response >> 1;
|
||||
if(bit) {
|
||||
|
@ -155,126 +596,6 @@ static void frame_send_tag(uint16_t response, int bits, int crypt)
|
|||
AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
|
||||
}
|
||||
|
||||
/* Send a frame in reader mode, the FPGA must have been set up by
|
||||
* LegicRfReader
|
||||
*/
|
||||
static void frame_send_rwd(uint32_t data, int bits)
|
||||
{
|
||||
/* Start clock */
|
||||
timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
|
||||
while(timer->TC_CV > 1) ; /* Wait till the clock has reset */
|
||||
|
||||
int i;
|
||||
for(i=0; i<bits; i++) {
|
||||
int starttime = timer->TC_CV;
|
||||
int pause_end = starttime + RWD_TIME_PAUSE, bit_end;
|
||||
int bit = data & 1;
|
||||
data = data >> 1;
|
||||
|
||||
if(bit ^ legic_prng_get_bit()) {
|
||||
bit_end = starttime + RWD_TIME_1;
|
||||
} else {
|
||||
bit_end = starttime + RWD_TIME_0;
|
||||
}
|
||||
|
||||
/* RWD_TIME_PAUSE time off, then some time on, so that the complete bit time is
|
||||
* RWD_TIME_x, where x is the bit to be transmitted */
|
||||
AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
|
||||
while(timer->TC_CV < pause_end) ;
|
||||
AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT;
|
||||
legic_prng_forward(1); /* bit duration is longest. use this time to forward the lfsr */
|
||||
|
||||
while(timer->TC_CV < bit_end) ;
|
||||
}
|
||||
|
||||
{
|
||||
/* One final pause to mark the end of the frame */
|
||||
int pause_end = timer->TC_CV + RWD_TIME_PAUSE;
|
||||
AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
|
||||
while(timer->TC_CV < pause_end) ;
|
||||
AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT;
|
||||
}
|
||||
|
||||
/* Reset the timer, to measure time until the start of the tag frame */
|
||||
timer->TC_CCR = AT91C_TC_SWTRG;
|
||||
while(timer->TC_CV > 1) ; /* Wait till the clock has reset */
|
||||
}
|
||||
|
||||
/* Receive a frame from the card in reader emulation mode, the FPGA and
|
||||
* timer must have been set up by LegicRfReader and frame_send_rwd.
|
||||
*
|
||||
* The LEGIC RF protocol from card to reader does not include explicit
|
||||
* frame start/stop information or length information. The reader must
|
||||
* know beforehand how many bits it wants to receive. (Notably: a card
|
||||
* sending a stream of 0-bits is indistinguishable from no card present.)
|
||||
*
|
||||
* Receive methodology: There is a fancy correlator in hi_read_rx_xcorr, but
|
||||
* I'm not smart enough to use it. Instead I have patched hi_read_tx to output
|
||||
* the ADC signal with hysteresis on SSP_DIN. Bit-bang that signal and look
|
||||
* for edges. Count the edges in each bit interval. If they are approximately
|
||||
* 0 this was a 0-bit, if they are approximately equal to the number of edges
|
||||
* expected for a 212kHz subcarrier, this was a 1-bit. For timing we use the
|
||||
* timer that's still running from frame_send_rwd in order to get a synchronization
|
||||
* with the frame that we just sent.
|
||||
*
|
||||
* FIXME: Because we're relying on the hysteresis to just do the right thing
|
||||
* the range is severely reduced (and you'll probably also need a good antenna).
|
||||
* So this should be fixed some time in the future for a proper receiver.
|
||||
*/
|
||||
static void frame_receive_rwd(struct legic_frame * const f, int bits, int crypt)
|
||||
{
|
||||
uint32_t the_bit = 1; /* Use a bitmask to save on shifts */
|
||||
uint32_t data=0;
|
||||
int i, old_level=0, edges=0;
|
||||
int next_bit_at = TAG_TIME_WAIT;
|
||||
|
||||
if(bits > 32) {
|
||||
bits = 32;
|
||||
}
|
||||
|
||||
AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN;
|
||||
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN;
|
||||
|
||||
/* we have some time now, precompute the cipher
|
||||
* since we cannot compute it on the fly while reading */
|
||||
legic_prng_forward(2);
|
||||
|
||||
if(crypt)
|
||||
{
|
||||
for(i=0; i<bits; i++) {
|
||||
data |= legic_prng_get_bit() << i;
|
||||
legic_prng_forward(1);
|
||||
}
|
||||
}
|
||||
|
||||
while(timer->TC_CV < next_bit_at) ;
|
||||
|
||||
next_bit_at += TAG_TIME_BIT;
|
||||
|
||||
for(i=0; i<bits; i++) {
|
||||
edges = 0;
|
||||
while(timer->TC_CV < next_bit_at) {
|
||||
int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN);
|
||||
if(level != old_level)
|
||||
edges++;
|
||||
old_level = level;
|
||||
}
|
||||
next_bit_at += TAG_TIME_BIT;
|
||||
|
||||
if(edges > 20 && edges < 60) { /* expected are 42 edges */
|
||||
data ^= the_bit;
|
||||
}
|
||||
the_bit <<= 1;
|
||||
}
|
||||
|
||||
f->data = data;
|
||||
f->bits = bits;
|
||||
|
||||
/* Reset the timer, to synchronize the next frame */
|
||||
timer->TC_CCR = AT91C_TC_SWTRG;
|
||||
while(timer->TC_CV > 1) ; /* Wait till the clock has reset */
|
||||
}
|
||||
|
||||
static void frame_append_bit(struct legic_frame * const f, int bit)
|
||||
{
|
||||
if(f->bits >= 31) {
|
||||
|
@ -290,250 +611,6 @@ static void frame_clean(struct legic_frame * const f)
|
|||
f->bits = 0;
|
||||
}
|
||||
|
||||
static uint32_t perform_setup_phase_rwd(int iv)
|
||||
{
|
||||
|
||||
/* Switch on carrier and let the tag charge for 1ms */
|
||||
AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT;
|
||||
SpinDelay(1);
|
||||
|
||||
legic_prng_init(0); /* no keystream yet */
|
||||
frame_send_rwd(iv, 7);
|
||||
legic_prng_init(iv);
|
||||
|
||||
frame_clean(¤t_frame);
|
||||
frame_receive_rwd(¤t_frame, 6, 1);
|
||||
legic_prng_forward(1); /* we wait anyways */
|
||||
while(timer->TC_CV < 387) ; /* ~ 258us */
|
||||
frame_send_rwd(0x19, 6);
|
||||
|
||||
return current_frame.data;
|
||||
}
|
||||
|
||||
static void LegicCommonInit(void) {
|
||||
FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
|
||||
SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
|
||||
FpgaSetupSsc();
|
||||
FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
|
||||
|
||||
/* Bitbang the transmitter */
|
||||
AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
|
||||
AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
|
||||
AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
|
||||
|
||||
setup_timer();
|
||||
|
||||
crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0);
|
||||
}
|
||||
|
||||
static void switch_off_tag_rwd(void)
|
||||
{
|
||||
/* Switch off carrier, make sure tag is reset */
|
||||
AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
|
||||
SpinDelay(10);
|
||||
|
||||
WDT_HIT();
|
||||
}
|
||||
/* calculate crc for a legic command */
|
||||
static int LegicCRC(int byte_index, int value, int cmd_sz) {
|
||||
crc_clear(&legic_crc);
|
||||
crc_update(&legic_crc, 1, 1); /* CMD_READ */
|
||||
crc_update(&legic_crc, byte_index, cmd_sz-1);
|
||||
crc_update(&legic_crc, value, 8);
|
||||
return crc_finish(&legic_crc);
|
||||
}
|
||||
|
||||
int legic_read_byte(int byte_index, int cmd_sz) {
|
||||
int byte;
|
||||
|
||||
legic_prng_forward(4); /* we wait anyways */
|
||||
while(timer->TC_CV < 387) ; /* ~ 258us + 100us*delay */
|
||||
|
||||
frame_send_rwd(1 | (byte_index << 1), cmd_sz);
|
||||
frame_clean(¤t_frame);
|
||||
|
||||
frame_receive_rwd(¤t_frame, 12, 1);
|
||||
|
||||
byte = current_frame.data & 0xff;
|
||||
if( LegicCRC(byte_index, byte, cmd_sz) != (current_frame.data >> 8) ) {
|
||||
Dbprintf("!!! crc mismatch: expected %x but got %x !!!",
|
||||
LegicCRC(byte_index, current_frame.data & 0xff, cmd_sz), current_frame.data >> 8);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
/* legic_write_byte() is not included, however it's trivial to implement
|
||||
* and here are some hints on what remains to be done:
|
||||
*
|
||||
* * assemble a write_cmd_frame with crc and send it
|
||||
* * wait until the tag sends back an ACK ('1' bit unencrypted)
|
||||
* * forward the prng based on the timing
|
||||
*/
|
||||
int legic_write_byte(int byte, int addr, int addr_sz) {
|
||||
//do not write UID, CRC, DCF
|
||||
if(addr <= 0x06) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
//== send write command ==============================
|
||||
crc_clear(&legic_crc);
|
||||
crc_update(&legic_crc, 0, 1); /* CMD_WRITE */
|
||||
crc_update(&legic_crc, addr, addr_sz);
|
||||
crc_update(&legic_crc, byte, 8);
|
||||
|
||||
uint32_t crc = crc_finish(&legic_crc);
|
||||
uint32_t cmd = ((crc <<(addr_sz+1+8)) //CRC
|
||||
|(byte <<(addr_sz+1)) //Data
|
||||
|(addr <<1) //Address
|
||||
|(0x00 <<0)); //CMD = W
|
||||
uint32_t cmd_sz = addr_sz+1+8+4; //crc+data+cmd
|
||||
|
||||
legic_prng_forward(2); /* we wait anyways */
|
||||
while(timer->TC_CV < 387) {}; /* ~ 258us */
|
||||
frame_send_rwd(cmd, cmd_sz);
|
||||
|
||||
//== wait for ack ====================================
|
||||
int t, old_level=0, edges=0;
|
||||
int next_bit_at =0;
|
||||
while(timer->TC_CV < 387) ; /* ~ 258us */
|
||||
for(t=0; t<80; t++) {
|
||||
edges = 0;
|
||||
next_bit_at += TAG_TIME_BIT;
|
||||
while(timer->TC_CV < next_bit_at) {
|
||||
int level = (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN);
|
||||
if(level != old_level) {
|
||||
edges++;
|
||||
}
|
||||
old_level = level;
|
||||
}
|
||||
if(edges > 20 && edges < 60) { /* expected are 42 edges */
|
||||
int t = timer->TC_CV;
|
||||
int c = t/TAG_TIME_BIT;
|
||||
timer->TC_CCR = AT91C_TC_SWTRG;
|
||||
while(timer->TC_CV > 1) ; /* Wait till the clock has reset */
|
||||
legic_prng_forward(c);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
timer->TC_CCR = AT91C_TC_SWTRG;
|
||||
while(timer->TC_CV > 1) {}; /* Wait till the clock has reset */
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LegicRfReader(int offset, int bytes) {
|
||||
int byte_index=0, cmd_sz=0, card_sz=0;
|
||||
|
||||
LegicCommonInit();
|
||||
|
||||
uint8_t *BigBuf = BigBuf_get_addr();
|
||||
memset(BigBuf, 0, 1024);
|
||||
|
||||
DbpString("setting up legic card");
|
||||
uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV);
|
||||
switch_off_tag_rwd(); //we lose to mutch time with dprintf
|
||||
switch(tag_type) {
|
||||
case 0x1d:
|
||||
DbpString("MIM 256 card found, reading card ...");
|
||||
cmd_sz = 9;
|
||||
card_sz = 256;
|
||||
break;
|
||||
case 0x3d:
|
||||
DbpString("MIM 1024 card found, reading card ...");
|
||||
cmd_sz = 11;
|
||||
card_sz = 1024;
|
||||
break;
|
||||
default:
|
||||
Dbprintf("Unknown card format: %x",tag_type);
|
||||
return -1;
|
||||
}
|
||||
if(bytes == -1) {
|
||||
bytes = card_sz;
|
||||
}
|
||||
if(bytes+offset >= card_sz) {
|
||||
bytes = card_sz-offset;
|
||||
}
|
||||
|
||||
perform_setup_phase_rwd(SESSION_IV);
|
||||
|
||||
LED_B_ON();
|
||||
while(byte_index < bytes) {
|
||||
int r = legic_read_byte(byte_index+offset, cmd_sz);
|
||||
if(r == -1 ||BUTTON_PRESS()) {
|
||||
DbpString("operation aborted");
|
||||
switch_off_tag_rwd();
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
return -1;
|
||||
}
|
||||
BigBuf[byte_index] = r;
|
||||
WDT_HIT();
|
||||
byte_index++;
|
||||
if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF();
|
||||
}
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
switch_off_tag_rwd();
|
||||
Dbprintf("Card read, use 'hf legic decode' or");
|
||||
Dbprintf("'data hexsamples %d' to view results", (bytes+7) & ~7);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LegicRfWriter(int bytes, int offset) {
|
||||
int byte_index=0, addr_sz=0;
|
||||
uint8_t *BigBuf = BigBuf_get_addr();
|
||||
|
||||
LegicCommonInit();
|
||||
|
||||
DbpString("setting up legic card");
|
||||
uint32_t tag_type = perform_setup_phase_rwd(SESSION_IV);
|
||||
switch_off_tag_rwd();
|
||||
switch(tag_type) {
|
||||
case 0x1d:
|
||||
if(offset+bytes > 0x100) {
|
||||
Dbprintf("Error: can not write to 0x%03.3x on MIM 256", offset+bytes);
|
||||
return;
|
||||
}
|
||||
addr_sz = 8;
|
||||
Dbprintf("MIM 256 card found, writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes);
|
||||
break;
|
||||
case 0x3d:
|
||||
if(offset+bytes > 0x400) {
|
||||
Dbprintf("Error: can not write to 0x%03.3x on MIM 1024", offset+bytes);
|
||||
return;
|
||||
}
|
||||
addr_sz = 10;
|
||||
Dbprintf("MIM 1024 card found, writing 0x%03.3x - 0x%03.3x ...", offset, offset+bytes);
|
||||
break;
|
||||
default:
|
||||
Dbprintf("No or unknown card found, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
LED_B_ON();
|
||||
perform_setup_phase_rwd(SESSION_IV);
|
||||
legic_prng_forward(2);
|
||||
while(byte_index < bytes) {
|
||||
int r = legic_write_byte(BigBuf[byte_index+offset], byte_index+offset, addr_sz);
|
||||
if((r != 0) || BUTTON_PRESS()) {
|
||||
Dbprintf("operation aborted @ 0x%03.3x", byte_index);
|
||||
switch_off_tag_rwd();
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
return;
|
||||
}
|
||||
WDT_HIT();
|
||||
byte_index++;
|
||||
if(byte_index & 0x10) LED_C_ON(); else LED_C_OFF();
|
||||
}
|
||||
LED_B_OFF();
|
||||
LED_C_OFF();
|
||||
DbpString("write successful");
|
||||
}
|
||||
|
||||
int timestamp;
|
||||
|
||||
/* Handle (whether to respond) a frame in tag mode */
|
||||
static void frame_handle_tag(struct legic_frame const * const f)
|
||||
{
|
||||
|
@ -588,7 +665,7 @@ static void frame_handle_tag(struct legic_frame const * const f)
|
|||
int key = get_key_stream(-1, 11); //legic_phase_drift, 11);
|
||||
int addr = f->data ^ key; addr = addr >> 1;
|
||||
int data = BigBuf[addr];
|
||||
int hash = LegicCRC(addr, data, 11) << 8;
|
||||
int hash = calc_crc4(addr, data, 11) << 8;
|
||||
BigBuf[OFFSET_LOG+legic_read_count] = (uint8_t)addr;
|
||||
legic_read_count++;
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#define __LEGICRF_H
|
||||
|
||||
extern void LegicRfSimulate(int phase, int frame, int reqresp);
|
||||
extern int LegicRfReader(int bytes, int offset);
|
||||
extern void LegicRfReader(int bytes, int offset);
|
||||
extern void LegicRfWriter(int bytes, int offset);
|
||||
|
||||
#endif /* __LEGICRF_H */
|
||||
|
|
|
@ -26,6 +26,11 @@ CXXFLAGS = -I../include -Wall -O3
|
|||
APP_CFLAGS =
|
||||
include ../common/Makefile_Enabled_Options.common
|
||||
CFLAGS += $(APP_CFLAGS)
|
||||
ifneq (,$(findstring WITH_SMARTCARD,$(APP_CFLAGS)))
|
||||
SRC_SMARTCARD = cmdsmartcard.c
|
||||
else
|
||||
SRC_SMARTCARD =
|
||||
endif
|
||||
|
||||
LUAPLATFORM = generic
|
||||
platform = $(shell uname)
|
||||
|
@ -93,7 +98,8 @@ CORESRCS = uart_posix.c \
|
|||
ui.c \
|
||||
comms.c
|
||||
|
||||
CMDSRCS = crapto1/crapto1.c\
|
||||
CMDSRCS = $(SRC_SMARTCARD) \
|
||||
crapto1/crapto1.c\
|
||||
crapto1/crypto1.c\
|
||||
polarssl/des.c \
|
||||
polarssl/aes.c\
|
||||
|
@ -310,9 +316,7 @@ DEPENDENCY_FILES = $(patsubst %.c, $(OBJDIR)/%.d, $(CORESRCS) $(CMDSRCS) $(ZLIBS
|
|||
$(patsubst %.cpp, $(OBJDIR)/%.d, $(QTGUISRCS)) \
|
||||
$(OBJDIR)/proxmark3.d $(OBJDIR)/flash.d $(OBJDIR)/flasher.d $(OBJDIR)/fpga_compress.d
|
||||
|
||||
|
||||
$(DEPENDENCY_FILES): ;
|
||||
.PRECIOUS: $(DEPENDENCY_FILES)
|
||||
|
||||
-include $(DEPENDENCY_FILES)
|
||||
|
||||
|
|
|
@ -353,6 +353,12 @@ uint16_t printTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace, ui
|
|||
|
||||
int CmdHFList(const char *Cmd)
|
||||
{
|
||||
#ifdef WITH_SMARTCARD
|
||||
PrintAndLog("TEST_WITH_SMARTCARD");
|
||||
#endif
|
||||
#ifdef WITH_TEST
|
||||
PrintAndLog("TEST_WITH_TEST");
|
||||
#endif
|
||||
bool showWaitCycles = false;
|
||||
bool markCRCBytes = false;
|
||||
bool loadFromFile = false;
|
||||
|
|
|
@ -26,7 +26,9 @@
|
|||
#include "util.h"
|
||||
#include "util_posix.h"
|
||||
#include "cmdscript.h"
|
||||
|
||||
#ifdef WITH_SMARTCARD
|
||||
#include "cmdsmartcard.h"
|
||||
#endif
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
static int CmdQuit(const char *Cmd);
|
||||
|
@ -39,6 +41,9 @@ static command_t CommandTable[] =
|
|||
{"hf", CmdHF, 1, "{ High Frequency commands... }"},
|
||||
{"hw", CmdHW, 1, "{ Hardware commands... }"},
|
||||
{"lf", CmdLF, 1, "{ Low Frequency commands... }"},
|
||||
#ifdef WITH_SMARTCARD
|
||||
{"sc", CmdSmartcard,1,"{ Smartcard commands... }"},
|
||||
#endif
|
||||
{"script",CmdScript,1, "{ Scripting commands }"},
|
||||
{"quit", CmdQuit, 1, "Exit program"},
|
||||
{"exit", CmdQuit, 1, "Exit program"},
|
||||
|
|
707
client/cmdsmartcard.c
Normal file
707
client/cmdsmartcard.c
Normal file
|
@ -0,0 +1,707 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018 iceman
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Proxmark3 RDV40 Smartcard module commands
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "cmdsmartcard.h"
|
||||
#include "smartcard.h"
|
||||
#include "comms.h"
|
||||
#include "protocols.h"
|
||||
|
||||
|
||||
static int CmdHelp(const char *Cmd);
|
||||
|
||||
int usage_sm_raw(void) {
|
||||
PrintAndLog("Usage: sc raw [h|r|c] d <0A 0B 0C ... hex>");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" r : do not read response");
|
||||
PrintAndLog(" a : active signal field ON without select");
|
||||
PrintAndLog(" s : active signal field ON with select");
|
||||
PrintAndLog(" t : executes TLV decoder if it is possible");
|
||||
PrintAndLog(" d <bytes> : bytes to send");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc raw d 11223344");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_reader(void) {
|
||||
PrintAndLog("Usage: sc reader [h|s]");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" s : silent (no messages)");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc reader");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_info(void) {
|
||||
PrintAndLog("Usage: sc info [h|s]");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" s : silent (no messages)");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc info");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_upgrade(void) {
|
||||
PrintAndLog("Upgrade firmware");
|
||||
PrintAndLog("Usage: sc upgrade f <file name>");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" f <filename> : firmware file name");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc upgrade f myfile");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("WARNING - Dangerous command, do wrong and you will brick the smart card socket");
|
||||
return 0;
|
||||
}
|
||||
int usage_sm_setclock(void) {
|
||||
PrintAndLog("Usage: sc setclock [h] c <clockspeed>");
|
||||
PrintAndLog(" h : this help");
|
||||
PrintAndLog(" c <> : clockspeed (0 = 16mhz, 1=8mhz, 2=4mhz) ");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Examples:");
|
||||
PrintAndLog(" sc setclock c 2");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartRaw(const char *Cmd) {
|
||||
|
||||
int hexlen = 0;
|
||||
bool active = false;
|
||||
bool active_select = false;
|
||||
uint8_t cmdp = 0;
|
||||
bool errors = false, reply = true, decodeTLV = false, breakloop = false;
|
||||
uint8_t data[USB_CMD_DATA_SIZE] = {0x00};
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_raw();
|
||||
case 'r':
|
||||
reply = false;
|
||||
cmdp++;
|
||||
break;
|
||||
case 'a':
|
||||
active = true;
|
||||
cmdp++;
|
||||
break;
|
||||
case 's':
|
||||
active_select = true;
|
||||
cmdp++;
|
||||
break;
|
||||
case 't':
|
||||
decodeTLV = true;
|
||||
cmdp++;
|
||||
break;
|
||||
case 'd': {
|
||||
switch (param_gethex_to_eol(Cmd, cmdp+1, data, sizeof(data), &hexlen)) {
|
||||
case 1:
|
||||
PrintAndLog("Invalid HEX value.");
|
||||
return 1;
|
||||
case 2:
|
||||
PrintAndLog("Too many bytes. Max %d bytes", sizeof(data));
|
||||
return 1;
|
||||
case 3:
|
||||
PrintAndLog("Hex must have an even number of digits.");
|
||||
return 1;
|
||||
}
|
||||
cmdp++;
|
||||
breakloop = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( breakloop )
|
||||
break;
|
||||
}
|
||||
|
||||
//Validations
|
||||
if (errors || cmdp == 0 ) return usage_sm_raw();
|
||||
|
||||
// arg0 = RFU flags
|
||||
// arg1 = length
|
||||
UsbCommand c = {CMD_SMART_RAW, {0, hexlen, 0}};
|
||||
|
||||
if (active || active_select) {
|
||||
c.arg[0] |= SC_CONNECT;
|
||||
if (active)
|
||||
c.arg[0] |= SC_NO_SELECT;
|
||||
}
|
||||
|
||||
if (hexlen > 0) {
|
||||
c.arg[0] |= SC_RAW;
|
||||
}
|
||||
|
||||
memcpy(c.d.asBytes, data, hexlen );
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
|
||||
// reading response from smart card
|
||||
if ( reply ) {
|
||||
UsbCommand resp;
|
||||
if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) {
|
||||
PrintAndLog("smart card response failed");
|
||||
return 1;
|
||||
}
|
||||
uint32_t datalen = resp.arg[0];
|
||||
|
||||
if ( !datalen ) {
|
||||
PrintAndLog("smart card response failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PrintAndLog("received %i bytes", datalen);
|
||||
|
||||
if (!datalen)
|
||||
return 1;
|
||||
|
||||
uint8_t *data = resp.d.asBytes;
|
||||
|
||||
// TLV decoder
|
||||
if (decodeTLV ) {
|
||||
|
||||
if (datalen >= 2) {
|
||||
PrintAndLog("%02x %02x | %s", data[datalen - 2], data[datalen - 1], GetAPDUCodeDescription(data[datalen - 2], data[datalen - 1]));
|
||||
}
|
||||
if (datalen > 4) {
|
||||
TLVPrintFromBuffer(data, datalen - 2);
|
||||
}
|
||||
} else {
|
||||
PrintAndLog("%s", sprint_hex(data, datalen));
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartUpgrade(const char *Cmd) {
|
||||
|
||||
PrintAndLog("WARNING - Smartcard socket firmware upgrade.");
|
||||
PrintAndLog("Dangerous command, do wrong and you will brick the smart card socket");
|
||||
|
||||
FILE *f;
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
uint8_t cmdp = 0;
|
||||
bool errors = false;
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'f':
|
||||
//File handling and reading
|
||||
if ( param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE ) {
|
||||
PrintAndLog("Filename too long");
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
cmdp += 2;
|
||||
break;
|
||||
case 'h':
|
||||
return usage_sm_upgrade();
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Validations
|
||||
if (errors || cmdp == 0 ) return usage_sm_upgrade();
|
||||
|
||||
// load file
|
||||
f = fopen(filename, "rb");
|
||||
if ( !f ) {
|
||||
PrintAndLog("File: %s: not found or locked.", filename);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get filesize in order to malloc memory
|
||||
fseek(f, 0, SEEK_END);
|
||||
long fsize = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
if (fsize < 0) {
|
||||
PrintAndLog("error, when getting filesize");
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t *dump = calloc(fsize, sizeof(uint8_t));
|
||||
if (!dump) {
|
||||
PrintAndLog("error, cannot allocate memory ");
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
size_t bytes_read = fread(dump, 1, fsize, f);
|
||||
if (f)
|
||||
fclose(f);
|
||||
|
||||
PrintAndLog("Smartcard socket firmware uploading to PM3");
|
||||
//Send to device
|
||||
uint32_t index = 0;
|
||||
uint32_t bytes_sent = 0;
|
||||
uint32_t bytes_remaining = bytes_read;
|
||||
|
||||
while (bytes_remaining > 0){
|
||||
uint32_t bytes_in_packet = MIN(USB_CMD_DATA_SIZE, bytes_remaining);
|
||||
UsbCommand c = {CMD_SMART_UPLOAD, {index + bytes_sent, bytes_in_packet, 0}};
|
||||
|
||||
// Fill usb bytes with 0xFF
|
||||
memset(c.d.asBytes, 0xFF, USB_CMD_DATA_SIZE);
|
||||
memcpy(c.d.asBytes, dump + bytes_sent, bytes_in_packet);
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2000) ) {
|
||||
PrintAndLog("timeout while waiting for reply.");
|
||||
free(dump);
|
||||
return 1;
|
||||
}
|
||||
|
||||
bytes_remaining -= bytes_in_packet;
|
||||
bytes_sent += bytes_in_packet;
|
||||
printf("."); fflush(stdout);
|
||||
}
|
||||
free(dump);
|
||||
printf("\n");
|
||||
PrintAndLog("Smartcard socket firmware updating, don\'t turn off your PM3!");
|
||||
|
||||
// trigger the firmware upgrade
|
||||
UsbCommand c = {CMD_SMART_UPGRADE, {bytes_read, 0, 0}};
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
PrintAndLog("timeout while waiting for reply.");
|
||||
return 1;
|
||||
}
|
||||
if ( (resp.arg[0] && 0xFF ) )
|
||||
PrintAndLog("Smartcard socket firmware upgraded successful");
|
||||
else
|
||||
PrintAndLog("Smartcard socket firmware updating failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartInfo(const char *Cmd){
|
||||
uint8_t cmdp = 0;
|
||||
bool errors = false, silent = false;
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_info();
|
||||
case 's':
|
||||
silent = true;
|
||||
break;
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
cmdp++;
|
||||
}
|
||||
|
||||
//Validations
|
||||
if (errors ) return usage_sm_info();
|
||||
|
||||
UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}};
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
smart_card_atr_t card;
|
||||
memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t));
|
||||
|
||||
// print header
|
||||
PrintAndLog("\n--- Smartcard Information ---------");
|
||||
PrintAndLog("-------------------------------------------------------------");
|
||||
PrintAndLog("ISO76183 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
PrintAndLog("look up ATR");
|
||||
PrintAndLog("http://smartcard-atr.appspot.com/parse?ATR=%s", sprint_hex_inrow(card.atr, card.atr_len) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartReader(const char *Cmd){
|
||||
uint8_t cmdp = 0;
|
||||
bool errors = false, silent = false;
|
||||
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_reader();
|
||||
case 's':
|
||||
silent = true;
|
||||
break;
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
cmdp++;
|
||||
}
|
||||
|
||||
//Validations
|
||||
if (errors ) return usage_sm_reader();
|
||||
|
||||
UsbCommand c = {CMD_SMART_ATR, {0, 0, 0}};
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
if (!silent) PrintAndLog("smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
smart_card_atr_t card;
|
||||
memcpy(&card, (smart_card_atr_t *)resp.d.asBytes, sizeof(smart_card_atr_t));
|
||||
PrintAndLog("ISO7816-3 ATR : %s", sprint_hex(card.atr, card.atr_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartSetClock(const char *Cmd){
|
||||
uint8_t cmdp = 0;
|
||||
bool errors = false;
|
||||
uint8_t clock = 0;
|
||||
while (param_getchar(Cmd, cmdp) != 0x00 && !errors) {
|
||||
switch (tolower(param_getchar(Cmd, cmdp))) {
|
||||
case 'h': return usage_sm_setclock();
|
||||
case 'c':
|
||||
clock = param_get8ex(Cmd, cmdp+1, 2, 10);
|
||||
if ( clock > 2)
|
||||
errors = true;
|
||||
|
||||
cmdp += 2;
|
||||
break;
|
||||
default:
|
||||
PrintAndLog("Unknown parameter '%c'", param_getchar(Cmd, cmdp));
|
||||
errors = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Validations
|
||||
if (errors || cmdp == 0) return usage_sm_setclock();
|
||||
|
||||
UsbCommand c = {CMD_SMART_SETCLOCK, {clock, 0, 0}};
|
||||
clearCommandBuffer();
|
||||
SendCommand(&c);
|
||||
UsbCommand resp;
|
||||
if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2500) ) {
|
||||
PrintAndLog("smart card select failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t isok = resp.arg[0] & 0xFF;
|
||||
if (!isok) {
|
||||
PrintAndLog("smart card set clock failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (clock) {
|
||||
case 0:
|
||||
PrintAndLog("Clock changed to 16mhz giving 10800 baudrate");
|
||||
break;
|
||||
case 1:
|
||||
PrintAndLog("Clock changed to 8mhz giving 21600 baudrate");
|
||||
break;
|
||||
case 2:
|
||||
PrintAndLog("Clock changed to 4mhz giving 86400 baudrate");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// iso 7816-3
|
||||
void annotateIso7816(char *exp, size_t size, uint8_t* cmd, uint8_t cmdsize){
|
||||
// S-block
|
||||
if ( (cmd[0] & 0xC0) && (cmdsize == 3) ) {
|
||||
switch ( (cmd[0] & 0x3f) ) {
|
||||
case 0x00 : snprintf(exp, size, "S-block RESYNCH req"); break;
|
||||
case 0x20 : snprintf(exp, size, "S-block RESYNCH resp"); break;
|
||||
case 0x01 : snprintf(exp, size, "S-block IFS req"); break;
|
||||
case 0x21 : snprintf(exp, size, "S-block IFS resp"); break;
|
||||
case 0x02 : snprintf(exp, size, "S-block ABORT req"); break;
|
||||
case 0x22 : snprintf(exp, size, "S-block ABORT resp"); break;
|
||||
case 0x03 : snprintf(exp, size, "S-block WTX reqt"); break;
|
||||
case 0x23 : snprintf(exp, size, "S-block WTX resp"); break;
|
||||
default : snprintf(exp, size, "S-block"); break;
|
||||
}
|
||||
}
|
||||
// R-block (ack)
|
||||
else if ( ((cmd[0] & 0xD0) == 0x80) && ( cmdsize > 2) ) {
|
||||
if ( (cmd[0] & 0x10) == 0 )
|
||||
snprintf(exp, size, "R-block ACK");
|
||||
else
|
||||
snprintf(exp, size, "R-block NACK");
|
||||
}
|
||||
// I-block
|
||||
else {
|
||||
|
||||
int pos = (cmd[0] == 2 || cmd[0] == 3) ? 2 : 3;
|
||||
switch ( cmd[pos] ) {
|
||||
case ISO7816_READ_BINARY :snprintf(exp, size, "READ BIN");break;
|
||||
case ISO7816_WRITE_BINARY :snprintf(exp, size, "WRITE BIN");break;
|
||||
case ISO7816_UPDATE_BINARY :snprintf(exp, size, "UPDATE BIN");break;
|
||||
case ISO7816_ERASE_BINARY :snprintf(exp, size, "ERASE BIN");break;
|
||||
case ISO7816_READ_RECORDS :snprintf(exp, size, "READ RECORDS");break;
|
||||
case ISO7816_WRITE_RECORDS :snprintf(exp, size, "WRITE RECORDS");break;
|
||||
case ISO7816_APPEND_RECORD :snprintf(exp, size, "APPEND RECORD");break;
|
||||
case ISO7816_UPDATE_RECORD :snprintf(exp, size, "UPDATE RECORD");break;
|
||||
case ISO7816_GET_DATA :snprintf(exp, size, "GET DATA");break;
|
||||
case ISO7816_PUT_DATA :snprintf(exp, size, "PUT DATA");break;
|
||||
case ISO7816_SELECT_FILE :snprintf(exp, size, "SELECT FILE");break;
|
||||
case ISO7816_VERIFY :snprintf(exp, size, "VERIFY");break;
|
||||
case ISO7816_INTERNAL_AUTHENTICATION :snprintf(exp, size, "INTERNAL AUTH");break;
|
||||
case ISO7816_EXTERNAL_AUTHENTICATION :snprintf(exp, size, "EXTERNAL AUTH");break;
|
||||
case ISO7816_GET_CHALLENGE :snprintf(exp, size, "GET CHALLENGE");break;
|
||||
case ISO7816_MANAGE_CHANNEL :snprintf(exp, size, "MANAGE CHANNEL");break;
|
||||
default :snprintf(exp, size, "?"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint16_t printScTraceLine(uint16_t tracepos, uint16_t traceLen, uint8_t *trace) {
|
||||
// sanity check
|
||||
if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) > traceLen) return traceLen;
|
||||
|
||||
bool isResponse;
|
||||
uint16_t data_len, parity_len;
|
||||
uint32_t duration, timestamp, first_timestamp, EndOfTransmissionTimestamp;
|
||||
char explanation[30] = {0};
|
||||
|
||||
first_timestamp = *((uint32_t *)(trace));
|
||||
timestamp = *((uint32_t *)(trace + tracepos));
|
||||
tracepos += 4;
|
||||
|
||||
duration = *((uint16_t *)(trace + tracepos));
|
||||
tracepos += 2;
|
||||
|
||||
data_len = *((uint16_t *)(trace + tracepos));
|
||||
tracepos += 2;
|
||||
|
||||
if (data_len & 0x8000) {
|
||||
data_len &= 0x7fff;
|
||||
isResponse = true;
|
||||
} else {
|
||||
isResponse = false;
|
||||
}
|
||||
|
||||
parity_len = (data_len-1)/8 + 1;
|
||||
if (tracepos + data_len + parity_len > traceLen) {
|
||||
return traceLen;
|
||||
}
|
||||
uint8_t *frame = trace + tracepos;
|
||||
tracepos += data_len;
|
||||
//uint8_t *parityBytes = trace + tracepos;
|
||||
tracepos += parity_len;
|
||||
|
||||
//--- Draw the data column
|
||||
char line[18][110];
|
||||
|
||||
if (data_len == 0 ) {
|
||||
sprintf(line[0],"<empty trace - possible error>");
|
||||
return tracepos;
|
||||
}
|
||||
|
||||
for (int j = 0; j < data_len && j/18 < 18; j++) {
|
||||
snprintf(line[j/18]+(( j % 18) * 4),110, "%02x ", frame[j]);
|
||||
}
|
||||
|
||||
EndOfTransmissionTimestamp = timestamp + duration;
|
||||
|
||||
annotateIso7816(explanation,sizeof(explanation),frame,data_len);
|
||||
|
||||
int num_lines = MIN((data_len - 1)/18 + 1, 18);
|
||||
for (int j = 0; j < num_lines ; j++) {
|
||||
if (j == 0) {
|
||||
PrintAndLog(" %10u | %10u | %s |%-72s | %s| %s",
|
||||
(timestamp - first_timestamp),
|
||||
(EndOfTransmissionTimestamp - first_timestamp),
|
||||
(isResponse ? "Tag" : "Rdr"),
|
||||
line[j],
|
||||
" ",
|
||||
(j == num_lines-1) ? explanation : "");
|
||||
} else {
|
||||
PrintAndLog(" | | |%-72s | %s| %s",
|
||||
line[j],
|
||||
" ",
|
||||
(j == num_lines-1) ? explanation : "");
|
||||
}
|
||||
}
|
||||
|
||||
// if is last record
|
||||
if (tracepos + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) >= traceLen) return traceLen;
|
||||
|
||||
return tracepos;
|
||||
}
|
||||
|
||||
int ScTraceList(const char *Cmd) {
|
||||
bool loadFromFile = false;
|
||||
bool saveToFile = false;
|
||||
char type[5] = {0};
|
||||
char filename[FILE_PATH_SIZE] = {0};
|
||||
|
||||
// parse command line
|
||||
param_getstr(Cmd, 0, type, sizeof(type));
|
||||
param_getstr(Cmd, 1, filename, sizeof(filename));
|
||||
|
||||
bool errors = false;
|
||||
if(type[0] == 'h') {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
if(!errors) {
|
||||
if (strcmp(type, "s") == 0) {
|
||||
saveToFile = true;
|
||||
} else if (strcmp(type,"l") == 0) {
|
||||
loadFromFile = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((loadFromFile || saveToFile) && strlen(filename) == 0) {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
if (loadFromFile && saveToFile) {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
if (errors) {
|
||||
PrintAndLog("List or save protocol data.");
|
||||
PrintAndLog("Usage: sc list [l <filename>]");
|
||||
PrintAndLog(" sc list [s <filename>]");
|
||||
PrintAndLog(" l - load data from file instead of trace buffer");
|
||||
PrintAndLog(" s - save data to file");
|
||||
PrintAndLog("");
|
||||
PrintAndLog("example: sc list");
|
||||
PrintAndLog("example: sc list save myCardTrace.trc");
|
||||
PrintAndLog("example: sc list l myCardTrace.trc");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t *trace;
|
||||
uint32_t tracepos = 0;
|
||||
uint32_t traceLen = 0;
|
||||
|
||||
if (loadFromFile) {
|
||||
#define TRACE_CHUNK_SIZE (1<<16) // 64K to start with. Will be enough for BigBuf and some room for future extensions
|
||||
FILE *tracefile = NULL;
|
||||
size_t bytes_read;
|
||||
trace = malloc(TRACE_CHUNK_SIZE);
|
||||
if (trace == NULL) {
|
||||
PrintAndLog("Cannot allocate memory for trace");
|
||||
return 2;
|
||||
}
|
||||
if ((tracefile = fopen(filename,"rb")) == NULL) {
|
||||
PrintAndLog("Could not open file %s", filename);
|
||||
free(trace);
|
||||
return 0;
|
||||
}
|
||||
while (!feof(tracefile)) {
|
||||
bytes_read = fread(trace+traceLen, 1, TRACE_CHUNK_SIZE, tracefile);
|
||||
traceLen += bytes_read;
|
||||
if (!feof(tracefile)) {
|
||||
uint8_t *p = realloc(trace, traceLen + TRACE_CHUNK_SIZE);
|
||||
if (p == NULL) {
|
||||
PrintAndLog("Cannot allocate memory for trace");
|
||||
free(trace);
|
||||
fclose(tracefile);
|
||||
return 2;
|
||||
}
|
||||
trace = p;
|
||||
}
|
||||
}
|
||||
fclose(tracefile);
|
||||
} else {
|
||||
trace = malloc(USB_CMD_DATA_SIZE);
|
||||
// Query for the size of the trace
|
||||
UsbCommand response;
|
||||
GetFromBigBuf(trace, USB_CMD_DATA_SIZE, 0, &response, -1, false);
|
||||
traceLen = response.arg[2];
|
||||
if (traceLen > USB_CMD_DATA_SIZE) {
|
||||
uint8_t *p = realloc(trace, traceLen);
|
||||
if (p == NULL) {
|
||||
PrintAndLog("Cannot allocate memory for trace");
|
||||
free(trace);
|
||||
return 2;
|
||||
}
|
||||
trace = p;
|
||||
GetFromBigBuf(trace, traceLen, 0, NULL, -1, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (saveToFile) {
|
||||
FILE *tracefile = NULL;
|
||||
if ((tracefile = fopen(filename,"wb")) == NULL) {
|
||||
PrintAndLog("Could not create file %s", filename);
|
||||
return 1;
|
||||
}
|
||||
fwrite(trace, 1, traceLen, tracefile);
|
||||
PrintAndLog("Recorded Activity (TraceLen = %d bytes) written to file %s", traceLen, filename);
|
||||
fclose(tracefile);
|
||||
} else {
|
||||
PrintAndLog("Recorded Activity (TraceLen = %d bytes)", traceLen);
|
||||
PrintAndLog("");
|
||||
PrintAndLog("Start = Start of Start Bit, End = End of last modulation. Src = Source of Transfer");
|
||||
PrintAndLog("");
|
||||
PrintAndLog(" Start | End | Src | Data (! denotes parity error) | CRC | Annotation |");
|
||||
PrintAndLog("------------|------------|-----|-------------------------------------------------------------------------|-----|--------------------|");
|
||||
|
||||
while(tracepos < traceLen)
|
||||
{
|
||||
tracepos = printScTraceLine(tracepos, traceLen, trace);
|
||||
}
|
||||
}
|
||||
|
||||
free(trace);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdSmartList(const char *Cmd) {
|
||||
ScTraceList(Cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static command_t CommandTable[] = {
|
||||
{"help", CmdHelp, 1, "This help"},
|
||||
{"list", CmdSmartList, 0, "List ISO 7816 history"},
|
||||
{"info", CmdSmartInfo, 1, "Tag information [rdv40]"},
|
||||
{"reader", CmdSmartReader, 1, "Act like an IS07816 reader [rdv40]"},
|
||||
{"raw", CmdSmartRaw, 1, "Send raw hex data to tag [rdv40]"},
|
||||
{"upgrade", CmdSmartUpgrade, 1, "Upgrade firmware [rdv40]"},
|
||||
{"setclock",CmdSmartSetClock, 1, "Set clock speed"},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
int CmdSmartcard(const char *Cmd) {
|
||||
clearCommandBuffer();
|
||||
CmdsParse(CommandTable, Cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CmdHelp(const char *Cmd) {
|
||||
CmdsHelp(CommandTable);
|
||||
return 0;
|
||||
}
|
39
client/cmdsmartcard.h
Normal file
39
client/cmdsmartcard.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2018 iceman
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Proxmark3 RDV40 Smartcard module commands
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef CMDSMARTCARD_H__
|
||||
#define CMDSMARTCARD_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "proxmark3.h"
|
||||
#include "ui.h"
|
||||
#include "cmdparser.h"
|
||||
#include "common.h"
|
||||
#include "util.h"
|
||||
#include "loclass/fileutils.h" // saveFile
|
||||
#include "cmdmain.h" // getfromdevice
|
||||
#include "emv/emvcore.h" // decodeTVL
|
||||
#include "emv/apduinfo.h" // APDUcode description
|
||||
|
||||
extern int CmdSmartcard(const char *Cmd);
|
||||
|
||||
extern int CmdSmartRaw(const char* cmd);
|
||||
extern int CmdSmartUpgrade(const char* cmd);
|
||||
extern int CmdSmartInfo(const char* cmd);
|
||||
extern int CmdSmartReader(const char *Cmd);
|
||||
|
||||
extern int usage_sm_raw(void);
|
||||
extern int usage_sm_reader(void);
|
||||
extern int usage_sm_info(void);
|
||||
extern int usage_sm_upgrade(void);
|
||||
#endif
|
|
@ -10,8 +10,8 @@
|
|||
//
|
||||
// NOTES:
|
||||
// LF Demod functions are placed here to allow the flexability to use client or
|
||||
// device side. Most BUT NOT ALL of these functions are currenlty safe for
|
||||
// device side use currently. (DetectST for example...)
|
||||
// device side. Most BUT NOT ALL of these functions are currently safe for
|
||||
// device side use. (DetectST for example...)
|
||||
//
|
||||
// There are likely many improvements to the code that could be made, please
|
||||
// make suggestions...
|
||||
|
|
|
@ -200,11 +200,12 @@ NXP/Philips CUSTOM COMMANDS
|
|||
#define TOPAZ_WRITE_NE8 0x1B // Write-no-erase (eight bytes)
|
||||
|
||||
|
||||
#define ISO_14443A 0
|
||||
#define ICLASS 1
|
||||
#define ISO_14443B 2
|
||||
#define TOPAZ 3
|
||||
#define PROTO_MIFARE 4
|
||||
#define ISO_14443A 0
|
||||
#define ICLASS 1
|
||||
#define ISO_14443B 2
|
||||
#define TOPAZ 3
|
||||
#define PROTO_MIFARE 4
|
||||
#define ISO_7816_4 5
|
||||
|
||||
//-- Picopass fuses
|
||||
#define FUSE_FPERS 0x80
|
||||
|
@ -216,6 +217,29 @@ NXP/Philips CUSTOM COMMANDS
|
|||
#define FUSE_FPROD0 0x02
|
||||
#define FUSE_RA 0x01
|
||||
|
||||
// ISO 7816-4 Basic interindustry commands. For command APDU's.
|
||||
#define ISO7816_READ_BINARY 0xB0
|
||||
#define ISO7816_WRITE_BINARY 0xD0
|
||||
#define ISO7816_UPDATE_BINARY 0xD6
|
||||
#define ISO7816_ERASE_BINARY 0x0E
|
||||
#define ISO7816_READ_RECORDS 0xB2
|
||||
#define ISO7816_WRITE_RECORDS 0xD2
|
||||
#define ISO7816_APPEND_RECORD 0xE2
|
||||
#define ISO7816_UPDATE_RECORD 0xDC
|
||||
#define ISO7816_GET_DATA 0xCA
|
||||
#define ISO7816_PUT_DATA 0xDA
|
||||
#define ISO7816_SELECT_FILE 0xA4
|
||||
#define ISO7816_VERIFY 0x20
|
||||
#define ISO7816_INTERNAL_AUTHENTICATION 0x88
|
||||
#define ISO7816_EXTERNAL_AUTHENTICATION 0x82
|
||||
#define ISO7816_GET_CHALLENGE 0xB4
|
||||
#define ISO7816_MANAGE_CHANNEL 0x70
|
||||
// ISO7816-4 For response APDU's
|
||||
#define ISO7816_OK 0x9000
|
||||
// 6x xx = ERROR
|
||||
|
||||
|
||||
|
||||
void printIclassDumpInfo(uint8_t* iclass_dump);
|
||||
void getMemConfig(uint8_t mem_cfg, uint8_t chip_cfg, uint8_t *max_blk, uint8_t *app_areas, uint8_t *kb);
|
||||
|
||||
|
|
|
@ -71,21 +71,8 @@ always @(negedge ssp_clk)
|
|||
|
||||
assign ssp_frame = (hi_byte_div == 3'b000);
|
||||
|
||||
// Implement a hysteresis to give out the received signal on
|
||||
// ssp_din. Sample at fc.
|
||||
assign adc_clk = ck_1356meg;
|
||||
assign ssp_din = 1'b0;
|
||||
|
||||
// ADC data appears on the rising edge, so sample it on the falling edge
|
||||
reg after_hysteresis;
|
||||
always @(negedge adc_clk)
|
||||
begin
|
||||
if(& adc_d[7:0]) after_hysteresis <= 1'b1;
|
||||
else if(~(| adc_d[7:0])) after_hysteresis <= 1'b0;
|
||||
end
|
||||
assign dbg = ssp_frame;
|
||||
|
||||
|
||||
assign ssp_din = after_hysteresis;
|
||||
|
||||
assign dbg = ssp_din;
|
||||
|
||||
endmodule
|
||||
endmodule
|
27
include/legic.h
Normal file
27
include/legic.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// (c) 2016 Iceman
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// LEGIC type prototyping
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef _LEGIC_H_
|
||||
#define _LEGIC_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LEGIC
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef struct {
|
||||
uint8_t uid[4];
|
||||
uint32_t tagtype;
|
||||
uint8_t cmdsize;
|
||||
uint8_t addrsize;
|
||||
uint16_t cardsize;
|
||||
} legic_card_select_t;
|
||||
|
||||
#endif // _LEGIC_H_
|
29
include/smartcard.h
Normal file
29
include/smartcard.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// (c) 2018 Iceman, adapted by Marshmellow
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// smart card type prototyping
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef __SMARTCARD_H
|
||||
#define __SMARTCARD_H
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ISO 7618 Smart Card
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef struct {
|
||||
uint8_t atr_len;
|
||||
uint8_t atr[30];
|
||||
} __attribute__((__packed__)) smart_card_atr_t;
|
||||
|
||||
typedef enum SMARTCARD_COMMAND {
|
||||
SC_CONNECT = (1 << 0),
|
||||
SC_NO_DISCONNECT = (1 << 1),
|
||||
SC_RAW = (1 << 2),
|
||||
SC_NO_SELECT = (1 << 3)
|
||||
} smartcard_command_t;
|
||||
|
||||
|
||||
#endif
|
|
@ -60,8 +60,17 @@ typedef struct{
|
|||
#define CMD_BUFF_CLEAR 0x0105
|
||||
#define CMD_READ_MEM 0x0106
|
||||
#define CMD_VERSION 0x0107
|
||||
#define CMD_STATUS 0x0108
|
||||
#define CMD_PING 0x0109
|
||||
#define CMD_STATUS 0x0108
|
||||
#define CMD_PING 0x0109
|
||||
|
||||
// RDV40, Smart card operations
|
||||
#define CMD_SMART_RAW 0x0140
|
||||
#define CMD_SMART_UPGRADE 0x0141
|
||||
#define CMD_SMART_UPLOAD 0x0142
|
||||
#define CMD_SMART_ATR 0x0143
|
||||
// CMD_SMART_SETBAUD is unused for now
|
||||
#define CMD_SMART_SETBAUD 0x0144
|
||||
#define CMD_SMART_SETCLOCK 0x0145
|
||||
|
||||
// For low-frequency tags
|
||||
#define CMD_READ_TI_TYPE 0x0202
|
||||
|
@ -126,10 +135,10 @@ typedef struct{
|
|||
#define CMD_READER_HITAG 0x0372
|
||||
|
||||
#define CMD_SIMULATE_HITAG_S 0x0368
|
||||
#define CMD_TEST_HITAGS_TRACES 0x0367
|
||||
#define CMD_READ_HITAG_S 0x0373
|
||||
#define CMD_WR_HITAG_S 0x0375
|
||||
#define CMD_EMU_HITAG_S 0x0376
|
||||
#define CMD_TEST_HITAGS_TRACES 0x0367
|
||||
#define CMD_READ_HITAG_S 0x0373
|
||||
#define CMD_WR_HITAG_S 0x0375
|
||||
#define CMD_EMU_HITAG_S 0x0376
|
||||
|
||||
|
||||
#define CMD_SIMULATE_TAG_ISO_14443B 0x0381
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue