/****************************************************************** 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 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; } }