The Smart Card Detective (SCD)
scd.c
Go to the documentation of this file.
00001 
00036 // #define F_CPU 16000000UL               // Not needed if defined in Makefile
00037 
00038 // this is needed for the delay on the new avr-libc-1.7.0
00039 #ifndef __DELAY_BACKWARD_COMPATIBLE__
00040 #define __DELAY_BACKWARD_COMPATIBLE__
00041 #endif
00042 
00043 #include <avr/io.h>
00044 #include <avr/interrupt.h>
00045 #include <avr/sleep.h>
00046 #include <avr/power.h>
00047 #include <avr/eeprom.h>
00048 #include <string.h>
00049 #include <util/delay.h>
00050 #include <util/delay_basic.h>
00051 #include <stdlib.h>
00052 #include <avr/wdt.h> 
00053 
00054 #include "apps.h"
00055 #include "emv.h"
00056 #include "scd_hal.h"
00057 #include "scd_io.h"
00058 #include "scd.h"
00059 #include "scd_logger.h"
00060 #include "utils.h"
00061 #include "emv_values.h"
00062 #include "scd_values.h"
00063 #include "VirtualSerial.h"
00064 #include "serial.h"
00065 
00067 #define LCD_ENABLED 1                   
00068 
00070 #define DEBUG 0
00071 
00073 #define ICC_PRES_INT_ENABLE 1           
00074 
00075 // If there are any assembler routines, they can be used
00076 // by declaring them as below
00077 // extern void StartClkICC(void);
00078 
00079 /* Functions used in the main program */
00080 //static inline void StopTerminal(const char *string);
00081 
00082 
00083 /* Static variables */
00084 #if LCD_ENABLED
00085 static char* strATRSent = "ATR Sent";
00086 static char* strError = "Error   Ocurred";
00087 static char* strDataSent = "Data    Sent";
00088 static char* strScroll = "BC to   scroll";
00089 static char* strSelect = "BD to   select";
00090 static char* strAvailable = "Avail.  apps:";
00091 #endif
00092 log_struct_t scd_logger;                // logger structure
00093 
00094 /* Global variables */
00095 uint8_t warmResetByte;
00096 uint8_t lcdAvailable;                           // non-zero if LCD is working
00097 uint8_t nCounter;                                   // number of transactions
00098 uint8_t selected;                                   // ID of application selected
00099 uint8_t bootkey;                        // used for bootloader jump
00100 uint16_t revision = 0x24;               // current revision number, saved as BCD
00101 
00102 // Use the LCD as stderr (see main)
00103 FILE lcd_str = FDEV_SETUP_STREAM(LcdPutchar, NULL, _FDEV_SETUP_WRITE);
00104 
00105 
00109 int main(void)
00110 {
00111    uint8_t sreg;
00112 
00113    // Init SCD
00114    InitSCD();
00115 
00116    // Check the hardware
00117    //TestHardware();
00118 
00119    // Select application if BB is pressed while restarting
00120    if(GetButtonB() == 0)
00121    {
00122        selected = SelectApplication();
00123 
00124        if(selected == APP_ERASE_EEPROM)
00125        {
00126                Led2On();
00127                ResetEEPROM();
00128                Led2Off();
00129                wdt_enable(WDTO_15MS);
00130        }
00131        else
00132        {
00133                sreg = SREG;
00134                cli();
00135                eeprom_write_byte((uint8_t*)EEPROM_APPLICATION, selected);
00136                SREG = sreg;
00137        }
00138 
00139        // restart micro-second counter every time we select an application
00140        ResetCounter();
00141 
00142        // restart SCD so that LCD power is reduced (small trick)
00143        wdt_enable(WDTO_15MS);
00144    }
00145    else
00146    {
00147        sreg = SREG;
00148        cli();
00149        selected = eeprom_read_byte((uint8_t*)EEPROM_APPLICATION);
00150        SREG = sreg;
00151    }
00152 
00153    // continuously run the selected application
00154    // add here any applications that can be selected from the user menu
00155    while(1)
00156    {
00157        switch(selected)
00158        {
00159            case APP_VIRTUAL_SERIAL_PORT:
00160              VirtualSerial(&scd_logger);
00161            break;
00162              
00163            case APP_FORWARD:
00164              ForwardData(&scd_logger);
00165            break;
00166 
00167            case APP_FILTER_GENERATEAC: 
00168              FilterGenerateAC(&scd_logger);
00169            break;
00170 
00171            case APP_TERMINAL:
00172              Terminal(&scd_logger);
00173            break;
00174 
00175            default:
00176              selected = APP_VIRTUAL_SERIAL_PORT;
00177              eeprom_write_byte((uint8_t*)EEPROM_APPLICATION, selected);
00178              VirtualSerial(&scd_logger);
00179        }
00180    }
00181 
00182    // if needed disable wdt to avoid restart
00183    // wdt_disable();
00184 
00185    SwitchLeds();                        
00186 }
00187 
00201 uint8_t SelectApplication()
00202 {
00203         volatile uint8_t tmp;
00204         uint8_t i;
00205 
00206         if(!lcdAvailable) return 0;
00207 
00208         InitLCD();
00209         fprintf(stderr, "\n");
00210 
00211         while(1){
00212                 fprintf(stderr, "%s\n", strScroll);
00213                 do{
00214                         tmp = GetButton();
00215                 }while((tmp & BUTTON_C) == 0);
00216                 _delay_ms(500);
00217 
00218                 fprintf(stderr, "%s\n", strSelect);
00219                 do{
00220                         tmp = GetButton();
00221                 }while((tmp & BUTTON_C) == 0);
00222                 _delay_ms(500);
00223 
00224                 fprintf(stderr, "%s\n", strAvailable);
00225                 do{
00226                         tmp = GetButton();
00227                 }while((tmp & BUTTON_C) == 0);  
00228                 _delay_ms(500);
00229 
00230                 for(i = 0; i < APPLICATION_COUNT; i++)
00231                 {
00232                         fprintf(stderr, "%s\n", appStrings[i]);
00233                         while(1)
00234                         {
00235                                 tmp = GetButton();
00236                                 if((tmp & BUTTON_D) != 0) return (i + 1);
00237                                 if((tmp & BUTTON_C) != 0) break;                        
00238                         }
00239                         _delay_ms(500);
00240                 }
00241         }
00242 
00243         return 0;
00244 }
00245 
00246 
00252 void InitSCD()
00253 {
00254         // disable any interrupts       
00255         cli();
00256         EICRA = 0;
00257         EICRB = 0;
00258         EIFR = 0xFF;
00259         EIMSK = 0;
00260 
00261         // Disable WDT to keep safe operation
00262     DisableWDT();
00263 
00264     // Reset log structure (the one in SRAM)
00265     ResetLogger(&scd_logger);
00266 
00267         // Read ms counter in order to continue from last value
00268     // We add the estimated startup time of 4 ms
00269     SetCounter(eeprom_read_dword((uint32_t*)EEPROM_TIMER_T2) + 4);
00270 
00271         // Ports setup
00272         DDRB = 0x00;
00273 
00274         DDRC = 0x00;
00275         PORTC = 0x18;           // PC4 with internal pull-up (Terminal I/O)
00276                                                 // PC3 with internal pull-up (terminal clock)
00277 
00278         DDRD = 0x80;            
00279         PORTD = 0x83;           // PD7 high (ICC VCC) , PD1 pull-up
00280                                                 // (ICC switch), PD0 pull-up (terminal reset)
00281 
00282         DDRF &= 0xF0;
00283         PORTF |= 0x0F;          // enable pull-up for buttons   
00284 
00285         // change CLK Prescaler value
00286         clock_prescale_set(clock_div_1);        
00287 
00288     // enable counter T2
00289     StartTimerT2();
00290 
00291         // light power led
00292         Led4On();       
00293 
00294 //#if ICC_PRES_INT_ENABLE
00295 //  EnableICCInsertInterrupt();
00296 //#endif        
00297 
00298         // Read Warm byte info from EEPROM
00299         warmResetByte = eeprom_read_byte((uint8_t*)EEPROM_WARM_RESET);
00300 
00301         // Read number of transactions in EEPROM
00302         nCounter = eeprom_read_byte((uint8_t*)EEPROM_COUNTER);  
00303         
00304         // Check LCD status and use as stderr if status OK
00305         if(CheckLCD())
00306         {
00307                 stderr = NULL;
00308                 lcdAvailable = 0;
00309         }
00310         else    
00311         {
00312                 stderr = &lcd_str;
00313                 lcdAvailable = 1;
00314         // disable LCD power
00315         LCDOff();
00316         }
00317 
00318         // Disable most modules; they should be re-enabled when needed
00319         power_adc_disable();
00320         power_spi_disable();
00321         power_twi_disable();
00322         power_usart1_disable();
00323         power_usb_disable();
00324 
00325         // Enable interrupts
00326         sei();  
00327 
00328     // Disable INT0 and INT1
00329     EIMSK &= ~(_BV(INT0));
00330     EIMSK &= ~(_BV(INT1));
00331 }
00332 
00333 
00334 /* Intrerupts */
00335 
00341 ISR(INT0_vect)
00342 {
00343         // disable wdt
00344     DisableWDT();
00345 
00346         // disable INT0 
00347         DisableTerminalResetInterrupt();
00348 
00349     // Log the event
00350     LogByte1(&scd_logger, LOG_TERMINAL_RST_LOW, 0);
00351 
00352     // Write the log to EEPROM and clear its contents
00353     WriteLogEEPROM(&scd_logger);
00354     ResetLogger(&scd_logger);
00355 
00356     // check for warm vs cold reset
00357     if(GetTerminalFreq())
00358     {
00359         // warm reset
00360         warmResetByte = eeprom_read_byte((uint8_t*)EEPROM_WARM_RESET);
00361 
00362         if(warmResetByte == WARM_RESET_VALUE)
00363         {
00364             // we already had a warm reset so go to initial state
00365             eeprom_write_byte((uint8_t*)EEPROM_WARM_RESET, 0);
00366             while(EECR & _BV(EEPE));
00367         }
00368         else
00369         {
00370             // set 0xAA in EEPROM meaning we have a warm reset
00371             eeprom_write_byte((uint8_t*)EEPROM_WARM_RESET, WARM_RESET_VALUE);
00372             while(EECR & _BV(EEPE));
00373         }
00374     }
00375     else
00376     {
00377         eeprom_write_byte((uint8_t*)EEPROM_WARM_RESET, 0);
00378         while(EECR & _BV(EEPE));
00379     }
00380 
00381         // re-enable wdt to restart device
00382         wdt_enable(WDTO_15MS);
00383 
00384     // Update timer value in EEPROM while we wait for restart
00385     eeprom_update_dword((uint32_t*)EEPROM_TIMER_T2, GetCounter());
00386 }
00387 
00392 ISR(INT1_vect)
00393 {       
00394         if(bit_is_set(PIND, PD1))
00395         {                       
00396                 Led3On();
00397         }
00398         else
00399         {
00400                 Led3Off();              
00401                 DeactivateICC();
00402         }
00403 
00404 }
00405 
00409 ISR(WDT_vect)
00410 {
00411     // Log the event
00412     LogByte1(&scd_logger, LOG_WDT_RESET, 0);
00413 
00414     // Write the log to EEPROM and clear its contents
00415     WriteLogEEPROM(&scd_logger);
00416     ResetLogger(&scd_logger);
00417 }
00418 
00419 
00425 ISR(TIMER3_COMPA_vect, ISR_NAKED)
00426 {       
00427         reti(); // Do nothing, used just to wake up the CPU
00428 }
00429 
00444 //ISR(TIMER2_COMPA_vect)
00445 //{     
00446 //    IncrementCounter();
00447 //}
00448 
00456 void BootloaderJumpCheck()
00457 {
00458     uint16_t bootloader_addr;
00459     uint8_t fuse_high;
00460 
00461     // Disable wdt in case it was enabled
00462     wdt_disable();
00463     
00464     // If the reset source was the bootloader and the key is correct, clear it and jump to the bootloader
00465     if ((MCUSR & (1<<WDRF)) && (bootkey == MAGIC_BOOT_KEY))
00466     {
00467         bootkey = 0;
00468         
00469         fuse_high = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS);
00470         fuse_high = (fuse_high & 0x07) >> 1;
00471         if(fuse_high == 0)
00472             bootloader_addr = 0xF000;
00473         else if(fuse_high == 1)
00474             bootloader_addr = 0xF800;
00475         else if(fuse_high == 2)
00476             bootloader_addr = 0xFC00;
00477         else if(fuse_high == 3)
00478             bootloader_addr = 0xFE00;
00479             
00480         ((void (*)(void))bootloader_addr)();
00481     }   
00482 } 
00483 
00487 void TestHardware()
00488 {
00489 #if LCD_ENABLED
00490         char* strBA = "Press BA";
00491         char* strBB = "Press BB";
00492         char* strBC = "Press BC";
00493         char* strBD = "Press BD";
00494         char* strAOK = "All fine!";     
00495 #endif
00496 
00497 
00498         Led1On();
00499         _delay_ms(50);
00500         Led1Off();
00501         Led2On();
00502         _delay_ms(50);
00503         Led2Off();
00504         Led3On();
00505         _delay_ms(50);
00506         Led3Off();
00507         Led4On();
00508         _delay_ms(50);
00509         Led4Off();
00510 
00511 #if LCD_ENABLED
00512         if(lcdAvailable)
00513         {
00514                 InitLCD();
00515                 fprintf(stderr, "\n");
00516 
00517                 WriteStringLCD(strBA, strlen(strBA));           
00518                 while(bit_is_set(PINF, PF3));
00519 
00520                 WriteStringLCD(strBB, strlen(strBB));           
00521                 while(bit_is_set(PINF, PF2));
00522 
00523                 WriteStringLCD(strBC, strlen(strBC));           
00524                 while(bit_is_set(PINF, PF1));
00525 
00526                 WriteStringLCD(strBD, strlen(strBD));           
00527                 while(bit_is_set(PINF, PF0));
00528 
00529                 WriteStringLCD(strAOK, strlen(strAOK));         
00530         }
00531 #endif  
00532 }
00533 
00542 void TestSCDTerminal()
00543 {
00544         char strLCD[16];
00545         uint8_t tmpa;
00546 
00547         //start Timer for Terminal
00548         StartCounterTerminal();
00549         
00550         // wait for Terminal CLK and send ATR
00551         while(ReadCounterTerminal() < 100);
00552         Led1On();       
00553         while(GetTerminalResetLine() == 0);
00554         Led2On();
00555         LoopTerminalETU(10);
00556         SendT0ATRTerminal(0, 0x0F, NULL);
00557         Led1Off();      
00558 
00559 
00560 #if LCD_ENABLED
00561         if(lcdAvailable)
00562         {
00563                 InitLCD();
00564                 fprintf(stderr, "\n");
00565                 WriteStringLCD(strATRSent, strlen(strATRSent));         
00566         }
00567 #endif
00568 
00569         while(1)
00570         {
00571                 // Get SELECT command for "1PAY.SYS.DDF01"
00572                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[0], MAX_WAIT_TERMINAL);       // CLA = 0x00
00573                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[1], MAX_WAIT_TERMINAL);       // INS = 0xA4
00574                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[2], MAX_WAIT_TERMINAL);       // P1 = 0x04
00575                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[3], MAX_WAIT_TERMINAL);       // P2 = 0x00
00576                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[4], MAX_WAIT_TERMINAL);       // P3 = 0x0E
00577 
00578                 strLCD[5] = 0;  
00579 
00580                 Led1On();
00581                 Led2Off();              
00582 
00583                 // Send INS (procedure byte) back
00584                 LoopTerminalETU(20);
00585                 //SendByteTerminalNoParity(0xA4, 0);            
00586                 SendByteTerminalParity(0xA4, 0);                
00587 
00588                 Led1Off();
00589                 Led2On();
00590 
00591                 // Get Select command data => "1PAY.SYS.DDF01"
00592                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[0], MAX_WAIT_TERMINAL);
00593                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[1], MAX_WAIT_TERMINAL);
00594                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[2], MAX_WAIT_TERMINAL);
00595                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[3], MAX_WAIT_TERMINAL);
00596                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[4], MAX_WAIT_TERMINAL);
00597                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[5], MAX_WAIT_TERMINAL);
00598                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[6], MAX_WAIT_TERMINAL);
00599                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[7], MAX_WAIT_TERMINAL);
00600                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[8], MAX_WAIT_TERMINAL);
00601                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[9], MAX_WAIT_TERMINAL);
00602                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[10], MAX_WAIT_TERMINAL);
00603                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[11], MAX_WAIT_TERMINAL);
00604                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[12], MAX_WAIT_TERMINAL);
00605                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[13], MAX_WAIT_TERMINAL);
00606                 strLCD[14] = 0; 
00607                 
00608                 Led1On();
00609                 Led2Off();
00610 
00611 #if LCD_ENABLED
00612                 if(lcdAvailable)
00613                 {
00614                         if(tmpa != 0)
00615                                 WriteStringLCD(strError, strlen(strError));
00616                         else
00617                                 WriteStringLCD(strLCD, 14);             
00618                 }
00619 #endif
00620 
00621                 // Send "6104" (procedure bytes) back           
00622                 SendByteTerminalParity(0x61, 0);                
00623                 LoopTerminalETU(2);             
00624                 SendByteTerminalParity(0x04, 0);                
00625 
00626                 Led1Off();
00627                 Led2On();
00628 
00629                 // Get GetResponse from Reader
00630                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[0], MAX_WAIT_TERMINAL);
00631                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[1], MAX_WAIT_TERMINAL);
00632                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[2], MAX_WAIT_TERMINAL);
00633                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[3], MAX_WAIT_TERMINAL);
00634                 tmpa = GetByteTerminalParity(0, (uint8_t*)&strLCD[4], MAX_WAIT_TERMINAL);
00635                 strLCD[5] = 0;  
00636 
00637                 Led1On();
00638                 Led2Off();
00639 
00640                 // Send some data back as response to select command
00641                 LoopTerminalETU(20);            
00642                 SendByteTerminalParity(0xC0, 0);                
00643                 LoopTerminalETU(2);
00644                 
00645                 SendByteTerminalParity(0xDE, 0);                
00646                 LoopTerminalETU(2);
00647                 
00648                 SendByteTerminalParity(0xAD, 0);                
00649                 LoopTerminalETU(2);
00650                 
00651                 SendByteTerminalParity(0xBE, 0);                
00652                 LoopTerminalETU(2);
00653                 
00654                 SendByteTerminalParity(0xEF, 0);                
00655                 LoopTerminalETU(2);
00656                 
00657                 SendByteTerminalParity(0x90, 0);                
00658                 LoopTerminalETU(2);
00659                 
00660                 SendByteTerminalParity(0x00, 0);                
00661 
00662                 Led1Off();
00663                 Led2On();
00664 
00665 #if LCD_ENABLED
00666                 if(lcdAvailable)
00667                         WriteStringLCD(strDataSent, strlen(strDataSent));
00668 #endif
00669         }
00670 }
00671 
00680 void TestSCDICC(log_struct_t *logger)
00681 {
00682         uint8_t inverse, proto, TC1, TA3, TB3;
00683         uint8_t byte;
00684 
00685         // Power ICC and get ATR
00686         if(ResetICC(0, &inverse, &proto, &TC1, &TA3, &TB3, logger)) return;
00687  
00688         // Send SELECT command (no data yet)
00689         LoopICCETU(5);
00690         SendByteICCParity(0x00, inverse);
00691         LoopICCETU(2);
00692         SendByteICCParity(0xA4, inverse);
00693         LoopICCETU(2);
00694         SendByteICCParity(0x04, inverse);
00695         LoopICCETU(2);
00696         SendByteICCParity(0x00, inverse);
00697         LoopICCETU(2);
00698         SendByteICCParity(0x0E, inverse);
00699 
00700         // Get INS back
00701         LoopICCETU(1);
00702         GetByteICCParity(inverse, &byte);
00703         if(byte != 0xA4) return;
00704 
00705         // Send "1PAY.SYS.DDF01"
00706         LoopICCETU(5);
00707         SendByteICCParity(0x31, inverse);
00708         LoopICCETU(2);
00709         SendByteICCParity(0x50, inverse);
00710         LoopICCETU(2);
00711         SendByteICCParity(0x41, inverse);
00712         LoopICCETU(2);
00713         SendByteICCParity(0x59, inverse);
00714         LoopICCETU(2);
00715         SendByteICCParity(0x2E, inverse);
00716         LoopICCETU(2);
00717         SendByteICCParity(0x53, inverse);
00718         LoopICCETU(2);
00719         SendByteICCParity(0x59, inverse);
00720         LoopICCETU(2);
00721         SendByteICCParity(0x53, inverse);
00722         LoopICCETU(2);
00723         SendByteICCParity(0x2E, inverse);
00724         LoopICCETU(2);
00725         SendByteICCParity(0x44, inverse);
00726         LoopICCETU(2);
00727         SendByteICCParity(0x44, inverse);
00728         LoopICCETU(2);
00729         SendByteICCParity(0x46, inverse);
00730         LoopICCETU(2);
00731         SendByteICCParity(0x30, inverse);
00732         LoopICCETU(2);
00733         SendByteICCParity(0x31, inverse);
00734         
00735         // Expect 0x61, 0xXX
00736         LoopICCETU(1);
00737         GetByteICCParity(inverse, &byte); 
00738         if(byte != 0x61) return;
00739         LoopICCETU(1);
00740         GetByteICCParity(inverse, &byte);
00741 
00742         // Send GET Response
00743         LoopICCETU(5);
00744         SendByteICCParity(0x00, inverse);
00745         LoopICCETU(2);
00746         SendByteICCParity(0xC0, inverse);
00747         LoopICCETU(2);
00748         SendByteICCParity(0x00, inverse);
00749         LoopICCETU(2);
00750         SendByteICCParity(0x00, inverse);
00751         LoopICCETU(2);
00752         SendByteICCParity(byte, inverse);
00753 
00754         // Data should follow from the ICC...
00755         Led1On();
00756 #if LCD_ENABLED
00757         if(lcdAvailable)
00758         {
00759                 InitLCD();
00760                 fprintf(stderr, "\n");
00761                 WriteStringLCD(strDataSent, strlen(strDataSent));               
00762         }
00763 #endif
00764 }
00765 
00769 void SwitchLeds()
00770 {
00771    while(1)
00772    {
00773       _delay_ms(500);
00774       Led1On();
00775       Led2Off();
00776       _delay_ms(500);
00777       Led1Off();
00778       Led2On();
00779    }
00780 }
00781 
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Defines