/**
   * Modifies this SHA iButton so that it refers to another DS1963S container. This function only
   * copies the reference to the OneWireContainer, copes the reference to it's 1-Wire address, and
   * then asserts that the iButton contains a valid acccount info file associated with the system.
   *
   * @param owc The <code>OneWireContainer18</code> this object will refer to.
   * @return <code>true</code> if a valid account service file exists on this <code>
   *     OneWireContainer18</code>.
   * @throws OneWireIOException on a 1-Wire communication error such as reading an incorrect CRC
   *     from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network
   *     due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
   * @throws OneWireException on a communication or setup error with the 1-Wire adapter
   */
  public synchronized boolean setiButton18(OneWireContainer18 owc)
      throws OneWireException, OneWireIOException {
    // hold container reference
    this.ibc = owc;

    // and address
    this.address = owc.getAddress(); // getAddress() doesn't malloc!

    // clear account information
    this.accountPageNumber = -1;

    // make sure account info is properly setup
    if (!checkAccountPageInfo(owc)) return false;

    // setup the fullBindCode with rest of info
    this.fullBindCode[4] = (byte) this.accountPageNumber;
    System.arraycopy(this.address, 0, this.fullBindCode, 5, 7);

    // \\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
    if (DEBUG) {
      IOHelper.writeLine("------------------------------------");
      IOHelper.writeLine("Loaded User");
      IOHelper.writeLine("address");
      IOHelper.writeBytesHex(owc.getAddress());
      IOHelper.writeLine("accountPageNumber: " + accountPageNumber);
      IOHelper.writeLine("serviceFilename: " + strServiceFilename);
      IOHelper.writeLine("------------------------------------");
    }
    // \\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

    return true;
  }
  /**
   * Initialize a DS1963S as a fresh user iButton for a given SHA service. This constructor not only
   * creates the service file for the user iButton using the TMEX file structure, but it also
   * installs the master authentication secret and binds it to the iButton (making it unique for a
   * particular button). Optionally, the device can be formatted before the service file is
   * installed.
   *
   * @param coprBindData The Coprocessor Bind Data, used to create a unique secret for this user
   *     token.
   * @param coprBindCode The Coprocessor Bind Code without the user-specific information, used to
   *     create a unique secret for this user token.
   * @param fileName The file name for the account info.
   * @param fileNameExt The file extenstion for the account info
   * @param owc The DS1963S iButton that this object will refer to.
   * @param formatDevice If <code>true</code>, the TMEX filesystem will be formatted before the
   *     account service file is created.
   * @param authSecret The master authentication secret for the systm.
   * @throws OneWireIOException on a 1-Wire communication error such as reading an incorrect CRC
   *     from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network
   *     due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
   * @throws OneWireException on a communication or setup error with the 1-Wire adapter
   * @see #SHAiButtonUser18(SHAiButtonCopr,OneWireContainer18)
   * @see #SHAiButtonUser18(SHAiButtonCopr)
   */
  public SHAiButtonUser18(
      byte[] coprBindData,
      byte[] coprBindCode,
      byte[] fileName,
      int fileNameExt,
      OneWireContainer18 owc,
      boolean formatDevice,
      byte[] authSecret)
      throws OneWireException, OneWireIOException {

    // hold container reference
    this.ibc = owc;

    // and address
    this.address = owc.getAddress();

    System.arraycopy(fileName, 0, this.serviceFile, 0, 4);
    // create string representation of service filename
    this.strServiceFilename = new String(fileName) + "." + (int) fileNameExt;

    if (!createServiceFile(owc, strServiceFilename, formatDevice))
      throw new OneWireException("Failed to create service file.");

    // save a copy of the binding code
    System.arraycopy(coprBindCode, 0, this.fullBindCode, 0, 7);
    System.arraycopy(this.fullBindCode, 4, this.fullBindCode, 12, 3);

    // setup the fullBindCode with rest of info
    this.fullBindCode[4] = (byte) this.accountPageNumber;
    System.arraycopy(this.address, 0, this.fullBindCode, 5, 7);

    if (!owc.installMasterSecret(accountPageNumber, authSecret, accountPageNumber & 7))
      throw new OneWireException("Install Master Secret failed");

    // not in critical path, so getBindBlah() is okay.
    if (!owc.bindSecretToiButton(
        accountPageNumber, coprBindData, this.fullBindCode, accountPageNumber & 7))
      throw new OneWireException("Bind Secret to iButton failed");

    // \\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
    if (DEBUG) {
      IOHelper.writeLine("------------------------------------");
      IOHelper.writeLine("Initialized User");
      IOHelper.writeLine("address");
      IOHelper.writeBytesHex(owc.getAddress());
      IOHelper.writeLine("serviceFilename: " + strServiceFilename);
      IOHelper.writeLine("accountPageNumber: " + accountPageNumber);
      IOHelper.writeLine("authSecret");
      IOHelper.writeBytesHex(authSecret);
      IOHelper.writeLine("bindCode");
      IOHelper.writeBytesHex(coprBindCode);
      IOHelper.writeLine("------------------------------------");
    }
    // \\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
  }
  /**
   * Writes the account data to the SHAiButton. First, this function asserts that the account page
   * number is known. The account data is copied from dataBuffer starting at the offset. If there
   * are less than 32 bytes available to copy, this function only copies the bytes that are
   * available.
   *
   * @param dataBuffer the buffer to copy the account data from
   * @param offset the index into the buffer where copying should begin
   * @return whether or not the data write succeeded
   * @throws OneWireIOException on a 1-Wire communication error such as reading an incorrect CRC
   *     from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network
   *     due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
   * @throws OneWireException on a communication or setup error with the 1-Wire adapter
   */
  public synchronized boolean writeAccountData(byte[] dataBuffer, int offset)
      throws OneWireException, OneWireIOException {
    // init local vars
    OneWireContainer18 ibcL = this.ibc;

    // make sure account info is properly setup
    if (!checkAccountPageInfo(ibcL)) return false;

    int numBytes = Math.min(32, dataBuffer.length - offset);
    System.arraycopy(dataBuffer, offset, this.accountData, 0, numBytes);
    if (ibcL.writeDataPage(this.accountPageNumber, this.accountData)) {
      if (this.writeCycleCounter >= 0) this.writeCycleCounter++;
      return true;
    }

    // if write failed, we don't know what the write cycle counter is
    this.writeCycleCounter = -1;
    // and this cache should be marked dirty
    this.accountData[0] = 0;
    return false;
  }
  /**
   * Reads the account data off the SHAiButton using a standard READ command. First, this function
   * asserts that the account page number is known as well as the length of the account file. The 32
   * byte account data page is copied into dataBuffer starting at the given offset.
   *
   * @param dataBuffer the buffer to copy the account data into
   * @param offset the index into the buffer where copying should begin
   * @return whether or not the read was successful
   * @throws OneWireIOException on a 1-Wire communication error such as reading an incorrect CRC
   *     from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network
   *     due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
   * @throws OneWireException on a communication or setup error with the 1-Wire adapter
   */
  public synchronized boolean readAccountData(byte[] dataBuffer, int offset)
      throws OneWireException, OneWireIOException {
    // init local vars
    OneWireContainer18 ibcL = this.ibc;

    // make sure account info is properly setup
    if (!checkAccountPageInfo(ibcL)) {
      return false;
    }

    // if the cache is empty
    if (this.accountData[0] == 0) {
      // read directly into local cache
      ibcL.readMemoryPage(this.accountPageNumber, this.accountData, 0);
    }

    // copy cached data into user's buffer
    System.arraycopy(this.accountData, 0, dataBuffer, offset, 32);

    // had to work, right?
    return true;
  }
  /**
   * Reads the account data off the SHAiButton using a READ_AUTHENTICATE command. First, this
   * function asserts that the account page number is known as well as the length of the account
   * file. Then it copies the 3 byte challenge to the scratchpad before sending the command for
   * READ_AUTHENTICATE. The 32 byte account data page is copied into dataBuffer starting at
   * dataStart.
   *
   * <p>In addition to the account data, this function also returns a calculated MAC. The MAC
   * requires 20 bytes after the start index. The return value is the write cycle counter value for
   * the account data page
   *
   * <p>
   *
   * @param chlg the buffer containing a 3-byte random challenge.
   * @param chlgStart the index into the buffer where the 3 byte challenge begins.
   * @param dataBuffer the buffer to copy the account data into
   * @param dataStart the index into the buffer where copying should begin
   * @param mac the buffer to copy the resulting Message Authentication Code
   * @param macStart the index into the mac buffer to start copying
   * @return the value of the write cycle counter for the page
   * @throws OneWireIOException on a 1-Wire communication error such as reading an incorrect CRC
   *     from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network
   *     due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
   * @throws OneWireException on a communication or setup error with the 1-Wire adapter
   */
  public synchronized int readAccountData(
      byte[] chlg, int chlgStart, byte[] dataBuffer, int dataStart, byte[] mac, int macStart)
      throws OneWireException, OneWireIOException {
    // init local variables
    OneWireContainer18 ibcL = this.ibc;
    byte[] rawData = this.readAccountData_rawData;
    byte[] scratchpad = this.readAccountData_scratchpad;

    // make sure account info is properly setup
    if (this.accountPageNumber < 0) {
      // user not setup
      return -1;
    }

    // copy challenge into scratchpad buffer
    System.arraycopy(chlg, 0, scratchpad, 20, 3);

    if (ibcL.eraseScratchPad(this.accountPageNumber)) {
      // send 1 byte RESUME, instead of 9 byte SELECT
      ibcL.useResume(true);

      // write the challenge to the device scratchpad
      if (ibcL.writeScratchPad(this.accountPageNumber, 0, scratchpad, 0, 32)) {

        // reads 42 bytes = 32 bytes of page data
        //               +  4 bytes page counter
        //               +  4 bytes secret counter
        //               +  2 bytes CRC
        boolean readOK = ibcL.readAuthenticatedPage(this.accountPageNumber, rawData, 0);

        // read the scratchpad for mac
        int len = ibcL.readScratchPad(scratchpad, 0);

        // disable RESUME
        ibcL.useResume(false);

        if ((!readOK) || (len < 0)) {
          // read authenticate failed
          return -1;
        }

        // get the value of the write cycle counter
        int wcc = (rawData[35] & 0x0ff);
        wcc = (wcc << 8) | (rawData[34] & 0x0ff);
        wcc = (wcc << 8) | (rawData[33] & 0x0ff);
        wcc = (wcc << 8) | (rawData[32] & 0x0ff);

        // put the accountData in our local cache
        System.arraycopy(rawData, 0, this.accountData, 0, 32);

        // put the account data into return buffer
        System.arraycopy(rawData, 0, dataBuffer, dataStart, 32);

        // copy the mac into the return buffer
        System.arraycopy(scratchpad, 8, mac, macStart, 20);

        // \\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
        if (DEBUG) {
          IOHelper.writeLine("----------------------------------------------------------");
          IOHelper.writeLine("User's ReadAuthPage");
          IOHelper.write("address: ");
          IOHelper.writeBytesHex(this.address);
          IOHelper.writeLine("speed: " + this.ibc.getAdapter().getSpeed());
          IOHelper.writeLine("RawData: ");
          IOHelper.writeBytesHex(rawData);
          IOHelper.writeLine("mac: ");
          IOHelper.writeBytesHex(mac, macStart, 20);
          IOHelper.writeLine("wcc: " + wcc);
          IOHelper.writeLine("----------------------------------------------------------");
        }
        // \\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

        // cache the write cycle counter
        this.writeCycleCounter = wcc;

        return wcc;
      }
      // write scratchpad failed
      return -1;
    }
    // erase scratchpad failed
    return -1;
  }