//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
//
// sha18.c - Low-level memory and SHA functions for the DS1963S.
//
// Version: 2.10
//

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

//--------------------------------------------------------------------------
// Attempt to erase the scratchpad of the specified SHA iButton for DS1963S.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'address'     - value for TA2/TA1 (Note: TA2/TA1 aren't always set by
//                 Erase Scratchpad command.)
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Returns: TRUE, success, scratchpad erased
//          FALSE, failure to erase scratchpad
//
SMALLINT EraseScratchpadSHA18(int portnum, int address, SMALLINT resume) {
  uchar send_block[50];
  int num_verf;
  short send_cnt = 0;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, FALSE);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  // change number of verification bytes if in overdrive
  num_verf = (in_overdrive[portnum & 0x0FF]) ? 6 : 2;

  // erase scratchpad command
  send_block[send_cnt++] = CMD_ERASE_SCRATCHPAD;
  // TA1
  send_block[send_cnt++] = (uchar)(address & 0xFF);
  // TA2
  send_block[send_cnt++] = (uchar)((address >> 8) & 0xFF);

  // now add the verification bytes
  // for (i = 0; i < num_verf; i++)
  //   send_block[send_cnt++] = 0xFF;
  memset(&send_block[send_cnt], 0x0FF, num_verf);
  send_cnt += num_verf;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // check verification
  OWASSERT(((send_block[send_cnt - 1] & 0xF0) == 0x50) || ((send_block[send_cnt - 1] & 0xF0) == 0xA0),
           OWERROR_NO_COMPLETION_BYTE, FALSE);

  return TRUE;
}

//----------------------------------------------------------------------
// Read the scratchpad with CRC16 verification for DS1963S.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'address'     - pointer to address that is read from scratchpad
// 'es'          - pointer to offset byte read from scratchpad
// 'data'        - pointer data buffer read from scratchpad
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: TRUE - scratch read, address, es, and data returned
//         FALSE - error reading scratch, device not present
//
//
SMALLINT ReadScratchpadSHA18(int portnum, int *address, uchar *es, uchar *data, SMALLINT resume) {
  short send_cnt = 0;
  uchar send_block[40];
  SMALLINT i;
  ushort lastcrc16 = 0;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, FALSE);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
    resume = 1; // for addition later
  }

  // read scratchpad command
  send_block[send_cnt++] = CMD_READ_SCRATCHPAD;

  // now add the read bytes for data bytes and crc16
  // for (i = 0; i < 37; i++)
  //   send_block[send_cnt++] = 0xFF;
  memset(&send_block[send_cnt], 0x0FF, 37);
  send_cnt += 37;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // calculate CRC16 of result
  setcrc16(portnum, 0);
  for (i = resume; i < send_cnt; i++)
    lastcrc16 = docrc16(portnum, send_block[i]);

  // verify CRC16 is correct
  OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, FALSE);

  // copy data to return buffers
  if (address)
    *address = (send_block[2 + resume] << 8) | send_block[1 + resume];
  if (es)
    *es = send_block[3 + resume];

  memcpy(data, &send_block[4 + resume], 32);

  // success
  return TRUE;
}

//----------------------------------------------------------------------
// Write the scratchpad with CRC16 verification for DS1963S.  The data
// is padded until the offset is 0x1F so that the CRC16 is retrieved.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'address'     - address to write data to
// 'data'        - data to write
// 'data_len'    - number of bytes of data to write
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: TRUE - write to scratch verified
//         FALSE - error writing scratch, device not present, or HIDE
//                 flag is in incorrect state for address being written.
//
SMALLINT WriteScratchpadSHA18(int portnum, int address, uchar *data, SMALLINT data_len, SMALLINT resume) {
  uchar send_block[50];
  short send_cnt = 0, i;
  ushort lastcrc16;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, FALSE);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  setcrc16(portnum, 0);
  // write scratchpad command
  send_block[send_cnt] = CMD_WRITE_SCRATCHPAD;
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  // address 1
  send_block[send_cnt] = (uchar)(address & 0xFF);
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  // address 2
  send_block[send_cnt] = (uchar)((address >> 8) & 0xFF);
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  // data
  for (i = 0; i < data_len; i++) {
    send_block[send_cnt] = data[i];
    lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  }
  // pad if needed
  for (i = 0; i < (0x1F - ((address + data_len - 1) & 0x1F)); i++) {
    send_block[send_cnt] = 0xFF;
    lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  }
  // CRC16
  send_block[send_cnt++] = 0xFF;
  send_block[send_cnt++] = 0xFF;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // perform CRC16 of last 2 byte in packet
  for (i = send_cnt - 2; i < send_cnt; i++)
    lastcrc16 = docrc16(portnum, send_block[i]);

  // verify CRC16 is correct
  OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, FALSE);

  // success
  return TRUE;
}

