//---------------------------------------------------------------------------
// Copyright (C) 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.
//---------------------------------------------------------------------------
//
// shadebitvm.c - Transaction-level functions for SHA Debits without a
//                hardware coprocessor.  Also, this file contains some
//                Protocol-level functions to replace their 'hardware-only'
//                counterparts..
//
// Version: 2.10

#include "ownet.h"
#include "shaib.h"

#define MAX_RETRY_CNT 255

// static functions
static int GetSecretVM(char *name, uchar **secret);

// static global vars
static uchar sign_secret[8];
static uchar auth_secret[8];
static uchar wspc_secret[8];

//-------------------------------------------------------------------------
// Returns the user's current balance as an int
//
// 'user'      - Structure for holding user token information.
//
// Return: Current value of the user's balance.
//
int GetBalance(SHAUser *user) {
  int balance = -1;

  if (user->devAN[0] == 0x18)
    balance = BytesToInt(((DebitFile *)user->accountFile)->balanceBytes, 3);
  else if ((user->devAN[0] & 0x7F) == 0x33) {
    DebitFile33 *acctFile = (DebitFile33 *)user->accountFile;
    if (acctFile->fileLength == RECORD_A_LENGTH)
      balance = BytesToInt(acctFile->balanceBytes_A, 3);
    else if (acctFile->fileLength == RECORD_B_LENGTH)
      balance = BytesToInt(acctFile->balanceBytes_B, 3);
    else
      OWERROR(OWERROR_BAD_SERVICE_DATA);
  } else
    OWERROR(OWERROR_BAD_SERVICE_DATA);

  return balance;
}

//-------------------------------------------------------------------------
// Installs new service data on a user token.
//
// 'copr'      - Structure for holding coprocessor information.
// 'user'      - Structure for holding user token information.
// 'secret'    - the authentication secret to install on user token.
//
// Return: If TRUE, new service installation succeeded.
//         If FALSE, an error occurred.
//
SMALLINT InstallServiceData(SHACopr *copr, SHAUser *user, uchar *secret, int secret_length, int initialBalance) {
  short handle;
  int maxwrite;
  FileEntry fe;
  uchar fullBindCode[15];

  // make sure user has a file directory structure
  memcpy(fe.Name, copr->serviceFilename, 4);
  fe.Ext = copr->serviceFilename[4];

  // install master authentication secret
  if (user->devAN[0] == 0x18) {
    // need to format the device
    if (!owFormat(user->portnum, user->devAN))
      return FALSE;

    // and create an empty stub for his account information
    if (!owCreateFile(user->portnum, user->devAN, &maxwrite, &handle, &fe))
      return FALSE;

    // need to know what page the stub is on
    user->accountPageNumber = fe.Spage;

    // set the serial number to that of the user
    owSerialNum(user->portnum, user->devAN, FALSE);
    OWASSERT(InstallSystemSecret18(user->portnum, user->accountPageNumber, user->accountPageNumber & 7, secret, secret_length,
                                   FALSE),
             OWERROR_INSTALL_SECRET_FAILED, FALSE);
  } else if ((user->devAN[0] & 0x7F) == 0x33) {
    // set the serial number to that of the user
    owSerialNum(user->portnum, user->devAN, FALSE);
    // because of copy-authorization, we need to install the
    // secret first on the DS1961S and _then_ format the system
    OWASSERT(InstallSystemSecret33(user->portnum, 0, 0, secret, secret_length, FALSE), OWERROR_INSTALL_SECRET_FAILED, FALSE);

    // need to format the device
    if (!owFormat(user->portnum, user->devAN))
      return FALSE;

    // and create an empty stub for his account information
    if (!owCreateFile(user->portnum, user->devAN, &maxwrite, &handle, &fe))
      return FALSE;

    // need to know what page the stub is on
    user->accountPageNumber = fe.Spage;
  } else {
    return FALSE;
  }

  // format the bind code properly
  // first four bytes of bind code
  memcpy(fullBindCode, copr->bindCode, 4);
  // followed by the pagenumber
  fullBindCode[4] = (uchar)user->accountPageNumber;
  // and 7 bytes of the address of current device
  memcpy(&fullBindCode[5], user->devAN, 7);
  // followed by the last 3 bytes of bind code
  memcpy(&fullBindCode[12], &(copr->bindCode[4]), 3);

  // create a unique secret for iButton
  if (user->devAN[0] == 0x18) {
    DebitFile *accountFile = (DebitFile *)user->accountFile;

    OWASSERT(BindSecretToiButton18(user->portnum, user->accountPageNumber, user->accountPageNumber & 7, copr->bindData,
                                   fullBindCode, TRUE),
             OWERROR_BIND_SECRET_FAILED, FALSE);

    // do a read just to get value of writecycle counter
    user->writeCycleCounter = ReadAuthPageSHA18(user->portnum, user->accountPageNumber, user->accountFile, NULL, TRUE);

    // setup user account file with initial balance
    IntToBytes(accountFile->balanceBytes, 3, initialBalance);

    // set transaction ID
    accountFile->transID[0] = 0;
    accountFile->transID[0] = 0;

  } else if ((user->devAN[0] & 0x7F) == 0x33) {
    DebitFile33 *accountFile33 = (DebitFile33 *)user->accountFile;

    OWASSERT(BindSecretToiButton33(user->portnum, user->accountPageNumber, 0, copr->bindData, fullBindCode, TRUE),
             OWERROR_BIND_SECRET_FAILED, FALSE);

    // Call VerifyUser just to get the user's secret in wspc
    if (!VerifyUser(copr, user, TRUE))
      return FALSE;

    // Record A
    // setup user account file with initial balance
    IntToBytes(accountFile33->balanceBytes_A, 3, initialBalance);

    // set transaction ID
    accountFile33->transID_A[0] = 0;
    accountFile33->transID_A[0] = 0;

    // Record B
    // setup user account file with initial balance
    IntToBytes(accountFile33->balanceBytes_B, 3, initialBalance);

    // set transaction ID
    accountFile33->transID_B[0] = 0;
    accountFile33->transID_B[0] = 0;
  }

  // sign the data with coprocessor and write it out
  return UpdateServiceData(copr, user);
}

