File: T1Hndlr.c

package info (click to toggle)
slbreflex 2.2.0-6
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 200 kB
  • ctags: 200
  • sloc: ansic: 1,523; makefile: 79; sh: 73
file content (348 lines) | stat: -rw-r--r-- 9,137 bytes parent folder | download | duplicates (3)
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;
  }
}