//----------------------------------------------------------------------
// Copy the scratchpad with verification for DS1963S.  Assume that the
// data was padded to get the CRC16 verification on write scratchpad.
// This will result in the 'es' byte to be 0x1F.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'address'     - address of destination
// 'len'         - length of data
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: TRUE - copy scratch verified
//         FALSE - error during copy scratch, device not present, or HIDE
//                 flag is in incorrect state for address being written.
//
SMALLINT CopyScratchpadSHA18(int portnum, int address, SMALLINT len, SMALLINT resume) {
  short send_cnt = 0;
  uchar send_block[10];
  int num_verf;
  uchar es = (address + len - 1) & 0x1F;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, FALSE);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  // change number of verification bytes if in overdrive
  num_verf = (in_overdrive[portnum & 0x0FF]) ? 4 : 2;

  // copy scratchpad command
  send_block[send_cnt++] = CMD_COPY_SCRATCHPAD;
  // address 1
  send_block[send_cnt++] = (uchar)(address & 0xFF);
  // address 2
  send_block[send_cnt++] = (uchar)((address >> 8) & 0xFF);
  // es
  send_block[send_cnt++] = es;

  // verification bytes
  // for (i = 0; i < num_verf; i++)
  //   send_block[send_cnt++] = 0xFF;
  memset(&send_block[send_cnt], 0x0FF, num_verf);
  send_cnt += num_verf;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // check verification
  OWASSERT(((send_block[send_cnt - 1] & 0xF0) == 0x50) || ((send_block[send_cnt - 1] & 0xF0) == 0xA0),
           OWERROR_NO_COMPLETION_BYTE, FALSE);

  return TRUE;
}

//----------------------------------------------------------------------
// Perform a scratchpad match using provided data. Fixed data length
// of 20 bytes.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'data'        - data to use in match scratch operation
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: TRUE - valid match
//         FALSE - no match or device not present
//
SMALLINT MatchScratchpadSHA18(int portnum, uchar *data, SMALLINT resume) {
  short send_cnt = 0;
  uchar send_block[50];
  int i;
  ushort lastcrc16;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, FALSE);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  setcrc16(portnum, 0);
  // match scratchpad command
  send_block[send_cnt] = CMD_MATCH_SCRATCHPAD;
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);

  // send 20 data bytes
  for (i = 0; i < 20; i++) {
    send_block[send_cnt] = data[i];
    lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  }
  // send two crc bytes and verification byte
  // for (i = 0; i < 3; i++)
  //   send_block[send_cnt++] = 0xFF;
  memset(&send_block[send_cnt], 0x0FF, 3);
  send_cnt += 3;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // check the CRC
  for (i = (send_cnt - 3); i < (send_cnt - 1); i++)
    lastcrc16 = docrc16(portnum, send_block[i]);

  // verify CRC16 is correct
  OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, FALSE);

  // check verification
  if (send_block[send_cnt - 1] != (uchar)0xFF)
    return TRUE;
  else
    return FALSE;
}

//----------------------------------------------------------------------
// Read Memory Page for DS1963S.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'pagenum'     - page number to do a read authenticate
// 'data'        - buffer to read into from page
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: TRUE - Read successfull
//         FALSE - error occurred during read.
//
SMALLINT ReadMemoryPageSHA18(int portnum, SMALLINT pagenum, uchar *data, SMALLINT resume) {
  short send_cnt = 0;
  uchar send_block[36];
  int address = pagenum << 5;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, -1);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  // create the send block
  // Read Memory command
  send_block[send_cnt++] = CMD_READ_MEMORY;
  // TA1
  send_block[send_cnt++] = (uchar)(address & 0xFF);
  // TA2
  send_block[send_cnt++] = (uchar)((address >> 8) & 0xFF);

  // now add the read bytes for data bytes
  memset(&send_block[send_cnt], 0x0FF, 32);
  send_cnt += 32;

  // now send the block
  OWASSERT(owBlock(portnum, TRUE, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  // transfer the results
  memcpy(data, &send_block[send_cnt - 32], 32);

  return TRUE;
}

//----------------------------------------------------------------------
// Read Authenticated Page for DS1963S.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'pagenum'     - page number to do a read authenticate
// 'data'        - buffer to read into from page
// 'sign'        - buffer for storing sha computation
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: Value of write cycle counter for the page
//         -1 for error
//
int ReadAuthPageSHA18(int portnum, SMALLINT pagenum, uchar *data, uchar *sign, SMALLINT resume) {
  short send_cnt = 0;
  uchar send_block[56];
  short num_verf;
  SMALLINT i;
  ushort lastcrc16;
  int address = pagenum * 32, wcc = -1;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, -1);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  // seed the crc
  setcrc16(portnum, 0);

  // change number of verification bytes if in overdrive
  num_verf = (in_overdrive[portnum & 0x0FF]) ? 10 : 2;

  // create the send block
  // Read Authenticated Page command
  send_block[send_cnt] = CMD_READ_AUTH_PAGE;
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);

  // TA1
  send_block[send_cnt] = (uchar)(address & 0xFF);
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);

  // TA2
  send_block[send_cnt] = (uchar)((address >> 8) & 0xFF);
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);

  // now add the read bytes for data bytes, counter, and crc16, verification
  // for (i = 0; i < (42 + num_verf); i++)
  //   send_block[send_cnt++] = 0xFF;
  memset(&send_block[send_cnt], 0x0FF, 42 + num_verf);
  send_cnt += 42 + num_verf;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, -1);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // check the CRC
  for (i = resume ? 4 : 3; i < (send_cnt - num_verf); i++)
    lastcrc16 = docrc16(portnum, send_block[i]);

  // verify CRC16 is correct
  OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, -1);

  // check verification
  OWASSERT(((send_block[send_cnt - 1] & 0xF0) == 0x50) || ((send_block[send_cnt - 1] & 0xF0) == 0xA0),
           OWERROR_NO_COMPLETION_BYTE, -1);

  // transfer results
  // cnt = 0;
  // for (i = send_cnt - 42 - num_verf; i < (send_cnt - 10 - num_verf); i++)
  //   data[cnt++] = send_block[i];
  memcpy(data, &send_block[send_cnt - 42 - num_verf], 32);

  wcc = BytesToInt(&send_block[send_cnt - 10 - num_verf], 4);

  if (sign != NULL) {
    OWASSERT(ReadScratchpadSHA18(portnum, 0, 0, send_block, TRUE), OWERROR_READ_SCRATCHPAD_FAILED, FALSE);

    // for(i=0; i<20; i++)
    //    sign[i] = send_block[i+8];
    memcpy(sign, &send_block[8], 20);
  }

  return wcc;
}

