1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
|
/******************************************************************
Title : T1Hndlr.c
Package: Utimaco Linux Driver
Author : David Corcoran
Date : 10/15/99
Purpose: This handles the T=1 protocol.
LICENSE: See LICENSE
********************************************************************/
#include "pcscdefines.h"
#include "LinuxDefines.h"
#include "T1Hndlr.h"
#include "ctapi.h"
/* This can actually be read from the ATR but since many
smartcard manufacturers still can't figure out what it
means to be ISO compliant I will just rely on a small
number. ( Most cards have a 32 byte IFSD_SIZE )
*/
#define MAX_IFSD_SIZE 20
#define BWT_MULTIPLIER 3
#define T1_SBLOCK_SUCCESS 0x200
#define T1_SBLOCK_WTXREQUEST 0x201
#define T1_RBLOCK_SUCCESS 0x210
#define T1_IBLOCK_SUCCESS 0x220
#define T1_ERROR_PARITY 0x230
#define T1_ERROR_OTHER 0x240
#define T1_INVALID_BLOCK 0x250
ULONG T1_Transaction( PUCHAR, ULONG, PUCHAR, PULONG );
ULONG T1_GetResponseType( PUCHAR, DWORD );
ULONG T1_WTXResponse( UCHAR, PUCHAR );
UCHAR T1CalculateLRC( PUCHAR pucBuffer, DWORD dwLength) {
UCHAR ucXSum;
short i;
ucXSum = 0;
for ( i=0; i<dwLength; i++ ) {
ucXSum ^= pucBuffer[i];
}
return ucXSum;
}
ULONG T1_GetResponseType( PUCHAR pucRBuffer, DWORD dwLength ) {
/* Checks to see what type of block it is */
if ( (pucRBuffer[1] & 0x80)&&(pucRBuffer[1] & 0x40) ) {
/* S Block Found */
#ifdef PCSC_DEBUG
printf("S Block Found\n");
#endif
if ( pucRBuffer[1] & 0x03 ) {
#ifdef PCSC_DEBUG
printf("WTX Request Made\n");
#endif
return T1_SBLOCK_WTXREQUEST;
} else if ( pucRBuffer[1] & 0x04 ) {
#ifdef PCSC_DEBUG
printf("Vpp Error Response Made\n");
#endif
} else if ( pucRBuffer[1] & 0x02 ) {
#ifdef PCSC_DEBUG
printf("ABORT Request Made\n");
#endif
} else if ( pucRBuffer[1] & 0x01 ) {
#ifdef PCSC_DEBUG
printf("IFS Request Made\n");
#endif
} else if ( pucRBuffer[1] == 0xC0 ) {
#ifdef PCSC_DEBUG
printf("RESYNCH Request Made\n");
#endif
} else {
#ifdef PCSC_DEBUG
printf("Unknown Request Made\n");
#endif
}
return T1_SBLOCK_SUCCESS;
} else if ( pucRBuffer[1] & 0x80 ) {
/* R Block Found */
#ifdef PCSC_DEBUG
printf("R Block Found\n");
#endif
if ( pucRBuffer[1] & 0x01 ) {
return T1_ERROR_PARITY;
}
if ( pucRBuffer[1] & 0x02 ) {
return T1_ERROR_OTHER;
}
return T1_RBLOCK_SUCCESS;
} else if ( (pucRBuffer[1] & 0x80) == 0 ) {
/* I Block Found */
#ifdef PCSC_DEBUG
printf("I Block Found\n");
#endif
return T1_IBLOCK_SUCCESS;
} else {
return T1_INVALID_BLOCK;
}
}
ULONG T1_WTXResponse( UCHAR ucWtx, PUCHAR ucRBuffer) {
ULONG rv;
DWORD dwRBufferLen;
UCHAR ucTBuffer[MAX_BUFFER_SIZE];
ucTBuffer[0] = 0x00; /* NAD - Addressing not used */
ucTBuffer[1] = 0xE3; /* PCB - WTX Response */
ucTBuffer[2] = 1; /* LEN - 1 */
ucTBuffer[3] = ucWtx; /* WTX Value */
ucTBuffer[4] = T1CalculateLRC( ucTBuffer, 4 );
dwRBufferLen = MAX_BUFFER_SIZE;
Adm_SetWWT( ucWtx*BWT_MULTIPLIER );
Adm_SetMode( 1, ucWtx );
rv = T1_Transaction( ucTBuffer, 5,
ucRBuffer, &dwRBufferLen );
return rv;
}
ULONG T1_ExchangeData( PUCHAR pucTBuffer, DWORD dwTLength,
PUCHAR pucRBuffer, DWORD* dwRLength ) {
ULONG rv, tv;
UCHAR ucTBuffer[MAX_BUFFER_SIZE];
UCHAR ucRBuffer[MAX_BUFFER_SIZE];
UCHAR ucRTemp[MAX_BUFFER_SIZE];
UCHAR ucRHeader[5];
DWORD dwCycles, dwRemaining, dwOffset;
DWORD dwRBufferLen, dwRCount;
int i;
static UCHAR ucSChainNum = 0;
static UCHAR ucRChainNum = 1;
/* Problem: Command may have a return that exceeds the
IFSD Length.
Solution: Wait for ICC's I-block transmission with
the MORE bit set and send appropriate R-blocks.
*/
if ( dwTLength + 4 < MAX_IFSD_SIZE ) {
retryblocka:
ucTBuffer[0] = 0x00; /* NAD - Addressing not used */
ucTBuffer[1] = 0x00; /* PCB - No chaining yet */
ucTBuffer[2] = dwTLength; /* LEN - Size of the APDU */
ucTBuffer[1] |= (ucSChainNum%2)?0x40:0x00; /* N(R) */
memcpy( &ucTBuffer[3], pucTBuffer, dwTLength );
ucTBuffer[3 + dwTLength] = T1CalculateLRC( ucTBuffer, dwTLength + 3 );
dwRBufferLen = sizeof(ucRBuffer);
rv = T1_Transaction( ucTBuffer, dwTLength+4,
ucRBuffer, &dwRBufferLen );
if ( rv != STATUS_SUCCESS ) {
*dwRLength = 0;
return rv;
}
tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );
switch( tv ) {
UCHAR ucWtx;
case T1_SBLOCK_WTXREQUEST:
ucWtx = ucRBuffer[3];
T1_WTXResponse( ucWtx, ucRBuffer );
break;
case T1_ERROR_OTHER:
ucSChainNum += 1;
goto retryblocka;
break;
default:
break;
}
/* Copy the response from the DATA field */
if ( ucRBuffer[2] > 0 && rv == STATUS_SUCCESS ) {
memcpy( ucRTemp, &ucRBuffer[3], ucRBuffer[2] );
dwRCount = ucRBuffer[2];
}
ucSChainNum += 1;
/* Start of loop which checks the More Data bit after every transfer
to determine whether or not another R-block must be sent
*/
if ( ucRBuffer[1] & 0x20 ) { /* PCB More Data Bit */
do {
/* Create the R-block */
ucTBuffer[0] = 0x00; /* DF Nad */
ucTBuffer[1] = (ucRChainNum%2)?0x90:0x80; /* N(R) */
ucTBuffer[2] = 0x00; /* Len = 0 */
ucTBuffer[3] = T1CalculateLRC( ucTBuffer, 3 ); /* Lrc */
dwRBufferLen = sizeof(ucRBuffer);
rv = T1_Transaction( ucTBuffer, 4,
ucRBuffer, &dwRBufferLen );
if (rv != STATUS_SUCCESS) {
*dwRLength = 0;
return rv;
}
memcpy( &ucRTemp[dwRCount], &ucRBuffer[3], ucRBuffer[2] );
dwRCount += ucRBuffer[2];
ucRChainNum += 1;
} while ( ucRBuffer[1] & 0x20 );
}
#ifdef PCSC_DEBUG
printf("Full T=1 Response Data APDU: ");
for (i=0; i < dwRCount; i++) {
printf("%x ", ucRTemp[i]);
} printf("\n");
#endif
/* Problem: Command is larger than the IFSD Length
This is a large outgoing command which
will have a return of 2 status bytes.
Solution: Use I-block chaining and wait for ICC
R-block responses
*/
} else {
dwCycles = dwTLength / MAX_IFSD_SIZE;
dwRemaining = dwTLength % MAX_IFSD_SIZE;
dwRBufferLen = sizeof(ucRBuffer);
for ( i=0; i < dwCycles; i++ ) {
retryblockb:
ucTBuffer[0] = 0x00; /* NAD - Addressing not used */
ucTBuffer[1] = 0x20; /* PCB - Chaining used */
ucTBuffer[2] = MAX_IFSD_SIZE; /* LEN - Size of the APDU */
ucTBuffer[1] |= (ucSChainNum%2)?0x40:0x00; /* N(R) */
dwOffset = MAX_IFSD_SIZE*i; /* Buffer Offset */
memcpy( &ucTBuffer[3], &pucTBuffer[dwOffset], MAX_IFSD_SIZE );
ucTBuffer[3 + MAX_IFSD_SIZE] = T1CalculateLRC( ucTBuffer,
MAX_IFSD_SIZE + 3 );
rv = T1_Transaction( ucTBuffer, 4+MAX_IFSD_SIZE,
ucRBuffer, &dwRBufferLen );
if ( rv != STATUS_SUCCESS ) {
*dwRLength = 0;
return rv;
}
tv = T1_GetResponseType( ucRBuffer, dwRBufferLen );
switch( tv ) {
UCHAR ucWtx;
case T1_SBLOCK_WTXREQUEST:
ucWtx = ucRBuffer[3];
T1_WTXResponse( ucWtx, ucRBuffer );
break;
case T1_ERROR_OTHER:
ucSChainNum += 1;
goto retryblockb;
break;
default:
break;
}
ucSChainNum += 1;
}
ucTBuffer[0] = 0x00; /* NAD - Addressing not used */
ucTBuffer[1] = 0x00; /* PCB - Chaining done */
ucTBuffer[2] = dwRemaining; /* LEN - Size of the APDU */
ucTBuffer[1] |= (ucSChainNum%2)?0x40:0x00; /* N(R) */
dwOffset = MAX_IFSD_SIZE*i; /* Buffer Offset */
memcpy( &ucTBuffer[3], &pucTBuffer[dwOffset], dwRemaining );
ucTBuffer[3 + MAX_IFSD_SIZE] = T1CalculateLRC( ucTBuffer,
dwRemaining + 3 );
rv = T1_Transaction( ucTBuffer, dwRemaining+4,
ucRBuffer, &dwRBufferLen );
if ( rv != STATUS_SUCCESS ) {
*dwRLength = 0;
return rv;
}
if ( ucRBuffer[2] > 0 && rv == STATUS_SUCCESS ) {
memcpy( ucRTemp, &ucRBuffer[3], ucRBuffer[2] );
dwRCount = ucRBuffer[2];
}
}
*dwRLength = dwRCount;
memcpy( pucRBuffer, ucRTemp, dwRCount );
return rv;
}
ULONG T1_Transaction( PUCHAR pRequest, ULONG RequestLen,
PUCHAR pReply, PULONG pReplyLen ) {
ULONG rv;
UCHAR sad, dad;
unsigned int lr;
sad = 2; dad = 0;
lr = 255;
rv = CT_data( 1, &dad, &sad, RequestLen, pRequest, &lr, pReply );
if ( rv == OK ) {
*pReplyLen = lr;
return STATUS_SUCCESS;
} else {
*pReplyLen = 0;
return STATUS_UNSUCCESSFUL;
}
}
|