
/*---------------------------------------------------------------------------
 * Copyright (C) 1999,2000 Dallas Semiconductor Corporation, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Dallas Semiconductor
 * shall not be used except as stated in the Dallas Semiconductor
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

// OneWireContainer16.java
package com.dalsemi.onewire.container;

import java.io.*;
import java.lang.*;
import java.util.*;
import com.dalsemi.onewire.*;
import com.dalsemi.onewire.utils.*;
import com.dalsemi.onewire.adapter.*;


/** OneWireContainer for iButton family type 0x16 (Java iButton).
 *
 *  @version   0.00, 28 Aug 2000
 *  @author    YL
 */
public class OneWireContainer16
   extends OneWireContainer
{

   /** CLA in the CommandAPDU header. */
   static final byte CLA = ( byte ) 0xD0;

   /** INS in the CommandAPDU header. */
   static final byte INS = ( byte ) 0x95;

   /** size of password length in byte */
   public static final int PASSWORD_LENGTH_SIZE = 1;

   /** maximum length of password in byte */
   public static final int PASSWORD_SIZE = 8;

   /** size of AID length in byte */
   public static final int AID_LENGTH_SIZE = 1;

   /** maximum length of AID in byte */
   public static final int AID_SIZE = 16;

   /** offset of AID length in applet APDU data stream */
   public static final int AID_LENGTH_OFFSET = PASSWORD_LENGTH_SIZE
                                               + PASSWORD_SIZE;

   /** offset of AID name in applet APDU data stream */
   public static final int AID_NAME_OFFSET = PASSWORD_LENGTH_SIZE
                                             + PASSWORD_SIZE
                                             + AID_LENGTH_SIZE;

   /** size of applet file header in byte */
   public static final int APPLET_FILE_HEADER_SIZE = PASSWORD_LENGTH_SIZE
                                                        + PASSWORD_SIZE
                                                        + AID_LENGTH_SIZE
                                                        + AID_SIZE;

   /** default APDU data packet length in byte */
   public static int APDU_PACKET_LENGTH = 64;

   /** password */
   private String password;

   /** current CommandaAPDU sent */
   private CommandAPDU capdu = null;

   /** current ResponseAPDU received */
   private ResponseAPDU rapdu = null;

   /** JibComm object for communicating with adapter */
   private JibComm jibComm;

   /** default JibComm run time */
   private int runTime = 0;

   /**
    * Constructs a generic instance of the OneWireContainer16 class.
    */
   public OneWireContainer16 ()
   {
      super();
   }   // OneWireContainer16()

   //-------------------------------------------------------------------------
   //-------- Methods
   //-------------------------------------------------------------------------
   //-------------------------------------------------------------------------

   /** Gets the Dallas Semiconductor part number of the Java iButton
    *  as a string.  For example "DS1954".
    *
    *  @return string represetation of the Java iButton name.
    */
   public String getName ()
   {
      return "DS1954";
   }   // getName()

   /** Gets the alternate Dallas Semiconductor part numbers or names.
    *  A 'family' of One-Wire devices may have more than one part number
    *  depending on packaging.  There can also be nicknames such as
    *  'Crypto iButton'.
    *
    *  @return string represetation of the alternate names.
    */
   public String getAlternateNames ()
   {
      return "Java iButton, Cryptographic iButton";
   }   // getAlternateNames

   /** Gets a short description of the function of the Java iButton.
    *
    *  @return string represetation of the function description.
    */
   public String getDescription ()
   {
      return "JavaCard 2.0 compliant device.";
   }   // getDescription()

   /**
    * Provides this container an adapter object to access the Java iButton.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this Java iButton.
    * @param  newAddress        address of this One-Wire device as a byte array.
    */
   public void setupContainer (DSPortAdapter sourceAdapter, byte[] newAddress)
   {
      super.setupContainer(sourceAdapter, newAddress);
      setupJibComm(adapter, address);
   }

   /**
    * Provides this container an adapter object to access the Java iButton.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this Java iButton.
    * @param  newAddress        address of this One-Wire device as a long.
    */
   public void setupContainer (DSPortAdapter sourceAdapter, long newAddress)
   {
      super.setupContainer(sourceAdapter, newAddress);
      setupJibComm(adapter, address);
   }

   /**
    * Provides this container an adapter object to access the Java iButton.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this Java iButton.
    * @param  newAddress        address of this One-Wire device as a string
    */
   public void setupContainer (DSPortAdapter sourceAdapter, String newAddress)
   {
      super.setupContainer(sourceAdapter, newAddress);
      setupJibComm(adapter, address);
   }

   /**
    * Provide this container an JibComm object to communicate with the Java iButton.
    *
    * @param  sourceAdapter     adapter object required to communicate with
    *                           this Java iButton.
    * @param  newAddress        address of this One-Wire device as a byte array.
    */
   public void setupJibComm (DSPortAdapter sourceAdapter, byte[] newAddress)
   {
      speed           = adapter.SPEED_OVERDRIVE;
      speedFallBackOK = false;
      jibComm         = new JibComm(sourceAdapter, newAddress);
   }

   /**
    * Gets the maximum speed this One-Wire device can communicate at.
    *
    * @return maximum speed of the One-Wire device
    */
   public int getMaxSpeed ()
   {
      return adapter.SPEED_OVERDRIVE;
   }

   /**
   * Gets the current CommandAPDU sent to the Java iButton
   *
   * @return current CommandAPDU sent to the Java iButton
   */
   public CommandAPDU getCommandAPDUInfo ()
   {
      return capdu;
   }

   /**
   * Gets the current ResponseAPDU received from the Java iButton
   *
   * @return current ResponseAPDU received from the Java iButton
   */
   public ResponseAPDU getResponseAPDUInfo ()
   {
      return rapdu;
   }

   /**
   * Gets the default run time value of the Java iButton
   *
   * @return run time value of the Java iButton
   */
   public int getRunTime ()
   {
      return runTime;
   }

   /**
   * Sets the run time value for the Java iButton
   *
   * @param newRunTime new run time value
   */
   public void setRunTime (int newRunTime)
   {
      runTime = newRunTime;
   }

   /**
    * Sets the size of the packets sent on load.
    * The range can be from 64 to 112 bytes.
    *
    * @param  size new packet size.
    *
    * @return true if packet size is set to the new value.
    *         false if invalid packet size is requested.
    */
   public boolean setLoadPacketSize (int size)
   {

      // maximum number of bytes send is 124, 
      // 4 for CommandAPDU header, 1 for data length, 
      // 3 to append at sendAPDU(),  4 for JibComm setData header 
      // and 112 for packet size
      if (size < 64 || size > 112)
         return false;

      APDU_PACKET_LENGTH = size;

      return true;
   }   // setLoadPacketSize()

   /**
    * Gets the size of the packets sent on load.
    *
    * @return  size of the packets sent on load.
    */
   public int getLoadPacketSize ()
   {
      return APDU_PACKET_LENGTH;
   }   // getLoadPacketSize()

   /**
    * Sets the PIN used to communicate with the Java iButton.
    * Once this method has been called, the PIN will be sent to every method
    * that requires a PIN.
    *
    * @param  passwd PIN to be set and sent to the Java iButton for each command
    *         that requires a PIN.
    */
   public void setPIN (String passwd)
   {
      password = passwd;
   }   // setPIN()

   //-------------------------------------------------------------------------
   //-------- Firmware Command methods
   //-------------------------------------------------------------------------

   /**
    * Gets the amount of free RAM in the Java iButton.
    *
    * @return ResponseAPDU with a data field containing the amount of free
    *         RAM in the Java iButton.  Value returned is in little endian format.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    *
    */
   public ResponseAPDU getFreeRAM ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x01);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getFreeRAM()

   /**
    * Gets requested number of bytes of random data generated by Java iButton.
    *
    * @param  numBytes the number of bytes requested.  <i>numByes</i> should not
    *         exceed 119.  If the number is greater than 119, the API only returns 119.
    *
    * @return ResponseAPDU with a data field containing random bytes generated.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getRandomBytes (int numBytes)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {

      // maximum number of bytes return is 124, 
      // 2 for status word, 3 to discard and 119 for data
      if (numBytes > 119)
         numBytes = 119;

      byte[] data = new byte [2];

      data [0] = ( byte ) (numBytes & 0xFF);
      data [1] = ( byte ) ((numBytes >> 8) & 0xFF);
      capdu    = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x0D,
                                 data);
      rapdu    = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getRandomBytes()

   /**
    * Gets the firmware version string.
    *
    * @return ResponseAPDU with a data field containing the firmware
    *         version string of the Java iButton
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getFirmwareVersionString ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x00);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getFirmwareVersionString()

   /**
    * Gets the last error value.
    * If the error reporting mode is set, then this method will return
    * the value of the last exception.
    *
    * @return ResponseAPDU containing the last exception value.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getLastError ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x0F);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getLastError()

   /**
    * Gets the Error Reporting Mode.  This function is not supported in
    * the .033 version of the firmware.
    *
    * @return ResponseAPDU containing the error reporting mode.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6A02. Command not supported by the Java iButton
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getErrorReportingMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x09);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getErrorReportingMode()

   /**
    * Sets the Error Reporting mode.
    * If the error reporting mode is set, the Java iButton stores the last
    * exception value code.  This code can be retreived by calling
    * getLastError().
    *
    *
    * @param mode
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @see getLastError
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setErrorReportingMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x09, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setErrorReportingMode()

   /**
    * Gets the AID of the applet by its installed number.
    *
    * @param index installed number of the applet on the Java iButton to get
    *              the AID of.
    *
    * @return ResponseAPDU containing the AID of the applet requested.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x8453.  Applet not found.
    *             Valid applet index are in the range of 0 - 15.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getAIDByNumber (int index)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data = new byte [1];

      data [0] = ( byte ) (index & 0xFF);
      capdu    = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x0E,
                                 data);
      rapdu    = sendAPDU(capdu, runTime);

      if (rapdu.getSW() == 0x8453)   // applet not found
         return rapdu;

      // bug fix for Firmware 0.33
      int i = rapdu.getData().length;

      data = new byte [(i + 4)];

      System.arraycopy(rapdu.getData(), 0, data, 0, i);

      // return a "SUCCESS" ResponseAPDU
      data [i]     = rapdu.getSW1();
      data [i + 1] = rapdu.getSW2();
      data [i + 2] = ( byte ) 0x90;
      data [i + 3] = ( byte ) 0x00;

      return (new ResponseAPDU(data));
   }   // getAIDByNumber()

   /**
    * Deletes the currently selected applet.
    * If PIN protection is enabled, a PIN must be supplied.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU deleteSelectedApplet ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [1 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x03, ( byte ) 0x00, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // deleteSelectedApplet()

   /**
    * Deletes an applet by its installed number.
    *
    * @param  index installed number of the applet to delete.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         <li>Failure SW 0x8453. Applet not found.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU deleteAppletByNumber (int index)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) index;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) index;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x03, ( byte ) 0x01, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // deleleAppletByNumber()

   /**
    * Deletes an applet by its AID.
    *
    * @param  aid AID of applet to be deleted.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         <li>Failure SW 0x8453. Applet not found.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU deleteAppletByAID (String aid)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length() + aid.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) aid.length();

         System.arraycopy(aid.getBytes(), 0, data, password.length() + 2,
                          aid.length());
      }
      else
      {
         data     = new byte [2 + aid.length()];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) aid.length();

         System.arraycopy(aid.getBytes(), 0, data, 2, aid.length());
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x03, ( byte ) 0x02, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // deleteAppletByAID()

   /**
    * Loads an applet onto the Java iButton.
    * This method takes an applet filename, directory
    * and AID.  The AID must NOT exceed 16 bytes.
    *
    * @param  fileName file name of the applet to be loaded into the Java iButton.
    * @param  directoryName path to the applet to be loaded.
    * @param  aid AID of the applet to be loaded.
    *
    * @return ResponseAPDU indicating success or failure.
    *         Possible return values are
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6400. Insufficient Memory
    *         <li>Failure SW 0x6901. Invalid AID Length
    *         <li>Failure SW 0x6902. Invalid API Version
    *         <li>Failure SW 0x6903. Invalid Password
    *         <li>Failure SW 0x6904. Invalid Signature Length
    *         <li>Failure SW 0x6905. Hash Corruption
    *         <li>Failure SW 0x6906. Hash Failure
    *         <li>Failure SW 0x6982. Invalid Signature
    *         <li>Failure SW 0x6A84. Class Length Overrun
    *         <li>Failure SW 0x6A86. Invalid Loader Command
    *         <li>Failure SW 0x6A87. Invalid Packet
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    * @throws FileNotFoundException
    * @throws IOException
    */
   public ResponseAPDU loadApplet (String fileName, String directoryName,
                                   String aid)
      throws OneWireException, OneWireIOException, IllegalArgumentException,
             FileNotFoundException, IOException
   {
      File            appletFile        = new File(directoryName, fileName);
      FileInputStream appletInputStream = new FileInputStream(appletFile);
      long            appletLength      = appletFile.length();
      long            blockSize         = appletInputStream.available();
      byte[]          appletBuffer      =
         new byte [APPLET_FILE_HEADER_SIZE + ( int ) appletLength];

      // Password FIX THIS TO HANDLE LOADING WITH A PASSWORD.
      if (password != null)
      {
         appletBuffer [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, appletBuffer, 1,
                          password.length());
      }

      // append AID to appletBuffer
      for (int offset = 0; offset < AID_SIZE; offset++)
      {
         if (offset < aid.length())
            appletBuffer [offset + AID_NAME_OFFSET] =
               ( byte ) aid.charAt(( int ) offset);
         else   // leave the rest in their default value (0)
            break;
      }

      appletBuffer [AID_LENGTH_OFFSET] = ( byte ) AID_SIZE;   // AID Length

      // get the applet file
      if ((appletLength) <= blockSize)   // in one chunk
         appletInputStream.read(appletBuffer, APPLET_FILE_HEADER_SIZE,
                                ( int ) appletLength);
      else
      {                                  // in multiple chunks
         long   bytesRead = 0;
         byte[] readBuffer;

         readBuffer = new byte [( int ) blockSize];

         while (bytesRead < appletLength)
         {
            appletInputStream.read(readBuffer);
            System.arraycopy(readBuffer, 0, appletBuffer,
                             ( int ) (bytesRead + APPLET_FILE_HEADER_SIZE),
                             readBuffer.length);

            bytesRead += blockSize;
         }
      }

      appletInputStream.close();

      int          i;
      int          bytesSent   = 0;
      boolean      firstPacket = true;
      ResponseAPDU rapdu       = null;
      byte[]       apduBuffer  = new byte [APDU_PACKET_LENGTH];

      while (bytesSent
             < (appletLength + APPLET_FILE_HEADER_SIZE - APDU_PACKET_LENGTH))
      {
         for (i = 0; (i < APDU_PACKET_LENGTH) && (i < appletBuffer.length);
                 i++)
            apduBuffer [i] = appletBuffer [i + bytesSent];

         bytesSent += APDU_PACKET_LENGTH;

         if (firstPacket == true)
         {
            capdu       = new CommandAPDU(CLA, ( byte ) 0xA6, ( byte ) 0x01,
                                          ( byte ) 0x00, apduBuffer);
            firstPacket = false;
         }
         else
         {
            capdu = new CommandAPDU(CLA, ( byte ) 0xA6, ( byte ) 0x02,
                                    ( byte ) 0x00, apduBuffer);
         }

         rapdu = sendAPDU(capdu, runTime);

         if (bytesSent < (appletLength + APPLET_FILE_HEADER_SIZE))
         {
            if (rapdu.getSW() != 0x6301)
               bytesSent = ( int ) (appletLength + APPLET_FILE_HEADER_SIZE
                                    + 1);
         }
      }

      if (bytesSent < (appletLength + APPLET_FILE_HEADER_SIZE))
      {
         apduBuffer =
            new byte [( int ) (appletLength + APPLET_FILE_HEADER_SIZE - bytesSent)];

         for (i = 0;
                 i < (appletLength + APPLET_FILE_HEADER_SIZE - bytesSent);
                 i++)
            apduBuffer [i] = appletBuffer [i + bytesSent];

         if (firstPacket == true)
         {
            capdu       = new CommandAPDU(CLA, ( byte ) 0xA6, ( byte ) 0x01,
                                          ( byte ) 0x00, apduBuffer);
            firstPacket = false;
         }
         else
         {
            capdu = new CommandAPDU(CLA, ( byte ) 0xA6, ( byte ) 0x02,
                                    ( byte ) 0x00, apduBuffer);
         }

         rapdu = sendAPDU(capdu, runTime);
      }

      return rapdu;
   }   // loadApplet()

   /**
    * Clears all memory in the Java iButton.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU masterErase ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [1 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());
      }
      else
      {
         data     = new byte [1];
         data [0] = ( byte ) 0;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x00, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // masterErase()

   /**
    * Gets the number of times the Java iButton has been power on reset (POR)
    * since the last master erase.  Return value is in little endian
    * format. No PIN required.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000. Data field contains the POR count.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getPORCount ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x02, ( byte ) 0x00);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getPORCount()

   /**
    * Gets the Real Time Clock.  No PIN required.
    *
    * @return ResponseAPDU containing the value of the real time clock.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getRealTimeClock ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x0C);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getRealTimeClock()

   /**
    * Gets the Answer To Reset (ATR) from Java iButton.
    *
    * @return ResponseAPDU containing the ATR in the data field.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getATR ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x0B);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getATR()

   /**
    * Gets the Ephemeral Gabage Collection Mode.
    * A value of 1 indicates ephemeral garbage collection is turned on,
    * 0 if turned off.
    *
    * @return ResponseAPDU containing the Ephemeral Garbage Collector Mode.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getEphemeralGCMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x02);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getEphemeralGCMode()

   /**
    * Gets the Applet Garbage Collection Mode.
    * A value of 1 indicates applet garbage collection is turned on,
    * 0 if turned off.
    *
    * @return ResponseAPDU containing the Applet Garbage Collector Mode.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getAppletGCMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x03);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getAppletGCMode()

   /**
    * Gets the Command PIN Mode.
    * A value of 1 indicates that a PIN is required for all Administrative
    * and AID commands, 0 indicates free access.
    *
    * @return ResponseAPDU containing the Command PIN Mode.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getCommandPINMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x04);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getCommandPINMode()

   /**
    * Gets the Load PIN Mode.
    * A value of 1 indicates that a PIN is required for Applet loading.
    *
    * @return ResponseAPDU containing the Load PIN Mode.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getLoadPINMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x05);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getLoadPINMode()

   /**
    * Gets the Restore Mode.
    * When Restore Mode is enabled, all field updates and
    * javacard.framework.System transactions are considered atomic.
    * If a tear occurs in the middle of these updates, values just
    * prior to the update are restored.
    *
    * @return ResponseAPDU containing the Restore Mode.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getRestoreMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x06);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getRestoreMode()

   /**
    * Gets the Exception Mode.
    * When Exception Mode is enabled, Java API exceptions are thrown.
    * All uncaught exceptions return 0x6F00 in the SW.
    * When disabled, an error is returned from the VM.
    *
    * @return ResponseAPDU containing the Exception Mode value.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getExceptionMode ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x07);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getExceptionMode()

   /**
    * Gets the size of the Commit Buffer.
    * Committing one field to the buffer requires 9 bytes. Therefore
    * the default size of 72 bytes allows 8 field updates. The minimum size
    * allowed is 72 bytes and the maximum is restricted by the amount of
    * free RAM. All values will be rounded up to the next multiple of 9.
    *
    * @return ResponseAPDU containing the Commit Buffer size in little
    *         endian format.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU getCommitBufferSize ()
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x01, ( byte ) 0x0A);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // getCommitBufferSize()

   /**
    * Sets the Common PIN.
    * This method is used to change the value of the Common PIN on the Java iButton.
    * If the Java iButton currently has a PIN, the setPIN() method should be called
    * prior to this method with the 'oldPIN' value.
    *
    * @param newPIN the value of the PIN to be set in the Java iButton
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setCommonPIN (String newPIN)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [newPIN.length() + password.length() + 2];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) newPIN.length();

         System.arraycopy(newPIN.getBytes(), 0, data, password.length() + 2,
                          newPIN.length());
      }
      else
      {
         data     = new byte [2 + newPIN.length()];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) newPIN.length();

         System.arraycopy(newPIN.getBytes(), 0, data, 2, newPIN.length());
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x01, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setCommonPin()

   /**
    * Sets the Ephemeral Garbage Collection Mode.
    * A value of 1 turns the Ephemeral Garbage Collection on
    * and a value of 0 turns it off.
    *
    * @param  mode to turn on/off ephemeral garbage collector.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setEphemeralGCMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x02, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // set EphemeralGCMode()

   /**
    * Sets the Applet Garbage Collection Mode.
    * A value of 1 turns the Applet Garbage Collection on
    * and a value of 0 turns it off.
    *
    * @param  mode to turn on/off applet garbage collector.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setAppletGCMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x03, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setAppletGCMode()

   /**
    * Sets the Command PIN Mode.
    * If command PIN mode is set to 1, a PIN is required for all
    * Administrative commands.
    *
    * @param  mode to turn on/off command PIN requirement.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setCommandPINMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x04, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setCommandPINMode()

   /**
    * Sets the Load PIN Mode.
    * If the load PIN mode is set, then PINs are required on applet loading.
    *
    * @param  mode to turn on/off load PIN mode.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setLoadPINMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x05, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setLoadPINMode()

   /**
    * Sets the Restore Mode.
    * When Restore Mode is enabled, all field updates and
    * javacard.framework.System transactions are considered atomic. If a tear
    * occurs in the middle of these updates, values just prior to the update
    * are restored.
    *
    * @param  mode to turn on/off restore mode.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setRestoreMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x06, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setRestoreMode()

   /**
    * Sets the Exception Mode.
    * When Exception Mode is enabled, Java API exceptions are thrown. All
    * uncaught exceptions return 0x6F00 in the SW. When disabled, an error
    * is returned from the VM.
    *
    *
    * @param mode
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setExceptionMode (int mode)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [2 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) mode;
      }
      else
      {
         data     = new byte [2];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) mode;
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x07, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setExceptionMode()

   /**
    * Sets the Commit Buffer size.
    * Committing one field to the buffer requires 9 bytes. Therefore
    * the default size of 72 bytes allows 8 field updates. The minimum size
    * allowed is 72 bytes and the maximum is restricted by the amount of
    * free RAM. All values will be rounded up to the next multiple of 9.
    *
    * @param  size size of the desired commit buffer.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x6681. Invalid PIN.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU setCommitBufferSize (int size)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      byte[] data;

      if (password != null)
      {
         data     = new byte [3 + password.length()];
         data [0] = ( byte ) password.length();

         System.arraycopy(password.getBytes(), 0, data, 1, password.length());

         data [password.length() + 1] = ( byte ) (size & 0xFF);
         data [password.length() + 2] = ( byte ) (size >>> 8);
      }
      else
      {
         data     = new byte [3];
         data [0] = ( byte ) 0;
         data [1] = ( byte ) (size & 0xFF);
         data [2] = ( byte ) (size >>> 8);
      }

      capdu = new CommandAPDU(CLA, INS, ( byte ) 0x00, ( byte ) 0x0A, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // setCommitBufferSize()

   /**
    * Selects an applet by AID.
    * Lenght of AID should not exceed AID_SIZE.
    *
    * @param  aid the AID of the applet to be selected.
    *
    * @return ResponseAPDU indicating success or failure.
    *         <ul>
    *         <li>Success SW 0x9000.
    *         <li>Failure SW 0x8453. Applet not found.
    *         </ul>
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU select (String aid)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      if (aid.length() > AID_SIZE)
         throw new IllegalArgumentException("AID length should not exceed "
                                            + AID_SIZE);

      byte[] data = new byte [AID_SIZE];

      System.arraycopy(aid.getBytes(), 0, data, 0, aid.length());

      capdu = new CommandAPDU(( byte ) 0x00, ( byte ) 0xA4, ( byte ) 0x04,
                              ( byte ) 0x00, data);
      rapdu = sendAPDU(capdu, runTime);

      return rapdu;
   }   // select

   /**
    * Sends a generic process command to the JavaiButton.
    * This method can be used to send any CommandAPDU.
    *
    * @param capdu CommandAPDU to be sent to the Java iButton.
    *
    * @return ResponseAPDU the response from the Java iButton.
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU process (CommandAPDU capdu)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      this.capdu = capdu;
      rapdu      = sendAPDU(capdu, runTime);

      return rapdu;
   }

   /**
    * Sends a CommandAPDU to the Java iButton.
    *
    * @param  capdu the CommandAPDU to be sent to the Java iButton
    *
    * @param  runTime the run time value sent to the OWUS register
    *
    * @return ResponseAPDU the ResponseAPDU from the Java iButton.
    *
    * @throws OneWireException
    * @throws OneWireIOException
    * @throws IllegalArgumentException
    */
   public ResponseAPDU sendAPDU (CommandAPDU capdu, int runTime)
      throws OneWireException, OneWireIOException, IllegalArgumentException
   {
      int    capduLength = capdu.getLength();
      byte[] apdu        = new byte [capduLength + 3];

      // append extra bytes for legacy reasons
      apdu [0] = ( byte ) (2 + capduLength);
      apdu [1] = ( byte ) 0x89;
      apdu [2] = ( byte ) 0x00;

      System.arraycopy(capdu.getBytes(), 0, apdu, 3, capduLength);

      // Attempt to put adapter into OverDrive Mode.
      doSpeed();

      rapdu = new ResponseAPDU(jibComm.transferJibData(apdu, runTime));

      return rapdu;
   }
}
