//---------------------------------------------------------------------------------------
// EEPROM Test
// Demonstration for a test of the measurement of the maximum EEPROM write/erase
// cycles of a smart card microcontroller
// file: EEPROMTest.java
// uses: ICC.java
//
// This program uses the PassThruCardService from OCF opencard.opt.util package.
// Additional informations and reference implementation: www.opencard.org.
// Testet with Omnikey CardMan 4000 on PCMCIA port and the appropriate PC/SC drivers
// from Omnikey on Windows 2000. Testet with JBuilder 6 and JDK 1.3.1 and a
// special smart card in non personalized state.
//
// Uses OCFPCSC1.DLL, OCFPCSC1.DLL, pcsc-wrapper-2.0.jar, base-core.jar, base-opt.jar
// from OCF 1.2
// The content of the file "opencard.properties" must be changed to:
// OpenCard.terminals = com.ibm.opencard.terminal.pcsc10.Pcsc10CardTerminalFactory
// For understanding the mechanisms of OCF it is useful to look first into the
// SimpleOCF example.
//
// WARNING: The usage of this programm is at your own risk. The execution of this
// program will damage your smart card because of analysing the maximum
// EEPROM write/erase-cycles using proprietary commands.
//
// Remark: For this test a special smart card with proprietary commands for writing
// and verifying data in EEPROM is neccessary.
//
// Run time estimation: 10 s run time -> 201 erase-write-cycles
// 60 s run time -> 1 171 erase-write-cycles
// 100 s run time -> 1 919 erase-write-cycles
// 600 s (= 10 min) run time -> 9 871 erase-write-cycles
// 3 600 s (= 1 h) run time -> 71 732 erase-write-cycles
// 10 800 s (= 3 h) run time -> 215 165 erase-write-cycles
// 36 000 s (= 10 h) run time -> 716 779 erase-write-cycles
// 180 000 s (= 50 h) run time -> 3 600 000 erase-write-cycles
//
// Results with typical microcontrollers:
// guaranted minimum write/erase cycles 100 000
// measured write/erase cycles in office environment > 1 300 000
//
// This source code is under GNU general public license (see www.opensource.org for details).
// Please send corrections and ideas for extensions to Wolfgang Rankl (www.wrankl.de)
// Copyright 2004 by Wolfgang Rankl, Munich
//
// 25. July 2004 - V 2, rw: improved documentation, fix bug concerning intermediate
// information, add run time estimation table, add results
// with typical microcontrollers, 1st published version
// 14. July 2004 - V 1, rw: initial runnable version
//---------------------------------------------------------------------------------------
package eeppack;
import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
public class EEPROMTest extends JFrame implements ActionListener {
static final boolean DEBUG = false; // true: show APDUs; false: show application level information
static final int FIRST_BYTE_ATR = 0; // first byte of the ATR
public static JTextArea ta = new JTextArea(20, 100);
// definition of the EERPOM test scenario
static final byte PATTERN1 = (byte) 0x55;
static final byte PATTERN2 = (byte) 0xAA;
static final byte STARTPAGE = (byte) 0x20;
static final byte ENDPAGE = (byte) 0x20;
static final long INTINFO = 5; // number of tests for intermediate information
static final long TIMEOUT = 10; // timeout of the test in s
/** build the GUI with all the components
*/
EEPROMTest() {
//----- create text area with scroll pane
ta.setTabSize(4);
ta.setFont(new Font("Monospaced", 0, 12)); // Courier, 12 point size
ta.setBackground(new Color( 0, 0, 0)); // black blue
ta.setForeground(new Color(200, 255, 100)); // yellowgreen
getContentPane().add(new JScrollPane(ta), BorderLayout.CENTER);
//----- create panel with buttons
JPanel buttonPanel = new JPanel(new GridLayout(25, 1));
JButton button1 = new JButton("Start Test");
button1.addActionListener(this);
buttonPanel.add(button1);
JButton button2 = new JButton("Exit");
button2.addActionListener(this);
buttonPanel.add(button2);
getContentPane().add(buttonPanel, BorderLayout.EAST);
} // EEPROMTest
/** dispatcher which the main parts of the programm
* @param event event for the pressed button
*/
public void actionPerformed(ActionEvent event) {
boolean loopbreak;
byte[] atr;
int result;
long starttime, deltatime, noofwrites;
showText("\n-------------------------------------------\n");
String cmd = event.getActionCommand();
ICC icc = new ICC();
if (cmd.equals("Start Test")) {
try {
showText("activate smart card\n\n");
atr = icc.start();
if (atr[FIRST_BYTE_ATR] != 0) {
// no error occur, a smart card is in the terminal, ATR received
showText ("ATR [hex]: " + formatATR(atr) + "\n");
showText("start time: " + getFormatedDateTime() + "\n");
loopbreak = false;
starttime = getLongTime();
noofwrites = 0;
//----- this is the main loop of the program
do {
deltatime = (getLongTime() - starttime)/1000; // deltatime is in seconds
noofwrites++;
if ((noofwrites % 2) == 0) {
result = icc.write(PATTERN1, STARTPAGE, ENDPAGE);
} // if
else {
result = icc.write(PATTERN2, STARTPAGE, ENDPAGE);
} // else
if (result != ICC.NO_ERROR) loopbreak = true; // write error occured
if (deltatime >= TIMEOUT) loopbreak = true; // timeout reached
if ((noofwrites % INTINFO) == 0) {
showText("\nnumber of write-erase-cycles: " + noofwrites + "\n");
showText("time from start : " + deltatime + " s\n");
System.out.println(noofwrites); // message that programm is still alive
} // if
} while (loopbreak == false);
showText("\n");
if (deltatime >= TIMEOUT) {
showText("reason for break: timeout of " + TIMEOUT + " s reached\n");
} // if
else {
if (result == ICC.ERROR_AFTER_WRITE) {
showText("reason for break: EEPROM error after write\n");
} // if
if (result == ICC.ERROR_AFTER_COMPARE) {
showText("reason for break: EEPROM error after compare\n");
} // if
} // else
showText("stop time: " + getFormatedDateTime() + "\n");
showText("test duration: " + deltatime + " s\n");
showText("no of EEPROM write-erase-cycles: " + noofwrites + "\n");
showText("\ndeactivate smart card\n");
icc.stop();
} // if
else {
showText ("no smart card in the terminal\n");
} // else
} // try
catch (Exception e) {
System.err.println("caught exception '" + e.getClass() + "' - " + e.getMessage() );
} // catch
} // if
else if (cmd.equals("Exit")) {
showText("exit button pressed\n");
showText("stop program GUI\n");
System.exit(0);
} // else if
showText("-------------------------------------------\n");
} // actionPerformed
/** show text in the text window
* @param text text string to show on the display
*/
public static void showText(String text) {
ta.setText(ta.getText() + text);
} // showText
/** creates a long value for the time
* @return: number of milliseconds since January 1, 1970,
*/
public static long getLongTime() {
long l;
Date time = new Date();
l = time.getTime();
return l;
} // getLongTime
/** creates a Windows conform formated date and time string
* @return: data and time string
*/
public static String getFormatedDateTime() {
String s;
Date t = new Date();
t.getTime();
s = DateFormat.getDateTimeInstance().format(t);
return s;
} // getFormatedDateTime
/** format a ATR
* @param atr ATR byte string with the raw ATR data in hex
* @return ATR string with the ATR in hex
*/
public String formatATR(byte[] atr) {
int n, x;
String w = new String();
String s = new String();
w = "ATR: ";
for (n = 0; n < atr.length; n++) {
x = (int) (0x000000FF & atr[n]);
w = Integer.toHexString(x).toUpperCase();
if (w.length()== 1) w = "0" + w;
s = s + w + " ";
} // for
return s;
} // showATR
/** format a command APDU
* @param CmdAPDU byte string with the raw command APDU
* @param LenCmdAPDU length of the raw command APDU bytw string
* @return formated string with the command APDU
*/
public static String formatCmdAPDU(byte[] CmdAPDU, int LenCmdAPDU) {
int n, x;
String w = new String();
String s = new String();
s = "IFD->ICC: ";
for (n = 0; n < LenCmdAPDU; n++) {
x = (int) (0x000000FF & CmdAPDU[n]);
w = Integer.toHexString(x).toUpperCase();
if (w.length() == 1) w = "0" + w;
s = s + w + " ";
} // for
return s;
} // formatCmdAPDU
/** format a response APDU
* @param RspAPDU byte string with the raw response APDU
* @param LenRspAPDU length of the raw response APDU bytw string
* @return formated string with the response APDU
*/
public static String formatRspAPDU(byte[] RspAPDU, int LenRspAPDU) {
int n, x;
String w = new String();
String s = new String();
s = "ICC->IFD: ";