//-------------------------------------------------------------------------
// Updates service data on a user token.  This includes signing the
// data if the part is a DS1963S.
//
// 'copr'      - Structure for holding coprocessor information.
// 'user'      - Structure for holding user token information.
//
// Return: If TRUE, update succeeded.
//         If FALSE, an error occurred.
//
SMALLINT UpdateServiceData(SHACopr *copr, SHAUser *user) {
  ushort crc16, i;
  uchar scratchpad[32];

  if (user->devAN[0] == 0x18) {
    DebitFile *accountFile = (DebitFile *)user->accountFile;

    // make sure length is right.
    accountFile->fileLength = 29;

    // update transaction ID
    // doesn't matter what it is, just needs to change
    accountFile->transID[0] += 1;
    if (accountFile->transID[0] == 0)
      accountFile->transID[1] += 1;

    // conversion factor - 2 data bytes
    accountFile->convFactor[0] = (uchar)0x8B;
    accountFile->convFactor[1] = (uchar)0x48;

    // clear out the old signature and CRC
    memcpy(accountFile->signature, copr->initSignature, 20);
    memset(accountFile->crc16, 0x00, 2);

    // reset data type code
    accountFile->dataTypeCode = 1;

    // file doesn't continue on another page
    accountFile->contPtr = 0;

    // ---  Set up the scratchpad for signing
    memset(scratchpad, 0x00, 32);
    // the write cycle counter +1 (since we are about to write this to it)
    if (user->writeCycleCounter > 0)
      IntToBytes(&scratchpad[8], 4, user->writeCycleCounter + 1);
    else
      // user doesn't have write cycle counter (DS1961S)
      memset(&scratchpad[8], 0x0FF, 4);
    // the pagenumber
    scratchpad[12] = (uchar)user->accountPageNumber;
    // and 7 bytes of the address of current device
    memcpy(&scratchpad[13], user->devAN, 7);
    // the coprocessor's signing challenge
    memcpy(&scratchpad[20], copr->signChlg, 3);

    OWASSERT(CreateDataSignatureVM(copr, sign_secret, user->accountFile, scratchpad, accountFile->signature, TRUE),
             OWERROR_SIGN_SERVICE_DATA_FAILED, FALSE);

    // add the crc at the end of the data.
    setcrc16(user->portnum, user->accountPageNumber);
    for (i = 0; i < 30; i++)
      crc16 = docrc16(user->portnum, user->accountFile[i]);
    crc16 = ~crc16;
    accountFile->crc16[0] = (uchar)crc16;
    accountFile->crc16[1] = (uchar)(crc16 >> 8);
  } else if ((user->devAN[0] & 0x7F) == 0x33) {
    DebitFile33 *accountFile33 = (DebitFile33 *)user->accountFile;

    // conversion factor - 2 data bytes
    accountFile33->convFactor[0] = (uchar)0x8B;
    accountFile33->convFactor[1] = (uchar)0x48;

    // reset data type code
    accountFile33->dataTypeCode = 1;

    if (accountFile33->fileLength == RECORD_A_LENGTH) {
      // Switch to Record B
      accountFile33->fileLength = RECORD_B_LENGTH;

      // update transaction ID
      // doesn't matter what it is, just needs to change
      accountFile33->transID_B[0] += 1;
      if (accountFile33->transID_B[0] == 0)
        accountFile33->transID_B[1] += 1;

      // file doesn't continue on another page
      accountFile33->contPtr_B = 0;

      // clear out the old CRC
      memset(accountFile33->crc16_B, 0x00, 2);

      // add the crc at the end of the data.
      setcrc16(user->portnum, user->accountPageNumber);
      for (i = 0; i < RECORD_B_LENGTH + 1; i++)
        crc16 = docrc16(user->portnum, user->accountFile[i]);
      crc16 = ~crc16;
      accountFile33->crc16_B[0] = (uchar)crc16;
      accountFile33->crc16_B[1] = (uchar)(crc16 >> 8);
    } else {
      // Switch to Record B
      accountFile33->fileLength = RECORD_A_LENGTH;

      // update transaction ID
      // doesn't matter what it is, just needs to change
      accountFile33->transID_A[0] += 1;
      if (accountFile33->transID_A[0] == 0)
        accountFile33->transID_A[1] += 1;

      // file doesn't continue on another page
      accountFile33->contPtr_A = 0;

      // clear out the old CRC
      memset(accountFile33->crc16_A, 0x00, 2);

      // add the crc at the end of the data.
      setcrc16(user->portnum, user->accountPageNumber);
      for (i = 0; i < RECORD_A_LENGTH + 1; i++)
        crc16 = docrc16(user->portnum, user->accountFile[i]);
      crc16 = ~crc16;
      accountFile33->crc16_A[0] = (uchar)crc16;
      accountFile33->crc16_A[1] = (uchar)(crc16 >> 8);
    }
  }

  // set the serial number to that of the user
  owSerialNum(user->portnum, user->devAN, FALSE);

  if (user->devAN[0] == 0x18) {
    // DS1963S - not too tough
    OWASSERT(WriteDataPageSHA18(user->portnum, user->accountPageNumber, user->accountFile, FALSE),
             OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);
  } else if ((user->devAN[0] & 0x7F) == 0x33) {
    OWASSERT(WriteDataPageSHA33(copr, user), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);
  } else
    return FALSE;

  return TRUE;
}

