/*
 * File:	TestGemClub.Java
 * Purpose:	To demonstrate the creation of a record file
 * Date:	05.05.2001
 * Author:	Mohammed Sadiq (mohammed.sadiq@gemplus.com)
 *
 */
 
// Java imports
import java.io.*;
import java.util.*;

// OCF imports
import opencard.core.service.*;
import opencard.core.terminal.*;
import opencard.opt.util.*;
import opencard.core.util.*;
import opencard.opt.terminal.UserInteraction;
import com.gemplus.opencard.terminal.*;

// Cryptix imports
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.DESKeySpec;

public class TestGemClub {
  
  static PassThruCardService myCardService = null;
  
  public static void main(String[] args) {
    ExtendedCommandAPDU commandToSend = null;
    ResponseAPDU        responseFromCard = null;

    int i;
    
    // true if you need the debug info, false otherwise.
    boolean deb = true;
    
    byte secretCode[] = new byte[8];
    byte terminalData[] = new byte[8];
    byte dataObjectAttributes[] = new byte[4];
    byte IV[] = new byte[8];
    byte incomingMAC[] = new byte[8];
    byte TAG_MAC_IN_CREATE = 0x61;
    byte SFI = 0x01;
    byte CTC[] = new byte[2];
    byte MAC_IN[] = new byte[8];
    byte MAC_OUT[] = new byte[8];
            
    byte Des3Key[] = {	(byte)0x54, (byte)0x45, (byte)0x53, (byte)0x54, 
    			(byte)0x4B, (byte)0x45, (byte)0x59, (byte)0x31, 
    			(byte)0x54, (byte)0x45, (byte)0x53, (byte)0x54, 
    			(byte)0x4B, (byte)0x45, (byte)0x59, (byte)0x32};
    byte DesKey[] = { 	(byte)0x54, (byte)0x45, (byte)0x53, (byte)0x54, 
    			(byte)0x4B, (byte)0x45, (byte)0x59, (byte)0x31};
    
    byte block1[] = new byte[8];
    byte block2[] = new byte[8];
    byte block3[] = new byte[8];

    byte tempBlock1[] = new byte[8];
    byte tempBlock2[] = new byte[8];
    
    byte commandHeader[] = new byte[5];
    byte dataCreateObject[] = new byte[20];
        
    byte rec1[] = "record1".getBytes();
    byte rec2[] = "record2".getBytes();
    
    try {

      // Init OCF
      SmartCard.start();
      System.out.print("\nWaiting for a smart card...");
      CardRequest cr = new CardRequest(CardRequest.ANYCARD, null, null);
      SmartCard   myCard = SmartCard.waitForCard(cr);
      System.out.println("FOUND");
      	
      myCardService = (PassThruCardService)myCard.getCardService(PassThruCardService.class, true);
            
      System.out.println("\n***** Read Param (CTC) *****");
      commandToSend = new ExtendedCommandAPDU((byte)0x80, (byte)0xBE, (byte)0x27, (byte)0x01, secretCode, (byte)0x02);
      System.out.println("Command: " + commandToSend.toString());
      responseFromCard = myCardService.sendCommandAPDU(commandToSend);
      System.out.println("Response: " + responseFromCard.toString());
      
      // Retrieve the Card Transaction Counter(2 bytes)
      CTC = responseFromCard.data();           
      //Util.debug(deb, "CTC = ", CTC);
      
      // Increment the CTC by 1
      int iCTC = CTC[0]*256 + CTC[1];
      iCTC++;
      CTC[0] = (byte)(iCTC/256);
      CTC[1] = (byte)(iCTC%256);
      
      ////////// Compute the MAC_IN //////////
      // Ingredients: Tag, CTC, Command Header, InputData
      // Tag = TAG_MAC_IN_CREATE
      // CTC = Just obtained
      // Command Header = 0x80, 0xEE, 0x02, SFI, 0x14
      // Input Data = Terminal Data(8 bytes) + Data Object Attributes(4 bytes) + Padding
      
      // Terminal Data
      for(i=0; i<8; i++)
        terminalData[i] = (byte)(i+1);

      // Assumption: The file contains only 2 records of 7 bytes("record1" & "record2")
      // The file size will be 8(OS) + <7+1(SB)+0(padding)> + <7+1(SB)+0(padding)> = 24 = 0x18
      
      dataObjectAttributes[0] = 0x04;	// 0x04, 0x05 are the only ones supported
      dataObjectAttributes[1] = 0x00;	// MSB of file size
      dataObjectAttributes[2] = 0x18;	// LSB of file size
      dataObjectAttributes[3] = 0x00;	// RFU

      // Block1 = Tag|CTC0..1|Header|
      // Block2 = Tag|Terminal Data0..6|
      // Block3 = Tab|Terminal Data7|Data Object Attributes|Padding
      // Padding = 0x80|0x00
      
      commandHeader[0] = (byte)0x80;	// CLA
      commandHeader[1] = (byte)0xEE;	// INS
      commandHeader[2] = (byte)0x02;	// P1
      commandHeader[3] = SFI;		// P2 = SFI
      commandHeader[4] = (byte)0x14;	// Lc = 20 = 0x14
      
      // Populate Block1
      block1[0] = TAG_MAC_IN_CREATE;
      Util.arrayCopy(CTC, 0, block1, 1, 2);         
      Util.arrayCopy(commandHeader, 0, block1, 3, 5);
      
      // Populate Block2    
      block2[0] = TAG_MAC_IN_CREATE;
      Util.arrayCopy(terminalData, 0, block2, 1, 7);
      
      // Populate Block3
      block3[0] = TAG_MAC_IN_CREATE;
      block3[1] = terminalData[7];      
      Util.arrayCopy(dataObjectAttributes, 0, block3, 2, 4);
      block3[6] = (byte)0x80;	// Start of padding
      block3[7] = (byte)0x00; 	// padding byte
      
      Cipher des = Cipher.getInstance("DES"+"/ECB/None");
      //SecretKeySpec skSpec = new SecretKeySpec(DesKey, 0, 8, new String("DES"));
      SecretKeySpec skSpec = new SecretKeySpec(Des3Key, 0, 8, new String("DES"));
      SecretKey skDesKey = (SecretKey)skSpec;	              
      des.init(Cipher.ENCRYPT_MODE, skDesKey);      
      
      // Do a {0x00..0x00} XOR block1 = TempBlock1
      // You don't really need to perform this step.      
      for(i=0; i<8; i++)
         tempBlock1[i] = (byte)(IV[i] ^ block1[i]);            
      tempBlock2 = des.doFinal(tempBlock1);
      
      // Do a TemBlock2 XOR block2 = TempBlock1
      // You don't really need to perform this step.      
      for(i=0; i<8; i++)
         tempBlock1[i] = (byte)(tempBlock2[i] ^ block2[i]);      
      tempBlock2 = des.doFinal(tempBlock1);
      
      // Do a TemBlock2 XOR block3 = TempBlock1
      // You don't really need to perform this step.      
      for(i=0; i<8; i++)
         tempBlock1[i] = (byte)(tempBlock2[i] ^ block3[i]);      
         
      // NOTE: The final encryption has to be with 3DES.   
      // TempBlock2 is the Cryptogram(MAC_IN)
      MAC_IN = Util.doEncrypt(tempBlock1, Des3Key);
      //Util.debug(deb, "MAC_IN = ", MAC_IN);
            
      // Data which is used in Create Object Command is as follows:
      // TerminalData(8 byte) + DataObjectAttributes(4 bytes) + MAC_IN(8 byte)
      Util.arrayCopy(terminalData, 0, dataCreateObject, 0, 8);
      Util.arrayCopy(dataObjectAttributes, 0, dataCreateObject, 8, 4);
      Util.arrayCopy(MAC_IN, 0, dataCreateObject, 12, 8);
      
      //Util.debug(deb, "dataCreateObject = ", dataCreateObject);            
            
      System.out.println("\n***** Create Object *****");
      commandToSend = new ExtendedCommandAPDU((byte)0x80, (byte)0xEE, (byte)0x02, SFI, dataCreateObject, (byte)0x10);
      System.out.println("Command: " + commandToSend.toString());
      responseFromCard = myCardService.sendCommandAPDU(commandToSend);
      System.out.println("Response: " + responseFromCard.toString());
      
      // Retrieve the card response & extract the Card Transaction Counter(2 bytes)
      //MAC_OUT = responseFromCard.data();           
      //Util.debug(deb, "MAC_OUT = ", MAC_OUT);                 
      
      System.out.println("\n***** Append Record 1 ('record1') *****");
      commandToSend = new ExtendedCommandAPDU((byte)0x00, (byte)0xE2, (byte)0x00, (byte)(SFI<<3), rec1);
      System.out.println("Command: " + commandToSend.toString());
      responseFromCard = myCardService.sendCommandAPDU(commandToSend);
      System.out.println("Response: " + responseFromCard.toString());
      
      System.out.println("\n***** Append Record 2 ('record2') *****");
      commandToSend = new ExtendedCommandAPDU((byte)0x00, (byte)0xE2, (byte)0x00, (byte)(SFI<<3), rec2);
      System.out.println("Command: " + commandToSend.toString());
      responseFromCard = myCardService.sendCommandAPDU(commandToSend);
      System.out.println("Response: " + responseFromCard.toString());      
            
      SmartCard.shutdown();
      
    } catch(Exception e) {
      e.printStackTrace(System.out);
    }
    
  } // main()
    
} // class TestGemClub
