简体   繁体   中英

Why am i receiving either input data length != Lc or SW1 SW2: 6700 when testing read and write APDU's in the console of the CREF JavaCard simulator?

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!

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, ie 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).

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM