/* * Copyright 2015 Licel Corporation. * * 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.framework; import com.licel.jcardsim.base.ApduCase; import com.licel.jcardsim.base.SimulatorSystem; import com.licel.jcardsim.utils.ByteUtil; import java.lang.reflect.Field; import java.util.Arrays; import javacard.framework.APDU; import javacard.framework.APDUException; import javacard.framework.ISO7816; import javacard.framework.Util; /** * Implementation for <code>APDU</code> * @see APDUProxy */ public class APDUProxy { // buffer size private static final short BUFFER_SIZE = 260; // buffer size (extended APDU) + (CLA,INS,P1,P2,0,Lc_Hi,Lc_Low,CData,Le_Hi,Le_Lo) private static final int BUFFER_EXTENDED_SIZE = Short.MAX_VALUE + 10; // input block size, for T0 protocol = 1 private static final short T0_IBS = 1; // output block size, for T0 protocol = 258 private static final short T0_OBS = 258; // block size, for T1 protocol private static final short T1_BLOCK_SIZE = 254; // NAD, for T0 protocol = 9 private static final byte T0_NAD = 0; // transient array to store variables private short[] ramVars; // LE variable offset in ramVars private static final byte LE = 0; // LR variable offset in ramVars private static final byte LR = 1; // LC variable offset in ramVars private static final byte LC = 3; // PRE_READ_LENGTH variable offset in ramVars private static final byte PRE_READ_LENGTH = 4; // CURRENT_STATE variable offset in ramVars private static final byte CURRENT_STATE = 5; // LOGICAL_CHN variable offset in ramVars private static final byte LOGICAL_CHN = 6; // ACTIVE_PROTOCOL variable offset in ramVars private static final byte ACTIVE_PROTOCOL = 7; // REMAINING_BYTES variable offset in ramVars private static final byte REMAINING_BYTES = 8; // total length ramVars private static final byte RAM_VARS_LENGTH = 9; // transient array to store boolean flags private boolean[] flags; // outgoingFlag; private static final byte OUTGOING_FLAG = 0; // outgoingLenSetFlag; private static final byte OUTGOING_LEN_SET_FLAG = 1; // noChainingFlag; private static final byte NO_CHAINING_FLAG = 2; // incomingFlag; private static final byte INCOMING_FLAG = 3; // notGetResponseFlag; private static final byte NO_GET_RESPONSE_FLAG = 4; // accessAllowedFlag; private static final byte ACCESS_ALLOWED_FLAG = 5; // total length flags private static final byte FLAGS_LENGTH = 6; // APDU input buffer private final byte[] buffer; // extended APDU flag private final boolean extended; APDUProxy(boolean extended) { this.extended = extended; buffer = new byte[extended ? BUFFER_EXTENDED_SIZE : BUFFER_SIZE]; ramVars = new short[RAM_VARS_LENGTH]; flags = new boolean[FLAGS_LENGTH]; internalReset(javacard.framework.APDU.PROTOCOL_T0, ApduCase.Case1, null); } /** * Returns the APDU buffer byte array. * <p>Note:<ul> * <li>References to the APDU buffer byte array * cannot be stored in class variables or instance variables or array components. * See <em>Runtime * Specification for the Java Card Platform</em>, section 6.2.2 for details. * </ul> * @return byte array containing the APDU buffer */ public byte[] getBuffer() { return buffer; } /** * Returns the configured incoming block size. * In T=1 protocol, this corresponds to IFSC (information field size for ICC), * the maximum size of incoming data blocks into the card. In T=0 protocol, * this method returns 1. * IFSC is defined in ISO 7816-3.<p> * This information may be used to ensure that there is enough space remaining in the * APDU buffer when <code>receiveBytes()</code> is invoked. * <p>Note: * <ul> * <li><em>On </em><code>receiveBytes()</code><em> the </em><code>bOff</code><em> param * should account for this potential blocksize.</em> * </ul> * @return incoming block size setting * @see #receiveBytes(short) */ public static short getInBlockSize() { return (getProtocol() & javacard.framework.APDU.PROTOCOL_T1) == javacard.framework.APDU.PROTOCOL_T1 ? T1_BLOCK_SIZE : T0_IBS; } /** * Returns the configured outgoing block size. * In T=1 protocol, this corresponds to IFSD (information field size for interface device), * the maximum size of outgoing data blocks to the CAD. * In T=0 protocol, this method returns 258 (accounts for 2 status bytes). * IFSD is defined in ISO 7816-3. * <p>This information may be used prior to invoking the <code>setOutgoingLength()</code> method, * to limit the length of outgoing messages when BLOCK CHAINING is not allowed. * <p>Note:<ul> * <li><em>On </em><code>setOutgoingLength()</code><em> the </em><code>len</code><em> param * should account for this potential blocksize.</em> * </ul> * @return outgoing block size setting * @see #setOutgoingLength(short) */ public static short getOutBlockSize() { return (getProtocol() & javacard.framework.APDU.PROTOCOL_T1) == javacard.framework.APDU.PROTOCOL_T1 ? T1_BLOCK_SIZE : T0_OBS; } /** * Returns the ISO 7816 transport protocol type, T=1 or T=0 in the low nibble * and the transport media in the upper nibble in use. * @return he protocol media and type in progress * Valid nibble codes are listed in PROTOCOL_ .. constants above. * @see <CODE>PROTOCOL_T0</CODE> */ public static byte getProtocol() { APDU apdu = SimulatorSystem.instance().getCurrentAPDU(); return (byte)((short[])getFieldInternal(apdu,"ramVars"))[ACTIVE_PROTOCOL]; } /** * Returns the Node Address byte (NAD) in T=1 protocol, and 0 * in T=0 protocol. * This may be used as additional information to maintain multiple contexts. * @return NAD transport byte as defined in ISO 7816-3 */ public byte getNAD() { return T0_NAD; } /** * This method is used to set the data transfer direction to * outbound and to obtain the expected length of response (Le). This method * should only be called on a case 2 or case 4 command, otherwise erroneous * behavior may result. * <p>Notes. <ul> * <li><em>On a case 4 command, the </em><code>setIncomingAndReceive()</code><em> must * be invoked prior to calling this method. Otherwise, erroneous * behavior may result in T=0 protocol.</em> * <li><em>Any remaining incoming data will be discarded.</em> * <li><em>In T=0 (Case 4S) protocol, this method will return 256 with normal * semantics.</em> * <li><em>In T=0 (Case 2E, 4S) protocol, this method will return 32767 when * the currently selected applet implements the * </em><code>javacardx.apdu.ExtendedLength</code><em> interface.</em> * <li><em>In T=1 (Case 2E, 4E) protocol, this method will return 32767 when the * Le field in the APDU command is 0x0000 and the currently selected applet implements the * </em><code>javacardx.apdu.ExtendedLength</code><em> interface.</em> * <li><em>This method sets the state of the </em><code>APDU</code><em> object to * </em><code>STATE_OUTGOING</code><em>.</em> * </ul> * @return Le, the expected length of response * @throws APDUException with the following reason codes:<ul> * <li><code>APDUException.ILLEGAL_USE</code> if this method, or <code>setOutgoingNoChaining()</code> method already invoked. * <li><code>APDUException.IO_ERROR</code> on I/O error. * </ul> */ public short setOutgoing() throws APDUException { if (flags[OUTGOING_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } flags[OUTGOING_FLAG] = true; ramVars[CURRENT_STATE] = javacard.framework.APDU.STATE_OUTGOING; return ramVars[LE]; } /** * This method is used to set the data transfer direction to * outbound without using BLOCK CHAINING (See ISO 7816-3/4) and to obtain the expected length of response (Le). * This method should be used in place of the * <code>setOutgoing()</code> method by applets which need * to be compatible with legacy CAD/terminals which do not support ISO 7816-3/4 defined block chaining. * See <em>Runtime Environment * Specification for the Java Card Platform</em>, section 9.4 for details. * <p>Notes. <ul> * <li><em>On a case 4 command, the </em><code>setIncomingAndReceive()</code><em> must * be invoked prior to calling this method. Otherwise, erroneous * behavior may result in T=0 protocol.</em> * <li><em>Any remaining incoming data will be discarded.</em> * <li><em>In T=0 (Case 4S) protocol, this method will return 256 with normal * semantics.</em> * <li><em>In T=0 (Case 2E, 4S) protocol, this method will return 256 when * the currently selected applet implements the * </em><code>javacardx.apdu.ExtendedLength</code><em> interface.</em> * <li><em>When this method is used, the </em><code>waitExtension()</code><em> method cannot be used.</em> * <li><em>In T=1 protocol, retransmission on error may be restricted.</em> * <li><em>In T=0 protocol, the outbound transfer must be performed * without using </em><code>(ISO7816.SW_BYTES_REMAINING_00+count)</code><em> response status chaining.</em> * <li><em>In T=1 protocol, the outbound transfer must not set the More(M) Bit in the PCB of the I block. See ISO 7816-3.</em> * <li><em>This method sets the state of the </em><code>APDU</code><em> object to * </em><code>STATE_OUTGOING</code><em>.</em> * </ul> * * @return Le, the expected length of response data * @throws APDUException with the following reason codes:<ul> * <li><code>APDUException.ILLEGAL_USE</code> if this method, or <code>setOutgoingNoChaining()</code> method already invoked. * <li><code>APDUException.IO_ERROR</code> on I/O error. * </ul> */ public short setOutgoingNoChaining() throws APDUException { if (flags[OUTGOING_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } flags[OUTGOING_FLAG] = true; flags[NO_CHAINING_FLAG] = true; ramVars[CURRENT_STATE] = javacard.framework.APDU.STATE_OUTGOING; return ramVars[LE]; } /** * Sets the actual length of response data. If a length of * <code>0</code> is specified, no data will be output. * <p>Note:<ul> * <li><em>In T=0 (Case 2&4) protocol, the length is used by the Java Card runtime environment to prompt the CAD for GET RESPONSE commands.</em> * <li><em>This method sets the state of the * <code>APDU</code> object to * <code>STATE_OUTGOING_LENGTH_KNOWN</code>.</em> * </ul> * <P> * * @param len the length of response data * @throws APDUException with the following reason codes:<ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setOutgoing()</code> or <code>setOutgoingNoChaining()</code> not called * or if <code>setOutgoingAndSend()</code> already invoked, or this method already invoked. * <li><code>APDUException.BAD_LENGTH</code> if any one of the following is true:<ul> * <li><code>len</code> is negative. * <li><code>len</code> is greater than 256 and the currently selected applet does not implement the <code>javacardx.apdu.ExtendedLength</code> interface. * <li>T=0 protocol is in use, non BLOCK CHAINED data transfer is requested and len is greater than 256. * <li>T=1 protocol is in use, non BLOCK CHAINED data transfer is requested and len is greater than (IFSD-2), where IFSD is the Outgoing Block Size. The -2 accounts for the status bytes in T=1. * </ul> * <li><code>APDUException.NO_T0_GETRESPONSE</code> if T=0 protocol is in use and the CAD does not respond to <code>(ISO7816.SW_BYTES_REMAINING_00+count)</code> response status * with GET RESPONSE command on the same origin logical channel number as that of the current APDU command. * <li><code>APDUException.NO_T0_REISSUE</code> if T=0 protocol * is in use and the CAD does not respond to <code>(ISO7816.SW_CORRECT_LENGTH_00+count)</code> response status by re-issuing same APDU command on the same origin * logical channel number as that of the current APDU command with the corrected length. * <li><code>APDUException.IO_ERROR</code> on I/O error. * </ul> * @see #getOutBlockSize() */ public void setOutgoingLength(short len) throws APDUException { final short max = extended ? Short.MAX_VALUE : T0_OBS; if (!flags[OUTGOING_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } if (flags[OUTGOING_LEN_SET_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } if (len > max || len < 0) { APDUException.throwIt(APDUException.BAD_LENGTH); } flags[OUTGOING_LEN_SET_FLAG] = true; ramVars[CURRENT_STATE] = javacard.framework.APDU.STATE_OUTGOING_LENGTH_KNOWN; ramVars[LR] = len; } public short receiveBytes(short bOff) throws APDUException { if (!flags[INCOMING_FLAG] || flags[OUTGOING_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } short remainingBytes = ramVars[REMAINING_BYTES]; if (bOff < 0 || remainingBytes >= 1 && (bOff + 1) > buffer.length) { APDUException.throwIt(APDUException.BUFFER_BOUNDS); } short pre = (short) (ramVars[PRE_READ_LENGTH] & 0xff); if (pre != 0) { ramVars[PRE_READ_LENGTH] = 0; if (remainingBytes == 0) { ramVars[CURRENT_STATE]= javacard.framework.APDU.STATE_FULL_INCOMING; } else { ramVars[CURRENT_STATE]= javacard.framework.APDU.STATE_PARTIAL_INCOMING; } return pre; } if (remainingBytes != 0) { short len = getIncomingLength(); remainingBytes -= len; ramVars[REMAINING_BYTES] = remainingBytes; if (remainingBytes == 0) { ramVars[CURRENT_STATE]= javacard.framework.APDU.STATE_FULL_INCOMING; } else { ramVars[CURRENT_STATE] = javacard.framework.APDU.STATE_PARTIAL_INCOMING; } return len; } else { ramVars[CURRENT_STATE]= javacard.framework.APDU.STATE_FULL_INCOMING; return 0; } } /** * 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.<p> * This method should only be called on a case 3 or case 4 command, otherwise erroneous behavior may result. * <p>Notes: * <ul> * <li><em>In T=0 ( Case 3&4 ) protocol, the P3 param is assumed to be Lc.</em> * <li><em>Data is read into the buffer at offset 5 for normal APDU semantics.</em> * <li><em>Data is read into the buffer at offset 7 for an extended length APDU (Case 3E/4E).</em> * <li><em>In T=1 protocol, if all the incoming bytes do not fit in the buffer, this method may * return less bytes than the maximum incoming block size (IFSC).</em> * <li><em>In T=0 protocol, if all the incoming bytes do not fit in the buffer, this method may * return less than a full buffer of bytes to optimize and reduce protocol overhead.</em> * <li><em>This method sets the transfer direction to be inbound * and calls <code>receiveBytes(5)</code> for normal semantics or <code>receiveBytes(7)</code> for extended semantics.</em> * <li><em>This method may only be called once in a </em><code>Applet.process()</code><em> method.</em> * <li><em>This method sets the state of the <code>APDU</code> object to * <code>STATE_PARTIAL_INCOMING</code> if all incoming bytes are not received.</em> * <li><em>This method sets the state of the <code>APDU</code> object to * <code>STATE_FULL_INCOMING</code> if all incoming bytes are received.</em> * </ul> * @return number of data bytes read. The Le byte, if any, is not included in the count. * Returns 0 if no bytes are available. * @throws APDUException with the following reason codes: * <ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setIncomingAndReceive()</code> already invoked or * if <code>setOutgoing()</code> or <code>setOutgoingNoChaining()</code> previously invoked. * <li><code>APDUException.IO_ERROR</code> on I/O error. * <li><code>APDUException.T1_IFD_ABORT</code> if T=1 protocol is in use and the CAD sends * in an ABORT S-Block command to abort the data transfer. * </ul> */ public short setIncomingAndReceive() throws APDUException { if (ramVars[PRE_READ_LENGTH] == 0) { if (flags[INCOMING_FLAG] || flags[OUTGOING_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } flags[INCOMING_FLAG] = true; } return receiveBytes(getOffsetCdata()); } public void sendBytes(short bOff, short len) throws APDUException { final short max = extended ? Short.MAX_VALUE : T0_OBS; if (bOff < 0 || len < 0 || (short) (bOff + len) > max) { APDUException.throwIt(APDUException.BUFFER_BOUNDS); } if (!flags[OUTGOING_LEN_SET_FLAG] || flags[NO_GET_RESPONSE_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } if (len == 0) { return; } short Lr = ramVars[LR]; if (len > Lr) { APDUException.throwIt(APDUException.ILLEGAL_USE); } SimulatorSystem.instance().sendAPDU(buffer, bOff, len); Lr -= len; if (Lr == 0) { ramVars[CURRENT_STATE] = javacard.framework.APDU.STATE_FULL_OUTGOING; } else { ramVars[CURRENT_STATE] = javacard.framework.APDU.STATE_PARTIAL_OUTGOING; } ramVars[LR] = Lr; } /** * Sends <code>len</code> more bytes from <code>outData</code> byte array starting at specified offset * <code>bOff</code>. <p>If the last of the response is being sent by the invocation * of this method, the APDU buffer must not be altered. If the data is altered, incorrect output may be sent to * the CAD. * Requiring that the buffer not be altered allows the implementation to reduce protocol overhead * by transmitting the last part of the response along with the status bytes. * <p>The Java Card runtime environment may use the APDU buffer to send data to the CAD. * <p>Notes: * <ul> * <li><em>If </em><code>setOutgoingNoChaining()</code><em> was invoked, output block chaining must not be used.</em> * <li><em>In T=0 protocol, if </em><code>setOutgoingNoChaining()</code><em> was invoked, Le bytes must be transmitted * before </em><code>(ISO7816.SW_BYTES_REMAINING_00+remaining bytes)</code><em> response status is returned.</em> * <li><em>In T=0 protocol, if this method throws an </em><code>APDUException</code><em> with * </em><code>NO_T0_GETRESPONSE</code><em> or </em><code>NO_T0_REISSUE</code><em> reason code, * the Java Card runtime environment will restart APDU command processing using the newly received command. No more output * data can be transmitted. No error status response can be returned.</em> * <li><em>In T=1 protocol, if this method throws an </em><code>APDUException</code><em> * with </em><code>T1_IFD_ABORT</code><em> reason code, the Java Card runtime environment will restart APDU command processing using the newly * received command. No more output data can be transmitted. No error status response can be returned.</em> * <li><em>This method sets the state of the <code>APDU</code> object to * <code>STATE_PARTIAL_OUTGOING</code> if all outgoing bytes have not been sent.</em> * <li><em>This method sets the state of the <code>APDU</code> object to * <code>STATE_FULL_OUTGOING</code> if all outgoing bytes have been sent.</em> * </ul> * @param outData the source data byte array * @param bOff the offset into OutData array * @param len the byte length of the data to send * @throws APDUException with the following reason codes: * <ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setOutgoingLength()</code> not called * or <code>setOutgoingAndSend()</code> previously invoked * or response byte count exceeded or if <code>APDUException.NO_T0_GETRESPONSE</code> or * <code>APDUException.NO_T0_REISSUE</code> or <code>APDUException.NO_T0_REISSUE</code> * previously thrown. * <li><code>APDUException.IO_ERROR</code> on I/O error. * <li><code>APDUException.NO_T0_GETRESPONSE</code> if T=0 protocol is in use and * CAD does not respond to <code>(ISO7816.SW_BYTES_REMAINING_00+count)</code> response status * with GET RESPONSE command on the same origin logical channel number as that of the current * APDU command. * <li><code>APDUException.T1_IFD_ABORT</code> if T=1 protocol is in use and the CAD sends * in an ABORT S-Block command to abort the data transfer. * </ul> * @throws SecurityException if the <code>outData</code> array is not accessible in the caller's context * @see #setOutgoing() * @see #setOutgoingNoChaining() */ public void sendBytesLong(byte outData[], short bOff, short len) throws APDUException, SecurityException { int sendLength = buffer.length; while (len > 0) { if (len < sendLength) { sendLength = len; } Util.arrayCopy(outData, bOff, buffer, (short) 0, (short)sendLength); sendBytes((short) 0, (short)sendLength); len -= sendLength; bOff += sendLength; } } /** * This is the "convenience" send method. It provides for the most efficient way to send a short * response which fits in the buffer and needs the least protocol overhead. * This method is a combination of <code>setOutgoing(), setOutgoingLength( len )</code> followed by * <code>sendBytes ( bOff, len )</code>. In addition, once this method is invoked, <code>sendBytes()</code> and * <code>sendBytesLong()</code> methods cannot be invoked and the APDU buffer must not be altered.<p> * Sends <code>len</code> byte response from the APDU buffer starting at the specified offset <code>bOff</code>. * <p>Notes: * <ul> * <li><em>No other </em><code>APDU</code><em> send methods can be invoked.</em> * <li><em>The APDU buffer must not be altered. If the data is altered, incorrect output may be sent to * the CAD.</em> * <li><em>The actual data transmission may only take place on return from </em><code>Applet.process()</code> * <li><em>This method sets the state of the <code>APDU</code> object to * <code>STATE_FULL_OUTGOING</code>.</em> * </ul> * @param bOff the offset into APDU buffer * @param len the bytelength of the data to send * @throws APDUException ith the following reason codes: * <ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setOutgoing()</code> * or <code>setOutgoingAndSend()</code> previously invoked * or response byte count exceeded. * <li><code>APDUException.IO_ERROR</code> on I/O error.</ul> */ public void setOutgoingAndSend(short bOff, short len) throws APDUException { setOutgoing(); setOutgoingLength(len); sendBytes(bOff, len); } /** * This method returns the current processing state of the * <CODE>APDU</CODE> object. It is used by the <CODE>BasicService</CODE> class to help * services collaborate in the processing of an incoming APDU command. * Valid codes are listed in STATE_ .. constants above. * @see #STATE_INITIAL * @return the current processing state of the APDU */ public byte getCurrentState() { return (byte) ramVars[CURRENT_STATE]; } /** * This method is called during the <code>Applet.process(APDU)</code> method * to obtain a reference to the current APDU object. * This method can only be called in the context of the currently selected applet. * <p>Note: * <ul> * <li><em>Do not call this method directly or indirectly from within a method * invoked remotely via Java Card RMI method invocation from the client. The * APDU object and APDU buffer are reserved for use by RMIService. Remote * method parameter data may become corrupted.</em> * </ul> * @return the current <CODE>APDU</CODE> object being processed * @throws SecurityException if * <ul> * <li>the current context is not the context of the currently selected applet instance or * <li>this method was not called, directly or indirectly, from the applet's * process method (called directly by the Java Card runtime environment), or * <li>the method is called during applet installation or deletion. * </ul> */ public static javacard.framework.APDU getCurrentAPDU() throws SecurityException { javacard.framework.APDU currentAPDU = SimulatorSystem.instance().getCurrentAPDU(); if (!((boolean[])getFieldInternal(currentAPDU, "flags"))[ACCESS_ALLOWED_FLAG]) { throw new SecurityException("getCurrentAPDU must not be called outside of Applet#process()"); } return currentAPDU; } /** * This method is called during the <code>Applet.process(APDU)</code> method * to obtain a reference to the current APDU object. * This method can only be called in the context of the currently selected applet. * <p>Note:<ul> * <li><em>Do not call this method directly or indirectly from within a method * invoked remotely via Java Card RMI method invocation from the client. The * <CODE>APDU</CODE> object and APDU buffer are reserved for use by <CODE>RMIService</CODE>. Remote * method parameter data may become corrupted.</em> * </ul> * @return the APDU buffer of the <CODE>APDU</CODE> object being processed * @throws SecurityException if * <ul> * <li>the current context is not the context of the currently selected applet or * <li>this method was not called, directly or indirectly, from the applet's * process method (called directly by the Java Card runtime environment), or * <li>the method is called during applet installation or deletion. * </ul> */ public static byte[] getCurrentAPDUBuffer() throws SecurityException { return getCurrentAPDU().getBuffer(); } /** * Returns the logical channel number associated with the current <CODE>APDU</CODE> command * based on the CLA byte. A number in the range 0-19 based on the CLA byte encoding * is returned if the command contains logical channel encoding. * If the command does not contain logical channel information, 0 is returned. * See <em>Runtime * Specification for the Java Card Platform</em>, section * 4.3 for encoding details. * @return logical channel number, if present, within the CLA byte, 0 otherwise */ public static byte getCLAChannel() { javacard.framework.APDU apdu = SimulatorSystem.instance().getCurrentAPDU(); return (byte)((short[])getFieldInternal(apdu,"ramVars"))[LOGICAL_CHN]; } /** * Requests additional processing time from CAD. The implementation should ensure that this method * needs to be invoked only under unusual conditions requiring excessive processing times. * <p>Notes: * <ul> * <li><em>In T=0 protocol, a NULL procedure byte is sent to reset the work waiting time (see ISO 7816-3).</em> * <li><em>In T=1 protocol, the implementation needs to request the same T=0 protocol work waiting time quantum * by sending a T=1 protocol request for wait time extension(see ISO 7816-3).</em> * <li><em>If the implementation uses an automatic timer mechanism instead, this method may do nothing.</em> * </ul> * @throws APDUException with the following reason codes: * <ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setOutgoingNoChaining()</code> previously invoked. * <li><code>APDUException.IO_ERROR</code> on I/O error.</ul> */ public static void waitExtension() throws APDUException { javacard.framework.APDU apdu = SimulatorSystem.instance().getCurrentAPDU(); boolean[] apduFlags = (boolean[])getFieldInternal(apdu, "flags"); if (!apduFlags[ACCESS_ALLOWED_FLAG] || apduFlags[NO_CHAINING_FLAG]) { APDUException.throwIt(APDUException.ILLEGAL_USE); } } /** * Returns whether the current * <code>APDU</code> command is the first or * part of a command chain. Bit b5 of the CLA byte if set, indicates * that the * <code>APDU</code> is the first or part of a chain of commands. * See Runtime Environment Specification for the Java Card Platform, section 4.3 for encoding details. * @return <code>true</code> if this APDU is not the last APDU of a command chain, <code>false</code> otherwise. * @since 2.2.2 */ @SuppressWarnings("unused") public boolean isCommandChainingCLA() { return (buffer[ISO7816.OFFSET_CLA] & 0x10) == 0x10; } /** * Returns * <code>true</code> if the encoding of the current * <code>APDU</code> * command based on the * CLA byte indicates secure messaging. The secure messaging information * is in bits (b4,b3) for commands with origin channel numbers 0-3, and in bit * b6 for origin channel numbers 4-19. * See Runtime Environment Specification for the Java Card Platform, section 4.3 for encoding details. * @return <code>true</code> if the secure messaging bit(s) is(are) nonzero, <code>false</code> otherwise * @since 2.2.2 */ @SuppressWarnings("unused") public boolean isSecureMessagingCLA() { return (buffer[ISO7816.OFFSET_CLA] & 0x40) == 0x40 ? (buffer[ISO7816.OFFSET_CLA] & 0x20) == 0x20 : (buffer[ISO7816.OFFSET_CLA] & 0x0C) != 0; } /** * Returns whether the current * <code>APDU</code> command CLA byte corresponds * to an interindustry command as defined in ISO 7816-4:2005 specification. * Bit b8 of the CLA byte if * <code>0</code>, indicates that the * <code>APDU</code> * is an interindustry command. * @return <code>true</code> if this APDU CLA byte corresponds to an interindustry command, <code>false</code> otherwise. * @since 2.2.2 */ @SuppressWarnings("unused") public boolean isISOInterindustryCLA() { return (buffer[ISO7816.OFFSET_CLA]& 0x80) != 0x80; } /** * Returns the incoming data length(Lc). This method can be invoked * whenever inbound data processing methods can be invoked during case 1, 3 or 4 * processing. It is most useful for an extended length enabled applet to avoid * parsing the variable length Lc format in the APDU header. * @return the incoming byte length indicated by the Lc field in the APDU header. Return 0 if no incoming data (Case 1) * @throws APDUException with the following reason codes:<ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setIncomingAndReceive()</code> not called * or if <code>setOutgoing()</code> or <code>setOutgoingNoChaining()</code> previously invoked. * </ul> * @see #getOffsetCdata() * @since 2.2.2 */ public short getIncomingLength() { if (!flags[INCOMING_FLAG] || flags[OUTGOING_FLAG]) { throw new APDUException(APDUException.ILLEGAL_USE); } return ramVars[LC]; } /** * Returns the offset within the APDU buffer for incoming command data. * This method can be invoked whenever inbound data processing methods can be * invoked during case 1, 3 or 4 processing. It is most useful for an extended * length enabled applet to avoid parsing the variable length Lc format in the * APDU header. * * @return the offset within the APDU buffer for incoming command data from the previous call to <code>setIncomingAndReceive()</code> method. The value returned is either 5 (Lc is 1 byte), or 7 (when Lc is 3 bytes) * @throws APDUException with the following reason codes:<ul> * <li><code>APDUException.ILLEGAL_USE</code> if <code>setIncomingAndReceive()</code> not called * or if <code>setOutgoing()</code> or <code>setOutgoingNoChaining()</code> previously invoked. * </ul> * @see #getIncomingLength() * @since 2.2.2 */ public short getOffsetCdata() { if (!flags[INCOMING_FLAG] || flags[OUTGOING_FLAG]) { throw new APDUException(APDUException.ILLEGAL_USE); } return internalGetOffsetCdata(); } private short internalGetOffsetCdata() { if (extended) { return ISO7816.OFFSET_CDATA + 2; } return ISO7816.OFFSET_CDATA; } /** * clear internal state of the APDU * called by SimulatorRuntime via reflection */ private void internalReset(byte protocol, ApduCase apduCase, byte[] inputBuffer) { if (inputBuffer == null) { flags[ACCESS_ALLOWED_FLAG] = false; ramVars[ACTIVE_PROTOCOL] = protocol; return; } Arrays.fill(buffer, (byte) 0); Arrays.fill(ramVars, (short) 0); System.arraycopy(inputBuffer, 0, buffer, 0, inputBuffer.length); for(byte i=0;i<flags.length;i++) {flags[i]=false;} flags[ACCESS_ALLOWED_FLAG] = true; ramVars[ACTIVE_PROTOCOL] = protocol; final short lc; final short le; switch (apduCase) { case Case2: { lc = (short) 0; final byte leByte = buffer[ISO7816.OFFSET_LC]; le = leByte == 0 ? 256 : (short) (0xFF & leByte); break; } case Case2Extended: lc = (short) 0; le = ByteUtil.getShort(buffer, ISO7816.OFFSET_LC + 1); break; case Case3: lc = (short) (0xFF & buffer[ISO7816.OFFSET_LC]); le = (short) 0; break; case Case3Extended: lc = ByteUtil.getShort(buffer, ISO7816.OFFSET_LC + 1); le = (short) 0; break; case Case4: { lc = (short) (0xFF & buffer[ISO7816.OFFSET_LC]); final byte leByte = buffer[ISO7816.OFFSET_CDATA + lc]; le = leByte == 0 ? 256 : (short) (0xFF & leByte); break; } case Case4Extended: lc = ByteUtil.getShort(buffer, ISO7816.OFFSET_LC + 1); le = ByteUtil.getShort(buffer, ISO7816.OFFSET_LC + 3 + lc); break; case Case1: default: lc = (short) 0; le = (short) 0; break; } ramVars[LC] = ramVars[REMAINING_BYTES] = lc; ramVars[LE] = le; } private static Object getFieldInternal(APDU apdu, String fieldName){ try { Field f = APDU.class.getDeclaredField(fieldName); f.setAccessible(true); return f.get(apdu); } catch (Exception e){ throw new RuntimeException("Internal reflection error", e); } } }