APDU Java Applet Sample


/*
* $Workfile: APDU.java $ $Revision: 1.5 $, $Date: 2009-11-25 16:25:22 $
*
* Copyright (c) 1999 Sun Microsystems, Inc. All Rights Reserved.
*
* This software is the confidential and proprietary information of Sun
* Microsystems, Inc. ("Confidential Information"). You shall not
* disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into
* with Sun.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
* SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
* SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
* THIS SOFTWARE OR ITS DERIVATIVES.
*/

// /*
// $Workfile: APDU.java $
// $Revision: 1.5 $
// $Date: 2009-11-25 16:25:22 $
// $Author: marche $
// $Archive: /Products/Europa/api21/javacard/framework/APDU.java $
// $Modtime: 5/02/00 7:13p $
// Original author: Ravi
// */

package javacard.framework;

import com.sun.javacard.impl.PrivAccess;
import com.sun.javacard.impl.PackedBoolean;
import com.sun.javacard.impl.NativeMethods;
import com.sun.javacard.impl.Constants;

/**
* Application Protocol Data Unit (APDU) is
* the communication format between the card and the off-card applications.
* The format of the APDU is defined in ISO specification 7816-4.

*
* This class only supports messages which conform to the structure of
* command and response defined in ISO 7816-4. The behavior of messages which
* use proprietary structure of messages ( for example with header CLA byte in range 0xD0-0xFE ) is
* undefined. This class does not support extended length fields.

*
* The APDU object is owned by the JCRE. The APDU class maintains a byte array
* buffer which is used to transfer incoming APDU header and data bytes as well as outgoing data.
* The buffer length must be at least 37 bytes ( 5 bytes of header and 32 bytes of data ).
* The JCRE must zero out the APDU buffer before each new message received from the CAD.

*
* The JCRE designates the APDU object as a temporary JCRE Entry Point Object
* (See Java Card Runtime Environment (JCRE) Specification, section 6.2.1 for details).
* A temporary JCRE Entry Point Object can be accessed from any applet context. References
* to these temporary objects cannot be stored in class variables or instance variables
* or array components.
*

The JCRE similarly marks the APDU buffer as a global array
* (See Java Card Runtime Environment (JCRE) Specification, section 6.2.2 for details).
* A global array
* can be accessed from any applet context. References to global arrays
* cannot be stored in class variables or instance variables or array components.
*

*
* The applet receives the APDU instance to process from
* the JCRE in the Applet.process(APDU) method, and
* the first five bytes [ CLA, INS, P1, P2, P3 ] are available
* in the APDU buffer.

*
* The APDU class API is designed to be transport protocol independent.
* In other words, applets can use the same APDU methods regardless of whether
* the underlying protocol in use is T=0 or T=1 (as defined in ISO 7816-3).

* The incoming APDU data size may be bigger than the APDU buffer size and may therefore
* need to be read in portions by the applet. Similarly, the
* outgoing response APDU data size may be bigger than the APDU buffer size and may
* need to be written in portions by the applet. The APDU class has methods
* to facilitate this.

*
* For sending large byte arrays as response data,
* the APDU class provides a special method sendBytesLong() which
* manages the APDU buffer.

*
*

* // The purpose of this example is to show most of the methods
* // in use and not to depict any particular APDU processing
*
*public void process(APDU apdu){
*  // ...
*  byte[] buffer = apdu.getBuffer();
*  byte cla = buffer[ISO7816.OFFSET_CLA];
*  byte ins = buffer[ISO7816.OFFSET_INS];
*  ...
*  // assume this command has incoming data
*  // Lc tells us the incoming apdu command length
*  short bytesLeft = (short) (buffer[ISO7816.OFFSET_LC] & 0x00FF);
*  if (bytesLeft < (short)55) ISOException.throwIt( ISO7816.SW_WRONG_LENGTH );
*
*  short readCount = apdu.setIncomingAndReceive();
*  while ( bytesLeft > 0){
*      // process bytes in buffer[5] to buffer[readCount+4];
*      bytesLeft -= readCount;
*      readCount = apdu.receiveBytes ( ISO7816.OFFSET_CDATA );
*      }
*  //
*  //...
*  //
*  // Note that for a short response as in the case illustrated here
*  // the three APDU method calls shown : setOutgoing(),setOutgoingLength() & sendBytes()
*  // could be replaced by one APDU method call : setOutgoingAndSend().
*
*  // construct the reply APDU
*  short le = apdu.setOutgoing();
*  if (le < (short)2) ISOException.throwIt( ISO7816.SW_WRONG_LENGTH );
*  apdu.setOutgoingLength( (short)3 );
*
*  // build response data in apdu.buffer[ 0.. outCount-1 ];
*  buffer[0] = (byte)1; buffer[1] = (byte)2; buffer[3] = (byte)3;
*  apdu.sendBytes ( (short)0 , (short)3 );
*  // return good complete status 90 00
*  }
* 

* @see APDUException
* @see ISOException
*/