//-------------------------------------------------------------------------
// Verifies a user token as a valid member of the system.  First, a random
// challenge is generated.  Then, the user must answer that challenge.
// The user's response signature is then verified against the proper
// response..
//
// 'copr'      - Structure for holding coprocessor information.
// 'user'      - Structure for holding user token information.
// 'doBind'    - if true, the user's unique secret is recreated on the
//               coprocessor.  If this function is called multiple times,
//               it is acceptable to skip the bind for all calls after
//               the first on the same user token.
//
// Return: If TRUE, user was verified.
//         If FALSE, an error occurred or user verification failed.
//
SMALLINT VerifyUser(SHACopr *copr, SHAUser *user, SMALLINT doBind) {
  uchar chlg[3];
  int randomNum, i;

  // Create Challenge
  randomNum = rand();
  for (i = 0; i < 3; i++) {
    chlg[i] = (uchar)randomNum;
    randomNum >>= 8;
  }

  OWASSERT(AnswerChallenge(user, chlg) >= 0, OWERROR_ANSWER_CHALLENGE_FAILED, FALSE);

  if (!VerifyAuthResponseVM(copr, user, chlg, doBind)) {
    OWERROR(OWERROR_VERIFY_AUTH_RESPONSE_FAILED);
    if ((user->devAN[0] & 0x7F) == 0x33)
      RefreshPage33(user->portnum, user->accountPageNumber, FALSE);
    return FALSE;
  }

  return TRUE;
}

