// Read and Write - Example for read and write into a data field in the card // File: ReadWrite.java // // Source code based on java card specification version 2.1. // Compiled with JBuilder 6 and JDK 1.3.1. Tested with Java Card Application Studio // (JCAST) V 2.2, JLoad 2.0, IFDSIM 4.0 and UniverSIM Java Card 64 kB from Giesecke & Devrient. // // Package AID: 'D2 76 00 00 60 50 02' // Applet AID: 'D2 76 00 00 60 41 02' // //--------------------------------------------------------------------------------------- // Specification of the proprietary command READ // command APDU CLA = '80' || INS = '02' || // P1 (= '00') || P2 (= offset [byte]) || // Le (= number of bytes to read = DATA) // response APDU (good case) DATA (with length Le) || SW1 || SW2 // response APDU (bad case) SW1 || SW2 // // Specification of the proprietary command WRITE // command APDU CLA = '80' || INS = '04' || // P1 (= '00') || P2 (= offset [byte]) || // Lc (= number of bytes to write) // DATA (bytes to write) // response APDU (all cases) SW1 || SW2 // //--------------------------------------------------------------------------------------- // Test Strategy (x: not used memory, D: data, for commands READ, WRITE) // // basic tests // Dxxxx xxxxx ... good case, inside lower border // DDDxx xxxxx ... good case, inside lower border // xxxxx xxxxD ... good case, inside upper border // xxxxx xxDDD ... good case, inside upper border // xDxxx xxxxx ... good case, offset and inside border // xDDDx xxxxx ... good case, offset and inside border // // length tests // Dxxxx xxxxx ... good case, inside lower border // ... good case, all possible length values inside the border // DDDDD DDDDD ... good case, max. possible length // // border tests // DDDDD DDDDD D.. bad case, outside upper border // xxxxx xxxxx D.. bad case, offset and outside upper border // xxxxx xxxxD D.. bad case, offset and partly outside upper border // //--------------------------------------------------------------------------------------- // 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 2003-2004 by Wolfgang Rankl, Munich //--------------------------------------------------------------------------------------- // 20. Nov. 2003 - V 1: initial runnable version // 25. Nov. 2003 - V 2: improved documentation // 11. Dez. 2003 - V 3: tested with a real Java Card, 1st published version // 22. March 2004 - V 4: add test strategy, fix a bug (Le=0 in READ command) // fix a bug (Lc=0 in WRITE command), 2nd published version //--------------------------------------------------------------------------------------- package packreadwrite; // this is the package name import javacard.framework.*; // import all neccessary packages for java card public class ReadWrite extends Applet { final static byte CLASS = (byte) 0x80; // class of the APDU commands final static byte INS_READ = (byte) 0x02; // instruction for the READ APDU command final static byte INS_WRITE = (byte) 0x04; // instruction for the WRITE APDU command final static short SIZE_MEMORY = (short) 9; // size of the data storage area static byte[] memory; // this is the data memory for the application //----- installation and registration of the applet ----- public static void install(byte[] buffer, short offset, byte length) { memory = new byte[SIZE_MEMORY]; // this is the data storage area new ReadWrite().register(); } // install //----- this is the command dispatcher ----- public void process(APDU apdu) { byte[] cmd_apdu = apdu.getBuffer(); if (cmd_apdu[ISO7816.OFFSET_CLA] == CLASS) { // it is the rigth class switch(cmd_apdu[ISO7816.OFFSET_INS]) { // check the instruction byte case INS_READ: // it is a READ instruction cmdREAD(apdu); break; case INS_WRITE: // it is a WRITE instruction cmdWRITE(apdu); break; default : // the instruction in the command apdu is not supported ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED); } // switch } // if else { // the class in the command apdu is not supported ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED); } // else } // process //----- program code for the APDU command READ ----- private void cmdREAD(APDU apdu) { byte[] cmd_apdu = apdu.getBuffer(); //----- check the preconditions ----- // check if P1=0 if (cmd_apdu[ISO7816.OFFSET_P1] != 0) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); // check if offset P2 is inside the bound of the memory array short offset = (short) (cmd_apdu[ISO7816.OFFSET_P2] & 0x00FF); // calculate offset if (offset >= SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); // check if offset P2 and expected length Le is inside the bounds of the memory array short le = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF); // calculate Le (expected length) if ((offset + le) > SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); // check if expected length Le is 0 if (le == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); //----- now all preconditions are fulfilled, the data can be send to the IFD ----- apdu.setOutgoing(); // set transmission to outgoing data apdu.setOutgoingLength((short)le); // set the number of bytes to send to the IFD apdu.sendBytesLong(memory, (short)offset, (short)le); // send the requested number of bytes to the IFD } // cmdREAD //----- program code for the APDU command WRITE ----- private void cmdWRITE(APDU apdu) { byte[] cmd_apdu = apdu.getBuffer(); //----- check the preconditions ----- // check if P1=0 if (cmd_apdu[ISO7816.OFFSET_P1] != 0) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); // check if offset P2 is inside the bound of the memory array short offset = (short) (cmd_apdu[ISO7816.OFFSET_P2] & 0x00FF); // calculate offset if (offset >= SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_P1P2); // check if offset P2 and expected length Le is inside the bounds of the memory array short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF); // calculate Lc (expected length) if ((offset + lc) > SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); // check if command length Lc is 0 if (lc == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); receiveAPDUBody(apdu); // receive now the rest of the APDU //----- now all preconditions are fulfilled, the data can be copied to the memory ----- Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), memory, offset, lc); // this copy precedure is atomic ISOException.throwIt(ISO7816.SW_NO_ERROR); // command proper executed } // cmdWRITE //----- receive the body of the command APDU public void receiveAPDUBody(APDU apdu) { byte[] buffer = apdu.getBuffer(); short lc = (short)(buffer[ISO7816.OFFSET_LC] & 0x00FF); // calculate Lc (expected length) // check if Lc != number of received bytes of the command APDU body if (lc != apdu.setIncomingAndReceive()) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); } // receiveAPDUBody } // class