public final class APDU{

// This APDU class implements the T=0 transport protocol.

private static final short BUFFERSIZE = Constants.APDU_BUFFER_LENGTH;
private static final byte IFSC = 1;//information field size for ICC i.e Maximum Incoming BlockSize in T=1
private static final short IFSD = 258;//information field size for IFD i.e Maximum Outgoing BlockSize in T=1

private static APDU theAPDU;
/**
* The APDU will use the buffer byte[]
* to store data for input and output.
*/
private /* spec_public @*/ byte[] buffer;

// Nicolas (for SCID.java)
//@ invariant buffer_inv: buffer.length >= 37;

// Added by Xavier
/* invariant
@ (buffer.length > 36)
@ &&
@ (\forall short i; 0 <= i && i < buffer.length ; @ -128 <= buffer[i] && buffer[i] <= 127); @*/ private static byte[] ramVars; private static final byte LE = (byte) 0; private static final byte LR = (byte)(LE+1); private static final byte LC = (byte)(LR+1); private static final byte PRE_READ_LENGTH = (byte)(LC+1); private static final byte RAM_VARS_LENGTH = (byte) (PRE_READ_LENGTH+1); // status code constants private static final short BUFFER_OVERFLOW = (short) 0xC001; private static final short READ_ERROR = (short) 0xC003; private static final short WRITE_ERROR = (short) 0xC004; private static final short INVALID_GET_RESPONSE = (short) 0xC006; // procedure byte type constants private static final byte ACK_NONE = (byte) 0; private static final byte ACK_INS = (byte)1; private static final byte ACK_NOT_INS = (byte)2; // Le = terminal expected length private short getLe() { if ( ramVars[LE]==(byte)0 ) return (short) 256; else return (short)(ramVars[LE] & 0xFF); } private void setLe( byte data ) { ramVars[LE] = data; } // Lr = our response length private short getLr() { if (getLrIs256Flag()) return 256; else return (short)(ramVars[LR] & 0xFF); } private void setLr( byte data ) { ramVars[LR] = data; } // Lc = terminal incoming length private byte getLc() { return ramVars[LC]; } private void setLc( byte data ) { ramVars[LC] = data; } // PreReadLength = length already received via setIncommingAndReceive(). Used for undo operation. private byte getPreReadLength() { return ramVars[PRE_READ_LENGTH]; } private void setPreReadLength( byte data ) { ramVars[PRE_READ_LENGTH] = data; } private PackedBoolean thePackedBoolean; private byte incomingFlag, outgoingFlag, outgoingLenSetFlag, lrIs256Flag, sendInProgressFlag, noChainingFlag, noGetResponseFlag; // IncomingFlag = setIncoming() has been invoked. private boolean getIncomingFlag() { return thePackedBoolean.get( incomingFlag ); } private void setIncomingFlag() { thePackedBoolean.set( incomingFlag ); } private void resetIncomingFlag() { thePackedBoolean.reset( incomingFlag ); } // SendInProgressFlag = No procedure byte needs to be sent. private boolean getSendInProgressFlag() { return thePackedBoolean.get( sendInProgressFlag ); } private void setSendInProgressFlag() { thePackedBoolean.set( sendInProgressFlag );} private void resetSendInProgressFlag() { thePackedBoolean.reset( sendInProgressFlag ); } // OutgoingFlag = setOutgoing() has been invoked. private boolean getOutgoingFlag() { return thePackedBoolean.get( outgoingFlag ); } private void setOutgoingFlag() { thePackedBoolean.set( outgoingFlag ); } private void resetOutgoingFlag() { thePackedBoolean.reset( outgoingFlag ); } // OutgoingLenSetFlag = setOutgoingLen() has been invoked. private boolean getOutgoingLenSetFlag() { return thePackedBoolean.get( outgoingLenSetFlag ); } private void setOutgoingLenSetFlag() { thePackedBoolean.set( outgoingLenSetFlag ); } private void resetOutgoingLenSetFlag() { thePackedBoolean.reset( outgoingLenSetFlag ); } // LrIs256Flag = Lr is not 0. It is actually 256. Saves 1 byte of RAM. private boolean getLrIs256Flag() { return thePackedBoolean.get( lrIs256Flag ); } private void setLrIs256Flag() { thePackedBoolean.set( lrIs256Flag ); } private void resetLrIs256Flag() { thePackedBoolean.reset( lrIs256Flag ); } // noChainingFlag = do not use <61,xx> chaining for outbound data transfer.
// Note that the "get" method is package visible. This ensures that it is
// entered via an "invokevirtual" instruction and not an "invokestatic"
// instruction thereby ensuring a context switch
boolean getNoChainingFlag() { return thePackedBoolean.get( noChainingFlag ); }
private void setNoChainingFlag() { thePackedBoolean.set( noChainingFlag ); }
private void resetNoChainingFlag() { thePackedBoolean.reset( noChainingFlag ); }

// noGetResponseFlag = GET RESPONSE command was not received from CAD.
private boolean getNoGetResponseFlag() { return thePackedBoolean.get( noGetResponseFlag ); }
private void setNoGetResponseFlag() { thePackedBoolean.set( noGetResponseFlag ); }
private void resetNoGetResponseFlag() { thePackedBoolean.reset( noGetResponseFlag ); }

/**
* Only JCRE should create an APDU.
*

* @no params
*/
APDU(){

/*
buffer = JCSystem.makeTransientByteArray(BUFFERSIZE, JCSystem.CLEAR_ON_RESET);
*/
buffer = NativeMethods.t0InitAPDUBuffer();
ramVars = JCSystem.makeTransientByteArray(RAM_VARS_LENGTH, JCSystem.CLEAR_ON_RESET);

thePackedBoolean = PrivAccess.getPackedBoolean();
incomingFlag = thePackedBoolean.allocate();
sendInProgressFlag = thePackedBoolean.allocate();
outgoingFlag = thePackedBoolean.allocate();
outgoingLenSetFlag = thePackedBoolean.allocate();
lrIs256Flag = thePackedBoolean.allocate();
noChainingFlag = thePackedBoolean.allocate();
noGetResponseFlag = thePackedBoolean.allocate();
theAPDU = this;
}

/**
* Returns the APDU buffer byte array.
*

Notes:

    *

  • References to the APDU buffer byte array
    * cannot be stored in class variables or instance variables or array components.
    * See Java Card Runtime Environment (JCRE) Specification, section 6.2.2 for details.

    *

* @return byte array containing the APDU buffer
*/

/*@ behavior normal:
@ ensures \result == 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.

* This information may be used to ensure that there is enough space remaining in the
* APDU buffer when receiveBytes() is invoked.
*

Notes:

    *

  • On receiveBytes() the bOff param
    * should account for this potential blocksize.

    *

* @return incoming block size setting.
* @see #receiveBytes(short)
*/
public static short getInBlockSize() {
return IFSC;
}

/**
* 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.
*

This information may be used prior to invoking the setOutgoingLength() method,
* to limit the length of outgoing messages when BLOCK CHAINING is not allowed.
*

Notes:

    *

  • On setOutgoingLength() the len param
    * should account for this potential blocksize.

    *

* @return outgoing block size setting.
* @see #setOutgoingLength(short)
*/
public static short getOutBlockSize() {
return IFSD;
}

/**
* ISO 7816 transport protocol type T=0
*/
public static final byte PROTOCOL_T0 = 0;

/**
* ISO 7816 transport protocol type T=1
*/
public static final byte PROTOCOL_T1 = 1;

/**
* Returns the ISO 7816 transport protocol type, T=1 or T=0 in progress.
* @return the protocol type in progress.
* One of PROTOCOL_T0, PROTOCOL_T1 listed above.
*/
public static byte getProtocol() {
return (byte) PROTOCOL_T0;
}

/**
* In T=1 protocol, this method returns the Node Address byte, NAD. 
* In T=0 protocol, this method returns 0.
* 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 (byte) 0;
}

/**
* This method is used to set the data transfer direction to
* outbound and to obtain the expected length of response (Le).
*

Notes.

  • Any remaining incoming data will be discarded.
    *

  • In T=0 (Case 4) protocol, this method will return 256.
    *

* @return Le, the expected length of response.
* @exception APDUException with the following reason codes:

    *

  • APDUException.ILLEGAL_USE if this method or setOutgoingNoChaining() method already invoked.
    *

  • APDUException.IO_ERROR on I/O error.

*/
public short setOutgoing() throws APDUException {
// if we've previously called this method, then throw an exception
if ( getOutgoingFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
setOutgoingFlag();
return getLe();
}

/**
* 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 setOutgoing() method by applets which need
* to be compatible with legacy CAD/terminals which do not support ISO 7816-3/4 defined block chaining.
* See Java Card Runtime Environment (JCRE) Specification, section 8.4 for details.
*

Notes.

    *

  • Any remaining incoming data will be discarded.
    *

  • In T=0 (Case 4) protocol, this method will return 256.
    *

  • When this method is used, the waitExtension() method cannot be used.
    *

  • In T=1 protocol, retransmission on error may be restricted.
    *

  • In T=0 protocol, the outbound transfer must be performed
    * without using response status chaining.

    *

  • 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.
    *

* @return Le, the expected length of response data.
* @exception APDUException with the following reason codes:

    *

  • APDUException.ILLEGAL_USE if this method or setOutgoing() method already invoked.
    *

  • APDUException.IO_ERROR on I/O error.

*/
public short setOutgoingNoChaining() throws APDUException {
// if we've previously called this method, then throw an exception
if ( getOutgoingFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
setOutgoingFlag();
setNoChainingFlag();
return getLe();
}

/**
* Sets the actual length of response data. Default is 0.
*

Note:

    *

  • In T=0 (Case 2&4) protocol, the length is used by the JCRE to prompt the CAD for GET RESPONSE commands.
    *

* @param len the length of response data.
* @exception APDUException with the following reason codes:

    *

  • APDUException.ILLEGAL_USE if setOutgoing() not called or this method already invoked.
    *

  • APDUException.BAD_LENGTH if len is greater than 256 or
    * if 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.
    *

  • APDUException.IO_ERROR on I/O error.

* @see #getOutBlockSize()
*/
public void setOutgoingLength(short len) throws APDUException{
if ( !getOutgoingFlag() ) APDUException.throwIt(APDUException.ILLEGAL_USE);
// if we've previously called this method, then throw an exception
if ( getOutgoingLenSetFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
if ( len>256 || len<0 ) APDUException.throwIt(APDUException.BAD_LENGTH); setOutgoingLenSetFlag(); setLr((byte)len); if ( len==256 ) setLrIs256Flag(); } /** * Indicates that this command has incoming data. *

Note.

    *

  • In T=0 ( Case 3&4 ) protocol, the P3 param is assumed to be Lc.

* @exception APDUException with the following reason codes:

    *

  • APDUException.ILLEGAL_USE if this method already invoked or
    * if setOutgoing() or setOutgoingNoChaining() previously invoked.
    *

  • APDUException.IO_ERROR on I/O error.

*/
private void setIncoming() throws APDUException{
// if JCRE has undone a previous setIncomingAndReceive ignore
if ( getPreReadLength() != 0 ) return;
// if we've previously called this or setOutgoing() method, then throw an exception
if ( getIncomingFlag() || getOutgoingFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
setIncomingFlag(); // indicate that this method has been called
byte Lc = (byte) getLe(); // what we stored in Le was really Lc
setLc( Lc );
setLe((byte)0); // in T=1, the real Le is now unknown (assume 256)
}

/**
* Gets as many data bytes as will fit without APDU buffer overflow,
* at the specified offset bOff.  Gets all the remaining bytes if they fit.
*

Notes:

    *

  • The space in the buffer must allow for incoming block size.
    *

  • In T=1 protocol, if all the remaining bytes do not fit in the buffer, this method may
    * return less bytes than the maximum incoming block size (IFSC).

    *

  • In T=0 protocol, if all the remaining bytes do not fit in the buffer, this method may
    * return less than a full buffer of bytes to optimize and reduce protocol overhead.

    *

  • In T=1 protocol, if this method throws an APDUException
    * with
    T1_IFD_ABORT reason code, the JCRE will restart APDU command processing using the newly
    * received command. No more input data can be received.
    * No output data can be transmitted. No error status response can be returned.

    *

* @param bOff the offset into APDU buffer.
* @return number of bytes read. Returns 0 if no bytes are available.
* @exception APDUException with the following reason codes:

    *

  • APDUException.ILLEGAL_USE if setIncomingAndReceive() not called or
    * if setOutgoing() or setOutgoingNoChaining() previously invoked.
    *

  • APDUException.BUFFER_BOUNDS if not enough buffer space for incoming block size.
    *

  • APDUException.IO_ERROR on I/O error.
    *

  • APDUException.T1_IFD_ABORT if T=1 protocol is in use and the CAD sends
    * in an ABORT S-Block command to abort the data transfer.
    *

* @see #getInBlockSize()
*/
public short receiveBytes(short bOff) throws APDUException {
if ( !getIncomingFlag() || getOutgoingFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
short Lc = (short)(getLc()&0xFF);
// T=1 check bOff against blocksize and Lc.
if ( (bOff<0) || ((Lc>=IFSC) && (((short)(bOff+IFSC))>=BUFFERSIZE)) ) APDUException.throwIt ( APDUException.BUFFER_BOUNDS);

short pre = (short)( getPreReadLength() & 0x00FF ) ;
if ( pre != 0 ){
setPreReadLength( (byte) 0 );
return pre;
}

if ( Lc!=0 ){
short len = NativeMethods.t0RcvData( bOff );
if (len<0) APDUException.throwIt( APDUException.IO_ERROR ); setLc((byte)(Lc - len)); // update RAM copy of Lc, the count remaining return len; } return (short)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. *

Notes:

    *

  • In T=0 ( Case 3&4 ) protocol, the P3 param is assumed to be Lc.
    *

  • Data is read into the buffer at offset 5.
    *

  • 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).

    *

  • 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.

    *

  • This method sets the transfer direction to be inbound
    * and calls
    receiveBytes(5).
    *

  • This method may only be called once in a Applet.process() method.
    *

* @return number of bytes read. Returns 0 if no bytes are available.
* @exception APDUException with the following reason codes:

    *

  • APDUException.ILLEGAL_USE if setIncomingAndReceive() already invoked or
    * if setOutgoing() or setOutgoingNoChaining() previously invoked.
    *

  • APDUException.IO_ERROR on I/O error.
    *

  • APDUException.T1_IFD_ABORT if T=1 protocol is in use and the CAD sends
    * in an ABORT S-Block command to abort the data transfer.
    *

*/
public short setIncomingAndReceive() throws APDUException {
setIncoming();
return receiveBytes( (short) 5 );
}

/**
* Send 61 status bytes to CAD and
* process resulting GET RESPONSE command in the ramVars buffer.
* **NOTE** : This method overwrites ramVars[0..4]. **
* @param len the length to prompt CAD with. ( 0 *

  • APDUException.NO_T0_GETRESPONSE if GET RESPONSE command not received.
    *

    */
    private short send61xx( short len ) {

    short expLen = len;
    do {
    NativeMethods.t0SetStatus ( (short)(ISO7816.SW_BYTES_REMAINING_00+(len&0xFF)) );
    short newLen = NativeMethods.t0SndGetResponse();
    if ( newLen == INVALID_GET_RESPONSE ) { // Get Response not received
    setNoGetResponseFlag();
    APDUException.throwIt(APDUException.NO_T0_GETRESPONSE);
    }
    else if ( newLen > 0 ) {
    ramVars[LE] = (byte) newLen;
    expLen = getLe();
    }
    else APDUException.throwIt( APDUException.IO_ERROR );
    }
    while ( expLen>len );

    resetSendInProgressFlag();
    return expLen;
    }

    /**
    * Sends len more bytes from APDU buffer at specified offset bOff.
    *

    If the last part 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.
    *

    Notes:

      *

    • If setOutgoingNoChaining() was invoked, output block chaining must not be used.
      *

    • In T=0 protocol, if setOutgoingNoChaining() was invoked, Le bytes must be transmitted
      * before
      response status is returned.
      *

    • In T=0 protocol, if this method throws an APDUException
      * with
      NO_T0_GETRESPONSE reason code, the JCRE will restart APDU command processing using the newly
      * received command. No more output data can be transmitted. No error status response can be returned.

      *

    • In T=1 protocol, if this method throws an APDUException
      * with
      T1_IFD_ABORT reason code, the JCRE will restart APDU command processing using the newly
      * received command. No more output data can be transmitted. No error status response can be returned.

      *

    * @param bOff the offset into APDU buffer.
    * @param len the length of the data in bytes to send.
    * @exception APDUException with the following reason codes:

      *

    • APDUException.ILLEGAL_USE if setOutgoingLen() not called
      * or setOutgoingAndSend() previously invoked
      * or response byte count exceeded or if APDUException.NO_T0_GETRESPONSE previously thrown.
      *

    • APDUException.BUFFER_BOUNDS if the sum of bOff and len exceeds the buffer size.
      *

    • APDUException.IO_ERROR on I/O error.
      *

    • APDUException.NO_T0_GETRESPONSE if T=0 protocol is in use and
      * the CAD does not respond to response status
      * with GET RESPONSE command.
      *

    • APDUException.T1_IFD_ABORT if T=1 protocol is in use and the CAD sends
      * in an ABORT S-Block command to abort the data transfer.
      *

    * @see #setOutgoing()
    * @see #setOutgoingNoChaining()
    */
    public void sendBytes(short bOff, short len) throws APDUException {

    short result;
    if ( (bOff<0) || (len<0) || ((short)((bOff+len))>BUFFERSIZE) ) APDUException.throwIt( APDUException.BUFFER_BOUNDS );
    if ( !getOutgoingLenSetFlag() || getNoGetResponseFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
    if (len==0) return;
    short Lr = getLr();
    if (len>Lr) APDUException.throwIt( APDUException.ILLEGAL_USE );

    short Le = getLe();

    if ( getNoChainingFlag() ) {

    // Need to force GET RESPONSE for
    // Case 4 or
    // Case 2 but sending less than CAD expects
    if ( getIncomingFlag() || (Lroutgoing switch.
    }

    while ( len > Le ) { //sending more than Le
    if ( !getSendInProgressFlag() ) {
    result = NativeMethods.t0SndData( buffer, bOff, Le, ACK_INS );
    setSendInProgressFlag();
    }
    else result = NativeMethods.t0SndData( buffer, bOff, Le, ACK_NONE );

    if ( result != 0 ) APDUException.throwIt( APDUException.IO_ERROR );

    bOff+=Le;
    len-=Le;
    Lr-=Le;
    Le = send61xx( Lr); // resets sendInProgressFlag.
    }

    if ( !getSendInProgressFlag() ) {
    result = NativeMethods.t0SndData( buffer, bOff, len, ACK_INS );
    setSendInProgressFlag();
    }

    else result = NativeMethods.t0SndData( buffer, bOff, len, ACK_NONE );

    if ( result != 0 ) APDUException.throwIt( APDUException.IO_ERROR );
    Lr-=len;
    Le-=len;
    }
    else { // noChainingFlag = FALSE
    while (len>0) {
    short temp=len;
    // Need to force GET RESPONSE for Case 4 & for partial blocks
    if ( (len!=Lr) || getIncomingFlag() || (Lr!=Le) || getSendInProgressFlag() ){
    temp = send61xx( len ); // resets sendInProgressFlag.
    resetIncomingFlag(); // no more incoming->outgoing switch.
    }
    result = NativeMethods.t0SndData( buffer, bOff, temp, ACK_INS );
    setSendInProgressFlag();

    if ( result != 0 ) APDUException.throwIt( APDUException.IO_ERROR );
    bOff+=temp;
    len-=temp;
    Lr-=temp;
    Le=Lr;
    }
    }

    setLe((byte)Le); // update RAM copy of Le, the expected count remaining
    setLr((byte)Lr); // update RAM copy of Lr, the response count remaining
    }

    /**
    * Sends len more bytes from outData byte array starting at specified offset
    * bOff.

    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.
    *

    The JCRE may use the APDU buffer to send data to the CAD.
    *

    Notes:

      *

    • If setOutgoingNoChaining() was invoked, output block chaining must not be used.
      *

    • In T=0 protocol, if setOutgoingNoChaining() was invoked, Le bytes must be transmitted
      * before
      response status is returned.
      *

    • In T=0 protocol, if this method throws an APDUException with NO_T0_GETRESPONSE reason code,
      * the JCRE will restart APDU command processing using the newly received command. No more output
      * data can be transmitted. No error status response can be returned.

      *

    • In T=1 protocol, if this method throws an APDUException
      * with
      T1_IFD_ABORT reason code, the JCRE will restart APDU command processing using the newly
      * received command. No more output data can be transmitted. No error status response can be returned.

      *

    *

    * @param outData the source data byte array.
    * @param bOff the offset into OutData array.
    * @param len the bytelength of the data to send.
    * @exception SecurityException if the outData byte array is not accessible in the caller's context.
    * @exception APDUException with the following reason codes:

      *

    • APDUException.ILLEGAL_USE if setOutgoingLen() not called
      * or setOutgoingAndSend() previously invoked
      * or response byte count exceeded or if APDUException.NO_T0_GETRESPONSE previously thrown.
      *

    • APDUException.IO_ERROR on I/O error.
      *

    • APDUException.NO_T0_GETRESPONSE if T=0 protocol is in use and
      * CAD does not respond to response status
      * with GET RESPONSE command.
      *

    • APDUException.T1_IFD_ABORT if T=1 protocol is in use and the CAD sends
      * in an ABORT S-Block command to abort the data transfer.
      *

    * @see #setOutgoing()
    * @see #setOutgoingNoChaining()
    */
    public void sendBytesLong(byte[] outData, short bOff, short len) throws APDUException
    {
    short sendLength = (short)buffer.length;
    while ( len>0 ) {
    if ( lensetOutgoing(), setOutgoingLength( len )
    followed by
    * sendBytes ( bOff, len ). In addition, once this method is invoked, sendBytes() and
    * sendBytesLong() methods cannot be invoked and the APDU buffer must not be altered.

    * Sends len byte response from the APDU buffer at starting specified offset bOff.
    *

    Notes:

      *

    • No other APDU send methods can be invoked.
      *

    • The APDU buffer must not be altered. If the data is altered, incorrect output may be sent to
      * the CAD.

      *

    • The actual data transmission may only take place on return from Applet.process()
      *

    *

    * @param bOff the offset into APDU buffer.
    * @param len the bytelength of the data to send.
    * @exception APDUException with the following reason codes:

      *

    • APDUException.ILLEGAL_USE if setOutgoing()
      * or setOutgoingAndSend() previously invoked
      * or response byte count exceeded.
      *

    • APDUException.IO_ERROR on I/O error.

    */
    public void setOutgoingAndSend( short bOff, short len) throws APDUException {
    setOutgoing();
    setOutgoingLength(len);
    sendBytes( bOff, len );
    }

    void resetAPDU() {
    // as described earlier, we assume case 1 or 2, so Le=P3 and Lc=0
    setLe( buffer[ISO7816.OFFSET_LC] );
    setLc( (byte)0 );

    // save Lr=0, reset flags
    setLr((byte)0);
    resetIncomingFlag();
    resetOutgoingFlag();
    resetOutgoingLenSetFlag();
    resetSendInProgressFlag();
    resetLrIs256Flag();
    resetNoChainingFlag();
    resetNoGetResponseFlag();
    setPreReadLength( (byte)0 );
    }

    /**
    * Only JCRE should call this method.

    * Sends 0s for any remaining data to be sent based on the
    * promised responseLength (default=0/Le) and status bytes to complete this APDUs
    * response and initiates new APDU exchange.
    * @param status the ISO R-Apdu status bytes sw1 and sw2 response
    * @exception APDUException with the following reason codes:

      *

    • APDUException.IO_ERROR on I/O error.

    */
    void complete(short status) throws APDUException{

    short result;

    // Zero out APDU buffer
    Util.arrayFillNonAtomic( buffer, (short)0, BUFFERSIZE, (byte)0 );

    if ( ( !getNoGetResponseFlag() ) && ( getSendInProgressFlag() ) ) {
    short Le = (byte)getLe();
    short sendLen = (short)32;
    while ( Le > 0 ) {
    if (Le<32) sendLen=Le; result = NativeMethods.t0SndData( buffer, (byte) 0, sendLen, ACK_NONE ); if ( result != 0 ) APDUException.throwIt( APDUException.IO_ERROR ); Le-=sendLen; } } buffer[0] = (byte) ( status >> 8);
    buffer[1] = (byte) status;

    if (status==0) result = NativeMethods.t0RcvCommand();
    else {
    NativeMethods.t0SetStatus ( status );
    result = NativeMethods.t0SndStatusRcvCommand();
    }

    if ( result != 0 ) APDUException.throwIt( APDUException.IO_ERROR );

    resetAPDU();
    }

    /**
    * Only JCRE should call this method.

    * This method puts the externally visible state of the apdu back as if
    * the setIncomingAndReceive has not yet been invoked. It needs to
    * ensure, though that no real I/O is performed when setIncomingAndReceive is
    * invoked next. This method must only be called it setIncomingAndReceive has
    * been invoked by the JCRE. This method must not be invoked however, if receiveBytes
    * has also been invoked.
    */
    void undoIncomingAndReceive() {
    setPreReadLength( (byte)(buffer[ISO7816.OFFSET_LC] - getLc()) );
    }

    /**
    * 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.
    *

    Notes:

      *

    • In T=0 protocol, a NULL procedure byte is sent to reset the work waiting time (see ISO 7816-3).
      *

    • 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).

      *

    • If the implementation uses an automatic timer mechanism instead, this method may do nothing.
      *

    *

    * @exception APDUException with the following reason codes:

      *

    • APDUException.ILLEGAL_USE if setOutgoingNoChaining() previously invoked.
      *

    • APDUException.IO_ERROR on I/O error.

    */
    public static void waitExtension() throws APDUException{

    if ( theAPDU.getNoChainingFlag() ) APDUException.throwIt( APDUException.ILLEGAL_USE );
    //send a procedure byte of 0x60 to request additional waiting time.
    short result = NativeMethods.t0Wait();
    if ( result != 0 ) APDUException.throwIt( APDUException.IO_ERROR );
    }
    }

  • Leave a Reply

    Your email address will not be published. Required fields are marked *