//-------------------------------------------------------------------------
// Verifies service data on a user token as a valid member of the system.
// Pre-condition: must call verify user first.
//
// 'copr'      - Structure for holding coprocessor information.
// 'user'      - Structure for holding user token information.
//
// Return: If TRUE, data was verified.
//         If FALSE, an error occurred or data verification failed.
//
SMALLINT VerifyData(SHACopr *copr, SHAUser *user) {
  uchar scratchpad[32];
  DebitFile acctFile;
  ushort lastcrc16 = 0;
  uchar i;

  // Check the CRC of the file
  setcrc16(user->portnum, user->accountPageNumber);
  for (i = 0; i < user->accountFile[0] + 3; i++)
    lastcrc16 = docrc16(user->portnum, user->accountFile[i]);

  if ((user->devAN[0] & 0x7F) == 0x33) {
    // save the original file length
    uchar origPtr = user->accountFile[0];
    SMALLINT validA = FALSE, validB = FALSE, validPtr = FALSE;

    // is the file length a valid value
    if (origPtr == RECORD_A_LENGTH || origPtr == RECORD_B_LENGTH)
      validPtr = TRUE;

    // was the crc of the file correct?
    if (lastcrc16 == 0xB001) {
      // if the header points to a valid record, we're done
      if (validPtr) {
        // nothing more to check for DS1961S/DS2432, since
        // it carries no signed data, we're finished
        return TRUE;
      }

      // header points to neither record A nor B, but
      // crc is absolutely correct.  that can only mean
      // we're looking at something that is the wrong
      // size from what was expected, but apparently is
      // exactly what was meant to be written.  I'm done.
      OWERROR(OWERROR_BAD_SERVICE_DATA);
      return FALSE;
    }

    // lets try Record A and check the crc
    user->accountFile[0] = RECORD_A_LENGTH;
    setcrc16(user->portnum, user->accountPageNumber);
    for (i = 0; i < user->accountFile[0] + 3; i++)
      lastcrc16 = docrc16(user->portnum, user->accountFile[i]);
    if (lastcrc16 == 0xB001)
      validA = TRUE;

    // lets try Record B and check the crc
    user->accountFile[0] = RECORD_B_LENGTH;
    setcrc16(user->portnum, user->accountPageNumber);
    for (i = 0; i < user->accountFile[0] + 3; i++)
      lastcrc16 = docrc16(user->portnum, user->accountFile[i]);
    if (lastcrc16 == 0xB001)
      validB = TRUE;

    if (validA && validB) {
      // Both A & B are valid!  And we know that we can only
      // get here if the pointer or the header was not valid.
      // That means that B was the last updated one but the
      // header got hosed and the debit was not finished...
      // which means A is the last known good value, let's go with A.
      user->accountFile[0] = RECORD_A_LENGTH;
      OWASSERT(WriteDataPageSHA33(copr, user), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);
      return TRUE;
    } else if (validA) {
      // B is invalid, A is valid.  Means A is the last updated one,
      // but B is the last known good value.  The header was not updated
      // to point to A before debit was aborted.  Let's go with B
      user->accountFile[0] = RECORD_B_LENGTH;
      // must fix B's CRC
      setcrc16(user->portnum, user->accountPageNumber);
      for (i = 0; i < RECORD_B_LENGTH + 1; i++)
        lastcrc16 = docrc16(user->portnum, user->accountFile[i]);
      lastcrc16 = ~lastcrc16;
      ((DebitFile33 *)user->accountFile)->crc16_B[0] = (uchar)lastcrc16;
      ((DebitFile33 *)user->accountFile)->crc16_B[1] = (uchar)(lastcrc16 >> 8);
      OWASSERT(WriteDataPageSHA33(copr, user), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);
      return TRUE;
    } else if (validB) {
      // A is invalid, B is valid. Should never ever happen.  Something
      // got completely hosed.  What should happen here?
      user->accountFile[0] = origPtr;
      OWERROR(OWERROR_BAD_SERVICE_DATA);
      return FALSE;
    }

    // neither record contains a valid CRC.  What should happen here?
    user->accountFile[0] = origPtr;
    OWERROR(OWERROR_CRC_FAILED);
    return FALSE;
  } else {
    // verify CRC16 is correct
    OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, FALSE);
  }

  memset(scratchpad, 0x00, 32);

  if (user->writeCycleCounter > 0)
    IntToBytes(&scratchpad[8], 4, user->writeCycleCounter);
  else
    // user doesn't have write cycle counter (DS1961S)
    memset(&scratchpad[8], 0x0FF, 4);
  // the pagenumber
  scratchpad[12] = (uchar)user->accountPageNumber;
  // and 7 bytes of the address of current device
  memcpy(&scratchpad[13], user->devAN, 7);
  // the coprocessor's signing challenge
  memcpy(&scratchpad[20], copr->signChlg, 3);

  // make a copy of the account file
  memcpy((uchar *)&acctFile, user->accountFile, 32);

  // clear out the old signature and CRC
  memcpy(acctFile.signature, copr->initSignature, 20);
  memset(acctFile.crc16, 0x00, 2);

  OWASSERT(CreateDataSignatureVM(copr, sign_secret, (uchar *)&acctFile, scratchpad, ((DebitFile *)user->accountFile)->signature,
                                 FALSE),
           OWERROR_MATCH_SERVICE_SIGNATURE_FAILED, FALSE);
  return TRUE;
}

//-------------------------------------------------------------------------
// Performs debit of service data on a user token, re-signs the data, and
// writes it back to the user token.
// Pre-condition: must call verify user first.
//
// 'copr'          - Structure for holding coprocessor information.
// 'user'          - Structure for holding user token information.
// 'debitAmount'   - the amount of money to debit from balance in cents.
// 'verifySuccess' - Paranoid double-check of account write by re-calling
//                   VerifyUser(copr,user)
//
// Return: If TRUE, data was updated.
//         If FALSE, an error occurred or couldn't verify success.
//
SMALLINT ExecuteTransaction(SHACopr *copr, SHAUser *user, int debitAmount, SMALLINT verifySuccess) {
  SMALLINT success, dataOK = TRUE;
  uchar oldAcctData[32], newAcctData[32];
  int cnt = MAX_RETRY_CNT;
  int balance, oldBalance;

  memcpy(oldAcctData, user->accountFile, 32);

  if (user->devAN[0] == 0x18) {
    DebitFile *accountFile = (DebitFile *)user->accountFile;

    oldBalance = BytesToInt(accountFile->balanceBytes, 3);
    if (oldBalance <= 0) {
      OWERROR(OWERROR_BAD_SERVICE_DATA);
      return FALSE;
    }
    balance = oldBalance - debitAmount;
    IntToBytes(accountFile->balanceBytes, 3, balance);
  } else if ((user->devAN[0] & 0x7F) == 0x33) {
    DebitFile33 *accountFile = (DebitFile33 *)user->accountFile;

    if (accountFile->fileLength == RECORD_A_LENGTH) {
      oldBalance = BytesToInt(accountFile->balanceBytes_A, 3);
      if (oldBalance <= 0) {
        OWERROR(OWERROR_BAD_SERVICE_DATA);
        return FALSE;
      }
      balance = oldBalance - debitAmount;
      IntToBytes(accountFile->balanceBytes_B, 3, balance);
    } else if (accountFile->fileLength == RECORD_B_LENGTH) {
      oldBalance = BytesToInt(accountFile->balanceBytes_B, 3);
      if (oldBalance <= 0) {
        OWERROR(OWERROR_BAD_SERVICE_DATA);
        return FALSE;
      }
      balance = oldBalance - debitAmount;
      IntToBytes(accountFile->balanceBytes_A, 3, balance);
    } else {
      OWERROR(OWERROR_BAD_SERVICE_DATA);
      return FALSE;
    }
  }

  success = UpdateServiceData(copr, user);

  // if write didn't succeeded or if we need to perform
  // a verification step anyways, let's double-check what
  // the user has on the button.
  if (verifySuccess || !success) {
    dataOK = FALSE;
    // save what the account data is supposed to look like
    memcpy(newAcctData, user->accountFile, 32);
    do {
      // calling verify user re-issues a challenge-response
      // and reloads the cached account data in the user object.
      // Does not re-bind the user's unique secret
      if (VerifyUser(copr, user, FALSE)) {
        // compare the user's account data against the working
        // copy and the backup copy.
        if (memcmp(user->accountFile, newAcctData, 32) == 0) {
          // looks like it worked
          dataOK = TRUE;
        } else if (memcmp(user->accountFile, oldAcctData, 32) == 0) {
          // if it matches the backup copy, we didn't write anything
          // and the data is still okay, but we didn't do a debit
          dataOK = TRUE;
          success = FALSE;
          OWERROR(OWERROR_SERVICE_DATA_NOT_UPDATED);
        } else {
          // retry the write
          success = UpdateServiceData(copr, user);
          // save what the account data is supposed to look like
          memcpy(newAcctData, user->accountFile, 32);
        }
      }
    } while (dataOK == FALSE && cnt-- > 0);
  }

  if (!dataOK) {
    // couldn't fix the data after 255 retries
    OWERROR(OWERROR_CATASTROPHIC_SERVICE_FAILURE);
    success = FALSE;
  }
  return success;
}

//-------------------------------------------------------------------------
// Verifies the authentication response of a user token without a
// hardware coprocessor.
//
// 'copr'      - Structure for holding coprocessor information.
// 'user'      - Structure for holding user token information.
// 'chlg'      - 3-byte buffer of challenge data.
// 'doBind'    - if true, the user's unique secret is recreated on the
//               coprocessor.  If this function is called multiple times,
//               it is acceptable to skip the bind for all calls after
//               the first on the same user token.
//
// Return: If TRUE, the user's authentication response matched exactly the
//            signature generated by the coprocessor.
//         If FALSE, an error occurred or the signature did not match.
//
SMALLINT VerifyAuthResponseVM(SHACopr *copr, SHAUser *user, uchar *chlg, SMALLINT doBind) {
  int wcc = user->writeCycleCounter;
  uchar scratchpad[32];
  uchar fullBindCode[15];
  uchar temp_buf[32];
  // M-X control byte
  uchar MXP = 0x00;

  memset(scratchpad, 0x00, 32);
  memset(fullBindCode, 0x0FF, 15);

  // format the bind code properly
  if (user->devAN[0] == 0x18) {
    // Format for DS1963S
    memcpy(fullBindCode, copr->bindCode, 4);
    memcpy(&fullBindCode[12], &(copr->bindCode[4]), 3);
    // copy wcc LSB first
    IntToBytes(&scratchpad[8], 4, wcc);
  } else if ((user->devAN[0] & 0x7F) == 0x33) {
    // Leave bindCode FF for DS1961S
    // Use AuthenticateHost command
    MXP = 0x40;
    // the user doesn't have a write cycle counter
    memset(&scratchpad[8], 0x0FF, 4);
  } else {
    OWERROR(OWERROR_WRONG_TYPE);
    return FALSE;
  }

  // the pagenumber
  fullBindCode[4] = (uchar)user->accountPageNumber;
  // and 7 bytes of the address of current device
  memcpy(&fullBindCode[5], user->devAN, 7);

  // get the user address and page num from fullBindCode
  memcpy(&scratchpad[12], &fullBindCode[4], 8);
  // set the same challenge bytes
  memcpy(&scratchpad[20], chlg, 3);

  // Fix the M-X control bits
  scratchpad[12] = (uchar)((scratchpad[12] & 0x3F) | (MXP & 0xC0));

  memset(temp_buf, 0x00, 32);
  memcpy(&temp_buf[8], fullBindCode, 15);

  // install user's unique secret on the wspc secret
  // Just like BindSecretToiButton for standard coprocessor
  if (doBind) {
    OWASSERT(CreateDataSignatureVM(copr, auth_secret, copr->bindData, temp_buf, temp_buf, TRUE), OWERROR_BIND_SECRET_FAILED,
             FALSE);
    memcpy(wspc_secret, temp_buf, 8);
  }

  // recreate the signature and verify
  OWASSERT(CreateDataSignatureVM(copr, wspc_secret, user->accountFile, scratchpad, user->responseMAC, FALSE),
           OWERROR_VERIFY_SIG_FAILED, FALSE);

  return TRUE;
}

//-------------------------------------------------------------------------
// Creates a data signature for the given data buffer without a hardware
// coprocessor.
//
// 'copr'          - Structure for holding coprocessor information.
// 'data'          - data written to the data page to sign
// 'scratchpad'    - data written to the scratchpad to sign
// 'signature'     - data buffer which is either holding the signature that
//                   must match exactly what is generated on the coprocessor
//                   -or- will hold the resulting signature created by the
//                   coprocessor.
// 'readSignature' - implies whether or not the signature buffer
//                   receives the contents of the scratchpad or is used to
//                   match the contents of the scratchpad.  If true,
//                   scratchpad contents are read into the signature buffer.
//
// Return: If TRUE, the user's authentication response matched exactly the
//            signature generated by the coprocessor or the signature was
//            successfully copied into the return buffer.
//         If FALSE, an error occurred or the signature did not match.
//
SMALLINT CreateDataSignatureVM(SHACopr *copr, uchar *secret, uchar *data, uchar *scratchpad, uchar *signature,
                               SMALLINT readSignature) {
  SMALLINT i, j, offset;
  int hash[5], temp;
  uchar digestBuff[64];

  // Set up the 64 byte buffer for computing the digest.
  memcpy(digestBuff, secret, 4);
  memcpy(&digestBuff[4], data, 32);
  memcpy(&digestBuff[36], &scratchpad[8], 12);
  memcpy(&digestBuff[48], &secret[4], 4);
  memcpy(&digestBuff[52], &scratchpad[20], 3);

  // digest buffer padding
  digestBuff[55] = (uchar)0x80;
  memset(&digestBuff[56], 0x00, 6);
  digestBuff[62] = (uchar)0x01;
  digestBuff[63] = (uchar)0xB8;

  ComputeSHAVM(digestBuff, hash);

  // iButtons use LSB first, so we have to turn
  // the result around a little bit.  Instead of
  // result A-B-C-D-E, our result is E-D-C-B-A,
  // where each letter represents four bytes of
  // the result.
  for (j = 4; j >= 0; j--) {
    temp = hash[j];
    offset = (4 - j) * 4;
    for (i = 0; i < 4; i++) {
      if (readSignature) {
        // reading the signature into a buffer
        signature[i + offset] = (uchar)temp;
      } else {
        // trying to match the signature;
        OWASSERT(signature[i + offset] == (uchar)temp, OWERROR_MATCH_SCRATCHPAD_FAILED, FALSE);
      }
      temp >>= 8;
    }
  }

  // PrintHexLabeled("signature", signature, 20);
  return TRUE;
}

//----------------------------------------------------------------------
// Installs new system secret for VM.  input_secret must be
// divisible by 47.  Then, each block of 47 is split up with 32 bytes
// written to a data page and 15 bytes are written to the scratchpad.
// Then, Compute first secret is called (or compute next if the secret
// is divisible by 47 more than once).
//
// 'copr'          - Structure for holding coprocessor information.
// 'input_secret'  - the input secret buffer used.
// 'secret_length' - the length of the input secret buffer, divisibly
//                   by 47.
// 'dst_secret'    - the buffer where resulting secret is copied.
//
// Return: TRUE - Install successfull
//         FALSE - error occurred during secret installation.
//
SMALLINT InstallSystemSecretVM(SHACopr *copr, uchar *secret, int secret_length, uchar *dst_secret) {
  int offset = 0, bytes_left = 0;
  uchar data[32], scratchpad[32];

  // PrintHexLabeled("Installing secret",secret,secret_length);
  for (offset = 0; offset < secret_length; offset += 47) {
    // clear the buffer
    memset(data, 0x0FF, 32);
    memset(scratchpad, 0x0FF, 32);

    // Determine the amount of bytes remaining to be installed.
    bytes_left = secret_length - offset;

    // copy secret data into page buffer and scratchpad buffer
    memcpy(data, &secret[offset], (bytes_left < 32 ? bytes_left : 32));
    if (bytes_left > 32) {
      memcpy(&scratchpad[8], &secret[offset + 32], (bytes_left < 47 ? bytes_left - 32 : 15));
    }

    // fix the M-X control bits
    scratchpad[12] = scratchpad[12] & 0x3F;

    // PrintHexLabeled("  using secret",dst_secret,8);
    // PrintHexLabeled("  using data",data,32);
    // PrintHexLabeled("  using spad",scratchpad,32);

    // install user's unique secret on the wspc secret
    // Just like BindSecretToiButton for standard coprocessor
    OWASSERT(CreateDataSignatureVM(copr, dst_secret, data, scratchpad, scratchpad, TRUE), OWERROR_BIND_SECRET_FAILED, FALSE);
    memcpy(dst_secret, scratchpad, 8);
  }

  return TRUE;
}

//----------------------------------------------------------------------
// Installs new system signing secret for VM.  input_secret must be
// divisible by 47.  Then, each block of 47 is split up with 32 bytes
// written to a data page and 15 bytes are written to the scratchpad.
// Then, Compute first secret is called (or compute next if the secret
// is divisible by 47 more than once).
//
// 'copr'          - Structure for holding coprocessor information.
// 'secretnum'     - destination secret for computation results
// 'input_secret'  - the input secret buffer used.
// 'secret_length' - the length of the input secret buffer, divisibly
//                   by 47.
//
// Return: TRUE - Install successfull
//         FALSE - error occurred during secret installation.
//
SMALLINT InstallSignSecretVM(SHACopr *copr, uchar *secret, int secret_length) {
  int i;
  memset(sign_secret, 0x00, 8);
  i = InstallSystemSecretVM(copr, secret, secret_length, sign_secret);
  return i;
}

//----------------------------------------------------------------------
// Installs new system signing secret for VM.  input_secret must be
// divisible by 47.  Then, each block of 47 is split up with 32 bytes
// written to a data page and 15 bytes are written to the scratchpad.
// Then, Compute first secret is called (or compute next if the secret
// is divisible by 47 more than once).
// Note: as a side-effect, the random number generator is seeded in this
// function as well.
//
// 'copr'          - Structure for holding coprocessor information.
// 'secretnum'     - destination secret for computation results
// 'input_secret'  - the input secret buffer used.
// 'secret_length' - the length of the input secret buffer, divisibly
//                   by 47.
//
// Return: TRUE - Install successfull
//         FALSE - error occurred during secret installation.
//
SMALLINT InstallAuthSecretVM(SHACopr *copr, uchar *secret, int secret_length) {
  int i;
  memset(auth_secret, 0x00, 8);
  i = InstallSystemSecretVM(copr, secret, secret_length, auth_secret);

  // at bare minimum, InstallAuthSecretVM must be called before the
  // VM coprocessor can be used.  We can take advantage of this fact
  // and use this opportunity to seed the random number generator.
  srand(((unsigned int)time(NULL)) ^ ((secret[3]) | (secret[5] << 8) | (secret[7] << 16) | (secret[11] << 24)));
  // is that random?  although deterministic, it's got to be at the
  // very least hard to predict.  it depends on user input which should
  // be kept top secret.

  return i;
}

