📄 tda8007.c
字号:
/*---------------------------------------------------------------------------
* Copyright (C) 2004 Dallas Semiconductor Corporation, All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Dallas Semiconductor
* shall not be used except as stated in the Dallas Semiconductor
* Branding Policy.
* ---------------------------------------------------------------------------
*/
/**
* \file lib8007.c
* \brief Interface library for 8007 smartcard interface chips
*
* This library contains routines required to communicate with smartcards
* via an 8007 while conforming to:
* ISO 7816
* EMV 4.1
* GSM ????
*/
// TODO:
// Test CRC16 CCITT against ISO 3309/CCITT vectors
// Test T=0 error modes
// Test T=1 error modes
// Implement BWT, CWT, etc in T=1
// Implement WWT in T=0
// Implement/test T=1 receive chaining
// Add error handling to detect power, serial, etc. problems and report to user.
// Implement baud rate detection/negotiation (this is called PPS?)
// Add Tx(i) parameter checking (e.g. EMV requires failure for lack of TA3 parameter)
// Automatically power down on certain errors (too many parity, etc.)
// Find out why MSR.CRED bit sometimes sticks at zero, and screws up dssc_writeregister() and readregister().
// Consider moving all ISO, EMV, etc special ATR checks into a single function.
#include <stdio.h>
#include <string.h>
#include <reg5000.h>
//#include <REG390.H>
#include <absacc.h>
#include "tda8007.h"
// FIXME: Remove after we find out why MSR.CRED sometimes never gets back to 1.
//#define DEBUG_CRED
// 0 - no debug, 1 - errors, 2 - basic, 3 - very verbose
#define DEBUG 1
void clearATRstruct(struct ATR*);
int16_t dssc_ATRsequence(uint8_t);
uint8_t workingBuffer[512];
// FIXME: Do I want to keep the ATR raw buffer around for both slots? If not, go to singular ATRLength.
uint8_t ATRLength[CARD_SLOTS];
struct ATR lastATR[CARD_SLOTS];
uint8_t TMode[CARD_SLOTS]; // T=0 or T=1
uint32_t WWT[CARD_SLOTS]; // Work wait time
uint32_t CWT[CARD_SLOTS]; // Character wait time
uint32_t BWT[CARD_SLOTS]; // Block wait time
uint8_t EDCtype[CARD_SLOTS];
uint8_t NAD[CARD_SLOTS]; // Node address byte to use when communicating in T=1
uint8_t IFSC[CARD_SLOTS]; // Maximum segment length when sending to card in T=1
uint8_t currentSlot = 0;
/*
Dump formatted contents of ATR struct
*/
#if DEBUG > 0
void dumpATRStruct(struct ATR myatr)
{
int i;
printf("ATR\n");
printf("TS: %02bx\n",(uint8_t)myatr.TS);
printf("T0: %02bx\n",(uint8_t)myatr.T0);
for (i = 1;i < 8;i++)
{
if (myatr.TA[i] != -1)
printf("TA%d: %02bx\n",i,(uint8_t)myatr.TA[i]);
if (myatr.TB[i] != -1)
printf("TB%d: %02bx\n",i,(uint8_t)myatr.TB[i]);
if (myatr.TC[i] != -1)
printf("TC%d: %02bx\n",i,(uint8_t)myatr.TC[i]);
if (myatr.TD[i] != -1)
printf("TD%d: %02bx\n",i,(uint8_t)myatr.TD[i]);
}
for (i = 0;i < myatr.HistoricalLength;i++)
{
printf("%02bx ",myatr.Historical[i]);
}
printf("\n");
if (myatr.TCK != -1)
printf("TCK: %02bx\n",(uint8_t)myatr.TCK);
}
#endif
/*
Used to clear the ATR struct before card power up.
*/
void clearATRStruct(struct ATR *myatr)
{
memset(myatr,0xFF,sizeof(struct ATR));
myatr->HistoricalLength = 0;
}
/*
CCITT CRC 16
X^16+X^12+X^5+1
Be sure to use 0xFFFF as initial crc value.
FIXME: Find test vectors and test.
NOTE: There is another CRC16 that is not used for smart cards. Here it is:
CRC16
X^16+X^15+X^2+1
0x8005
0xA001
*/
uint16_t update_crc(uint8_t value, uint16_t crc)
{
int i;
uint16_t newval = value << 8;
for (i = 0;i < 8;i++)
{
if ((crc ^ newval) & 0x8000)
{
crc <<= 1;
crc ^= 0x1021;
}
else
{
crc <<= 1;
}
newval <<= 1;
}
/*
// Fast implementation from www.eagleairaust.com.au
crc = (crc >> 8) | (crc << 4);
crc ^= value;
crc ^= (crc & 0xFF) >> 4;
crc ^= (crc << 8) << 4;
crc ^= ((crc & 0xFF) << 4) << 1;
*/
return crc;
}
/*
Read a byte from the UART or timeout after BWT (T=1), CWT (T=1) or WWT (T=0).
Not tested.
*/
int16_t readByte()
{
uint8_t val;
uint32_t timeout;
while(!(dssc_readregister(MSR) & MSR_TBE_RBF_MASK))
{
val = dssc_readregister(USR);
if (val & USR_PE_MASK)
{
#if DEBUG > 0
printf("parity error\n");
#endif
return ERR_RECEIVE_PARITY;
}
if (val & (USR_TOL3_MASK|USR_TOL2_MASK|USR_TOL1_MASK))
{
#if DEBUG > 0
printf("timeout error %02bx\n",val);
#endif
return ERR_RECEIVE_TIMEOUT;
}
}
// Read and store byte
val = dssc_readregister(URR);
if (TMode[currentSlot] == 0)
timeout = WWT[currentSlot]; // Use WWT for T=0
else
timeout = CWT[currentSlot]; // Use CWT for T=1
// Set up timer for 24 bit timer, start immediately
dssc_writeregister(TOC,0x00);
dssc_writeregister(TOR3,timeout >> 16);
dssc_writeregister(TOR2,timeout >> 8);
dssc_writeregister(TOR1,timeout);
dssc_writeregister(TOC,0x68);
return val;
}
/*
FIXME: This is not the proper way to timeout. Use the 8007!!!!
Read a byte from the UART or timeout after some arbitrary loop timeout.
*/
/*int16_t readByteOld()
{
uint8_t val;
int count = 0;
while(!(dssc_readregister(MSR) & MSR_TBE_RBF_MASK))
{
count++;
if (count > 5000)
{
printf("timeout\n");
return ERR_RECEIVE_TIMEOUT;
}
val = dssc_readregister(USR);
if (val & USR_PE_MASK)
{
printf("parity error\n");
return ERR_RECEIVE_PARITY;
}
}
// Read and store byte
val = dssc_readregister(URR);
return val;
}
*/
/*
Write a byte and leave the 8007 in transmit mode
*/
void writeByte(uint8_t onebyte)
{
uint8_t val;
val = dssc_readregister(UCR1);
// set T bit
dssc_writeregister(UCR1,val | UCR1_T_R_MASK);
dssc_writeregister(UTR,onebyte);
// wait for byte to go out
while (!(dssc_readregister(USR) & USR_TBE_RBF_MASK));
}
/*
Write a byte and put the 8007 in receive mode
*/
void writeLastByte(uint8_t onebyte)
{
uint8_t val;
uint32_t timeout;
if (TMode[currentSlot] == 0)
timeout = WWT[currentSlot]; // Use WWT for T=0
else
timeout = BWT[currentSlot]; // Use BWT for T=1
// Set up timer for 24 bit timer, edge trigger start
dssc_writeregister(TOC,0x00);
dssc_writeregister(TOR3,timeout >> 16);
dssc_writeregister(TOR2,timeout >> 8);
dssc_writeregister(TOR1,timeout);
dssc_writeregister(TOC,0x7C);
val = dssc_readregister(UCR1);
// set LCT and T bit
dssc_writeregister(UCR1,val | (UCR1_T_R_MASK|UCR1_LCT_MASK));
dssc_writeregister(UTR,onebyte);
// wait for byte to go out
while (!(dssc_readregister(USR) & USR_TBE_RBF_MASK));
}
/*
Write a byte and put the 8007 in receive mode
*/
/*void writeLastByteOld(uint8_t onebyte)
{
uint8_t val;
writeLastByteTimeout(onebyte);
return;
val = dssc_readregister(UCR1);
// set LCT and T bit
dssc_writeregister(UCR1,val | (UCR1_T_R_MASK|UCR1_LCT_MASK));
dssc_writeregister(UTR,onebyte);
// wait for byte to go out
while (!(dssc_readregister(USR) & USR_TBE_RBF_MASK));
}
*/
/*
Generate Error Data Check value depending on EDC type
*/
uint16_t generateEDC(uint8_t type,uint8_t onebyte,uint16_t value)
{
if (type == EDC_TYPE_LRC)
{
return (value ^ onebyte);
}
else // type = CRC
{
return update_crc(onebyte,value);
}
}
/*
Send a T=1 formatted block.
Formats T=1 packet from raw input data. Length must already be
within the requirements of the destination smart card.
*/
void sendBlock(uint8_t NAD,uint8_t PCB,int16_t length,uint8_t *buffer,uint8_t type)
{
int i;
uint16_t EDC;
if (type == EDC_TYPE_LRC)
{
EDC = 0;
}
else // type = CRC
{
EDC = 0xFFFF;
}
writeByte(NAD);
EDC = generateEDC(type,NAD,EDC);
writeByte(PCB);
EDC = generateEDC(type,PCB,EDC);
writeByte(length);
EDC = generateEDC(type,length,EDC);
for (i=0;i<length;i++)
{
writeByte(buffer[i]);
EDC = generateEDC(type,buffer[i],EDC);
}
if (type == EDC_TYPE_LRC)
{
writeLastByte(EDC);
}
else // type = CRC
{
writeByte(EDC);
writeLastByte(EDC>>8);
}
}
/*
Receive a T=1 formatted block.
This does a raw receive, it does not check sequence numbers.
FIXME: The receive block function should do more to handle checking sequencing and
only return the raw data and some sort of pass/fail code to the caller.
*/
int16_t receiveBlock(uint8_t *rNAD,uint8_t *rPCB,uint8_t *rLEN,uint8_t *buffer,uint8_t type)
{
int16_t retval;
int16_t index = 0;
uint16_t EDC;
int16_t i;
uint8_t expectedLength;
// Get NAD, PCB, and Length
retval = readByte();
if (retval < 0) return retval;
*rNAD = retval;
retval = readByte();
if (retval < 0) return retval;
*rPCB = retval;
retval = readByte();
if (retval < 0) return retval;
*rLEN = retval;
// Add one to length for LRC
expectedLength = *rLEN+1;
// Add additional byte if using CRC
if (type == EDC_TYPE_CRC)
expectedLength++;
// Get all data bytes plus EDC (1 or 2 bytes at end)
for (i = 0;i < expectedLength;i++)
{
retval = readByte();
if (retval < 0)
{
return retval;
}
buffer[index++] = retval;
}
// Check the LRC or CRC
if (type == EDC_TYPE_LRC)
{
EDC = 0;
EDC = generateEDC(EDC_TYPE_LRC,*rNAD,EDC);
EDC = generateEDC(EDC_TYPE_LRC,*rPCB,EDC);
EDC = generateEDC(EDC_TYPE_LRC,*rLEN,EDC);
for (i = 0;i < index;i++)
{
EDC = generateEDC(EDC_TYPE_LRC,buffer[i],EDC);
}
if (EDC != 0)
{
#if DEBUG > 0
printf("Bad receive LRC\n");
#endif
return ERR_RECEIVE_LRC;
}
}
else // EDC is CRC
{
EDC = 0xFFFF;
EDC = generateEDC(EDC_TYPE_LRC,*rNAD,EDC);
EDC = generateEDC(EDC_TYPE_LRC,*rPCB,EDC);
EDC = generateEDC(EDC_TYPE_LRC,*rLEN,EDC);
for (i = 0;i < (index-2);i++)
{
EDC = generateEDC(EDC_TYPE_CRC,buffer[i],EDC);
}
if (((EDC >> 8) != buffer[index-2]) ||
((EDC & 0xFF) != buffer[index-1]))
{
#if DEBUG > 0
printf("Bad receive CRC\n");
#endif
return ERR_RECEIVE_CRC;
}
}
#if DEBUG > 1
printf("NAD: %02bx ",*rNAD);
printf("PCB: %02bx ",*rPCB);
printf("LEN: %02bx ",*rLEN);
printf("D: ");
for (i = 0;i < index;i++)
printf("%02bx ",buffer[i]);
printf("\n");
#endif
return *rLEN;
}
/*
Send an S block for setting IFSD (card terminal buffer size)
*/
int16_t dssc_sendsblockIFSD(uint8_t IFSD)
{
uint8_t buffer[1];
uint8_t rNAD,rPCB,rLEN;
uint8_t rbuffer[100];
#if DEBUG > 1
printf("T=1 Sending IFSD\n");
#endif
buffer[0] = IFSD;
sendBlock(0,0xC1,1,buffer,EDCtype[currentSlot]);
receiveBlock(&rNAD,&rPCB,&rLEN,rbuffer,EDCtype[currentSlot]);
// If we do not get a confirmation response, fail.
if ((rPCB == 0xE1) && (rLEN == 1) && (rbuffer[0] == IFSD))
return 0;
else
return ERR_SETIFSD_FAILURE;
}
/*
Set Node address info for this card
*/
void dssc_setNAD(uint8_t value)
{
NAD[currentSlot] = value;
}
/*
Send a message using T=1 protocol
*/
int16_t dssc_sendAPDUT1(uint8_t *buffer,int16_t length,uint8_t *rbuffer)
{
#if DEBUG > 1
int index;
#endif
int16_t maxSegLength = IFSC[currentSlot]; // Set by ATR (TA3), or default to 0x20
int16_t retval;
uint8_t sequenceNumber = 0;
uint8_t rNAD,rPCB,rLEN;
uint8_t tempbuffer[512]; // FIXME: What size should the temp buffer be??
#if DEBUG > 1
printf("T=1 Sending APDU: ");
for (index = 0;index < length;index++)
printf("%02bx ",buffer[index]);
printf("\n");
#endif
while (length > maxSegLength)
{
// Send block with chaining mode, current sequence number, and maximum length.
sendBlock(NAD[currentSlot],sequenceNumber|0x20,maxSegLength,buffer,EDCtype[currentSlot]);
retval = receiveBlock(&rNAD,&rPCB,&rLEN,tempbuffer,EDCtype[currentSlot]);
if (retval < 0) return retval;
// Check for bad sequence number return
if ((rPCB ^ ((sequenceNumber>>2) ^ 0x10)) != 0x80)
{
#if DEBUG > 0
printf("Bad return: %02bx\n",rPCB);
printf("%02bx, %02bx, %02bx\n",sequenceNumber,sequenceNumber ^ 0x10,rPCB^sequenceNumber^0x10);
#endif
return ERR_RECEIVE_SEQUENCENUM;
}
sequenceNumber ^= 0x40;
buffer += maxSegLength;
length -= maxSegLength;
}
// Send last (or only) block. Then, we can wait for the receive side.
sendBlock(NAD[currentSlot],sequenceNumber,length,buffer,EDCtype[currentSlot]);
// FIXME: Implement receive chaining here. Loop while chaining bit set and info packets coming.
// FIXME: How do we handle non-I-block PCBs?
retval = receiveBlock(&rNAD,&rPCB,&rLEN,tempbuffer,EDCtype[currentSlot]);
if (retval < 0) return retval;
memcpy(rbuffer,tempbuffer,rLEN);
return retval;
}
/*
Send a message using T=0 protocol
*/
int16_t dssc_sendAPDUT0(uint8_t *buffer,int16_t length,uint8_t *rbuffer)
{
int index;
int16_t rindex = 0;
uint8_t val;
uint8_t INS = buffer[1];
int retval;
#if DEBUG > 1
printf("T=0 Sending APDU: ");
for (index = 0;index < length;index++)
printf("%02bx ",buffer[index]);
printf("\n");
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -