//---------------------------------------------------------------------------
// 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.
//---------------------------------------------------------------------------
//
// sha_init.c - main function for initializing software authentication
//            application.
//
#include "ownet.h"
#include "shaib.h"

// file where the hard code data is for authentication
#define SA_Filename  "sha_data.cnf"

// local functions
SMALLINT UpdateServiceDataSA(SHACopr* copr, SHAUser* user, uchar* data);
SMALLINT InstallServiceDataSA(SHACopr* copr, SHAUser* user,
                              uchar* secret, int secret_length,
                              uchar* data);
int getNumber (int min, int max);


static uchar sign_secret[8] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};

// main body of application
int main(int argc, char** argv)
{
   int i = 0;
   SHACopr copr;
   SHAUser user;
   int   len, data_type;
   uchar authSecret[47];
   int authlen = 21;
   uchar signSecret[11] = {(uchar)'s',(uchar)'i',(uchar)'g',(uchar)'n',
                           (uchar)' ',(uchar)'s',(uchar)'e',(uchar)'c',
                           (uchar)'r',(uchar)'e',(uchar)'t'};
   int signlen = 11;
   uchar data[7];

   copr.portnum = 0;
   user.portnum = 0;

   puts("\nStarting Software Authentication setup Application\n");

   // check for required port name
   if (argc != 2)
   {
      printf("1-Wire Net name required on command line!\n"
             " (example: \"COM1\" (Win32 DS2480),\"/dev/cua0\" "
             "(Linux DS2480),\"1\" (Win32 TMEX)\n");
      exit(1);
   }

   if((copr.portnum = owAcquireEx(argv[1])) < 0)
   {
      printf("Failed to acquire port.\n");
      exit(1);
   }

   user.portnum = copr.portnum;

   // MANUALLY SETTING DATA FOR COPROCESSOR
   memcpy(copr.serviceFilename, "DLSM", 4);
   copr.serviceFilename[4] = (uchar) 102;
   copr.signPageNumber     = (uchar) 8;
   copr.authPageNumber     = (uchar) 7;
   copr.wspcPageNumber     = (uchar) 9;
   copr.versionNumber      = (uchar) 1;
   memcpy(copr.bindCode, "bindcde", 7);
   for(i=0;i<8;i++)
   {
      memcpy(&copr.bindData[i*4], "bind", 4);
   }
   copr.encCode           = 0x01;
   copr.ds1961Scompatible = 0x01;

   // Prompt for Password and User data
   printf("Enter up to 47 bytes of the Authentication Secret.\n");
   printf("  Data Entry Mode\n");
   printf("  (0) Text (single line)\n");
   printf("  (1) Hex (XX XX XX XX ...)\n");
   data_type = getNumber(0,1);
   len = getData(authSecret,47,data_type);
   // padd the data with spaces or 0's depending on type
   if(len < 47)
   {
      for(i=len;i<47;i++)
            authSecret[i] = 0x00;
   }
   PrintHex(authSecret, 47);
   printf("\n");

   printf("Enter up to 7 bytes of user data.\n");
   printf("  Data Entry Mode\n");
   printf("  (0) Text (single line)\n");
   printf("  (1) Hex (XX XX XX XX ...)\n");
   data_type = getNumber(0,1);
   len = getData(data,7,data_type);
   // padd the data with spaces or 0's depending on type
   if(len < 7)
   {
      for(i=len;i<7;i++)
      {
         if (data_type == 1)
            data[i] = 0x00;
         else
            data[i] = 0x20;
      }
   }
   PrintHex(data, 7);
   printf("\n");

   ReformatSecretFor1961S(authSecret, authlen);
   //Debug: PrintHex(authSecret, authlen);
   printf("\n");
   copr.ds1961Scompatible = 0x55;
   InstallAuthSecretVM(&copr, authSecret, authlen);
   InstallSignSecretVM(&copr, signSecret, signlen);

   // Find the token to initialize
   puts("\nPlease place user token on the 1-Wire bus.\n");
   do
   {
      while(!FindNewSHA(user.portnum, user.devAN, (i==0)))
      {
         if(owHasErrors())
         {
            msDelay(10);
         }
      }
      i++;
   }
   while(user.devAN[7]==copr.devAN[7]);
   // just check the crc of the two devices

   // Initialize the token
   printf("Installing Service Data on device: ");
   PrintSerialNum(user.devAN);
   puts("\n");
   if(InstallServiceDataSA(&copr, &user, authSecret, authlen, data))
   {
      puts("User token successfully set up");
   }
   else
   {
      puts("User token setup failed");
      OWERROR_DUMP(stdout);
   }

   // and we're done
   owRelease(copr.portnum);

   return 0;
}

//-------------------------------------------------------------------------
// 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 InstallServiceDataSA(SHACopr* copr, SHAUser* user,
                              uchar* secret, int secret_length,
                              uchar* data)
{
   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]==0x33) || (user->devAN[0]==0xB3))
   {
      // 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)
   {
      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);

   }
   else if((user->devAN[0]==0x33) || (user->devAN[0]==0xB3))
   {
      OWASSERT( BindSecretToiButton33(user->portnum,
                                      user->accountPageNumber,
                                      user->accountPageNumber&7,
                                      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;
   }

   // set transaction ID
   user->accountFile[27] = 0;
   user->accountFile[28] = 0;
   user->accountFile[29] = 0;

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

//-------------------------------------------------------------------------
// 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 UpdateServiceDataSA(SHACopr* copr, SHAUser* user, uchar* data)
{
   ushort crc16, i;
   uchar scratchpad[32];

   // make sure length is right.
   user->accountFile[0] = 29;

   // clear out the old signature and CRC
   memcpy(&user->accountFile[2], copr->initSignature, 20);
   memset(&user->accountFile[30], 0x00, 2);

   // reset data type code
   user->accountFile[1] = 0;

   for(i=0;i<7;i++)
      user->accountFile[i+22] = data[i];

   if((user->devAN[0]==0x33) || (user->devAN[0]==0xB3))
   {
      // ---  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,
                                      &user->accountFile[2],
                                      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;
   user->accountFile[30] = (uchar)crc16;
   user->accountFile[31] = (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]==0x33) || (user->devAN[0]==0xB3))
   {
      OWASSERT( WriteDataPageSHA33(copr, user),
                OWERROR_WRITE_DATA_PAGE_FAILED, FALSE);
   }
   else
      return FALSE;

   return TRUE;
}

/**
 * Retrieve user input from the console.
 *
 * min  minimum number to accept
 * max  maximum number to accept
 *
 * @return numeric value entered from the console.
 */
int getNumber (int min, int max)
{
   int value = min,cnt;
   int done = FALSE;
   do
   {
      cnt = scanf("%d",&value);
      if(cnt>0 && (value>max || value<min))
      {
         printf("Value (%d) is outside of the limits (%d,%d)\n",value,min,max);
         printf("Try again:\n");
      }
      else
         done = TRUE;

   }
   while(!done);

   return value;
}