//----------------------------------------------------------------------
// Loads the Coprocessor VM's config info from either a config file or
// an emulated coprocessor.
//
// 'copr'          - Structure for holding coprocessor information.
// 'fe'            - FileEntry to look for on emulated coprocessor
//
// Return: TRUE - GetCoprocessor successfull
//         FALSE - error occurred
//
SMALLINT GetCoprVM(SHACopr *copr, FileEntry *fe) {
  FILE *fp;
  uchar *signSecret, *authSecret;
  int signlen, authlen, len, index;
  int lvalue = 1;
  char test[2] = {'y', 0};
  uchar buffer[255];

  printf("How would you like to initialize the VM?");
  EnterNum("\n     1) Load VM info from file\n     "
           "2) Emulate another coprocessor\n",
           1, &lvalue, 1, 2);
  if (lvalue == 1) {
    // open up a file, maybe "shaVM.cnf"
    fp = fopen(SHACoprFilename, "rb");
    if (fp == NULL) {
      printf("file not found: %s\n", SHACoprFilename);
      return FALSE;
    }
    len = fread(buffer, 1, 255, fp);
    if (len <= 0) {
      printf("Could not read coprocessor config file\n");
      return FALSE;
    }

    index = GetCoprFromRawData(copr, buffer, len);

    // get the signing secret
    signlen = buffer[index];
    signSecret = malloc(signlen);
    memcpy(signSecret, &buffer[index + 1], signlen);

    // get the authentication secret
    index += signlen + 1;
    authlen = buffer[index];
    authSecret = malloc(authlen);
    memcpy(authSecret, &buffer[index + 1], authlen);
  } else {
    if (!FindCoprSHA(copr, fe)) {
      printf("no coprocessor found!\n");
      return FALSE;
    }

    signlen = GetSecretVM("System Signing Secret", &signSecret);

    authlen = GetSecretVM("System Authentication Secret", &authSecret);

    EnterString("Reformat the secret for DS1961S compatibility", test, 1, 1);
    if (test[0] == 'y') {
      ReformatSecretFor1961S(authSecret, authlen);
      PrintHex(authSecret, authlen);
      printf("\n");
      copr->ds1961Scompatible = 0x55;
    }
  }

  InstallAuthSecretVM(copr, authSecret, authlen);
  InstallSignSecretVM(copr, signSecret, signlen);

  free(authSecret);
  free(signSecret);

  return TRUE;
}

//----------------------------------------------------------------------
// Used for querying the user for secret information
//
// 'name'   - type of secret (authentication or signing)
// 'secret' - resulting secret data
//
// Returns: The length of the secret
//
static int GetSecretVM(char *name, uchar **secret) {
  uchar inputBuffer[255];
  int lvalue = 1, length;

  printf("How would you like to enter the %s?\n", name);
  EnterNum("\n   1) Hex\n   2) Text\n", 1, &lvalue, 1, 2);

  lvalue = getData(inputBuffer, 255, (lvalue == 1));

  if (lvalue % 47 != 0)
    length = ((lvalue / 47) + 1) * 47;
  else
    length = lvalue;
  *secret = malloc(length);
  memset(*secret, 0x00, length);
  memcpy(*secret, inputBuffer, lvalue);

  printf("length=%d\n", length);
  PrintHex(*secret, length);
  printf("\n");

  return (int)length;
}

//----------------------------------------------------------------------
// Writes the contents of the user's account file to the page specified
// by the user's account page number for DS1961.
//
// 'copr'       - Structure for holding coprocessor information.
// 'user'       - Structure for holding user token information.
//
// Return: TRUE - Write successfull
//         FALSE - error occurred during write.
//
SMALLINT WriteDataPageSHA33(SHACopr *copr, SHAUser *user) {
  // DS1961S - a bit tougher
  // assumes wspc_secret already contains device's unique secret.
  uchar pageContents[32], data[32], scratchpad[32];
  uchar MAC[20];
  int addr = user->accountPageNumber << 5;
  int i;

  OWASSERT(ReadMemoryPageSHA33(user->portnum, user->accountPageNumber, pageContents, FALSE), OWERROR_READ_MEMORY_PAGE_FAILED,
           FALSE);

  for (i = 24; i >= 0; i -= 8) {
    if (memcmp(&pageContents[i], &user->accountFile[i], 8) != 0) {
      memcpy(data, pageContents, 28);
      memcpy(&data[28], &user->accountFile[i], 4);
      memcpy(&scratchpad[8], &user->accountFile[i + 4], 4);
      scratchpad[12] = (uchar)(user->accountPageNumber & 0x3F);
      memcpy(&scratchpad[13], user->devAN, 7);
      memset(&scratchpad[20], 0xFF, 3);

      OWASSERT(CreateDataSignatureVM(copr, wspc_secret, data, scratchpad, MAC, TRUE), OWERROR_SIGN_SERVICE_DATA_FAILED, FALSE);

      OWASSERT(WriteScratchpadSHA33(user->portnum, addr + i, &user->accountFile[i], TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED,
               FALSE);

      OWASSERT(CopyScratchpadSHA33(user->portnum, addr + i, MAC, TRUE), OWERROR_COPY_SCRATCHPAD_FAILED, FALSE);
      memcpy(&pageContents[i], &user->accountFile[i], 8);
    }
  }

  return TRUE;
}