//----------------------------------------------------------------------
// Write Data Page for DS1963S.
//
// 'portnum'     - number 0 to MAX_PORTNUM-1.  This number is provided to
//                 indicate the symbolic port number.
// 'pagenum'     - page number to write to
// 'data'        - buffer to write into page
// 'resume'      - if true, device access is resumed using the RESUME
//                 ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                 used along with the device's entire address number.
//
// Return: TRUE - Write successfull
//         FALSE - error occurred during write.
//
SMALLINT WriteDataPageSHA18(int portnum, SMALLINT pagenum, uchar *data, SMALLINT resume) {
  uchar buffer[32];
  int addr = pagenum << 5, addr_buff;
  uchar es = 0;

  OWASSERT(EraseScratchpadSHA18(portnum, addr, resume), OWERROR_ERASE_SCRATCHPAD_FAILED, FALSE);

  OWASSERT(WriteScratchpadSHA18(portnum, addr, data, 32, TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE);

  OWASSERT(ReadScratchpadSHA18(portnum, &addr_buff, &es, buffer, TRUE), OWERROR_READ_SCRATCHPAD_FAILED, FALSE);

  // verify that what we read is exactly what we wrote
  OWASSERT((addr == addr_buff) && (es == 0x1F) && (memcmp(buffer, data, 32) == 0), OWERROR_READ_SCRATCHPAD_FAILED, FALSE);

  OWASSERT(CopyScratchpadSHA18(portnum, addr, 32, TRUE), OWERROR_COPY_SCRATCHPAD_FAILED, FALSE);

  return TRUE;
}

//----------------------------------------------------------------------
// Compute sha command based on control_byte and page address.
//
// 'portnum'      - number 0 to MAX_PORTNUM-1.  This number is provided to
//                  indicate the symbolic port number.
// 'control_byte' - control byte used in sha operation
// 'address'      - address used in compute sha operation
// 'resume'       - if true, device access is resumed using the RESUME
//                  ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                  used along with the device's entire address number.
//
// Return: TRUE - compute sha finished
//         FALSE - CRC error, device not present
//
SMALLINT SHAFunction18(int portnum, uchar control_byte, int address, SMALLINT resume) {
  short send_cnt = 0;
  uchar send_block[18];
  int i, num_verf;
  ushort lastcrc16;

  if (!resume) {
    // access the device
    OWASSERT(SelectSHA(portnum), OWERROR_ACCESS_FAILED, FALSE);
  } else {
    // transmit RESUME command
    send_block[send_cnt++] = ROM_CMD_RESUME;
  }

  setcrc16(portnum, 0);
  // change number of verification bytes if in overdrive
  num_verf = (in_overdrive[portnum & 0x0FF]) ? 10 : 2;

  // Compute SHA Command
  send_block[send_cnt] = CMD_COMPUTE_SHA;
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  // address 1
  send_block[send_cnt] = (uchar)(address & 0xFF);
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  // address 2
  send_block[send_cnt] = (uchar)((address >> 8) & 0xFF);
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);
  // control byte
  send_block[send_cnt] = control_byte;
  lastcrc16 = docrc16(portnum, send_block[send_cnt++]);

  // now read bytes crc16, and verification
  // for (i = 0; i < 2 + num_verf; i++)
  //   send_block[send_cnt++] = 0xFF;
  memset(&send_block[send_cnt], 0x0FF, 2 + num_verf);
  send_cnt += 2 + num_verf;

  // now send the block
  OWASSERT(owBlock(portnum, resume, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // check the CRC
  for (i = resume ? 5 : 4; i < (send_cnt - num_verf); i++)
    lastcrc16 = docrc16(portnum, send_block[i]);

  // verify CRC16 is correct
  OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, FALSE);

  // check verification
  OWASSERT(((send_block[send_cnt - 1] & 0xF0) == 0x50) || ((send_block[send_cnt - 1] & 0xF0) == 0xA0),
           OWERROR_NO_COMPLETION_BYTE, FALSE);

  return TRUE;
}

//----------------------------------------------------------------------
// Copies hidden scratchpad data into specified secret.  Resume command
// is used by default.
//
// 'portnum'      - number 0 to MAX_PORTNUM-1.  This number is provided to
//                  indicate the symbolic port number.
// 'secretnum'    - secret number to replace with scratchpad data.
//
// Return: TRUE - copy secret succeeded
//         FALSE - error or device not present
//
SMALLINT CopySecretSHA18(int portnum, SMALLINT secretnum) {
  // change number of verification bytes if in overdrive
  SMALLINT num_verf = (in_overdrive[portnum & 0x0FF]) ? 10 : 2;

  // each page has 4 secrets, so look at 2 LS bits to
  // determine offset in the page.
  SMALLINT secret_offset = (secretnum & 3) << 3;

  // secrets 0-3 are stored starting at address 0200h
  // and 4-7 are stored starting at address 0220h.
  int address = (secretnum < 4 ? 0x0200 : 0x0220) + secret_offset;

  SMALLINT length = 32 - secret_offset;
  SMALLINT send_cnt = 0, i;
  uchar send_block[37];
  ushort lastcrc16 = 0;

  // Since other functions must be called before this one
  // that are communicating with the button, resume is assumed.
  send_block[send_cnt++] = ROM_CMD_RESUME;
  send_block[send_cnt++] = CMD_WRITE_SCRATCHPAD;
  send_block[send_cnt++] = (uchar)address;
  send_block[send_cnt++] = (uchar)(address >> 8);

  // for(i=0; i<length+2; i++)
  //    send_block[send_cnt++] = (uchar)0x0FF;
  memset(&send_block[send_cnt], 0x0FF, 2 + length);
  send_cnt += 2 + length;

  // now send the block
  OWASSERT(owBlock(portnum, TRUE, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // calculate CRC16 of result
  setcrc16(portnum, 0);
  for (i = 1; i < send_cnt; i++)
    lastcrc16 = docrc16(portnum, send_block[i]);

  // verify CRC16 is correct
  OWASSERT(lastcrc16 == 0xB001, OWERROR_CRC_FAILED, FALSE);

  // Now read TA1/TA2 and ES, but not rest of data;
  send_cnt = 1;
  send_block[send_cnt++] = CMD_READ_SCRATCHPAD;

  // for(i=0; i<3; i++)
  //    send_block[send_cnt++] = (uchar)0x0FF;
  memset(&send_block[send_cnt], 0x0FF, 3);
  send_cnt += 3;

  // now send the block to get TA1/TA2 and ES
  OWASSERT(owBlock(portnum, TRUE, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // Use the same buffer to call copyScratchpad with proper
  // authorization bytes.
  send_block[1] = CMD_COPY_SCRATCHPAD;

  // for(i=0; i<num_verf; i++)
  //    send_block[send_cnt++] = (uchar)0x0FF;
  memset(&send_block[send_cnt], 0x0FF, num_verf);
  send_cnt += num_verf;

  // now send the block
  OWASSERT(owBlock(portnum, TRUE, send_block, send_cnt), OWERROR_BLOCK_FAILED, FALSE);

  //\\//\\//\\//\\//\\//\\//\\//\\//\\//
#ifdef DEBUG_DUMP
  debugout(send_block, send_cnt);
#endif
  //\\//\\//\\//\\//\\//\\//\\//\\//\\//

  // check verification
  OWASSERT(((send_block[send_cnt - 1] & 0xF0) == 0x50) || ((send_block[send_cnt - 1] & 0xF0) == 0xA0),
           OWERROR_NO_COMPLETION_BYTE, FALSE);

  return TRUE;
}

//----------------------------------------------------------------------
// Installs new system secret for DS1931S.  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).
//
// 'portnum'       - number 0 to MAX_PORTNUM-1.  This number is provided to
//                   indicate the symbolic port number.
// 'pagenum'       - page number to do a read authenticate
// '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.
// 'resume'        - if true, device access is resumed using the RESUME
//                   ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                   used along with the device's entire address number.
//
// Return: TRUE - Install successfull
//         FALSE - error occurred during secret installation.
//
SMALLINT InstallSystemSecret18(int portnum, SMALLINT pagenum, SMALLINT secretnum, uchar *secret, int secret_length,
                               SMALLINT resume) {
  int addr = pagenum << 5;
  int offset = 0, bytes_left = 0;
  uchar data[32], scratchpad[32];

  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));
    }

    // write secret data into data page
    OWASSERT(WriteDataPageSHA18(portnum, pagenum, data, resume), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);

    // write secret data into scratchpad
    OWASSERT(WriteScratchpadSHA18(portnum, addr, scratchpad, 32, TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE);

    // perform secret computation
    OWASSERT(SHAFunction18(portnum, (uchar)(offset == 0 ? SHA_COMPUTE_FIRST_SECRET : SHA_COMPUTE_NEXT_SECRET), addr, TRUE),
             OWERROR_SHA_FUNCTION_FAILED, FALSE);

    // copy the resulting secret into secret location
    OWASSERT(CopySecretSHA18(portnum, secretnum), OWERROR_COPY_SECRET_FAILED, FALSE);

    resume = TRUE;
  }

  return TRUE;
}

//----------------------------------------------------------------------
// Binds unique secret to DS1963S.  bindData must be 32 bytes and
// bindCode must be 15 bytes.
//
// 'portnum'       - number 0 to MAX_PORTNUM-1.  This number is provided to
//                   indicate the symbolic port number.
// 'pagenum'       - page number to do a read authenticate
// 'secretnum'     - destination secret for computation results
// 'bindData'      - the input data written to the data page for unique
//                   secret computation.
// 'bindCode'      - the input data written to the scratchpad for unique
//                   secret computation.
// 'resume'        - if true, device access is resumed using the RESUME
//                   ROM command (0xA5).  Otherwise, a a MATCH ROM is
//                   used along with the device's entire address number.
//
// Return: TRUE - bind successfull
//         FALSE - error occurred during secret installation.
//
SMALLINT BindSecretToiButton18(int portnum, SMALLINT pagenum, SMALLINT secretnum, uchar *bindData, uchar *bindCode,
                               SMALLINT resume) {
  int addr = pagenum << 5;
  uchar scratchpad[32];

  memset(scratchpad, 0x00, 32);
  memcpy(&scratchpad[8], bindCode, 15);

  // write secret data into data page
  OWASSERT(WriteDataPageSHA18(portnum, pagenum, bindData, resume), OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);

  // write secret data into scratchpad
  OWASSERT(WriteScratchpadSHA18(portnum, addr, scratchpad, 32, TRUE), OWERROR_WRITE_SCRATCHPAD_FAILED, FALSE);

  // perform secret computation
  OWASSERT(SHAFunction18(portnum, (uchar)SHA_COMPUTE_NEXT_SECRET, addr, TRUE), OWERROR_SHA_FUNCTION_FAILED, FALSE);

  // copy the resulting secret into secret location
  OWASSERT(CopySecretSHA18(portnum, secretnum), OWERROR_COPY_SECRET_FAILED, FALSE);

  return TRUE;
}
