/*
* Copyright 2012 Licel LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.licel.jcardsim.samples;
import javacard.framework.*;
/**
* Basic HelloWorld JavaCard Applet.
* @author LICEL LLC
*/
public class HelloWorldApplet extends BaseApplet {
/**
* Instruction: say hello
*/
private final static byte SAY_HELLO_INS = (byte) 0x01;
/**
* Instruction: say echo v2
*/
private final static byte SAY_ECHO2_INS = (byte) 0x03;
/**
* Instruction: get install params
*/
private final static byte SAY_IPARAMS_INS = (byte) 0x04;
/**
* Instruction: NOP
*/
private final static byte NOP_INS = (byte) 0x02;
/**
* Instruction: queue data and return 61xx
*/
private final static byte SAY_CONTINUE_INS = (byte) 0x06;
/**
* Instruction: CKYListObjects (http://pki.fedoraproject.org/images/7/7a/CoolKeyApplet.pdf 2.6.17)
*/
private final static byte LIST_OBJECTS_INS = (byte) 0x58;
/**
* Instruction: "Hello Java Card world!" + Application Specific SW 9XYZ
*/
private final static byte APPLICATION_SPECIFIC_SW_INS = (byte) 0x7;
/**
* Instruction: return maximum data.
*/
private final static byte MAXIMUM_DATA_INS = (byte) 0x8;
/**
* Byte array representing "Hello Java Card world!" string.
*/
private static byte[] helloMessage = new byte[]{
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, // "Hello "
0x77, 0x6F, 0x72, 0x6C, 0x64, 0x20, 0x21 // "world !"
};
private byte[] echoBytes;
private byte[] initParamsBytes;
private final byte[] transientMemory;
private static final short LENGTH_ECHO_BYTES = 256;
/**
* Only this class's install method should create the applet object.
* @param bArray the array containing installation parameters
* @param bOffset the starting offset in bArray
* @param bLength the length in bytes of the parameter data in bArray
*/
protected HelloWorldApplet(byte[] bArray, short bOffset, byte bLength) {
echoBytes = new byte[LENGTH_ECHO_BYTES];
if (bLength > 0) {
byte iLen = bArray[bOffset]; // aid length
bOffset = (short) (bOffset + iLen + 1);
byte cLen = bArray[bOffset]; // info length
bOffset = (short) (bOffset + 3);
byte aLen = bArray[bOffset]; // applet data length
initParamsBytes = new byte[aLen];
Util.arrayCopyNonAtomic(bArray, (short) (bOffset + 1), initParamsBytes, (short) 0, aLen);
}
transientMemory = JCSystem.makeTransientByteArray(LENGTH_ECHO_BYTES, JCSystem.CLEAR_ON_RESET);
register();
}
/**
* This method is called once during applet instantiation process.
* @param bArray the array containing installation parameters
* @param bOffset the starting offset in bArray
* @param bLength the length in bytes of the parameter data in bArray
* @throws ISOException if the install method failed
*/
public static void install(byte[] bArray, short bOffset, byte bLength)
throws ISOException {
new HelloWorldApplet(bArray, bOffset,bLength);
}
/**
* This method is called each time the applet receives APDU.
*/
public void process(APDU apdu) {
// good practice
if(selectingApplet()) return;
byte[] buffer = apdu.getBuffer();
// Now determine the requested instruction:
switch (buffer[ISO7816.OFFSET_INS]) {
case SAY_HELLO_INS:
sayHello(apdu, (short)0x9000);
return;
case SAY_ECHO2_INS:
sayEcho2(apdu);
return;
case SAY_IPARAMS_INS:
sayIParams(apdu);
return;
case SAY_CONTINUE_INS:
sayContinue(apdu);
return;
case LIST_OBJECTS_INS:
listObjects(apdu);
return;
case APPLICATION_SPECIFIC_SW_INS:
sayHello(apdu, (short)0x9B00);
return;
case MAXIMUM_DATA_INS:
maximumData(apdu);
return;
case NOP_INS:
return;
default:
// We do not support any other INS values
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
/**
* Sends hello message to host using given APDU.
*
* @param apdu APDU that requested hello message
* @param sw response sw code
*/
private void sayHello(APDU apdu, short sw) {
// Here all bytes of the APDU are stored
byte[] buffer = apdu.getBuffer();
// receive all bytes
// if P1 = 0x01 (echo)
short incomeBytes = apdu.setIncomingAndReceive();
byte[] echo = transientMemory;
short echoLength;
if (buffer[ISO7816.OFFSET_P1] == 0x01) {
echoLength = incomeBytes;
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, echo, (short) 0, incomeBytes);
} else {
echoLength = (short) helloMessage.length;
Util.arrayCopyNonAtomic(helloMessage, (short) 0, echo, (short) 0, (short) helloMessage.length);
}
// Tell JVM that we will send data
apdu.setOutgoing();
// Set the length of data to send
apdu.setOutgoingLength(echoLength);
// Send our message starting at 0 position
apdu.sendBytesLong(echo, (short) 0, echoLength);
// Set application specific sw
if(sw!=0x9000) {
ISOException.throwIt(sw);
}
}
/**
* echo v2
*/
private void sayEcho2(APDU apdu) {
byte buffer[] = apdu.getBuffer();
short bytesRead = apdu.setIncomingAndReceive();
short echoOffset = (short) 0;
while (bytesRead > 0) {
Util.arrayCopyNonAtomic(buffer, ISO7816.OFFSET_CDATA, echoBytes, echoOffset, bytesRead);
echoOffset += bytesRead;
bytesRead = apdu.receiveBytes(ISO7816.OFFSET_CDATA);
}
apdu.setOutgoing();
apdu.setOutgoingLength(echoOffset);
// echo data
apdu.sendBytesLong(echoBytes, (short) 0, echoOffset);
}
/**
* echo install params
*/
private void sayIParams(APDU apdu) {
apdu.setOutgoing();
apdu.setOutgoingLength((short)initParamsBytes.length);
// echo install parmas
apdu.sendBytesLong(initParamsBytes, (short) 0, (short)initParamsBytes.length);
}
/**
* send some hello data, and indicate there's more
*/
private void sayContinue(APDU apdu) {
byte[] echo = transientMemory;
short echoLength = (short) 6;
Util.arrayCopyNonAtomic(helloMessage, (short)0, echo, (short)0, (short)6);
apdu.setOutgoing();
apdu.setOutgoingLength(echoLength);
apdu.sendBytesLong(echo, (short) 0, echoLength);
ISOException.throwIt((short) (ISO7816.SW_BYTES_REMAINING_00 | 0x07));
}
/**
* send the maximum amount of data the apdu will accept
*
* @param apdu APDU that requested hello message
*/
private void maximumData(APDU apdu) {
short maxData = APDU.getOutBlockSize();
byte[] buffer = apdu.getBuffer();
Util.arrayFillNonAtomic(buffer, (short) 0, maxData, (byte) 0);
apdu.setOutgoingAndSend((short) 0, maxData);
}
// prototype
private void listObjects(APDU apdu)
{
byte buffer[] = apdu.getBuffer();
if (buffer[ISO7816.OFFSET_P2] != 0) {
ISOException.throwIt((short)0x9C11);
}
byte expectedBytes = buffer[ISO7816.OFFSET_LC];
if (expectedBytes < 14) {
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
ISOException.throwIt((short)0x9C12);
}
}