/*++ Copyright (C) Microsoft Corporation, 1996 - 1999 Module Name: scdrvtst Abstract: IOCTL test program for smart card driver. Author: Klaus Schutz (kschutz) Dec-1996 Revision History: --*/ #include #include #include #include #include #include "ifdtest.h" class CCardList; // This represents a single function of a card class CCardFunction { CString m_CName; CHAR m_chShortCut; CByteArray m_CData; CCardFunction *m_pCNextFunction; public: CCardFunction( CString &in_CName, CHAR in_chShortCut, CByteArray &in_CData ); friend CCardList; }; // This is a single card class CCard { CString m_CName; CHAR m_chShortCut; CCardFunction *m_pCFirstFunction; CCard *m_pCNextCard; public: CCard( CString & in_CCardName, CHAR in_chShortCut ); friend CCardList; }; // This implements a list of cards class CCardList { CString m_CScriptFileName; CCard *m_pCFirstCard; CCard *m_pCCurrentCard; ULONG m_uNumCards; public: CCardList(CString &in_CScriptFileName); void AddCard( CString &in_CardName, CHAR in_chShortCut ); void AddCardFunction( CString &in_CFunctionName, CHAR in_chShortCut, CByteArray &l_pCData ); void ShowCards( void (__cdecl *in_pCallBack)(void *in_pContext, PCHAR in_pchCardName), void *in_pContext ); BOOL SelectCard(CHAR in_chShortCut); void ReleaseCard(void); CString GetCardName(void); ULONG GetNumCards(void) { return m_uNumCards; } BOOL IsCardSelected(void); BOOL ListFunctions(void); CByteArray *GetApdu(CHAR in_chShortCut); }; CCardFunction::CCardFunction( CString &in_CName, CHAR in_chShortCut, CByteArray &in_CData ) /*++ Adds a function to the current card --*/ { m_CName = in_CName; m_chShortCut = in_chShortCut; m_CData.Copy(in_CData); m_pCNextFunction = NULL; } CCard::CCard( CString &in_CCardName, CHAR in_chShortCut ) /*++ Routine Description: Constructor for a new card Arguments: CardName - Reference to card to add in_uPos - index of shortcut key Return Value: --*/ { m_CName = in_CCardName; m_chShortCut = in_chShortCut; m_pCNextCard = NULL; m_pCFirstFunction = NULL; } void CCardList::AddCard( CString &in_CCardName, CHAR in_chShortCut ) /*++ Routine Description: Adds a new card to CardList Arguments: in_CCardName - Reference to card to add --*/ { CCard *l_pCNewCard = new CCard(in_CCardName, in_chShortCut); if (m_pCFirstCard == NULL) { m_pCFirstCard = l_pCNewCard; } else { CCard *l_pCCurrent = m_pCFirstCard; while (l_pCCurrent->m_pCNextCard) { l_pCCurrent = l_pCCurrent->m_pCNextCard; } l_pCCurrent->m_pCNextCard = l_pCNewCard; } m_pCCurrentCard = l_pCNewCard; m_uNumCards += 1; } void CCardList::AddCardFunction( CString &in_CFunctionName, CHAR in_chShortCut, CByteArray &in_pCData ) /*++ Routine Description: Adds a new function to the current card Arguments: in_CCardName - Reference to card to add in_chShortCut - Shortcut key Return Value: --*/ { CCardFunction *l_pCNewFunction = new CCardFunction( in_CFunctionName, in_chShortCut, in_pCData ); if (m_pCCurrentCard->m_pCFirstFunction == NULL) { m_pCCurrentCard->m_pCFirstFunction = l_pCNewFunction; } else { CCardFunction *l_pCCurrent = m_pCCurrentCard->m_pCFirstFunction; while (l_pCCurrent->m_pCNextFunction) { l_pCCurrent = l_pCCurrent->m_pCNextFunction; } l_pCCurrent->m_pCNextFunction = l_pCNewFunction; } } CCardList::CCardList( CString &in_CScriptFileName ) /*++ Routine Description: Adds a new function to the current card Arguments: CardName - Reference to card to add in_uPos - index of shortcut key Return Value: --*/ { CStdioFile l_CScriptFile; CHAR l_rgchBuffer[255], l_chKey; ULONG l_uLineNumber = 0; BOOL l_bContinue = FALSE; CByteArray l_Data; CString l_CCommand; m_pCFirstCard = NULL; m_pCCurrentCard = NULL; if (l_CScriptFile.Open(in_CScriptFileName, CFile::modeRead) == NULL) { printf("Script file cannot be opened: %s\n", in_CScriptFileName); return; } m_CScriptFileName = in_CScriptFileName; while (l_CScriptFile.ReadString(l_rgchBuffer, sizeof(l_rgchBuffer) - 1)) { try { CString l_CLine(l_rgchBuffer); CString l_CCommandApdu; l_uLineNumber += 1; if (l_CLine.GetLength() != 0 && l_CLine[0] == '#') { // comment line found, skip this line continue; } // Get rid of leading and trailing spaces l_CLine.TrimLeft(); l_CLine.TrimRight(); int l_ichStart = l_CLine.Find('['); int l_ichKey = l_CLine.Find('&'); int l_ichEnd = l_CLine.Find(']'); if(l_ichStart == 0 && l_ichKey > 0 && l_ichEnd > l_ichKey + 1) { // // Add new card to list // CString l_CardName; // Change card name from [&Card] to [C]ard l_CardName = l_CLine.Mid(l_ichStart + 1, l_ichKey - l_ichStart - 1) + '[' + l_CLine[l_ichKey + 1] + ']' + l_CLine.Mid(l_ichKey + 2, l_ichEnd - l_ichKey - 2); AddCard( l_CardName, l_CardName[l_ichKey] ); } else if (l_ichStart == -1 && l_ichKey >= 0 && l_ichEnd == -1) { // // Add new function to current card // // Get function name CString l_CToken = l_CLine.SpanExcluding(","); // Search for shurtcut key l_ichKey = l_CToken.Find('&'); if (l_ichKey == -1) { throw "Missing '&' in function name"; } l_chKey = l_CToken[l_ichKey + 1]; // Change card function from &Function to [F]unction l_CCommand = l_CToken.Mid(l_ichStart + 1, l_ichKey - l_ichStart - 1) + '[' + l_CToken[l_ichKey + 1] + ']' + l_CToken.Right(l_CToken.GetLength() - l_ichKey - 2); LONG l_lComma = l_CLine.Find(','); if (l_lComma == -1) { throw "Missing command APDU"; } else { l_CCommandApdu = l_CLine.Right(l_CLine.GetLength() - l_lComma - 1); } } else if (l_bContinue) { l_CCommandApdu = l_CLine; } else if (l_CLine.GetLength() != 0 && l_CLine[0] != '#') { throw "Line invalid"; } if (l_CCommandApdu != "") { do { CHAR l_chData; l_CCommandApdu.TrimLeft(); ULONG l_uLength = l_CCommandApdu.GetLength(); if (l_uLength >= 3 && l_CCommandApdu[0] == '\'' && l_CCommandApdu[2] == '\'') { // add ascsii character like 'c' l_chData = l_CCommandApdu[1]; l_Data.Add(l_chData); } else if(l_uLength >= 3 && l_CCommandApdu[0] == '\"' && l_CCommandApdu.Right(l_uLength - 2).Find('\"') != -1) { // add string like "string" for (INT l_iIndex = 1; l_CCommandApdu[l_iIndex] != '\"'; l_iIndex++) { l_Data.Add(l_CCommandApdu[l_iIndex]); } } else if (l_CCommandApdu.SpanIncluding("0123456789abcdefABCDEF").GetLength() == 2) { sscanf(l_CCommandApdu, "%2x", &l_chData); l_Data.Add(l_chData); } else { l_CCommandApdu = l_CCommandApdu.SpanExcluding(","); static CString l_CError; l_CError = "Illegal value found: " + l_CCommandApdu; throw (PCHAR) (LPCSTR) l_CError; } l_ichStart = l_CCommandApdu.Find(','); if (l_ichStart != -1) { l_CCommandApdu = l_CLine.Right(l_CCommandApdu.GetLength() - l_ichStart - 1); } } while (l_ichStart != -1); if (l_CLine.Find('\\') != -1) { // we have to read more data from the file l_bContinue = TRUE; } else { if (m_pCCurrentCard == NULL) { throw "Card command found, but no card defined"; } AddCardFunction( l_CCommand, l_chKey, l_Data ); l_CCommand = ""; l_Data.RemoveAll(); l_bContinue = FALSE; } } } catch (PCHAR in_pchError){ printf( "%s (%d): %s\n", in_CScriptFileName, l_uLineNumber, in_pchError ); l_CCommand = ""; l_Data.RemoveAll(); l_bContinue = FALSE; } } m_pCCurrentCard = NULL; } void CCardList::ShowCards( void (__cdecl *in_pCallBack)(void *in_pContext, PCHAR in_pchCardName), void *in_pContext ) { CCard *l_pCCurrentCard = m_pCFirstCard; if (l_pCCurrentCard == NULL) { return; } while(l_pCCurrentCard) { (*in_pCallBack) (in_pContext, (PCHAR) (LPCSTR) l_pCCurrentCard->m_CName); l_pCCurrentCard = l_pCCurrentCard->m_pCNextCard; } } BOOL CCardList::ListFunctions( void ) /*++ List all card functions --*/ { if (m_pCCurrentCard == NULL) return FALSE; CCardFunction *l_pCCurrentFunction = m_pCCurrentCard->m_pCFirstFunction; while(l_pCCurrentFunction) { printf(" %s\n", (LPCSTR) l_pCCurrentFunction->m_CName); l_pCCurrentFunction = l_pCCurrentFunction->m_pCNextFunction; } return TRUE; } BOOL CCardList::SelectCard( CHAR in_chShortCut ) /*++ Routine Description: Selectd a card by shorcut Arguments: chShortCut - Shortcut key Return Value: TRUE - card found and selected FALSE - no card with that shortcut found --*/ { m_pCCurrentCard = m_pCFirstCard; while(m_pCCurrentCard) { if (m_pCCurrentCard->m_chShortCut == in_chShortCut) { return TRUE; } m_pCCurrentCard = m_pCCurrentCard->m_pCNextCard; } m_pCCurrentCard = NULL; return FALSE; } void CCardList::ReleaseCard( void ) { m_pCCurrentCard = NULL; } BOOL CCardList::IsCardSelected( void ) { return (m_pCCurrentCard != NULL); } CString CCardList::GetCardName( void ) { CString l_CCardName; INT l_iLeft = m_pCCurrentCard->m_CName.Find('['); INT l_iLength = m_pCCurrentCard->m_CName.GetLength(); l_CCardName = m_pCCurrentCard->m_CName.Left(l_iLeft) + m_pCCurrentCard->m_CName[l_iLeft + 1] + m_pCCurrentCard->m_CName.Right(l_iLength - l_iLeft - 3); return l_CCardName; } CByteArray * CCardList::GetApdu( CHAR in_chShortCut ) { CCardFunction *l_pCCurrentFunction = m_pCCurrentCard->m_pCFirstFunction; while(l_pCCurrentFunction) { if (l_pCCurrentFunction->m_chShortCut == in_chShortCut) { return &l_pCCurrentFunction->m_CData; } l_pCCurrentFunction = l_pCCurrentFunction->m_pCNextFunction; } return NULL; } void ManualTest( CReader &in_CReader ) { CCardList l_CCardList(CString("ifdtest.dat")); ULONG l_uRepeat = 0; LONG l_lResult; CHAR l_chSelection; PUCHAR l_pbResult; ULONG l_uState, l_uPrevState; CString l_CAnswer; CString l_CCardStates[] = { "Unknown", "Absent", "Present" , "Swallowed", "Powered", "Negotiable", "Specific" }; BOOL l_bWaitForInsertion, l_bWaitForRemoval; while (TRUE) { ULONG l_uResultLength = 0; printf("Manual reader test\n"); printf("------------------\n"); if (l_CCardList.IsCardSelected()) { printf("%s Commands:\n", l_CCardList.GetCardName()); l_CCardList.ListFunctions(); printf("Other Commands:\n"); printf(" [r]epeat command\n"); printf(" E[x]it\n"); } else { printf("Reader Commands:\n"); printf(" Protocol: T=[0], T=[1]\n"); printf(" Power : [c]oldReset, Power[d]own, Warm[r]eset\n"); printf(" Card : [p]resent, [a]bsent, [s]tatus\n"); printf(" PwrMngnt: [h]ibernation\n"); printf(" Test : [v]endor IOCTL\n"); if (l_CCardList.GetNumCards() != 0) { printf("Card Commands:\n"); l_CCardList.ShowCards((void (__cdecl *)(void *,char *)) printf, " %s\n"); } printf("Other Commands:\n"); printf(" E[x]it\n"); } printf( "\n[%s|%s|%ld] - Command: ", in_CReader.GetVendorName(), in_CReader.GetIfdType(), in_CReader.GetDeviceUnit() ); l_chSelection = (CHAR) _getche(); putchar('\n'); if (l_CCardList.IsCardSelected()) { switch (l_chSelection) { case 'x': l_CCardList.ReleaseCard(); continue; case 'r': printf("Enter repeat count: "); scanf("%2d", &l_uRepeat); if (l_uRepeat > 99) { l_uRepeat = 0; } printf("Enter command: "); l_chSelection = (CHAR) _getche(); // no bbreak; default: CByteArray *l_pCData; if((l_pCData = l_CCardList.GetApdu(l_chSelection)) != NULL) { l_lResult = in_CReader.Transmit( l_pCData->GetData(), (ULONG) l_pCData->GetSize(), &l_pbResult, &l_uResultLength ); } else { printf("Invalid Selection"); continue; } break; } } else { switch(l_chSelection){ case '0': printf("Changing to T=0"); l_lResult = in_CReader.SetProtocol(SCARD_PROTOCOL_T0); break; case '1': printf("Changing to T=1"); l_lResult = in_CReader.SetProtocol(SCARD_PROTOCOL_T1); break; case 'c': printf("Cold reset"); l_lResult = in_CReader.ColdResetCard(); in_CReader.GetAtr(&l_pbResult, &l_uResultLength); break; case 'r': printf("Warm reset"); l_lResult = in_CReader.WarmResetCard(); in_CReader.GetAtr(&l_pbResult, &l_uResultLength); break; case 'd': printf("Power down"); l_lResult = in_CReader.PowerDownCard(); break; case 'h': printf("Hibernation test...(1 min)\nHibernate machine now!"); l_uPrevState = SCARD_UNKNOWN; l_bWaitForInsertion = FALSE; l_bWaitForRemoval = FALSE; for (l_uRepeat = 0; l_uRepeat < 60; l_uRepeat++) { l_lResult = in_CReader.GetState(&l_uState); l_lResult = in_CReader.FinishWaitForCard(FALSE); if (l_uPrevState != SCARD_UNKNOWN && l_lResult == ERROR_SUCCESS) { printf("\n Card %s", l_bWaitForInsertion ? "inserted" : "removed"); l_uPrevState = SCARD_UNKNOWN; l_bWaitForInsertion = FALSE; l_bWaitForRemoval = FALSE; } if (l_uState == SCARD_ABSENT) { if (l_bWaitForInsertion == FALSE) { l_lResult = in_CReader.StartWaitForCardInsertion(); l_bWaitForInsertion = TRUE; l_bWaitForRemoval = FALSE; } } else { if (l_bWaitForRemoval == FALSE) { l_lResult = in_CReader.StartWaitForCardRemoval(); l_bWaitForRemoval = TRUE; l_bWaitForInsertion = FALSE; } } if (l_uState != l_uPrevState) { printf("\n %s", l_CCardStates[l_uState]); } if (l_uState >= SCARD_PRESENT && l_uState < SCARD_NEGOTIABLE) { l_lResult = in_CReader.ColdResetCard(); } if (l_uState == SCARD_NEGOTIABLE) { l_lResult = in_CReader.SetProtocol( SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1 ); } printf("."); l_uPrevState = l_uState; LONG l_uGoal = clock() + CLOCKS_PER_SEC; while(l_uGoal > clock()) ; } printf("\nPlease %s card", l_uState >= SCARD_PRESENT ? "remove" : "insert"); in_CReader.FinishWaitForCard(TRUE); printf("\n"); continue; break; case 's': printf("Get state"); l_lResult = in_CReader.GetState(&l_uState); printf("Get state 1"); l_pbResult = (PBYTE) &l_uState; l_uResultLength = sizeof(ULONG); break; case 'a': printf("Waiting for removal..."); l_lResult = in_CReader.WaitForCardRemoval(); break; case 'p': printf("Waiting for insertion..."); l_lResult = in_CReader.WaitForCardInsertion(); break; case 'v': printf("Test Vendor IOCTL..."); l_lResult = in_CReader.VendorIoctl(l_CAnswer); l_pbResult = (PUCHAR) ((LPCSTR) l_CAnswer); l_uResultLength = l_CAnswer.GetLength(); break; case 'x': exit(0); default: // Try to select a card if (l_CCardList.SelectCard(l_chSelection) == FALSE) { printf("Invalid selection\n"); } l_uRepeat = 0; continue; } } printf( "\nReturn value: %lxh (NTSTATUS %lxh)\n", l_lResult, MapWinErrorToNtStatus(l_lResult) ); if (l_lResult == ERROR_SUCCESS && l_uResultLength) { ULONG l_uIndex, l_uLine, l_uCol; // The I/O request has data returned printf("Data returned (%ld bytes):\n %04x: ", l_uResultLength, 0); for (l_uLine = 0, l_uIndex = 0; l_uLine < ((l_uResultLength - 1) / 8) + 1; l_uLine++) { for (l_uCol = 0, l_uIndex = l_uLine * 8; l_uCol < 8; l_uCol++, l_uIndex++) { printf( l_uIndex < l_uResultLength ? "%02x " : " ", l_pbResult[l_uIndex] ); } putchar(' '); for (l_uCol = 0, l_uIndex = l_uLine * 8; l_uCol < 8; l_uCol++, l_uIndex++) { printf( l_uIndex < l_uResultLength ? "%c" : " ", isprint(l_pbResult[l_uIndex]) ? l_pbResult[l_uIndex] : '.' ); } putchar('\n'); if (l_uIndex < l_uResultLength) { printf(" %04x: ", l_uIndex + 1); } } } putchar('\n'); } }