How to send a data array to my Applet and manipulation it by Applet and return new data in response apdu?

820 views Asked by At

Update 1: I installed my applet on the javacard(I used from the source code which is as accepted answer In my already question) .When I send generatedKey command via OpenSc ,It just returns 9000 as response instead of sending XORed Data! I created my project with Javacard version 2.2.1 and i am certain that my card is compattible with that version. Why expected data not recieved by OpenSc?


I want to send a random byte array including for example 24 elements to my JavaCard applet and then my applet is supposed to change that array using a specific method. For example that method XOR each elements with 0x05 and returns the result array in APDU response.

To aim the above goal I wrote the following program so far:

package keyGeneratorPackage;
import javacard.framework.*;

public class keyGeneratorPackage extends Applet {

    private static final byte HW_CLA = (byte) 0x80;
    private static final byte HW_INS = (byte) 0x00;

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new keyGeneratorPackage().register(bArray, (short) (bOffset + 1),
                bArray[bOffset]);
    }

    public void process(APDU apdu) {

        if (selectingApplet()) {
            return;
        }

        byte[] buffer = apdu.getBuffer();
        byte CLA = (byte) (buffer[ISO7816.OFFSET_CLA] & 0xFF);
        byte INS = (byte) (buffer[ISO7816.OFFSET_INS] & 0xFF);
        byte[] Data = new byte[] { (byte) (buffer[ISO7816.OFFSET_CDATA] & 0xFF) };

        if (CLA != HW_CLA) {
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }

        switch (INS) {
        case HW_INS:

            getKey(apdu, Data);
            break;

        default:
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
    }

    private void getKey(APDU apdu, byte[] data) {
        byte[] buffer = apdu.getBuffer();
        byte[] generatedKey = generateKey(data);
        short length = (short) generatedKey.length;

        Util.arrayCopyNonAtomic(generatedKey, (short) 0, buffer, (short) 0,
                (short) length);

        apdu.setOutgoingAndSend((short) 0, length);
    }

    private byte[] generateKey(byte[] Data) {
        byte[] key = new byte[] { (byte) 0x00 };
        for (int i = 0; i < Data.length; i++) {
            key[i] = (byte) (Data[i] ^ 5);
        }
        return key;
    }

}

I must send the following APDU command after compiling and selecting my applet:

>>> 80 00 00 00 18 11 22 33 44 55 66 77 88 99 10 20 30 40 50 60 70 80 90 b1 b2 b3 b4 b5 b6 b7 26

Is there something wrong with my applet?

1

There are 1 answers

7
mazhar islam On BEST ANSWER

In method, private void getKey( APDU apdu , byte[] data) you need to call,

apdu.setIncomingAndReceive();

Remember:

This is the primary receive method. Calling this method indicates that this APDU has incoming data. This method gets as many bytes as will fit without buffer overflow in the APDU buffer following the header. It gets all the incoming bytes if they fit.

So update your method like this:

private void getKey( APDU apdu , byte[] data)
  {
      apdu.setIncomingAndReceive();
      byte[] buffer = apdu.getBuffer();
      byte[] generatedKey = generateKey(data);
      short length = (short) generatedKey.length;
      //short length =1;

      Util.arrayCopyNonAtomic(generatedKey, (short)0, buffer, (short)0, (short) length);

      apdu.setOutgoingAndSend((short)0, length);

}

Note: setIncomingAndReceive method may only be called once in a Applet.process() method. For more detail, read setIncomingAndReceive.

EDIT: There are several problems in your code. I'm mentioning all of them one by one.

Problem 1:

byte[] Data =new byte[] {(byte) (buffer[ISO7816.OFFSET_CDATA] & 0xFF)};

It creates a byte[] Data of length 1 with value 0x11.

Solution: new creates space into persistent EEP memory for Data. If you don't need Data again you can make it transient byte array.

Rewrite it like this (Persistent):

// it will create a byte array of length of "lc". Content will be `0x00`.
byte[] Data = new byte[(byte) (buffer[ISO7816.OFFSET_LC] & 0xFF)]; 

