0

I wonder if anyone can help me please as I am very new to Java?! I have a read/ write applet which i developed from the code here (basically the same, just the instruction codes and applet name changed) (http://www.wrankl.de/Javacard/ReadWriteJava.txt), shown below:

// @(#)SmartTransfer.java   1.0 07/05/17

//Applet package package smartTransfer;

import javacard.framework.*;

//Transfer class extends the base Applet class
public class Transfer extends Applet {

// Declare constants
// code of instruction class; CLA - First (1 byte) - in the command APDU header
final static byte CLASS = (byte) 0x80;
// codes of INS byte in the command APDU header - for write instruction
final static byte WRITE_USER_INFO_INS = 0x07;
// codes of INS byte in the command APDU header - for read instruction
final static byte READ_USER_INFO_INS = 0x09;
// Size of storage area
final static byte SIZE_MEMORY = (short) 9;
static byte[] memory;

//Member variables - contain values for objects
//OwnerPIN pin;
/************************************/

// Installs the Applet, constructs the transfer object and registers with JCRE
public static void install(byte[] bArray, short bOffset, byte bLength) throws ISOException {
    new Transfer().register();
    memory = new byte[SIZE_MEMORY];
}


/************************************/
// Processing the APDU commands 
@Override
public void process(APDU apdu)
throws ISOException {
    if (selectingApplet()) return;
    byte[] buffer = apdu.getBuffer();
    if (buffer[ISO7816.OFFSET_CLA] !=CLASS) {
        ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
    }
    byte ins = buffer[ISO7816.OFFSET_INS];
    switch (ins) {
    case READ_USER_INFO_INS:
        readUserInfo(apdu);
        break;
    case WRITE_USER_INFO_INS:
        writeUserInfo(apdu);
    default:
        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);

    }
}

private void writeUserInfo(APDU apdu) {
    byte[] cmd_apdu = apdu.getBuffer();
    // 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 expected length is within the memory array
    short lc = (short)(cmd_apdu[ISO7816.OFFSET_LC] & 0x00FF);  
    // check no. off bytes against that in memory object
    if ((offset + lc) > SIZE_MEMORY) 
        ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
    // check there are bytes in the command
    if (lc == 0) 
        ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
    // points to method to get rest of the APDU
    getAPDUBody(apdu);    
    //Data copied to the memory - atomic procedure
    Util.arrayCopy(cmd_apdu, (short)((ISO7816.OFFSET_CDATA) & 0x00FF), memory, offset, lc);  
    // command complete message
    ISOException.throwIt(ISO7816.SW_NO_ERROR);   
}  

//Receive the body of the command APDU method
public void getAPDUBody(APDU apdu) {
    byte[] buffer = apdu.getBuffer();
    // check expected length against actual length in command APDU body
    short lc = (short)(buffer[ISO7816.OFFSET_LC] & 0x00FF);  
    // If not send error message`
    if (lc != apdu.setIncomingAndReceive()) 
        ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}  

private void readUserInfo(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); 
    if ((offset + le) > SIZE_MEMORY) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
    // check if expected length Le of return bytes is 0
    if (le == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
    // set transmission to outgoing data
    apdu.setOutgoing(); 
    // set the number of bytes to send to the IFD
    apdu.setOutgoingLength((short)le);  
    // send the requested number of bytes to the IFD
    apdu.sendBytesLong(memory, (short)offset, (short)le); 

    }
}

I am using the CREF JavaCard simulator from the Eclipse IDE and have run the CAP file and the create and select applet scripts that were generated and all work fine. The problem I have is when I try to run read apdu's. The write command example below and anything I try (within the bounds set by the writeUserinfo APDU method in the code) works, but no matter what I try for the readUserInfo APDU, nothing works. I either receive data input length != Lc or Le = 00 | SW1 SW2: 6700. I know what these mean but I have tried everything I can think of or find from the available literature and nothing seems to work. I understand as stated on the website that this applet is tried and tested but I have not as yet received a javacard to test if its a differentiation between support as I also read that the simulator is based on an old JavaCard.

Here is an example of the write apdu I am using:

//Select Transfer applet
0x00 0xA4 0x04 0x00 0x07 0xFF 0x00 0x20 0x00 0x00 0x00 0x20 0x7F;
APDU|CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 07, ff, 00, 20, 00, 00, 00, 20, Le: 00, SW1: 90, SW2: 00

//Write Ross to memory byte array at offset 2
CMD>0x80 0x07 0x00 0x02 0x04 0x52 0x6f 0x73 0x73 0x7F;
APDU|CLA: 80, INS: 07, P1: 00, P2: 02, Lc: 04, 52, 6f, 73, 73, Le: 00, SW1: 90, SW2: 00

And then here are all the kinds of read APDU's I have tried to read the 4 bytes from offset 2;

First I tried with le = 4 bytes CMD>0x80 0x09 0x00 0x02 0x04 0x7F; CREF|C-JCRE was powered down.

CREF exited with code 0

User input thread exited
ApduTool thread exited
ApduTool process finished with code: 1

APDU|Input data length != Lc around line 50.

I also read that using the CREF is at the card level and not the terminal level so no need to specify the le field, so I tried;

CMD>0x80 0x09 0x00 0x02 0x00 0x7F;
APDU|CLA: 80, INS: 09, P1: 00, P2: 02, Lc: 00, Le: 00, SW1: 67, SW2: 00

I also tried writing to many different offsets, changing the memory byte array length. I changed the memory byte array object to under the applet install (as I thought this may create the object in the applet). I tried taking out the check for Lc = 0 in both read and write methods (incase that affected the Le = 0 that I read about). I tried changing the write and read commands to not include the 0x7F incase this was being interpreted as something else); where the write command was successful; 9000, but when I ran the read command without the 0x7F I got this response; CMD>0x80 0x09 0x00 0x02 0x00; CREF|C-JCRE was powered down.

CREF exited with code 0

User input thread exited
ApduTool thread exited
ApduTool process finished with code: 1

APDU|Invalid Token after "0x00", was expecting one of <INTEGER_LITERAL>  <CHARACTER_LITERAL>  <STRING_LITERAL>

Sorry if this is very long and probably a very simple issue but I didn't realise when I took on this project that this is what it would come to (lol at me! I know!), but i have been on this for two days and tried various things to no avail so please if any of you nice people can help me, I would be very greatful!

  • have you tried to compare both buffers? what is line 50? – bichito Mar 31 '17 at 20:47
  • 50 writeUserInfo(apdu); – Ross Larkin Mar 31 '17 at 21:16
  • So the second INS case – Ross Larkin Mar 31 '17 at 21:18
  • If I understand this correctly it is the reads that are giving you trouble – bichito Mar 31 '17 at 21:18
  • Are the checks prior to reading/writing the same? – bichito Mar 31 '17 at 21:24
  • yh write receives sw1 sw2: 90 00, but just cannot get any data back – Ross Larkin Mar 31 '17 at 21:26
  • how do you mean? the cap file and create and select scripts both work fine – Ross Larkin Mar 31 '17 at 21:27
  • just looking at the code. – bichito Mar 31 '17 at 21:30
  • Sorry that I don't have more time to read the specs. But it caught my attention that line 50 is writeUserInfo but you can't read data back. I would expect line 50 to be closer to readUserInfo but that may be the accuracy of the jvm – bichito Mar 31 '17 at 22:18
  • yh i have lots of variations of these commands and it always gives me errors from 49 to 51. – Ross Larkin Mar 31 '17 at 22:22
  • Send APDU command "0x80 0x09 0x00 0x02 0x7F", not "0x80 0x09 0x00 0x02 0x00 0x7F". And in the code, you can get value le with api APDU.setOutgoing(). – JavaCardOS Apr 01 '17 at 05:08
  • Thank you but i get an error with that; – Ross Larkin Apr 01 '17 at 07:49
  • APDU|Invalid Token after "0x00", was expecting one of – Ross Larkin Apr 01 '17 at 07:50
  • Also with 0x80 0x09 0x00 0x02 i get the expected – Ross Larkin Apr 01 '17 at 07:51
  • I just do not understand as the application is tried and tested. Do you think it is the outdated simulator or the fact that this applet was created in 2004. do you think if i bought a Java Card and installed the applet it would work with command from a java host application (instead of the console). It is only for testing. The main project is centred around the host application. – Ross Larkin Apr 01 '17 at 08:50
  • Can you try to change line `if (le == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);` into `if (le == 0) le = (short)(SIZE_MEMORY - offset);`? I've never used this simulator, but what is this `0x7F` appended to APDUs for (it does not make any sense to me)? – vlp Apr 02 '17 at 22:41
  • sorry, thank you! I think its a separator, but i guess thats what ';' is for.. I'm not sure tbh... all i know is from what i am reading.. this should work! – Ross Larkin Apr 06 '17 at 11:04

2 Answers2

0

The value of Ne, the maximum number of bytes to return is encoded into the byte(s) representation called Le. The value of Ne can simply be retrieved from an applet by calling setOutgoing (check the return value!). This will also handle the translation of the encoding of Ne = 256, which is an Le set to 00.

So you can then check if Ne is large enough to hold your data. If that is the case you can return it. It's fine to return less data than requested. Otherwise you may still have to "throw" a 6700 length invalid status word.


Your command:

0x80 0x09 0x00 0x02 0x00 0x7F

is of course not correct. The encoding Lc of the command data size (Nc) is simply the empty string, i.e. Lc is absent from the command APDU. So what you show here is Le = 0x00 which gets wrongly interpreted as Ne = 0 in your code. Then there is a spurious byte set to 0x7F.

Combined with my remarks above, you should be able to send:

0x80 0x09 0x00 0x02 0x00

so that Le = 0x00 gets interpreted to be the maximum size of Ne which is 256. If your data fits in that then return it. If it doesn't fit you have to check out command chaining or extended length APDU's (but according to your single APDU write command it would fit easily - this is just a remark for future readers).

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Apologies, didn't see the question until today. – Maarten Bodewes Apr 04 '17 at 15:24
  • thank you Maarten, sorry I have been trying to complete another part of the project. I have the set outgoing command. at the bottom of the applet a – Ross Larkin Apr 06 '17 at 10:47
  • ... the 0x7F has to be used to separate the commands or something in the CREF simulator, otherwise I get an error (integer literal expected or something). I don't understand what you mean by the first command byte=lc and second command byte=le. Also i tried what you said before and it gave the the literal expected error – Ross Larkin Apr 06 '17 at 10:50
  • I did want to show you my code sorry but can't here as will only allow a few lines.. basically after running the cap and create applet file in the simulator i run a script with all usual contact details writing into the memory byte (120) at various offsets e.g; //Write Ross (First name) 0x80 0x07 0x00 0x04 0x04 0x52 0x6f 0x73 0x73 0x72; - and they are all working fine 9000, but when i use the same offsets and lengths- not working. setoutgoing is present on the code I am using and it does say its tried and tested. I just don't understand if your asking me to change something in the code? – Ross Larkin Apr 06 '17 at 11:02
  • in my applet code i have: aped.setOutgoing(); ... apdu.setOutgoingLength((short)le);... apdu.setBytesLong(memory, (short)offset, (short)le); ? – Ross Larkin Apr 06 '17 at 11:10
  • sorry for so many comments; strangely enough i just tried 0x80 0x09 0x00 0x02 0x00 0x00 - getting le=76 and then 76 0's so i guess the last byte is le and i have to put lc as blank (for the CREF at least).. but whats strange is i set the memory byte to 120 and wrote roughly 76 bytes to it and the the read command from offset 0 shows 78 0's - no actual data - like it registers the amount of bytes written but is not giving actual data. and the response is 9000, so does that mean the read command is wrong then?? – Ross Larkin Apr 06 '17 at 11:38
0

Thank you to all that helped.. i really do appreciate.. I always use this site and admire all of you for using your free time to help people like me (for free!), so thanks!

I have managed to get it to work... basically i did this..

'Can you try to change line if (le == 0) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH); into if (le == 0) le = (short)(SIZE_MEMORY - offset);? I've never used this simulator, but what is this 0x7F appended to APDUs for (it does not make any sense to me)? – vlp'

... I then put this line before the line; 'if ((offset+le)>SIZE_MEMORY)... and it worked! - again really appreciated!