Or this (Transient):

byte[] Data = JCSystem.makeTransientByteArray((short) (buffer[ISO7816.OFFSET_LC] & 0x00FF), JCSystem.CLEAR_ON_DESELECT);

Problem 2:

i) Your generateKey() method will crash, because you are creating byte[] key same as you do for byte[] Data.

ii) You may not declare int i because only few cards supports it, use byte or short.

Solution: As far as I understand what are you trying to do in the generateKey() method, I rewrite it for you like this:

// the byte array preparation of key is the callers duty 
private byte[] generateKey(byte[] Data, byte[] key) {
    short i;
    for (i = 0; i < Data.length; i++) {
        key[i] = (byte) (Data[i] ^ (byte)0x05);
    }
    return key;
}

The full working code is:

JavaCard: v.2.2.2

globalPlatform: v.2.1.1

Suggestion: Read this document carefully first.

package keyGeneratorPackage;

import javacard.framework.APDU;
import javacard.framework.ISO7816;
import javacard.framework.Applet;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;

/**
 * KeyGeneratorPackage <br>
 * 
 * @author rakeb.void
 * 
 */
public class KeyGeneratorPackage extends Applet {
    private static final byte HW_CLA = (byte) 0x80;
    private static final byte HW_INS = (byte) 0x00;

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new keyGeneratorPackage.KeyGeneratorPackage().register(bArray, (short) (bOffset + 1),
                bArray[bOffset]);
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }
        apdu.setIncomingAndReceive();
        byte[] buffer = apdu.getBuffer();
        byte CLA = (byte) (buffer[ISO7816.OFFSET_CLA] & 0xFF);
        byte INS = (byte) (buffer[ISO7816.OFFSET_INS] & 0xFF);
        short  lc =  (short) (buffer[ISO7816.OFFSET_LC] & (short)0x00FF); 
//      byte[] Data = new byte[(byte) (buffer[ISO7816.OFFSET_LC] & 0xFF)];
        byte[] Data = JCSystem.makeTransientByteArray(lc, JCSystem.CLEAR_ON_DESELECT);

        if (CLA != HW_CLA) {
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }
        switch (INS) {
        case HW_INS: {
            // copying the apdu data into byte array Data
            Util.arrayCopy(buffer, ISO7816.OFFSET_CDATA, Data, (short) 0, lc);
            getKey(apdu, Data);
        }
        // you forget to put a break here!
        break;
        default:
            // good practice: If you don't know the INStruction, say so:
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
    }

    private void getKey(APDU apdu, byte[] data) {
        byte[] buffer = apdu.getBuffer();
        short length = (short) data.length;
        //prepareing the key array of same length of Data
        byte[] key = JCSystem.makeTransientByteArray(length, JCSystem.CLEAR_ON_DESELECT);
//      byte[] generatedKey = generateKey(data, key);
        // no need another array generatedKey, as we are passing key as parameter 
        generateKey(data, key);
//      length = (short) generatedKey.length;

        Util.arrayCopyNonAtomic(key, (short) 0, buffer, (short) 0, (short) length);

        apdu.setOutgoingAndSend((short) 0, length);
    }

    // .....................................
    private byte[] generateKey(byte[] Data, byte[] key) {
        short i;
        for (i = 0; i < Data.length; i++) {
            // i've no idea why you use 0x05 here,
            // in the question you mentioned 0x9D
            key[i] = (byte) (Data[i] ^ (byte)0x05); 
        } 
        return key;
    }

}

APDU's I sent :

Select Command : 00 A4 04 00 06 A1 A2 A3 A4 A5 00 00

Select Command Response : 90 00

generateKey Command : 80 00 00 00 18 11 22 33 44 55 66 77 88 99 10 20 30 40 50 60 70 80 90 B1 B2 B3 B4 B5 B6 B7

generateKey Command Response : 14 27 36 41 50 63 72 8D 9C 15 25 35 45 55 65 75 85 95 B4 B7 B6 B1 B0 B3 90 00

Cheers!