
/*---------------------------------------------------------------------------
 * Copyright (C) 1999,2000 Dallas Semiconductor Corporation, All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Dallas Semiconductor
 * shall not be used except as stated in the Dallas Semiconductor
 * Branding Policy.
 *---------------------------------------------------------------------------
 */

import com.dalsemi.onewire.*;
import com.dalsemi.onewire.adapter.*;
import com.dalsemi.onewire.container.*;
import java.util.Vector;
import java.io.*;
import com.dalsemi.onewire.utils.CRC16;
import java.util.*;


public class initcopr
{
   static byte[] page0 =
   {
      ( byte ) 0x0F, ( byte ) 0xAA, ( byte ) 0x00, ( byte ) 0x80,
      ( byte ) 0x03, ( byte ) 0x00, ( byte ) 0x00, ( byte ) 0x00,
      ( byte ) 0x43, ( byte ) 0x4F, ( byte ) 0x50, ( byte ) 0x52,
      ( byte ) 0x00, ( byte ) 0x01, ( byte ) 0x01, ( byte ) 0x00,
      ( byte ) 0x74, ( byte ) 0x9C, ( byte ) 0xFF, ( byte ) 0xFF,
      ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF,
      ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF,
      ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF, ( byte ) 0xFF
   };

   static byte[] parse (BufferedReader in, int count, int pad,
                        boolean hex_ify_it)
   {
      try
      {
         String s   = in.readLine();
         int    len = s.length() > count ? count
                                         : s.length();
         byte[] ret;

         if (count > 0)
            ret = new byte [count];
         else
            ret = new byte [s.length()];

         if (hex_ify_it)
         {
            byte[] temp = readHex(s, 0);

            if (count == 0)
               return temp;

            len = temp.length;

            System.arraycopy(temp, 0, ret, 0, len);
         }
         else
         {
            if (count == 0)
            {
               System.arraycopy(s.getBytes(), 0, ret, 0, s.length());

               return ret;
            }

            System.arraycopy(s.getBytes(), 0, ret, 0, len);
         }

         for (; len < count; len++)
            ret [len] = ( byte ) pad;

         return ret;
      }
      catch (Exception e)
      {
         return new byte [count];
      }
   }

   static int parseInt (BufferedReader in, int def)
   {
      try
      {
         return Integer.parseInt(in.readLine());
      }
      catch (Exception e)
      {
         return def;
      }
   }

   /**
    * Method printUsageString
    *
    *
    */
   public static void printUsageString ()
   {
      System.out.println(
         "DS1963S SHA iButton Java Demo Transaction Program.\r\n");
      System.out.println("Usage: ");
      System.out.println("   java initcopr ADAPTER_PORT\r\n");
      System.out.println(
         "ADAPTER_PORT is a String that contains the name of the");
      System.out.println(
         "adapter you would like to use and the port you would like");
      System.out.println("to use, for example: ");
      System.out.println("   java initcopr {DS1410E}_LPT1");
      System.out.println(
         "You can leave ADAPTER_PORT blank to use the default one-wire adapter and port.");
   }

   /**
    * Method main
    *
    *
    * @param args
    *
    * @throws OneWireException
    * @throws OneWireIOException
    *
    */
   public static void main (String[] args)
      throws OneWireIOException, OneWireException
   {
      byte[]        ID         = new byte [8];
      byte[]        filebuffer = new byte [200];
      int           filelength;
      int           i;
      long[]        buttons      = new long [10];
      DSPortAdapter access       = null;
      long          coprocessor  = 0;
      SHAiButton    copr         = null;
      boolean       usedefault   = false;
      String        adapter_name = null;
      String        port_name    = null;

      if ((args == null) || (args.length < 1))
      {
         try
         {
            access = OneWireAccessProvider.getDefaultAdapter();

            if (access == null)
               throw new Exception();
         }
         catch (Exception e)
         {
            System.out.println("Couldn't get default adapter!");
            printUsageString();

            return;
         }

         usedefault = true;
      }

      if (!usedefault)
      {
         StringTokenizer st = new StringTokenizer(args [0], "_");

         if (st.countTokens() != 2)
         {
            printUsageString();

            return;
         }

         adapter_name = st.nextToken();
         port_name    = st.nextToken();

         System.out.println("Adapter Name: " + adapter_name);
         System.out.println("Port Name: " + port_name);
      }

      if (access == null)
      {
         try
         {
            access = OneWireAccessProvider.getAdapter(adapter_name,
                                                      port_name);
         }
         catch (Exception e)
         {
            System.out.println(
               "That is not a valid adapter/port combination.");

            Enumeration en = OneWireAccessProvider.enumerateAllAdapters();

            while (en.hasMoreElements())
            {
               DSPortAdapter temp = ( DSPortAdapter ) en.nextElement();

               System.out.println("Adapter: " + temp.getAdapterName());

               Enumeration f = temp.getPortNames();

               while (f.hasMoreElements())
               {
                  System.out.println("   Port name : "
                                     + (( String ) f.nextElement()));
               }
            }

            return;
         }
      }

      access.adapterDetected();
      access.targetFamily(0x18);
      access.beginExclusive(true);
      access.reset();
      access.setSearchAllDevices();

      boolean next = access.findFirstDevice();

      if (!next)
      {
         System.out.println("Could not find any DS1963S iButtons!");

         return;
      }

      OneWireContainer18 ibc = new OneWireContainer18();

      ibc.setupContainer(access, access.getAddressAsLong());
      ibc.setSpeedCheck(false);

      BufferedReader in  =
         new BufferedReader(new InputStreamReader(System.in));
      SHAiButton     sha = new SHAiButton(ibc);

      System.out.print(
         "Enter the name of the service file (4 characters) : ");

      byte[] name = parse(in, 4, ' ', false);

      System.out.print("Enter authentication page number (7) : ");

      int auth_page = parseInt(in, 7);

      if (auth_page < 7)
      {
         System.out.println("Authentication page too low, default to 7");

         auth_page = 7;
      }

      if (auth_page == 8)
      {
         System.out.println("Page already taken, default to 7");

         auth_page = 7;
      }

      System.out.print("Enter workspace page number (9) : ");

      int work_page = parseInt(in, 9);

      if (work_page < 7)
      {
         System.out.println("Authentication page too low, default to 9");

         work_page = 9;
      }

      if ((work_page == 8) || (work_page == auth_page))
      {
         System.out.println("Page already taken, default to 9");

         work_page = 9;
      }

      System.out.print("Enter version number (1) : ");

      int  version = parseInt(in, 1);
      Date t       = new Date();
      int  month   = t.getMonth() + 1;
      int  date    = t.getDate();
      int  yearMSB = (t.getYear() + 1900) / 100;
      int  yearLSB = (t.getYear() + 1900) % 100;

      System.out.println("Month " + month + ", Date " + date + ", Year "
                         + (yearMSB * 100 + yearLSB));
      System.out.println(
         "How would you like to enter the binding data (32 bytes)? ");
      System.out.println("   1 HEX");
      System.out.println("   2 ASCII");
      System.out.print("  ? ");

      int    choice    = parseInt(in, 2);
      byte[] bind_data = parse(in, 32, ((choice == 1) ? 0x00
                                                      : 0x20), (choice == 1));

      System.out.println(
         "How would you like to enter the binding code (7 bytes)? ");
      System.out.println("   1 HEX");
      System.out.println("   2 ASCII");
      System.out.print("  ? ");

      choice = parseInt(in, 2);

      byte[] bind_code = parse(in, 7, ((choice == 1) ? 0x00
                                                     : 0x20), (choice == 1));
      byte[] chlg      = new byte [3];

      sha.generateChallenge(( int ) (System.currentTimeMillis() & 15), 0,
                            chlg);

      if (chlg == null)
      {
         chlg = new byte [3];

         java.util.Random random = new java.util.Random();

         random.nextBytes(chlg);
      }

      String provider_name = "";

      try
      {
         System.out.println("Enter a human-readable provider name: ");

         provider_name = in.readLine();
      }
      catch (Exception e)
      {

         //default to ""
      }

      System.out.println(
         "Enter an initial signature in HEX (all 0' default): ");

      byte[] sig_ini  = parse(in, 20, 0, true);
      String aux_data = "";

      try
      {
         System.out.println(
            "Enter any additional text you would like store on the coprocessor: ");

         aux_data = in.readLine();
      }
      catch (Exception e)
      {

         //default to ""
      }

      System.out.println("Enter an encryption code (0): ");

      int enc_code = parseInt(in, 0);

      try
      {
         ByteArrayOutputStream baos = new ByteArrayOutputStream();

         baos.write(name);
         baos.write(102);
         baos.write(8);
         baos.write(auth_page);
         baos.write(work_page);
         baos.write(version);
         baos.write(month);
         baos.write(date);
         baos.write(yearMSB);
         baos.write(yearLSB);
         baos.write(bind_data);
         baos.write(bind_code);
         baos.write(chlg);
         baos.write(provider_name.length());
         baos.write(sig_ini.length);
         baos.write(aux_data.length());
         baos.write(provider_name.getBytes());
         baos.write(sig_ini);
         baos.write(aux_data.getBytes());
         baos.write(enc_code);

         byte[] data = baos.toByteArray();

         dump(data);

         int    page      = 1;
         byte[] this_page = new byte [32];
         int    index     = 0;
         int    length;

         while (index < data.length)
         {
            if (data.length - index > 28)
               length = 28;
            else
               length = data.length - index;

            this_page [0] = ( byte ) (length + 1);

            System.arraycopy(data, index, this_page, 1, length);

            index += length;

            if (index < data.length)
               this_page [length + 1] = ( byte ) (page + 1);
            else
               this_page [length + 1] = ( byte ) 0;

            int crc = CRC16.compute(this_page, 0, length + 2, page);

            //make sure this is an inverted CRC
            crc                    = (~crc) & 0x0ffff;
            this_page [length + 2] = ( byte ) crc;
            this_page [length + 3] = ( byte ) (crc >> 8);

            System.out.println("CRC is "
                               + Integer.toHexString(CRC16.compute(this_page,
                                  0, this_page [0] + 1, page)));
            ibc.writeDataPage(page, this_page);

            page++;
         }

         //we've used pages 0 to (page-1)
         //we want our int to look like this:
         //   00000000...0001111...111  
         //where the 1's represent used pages
         //if we shift 0x01 left by (page) and then subtract 1 this will work
         int mask = (0x01 << (page)) - 1;

         //now put that int (LSB first) in page0[4..7]
         page0 [4] = ( byte ) (mask);
         page0 [5] = ( byte ) (mask >> 8);
         page0 [6] = ( byte ) (mask >> 16);
         page0 [7] = ( byte ) (mask >> 24);

         //now how many pages were used for file COPR.000?
         //if we used (page) pages, and page 0 is the root, then there are page-1 pages in the file
         //stick it in the file at position 14
         page0 [14] = ( byte ) (page - 1);

         //now recalc the CRC
         int CRC = ~CRC16.compute(page0, 0, page0 [0] + 1);

         page0 [page0 [0] + 1] = ( byte ) CRC;
         page0 [page0 [0] + 2] = ( byte ) (CRC >> 8);

         //now we have to fix page 0, there's a bitmap and a file length thing to fix    
         if ((!ibc.eraseScratchPad(0)) || (!ibc.writeDataPage(0, page0)))
         {
            System.out.println("Failed to write page 0");

            return;
         }
      }
      catch (IOException ridiculous)
      {
         System.out.println(
            "Why is a byteArrayoutputStream throwing an IOException?");
         ridiculous.printStackTrace();
      }

      //now that that is done, we need a signing secret and an authentication secret
      System.out.println(
         "How would you like to enter the signing secret (unlimited bytes)? ");
      System.out.println("   1 HEX");
      System.out.println("   2 ASCII");
      System.out.print("  ? ");

      choice = parseInt(in, 2);

      byte[] sign_secret = parse(in, 0, ((choice == 1) ? 0x00
                                                       : 0x20), (choice
                                                                 == 1));

      dump(sign_secret);
      System.out.println(
         "How would you like to enter the authentication secret (unlimited bytes)? ");
      System.out.println("   1 HEX");
      System.out.println("   2 ASCII");
      System.out.print("  ? ");

      choice = parseInt(in, 2);

      byte[] auth_secret = parse(in, 0, ((choice == 1) ? 0x00
                                                       : 0x20), (choice
                                                                 == 1));

      dump(auth_secret);

      if (!ibc.installMasterSecret(8, sign_secret, 0))
         System.out.println("Could not install master signing secret!");
      else
         System.out.println("Signing secret installed.");

      if (!ibc.installMasterSecret(auth_page, auth_secret, auth_page & 7))
         System.out.println(
            "Could not install master authentication secret!");
      else
         System.out.println("Authentication secret installed.");
   }

   /**
    * Method help
    *
    *
    */
   public void help ()
   {
      System.out.println("command: wcc\r\n");
      System.out.println("Usage: coprocessor");
      System.out.println();
      System.out.println(
         "Displays the write cycle counter for page PAGE_NUM");
   }

   static char[] hex = "0123456789ABCDEF".toCharArray();

   /**
    * Method dump
    *
    *
    * @param A
    *
    */
   public static void dump (byte[] A)
   {
      StringBuffer sb   = new StringBuffer();
      int          last = 0;
      int          i    = 0;

      for (i = 0; i < A.length; i++)
      {
         if (((i & 15) == 0) && (i != 0))
         {
            sb.append("  ");

            for (int k = last; k < i; k++)
               sb.append(( char ) A [k]);

            sb.append("\r\n");

            last = i;
         }

         sb.append(hex [(A [i] >> 4) & 0x0f]);
         sb.append(hex [(A [i]) & 0x0f]);
         sb.append('.');
      }
      sb.append("  ");

      for (int k = last; k < i; k++)
         sb.append(( char ) A [k]);
      System.out.println(sb);
   }

   /**
    * Method dump
    *
    *
    * @param A
    * @param start
    * @param offset
    *
    */
   public static void dump (byte[] A, int start, int offset)
   {
      StringBuffer sb   = new StringBuffer();
      int          last = start;
      int          i    = start;

      for (i = start; i < start + offset; i++)
      {
         if ((((i - start) & 15) == 0) && (i != start))
         {
            sb.append("  ");

            for (int k = last; k < i; k++)
               sb.append(( char ) A [k]);

            sb.append("\r\n");

            last = i;
         }

         sb.append(hex [(A [i] >> 4) & 0x0f]);
         sb.append(hex [(A [i]) & 0x0f]);
         sb.append('.');
      }
      sb.append("  ");

      for (int k = last; k < i; k++)
         sb.append(( char ) A [k]);
      System.out.println(sb);
   }

   /**
    * Method readHex
    *
    *
    * @param s
    * @param size
    *
    * @return
    *
    */
   public static byte[] readHex (String s, int size)
   {
      int    index = 0;
      int    i     = 0;
      char[] x     = s.toLowerCase().toCharArray();
      byte[] temp;

      if (size > 0)
         temp = new byte [size];
      else
         temp = new byte [x.length];

      try
      {
         while ((i < x.length) && (index < (temp.length * 2)))
         {
            int  shift = ((index & 0x01) == 0x01) ? 0
                                                  : 4;
            char c     = x [i++];

            switch (c)
            {

               case '0' :
                  index++;   //special case--zero
                  break;
               case '1' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (1 << shift));

                  index++;
                  break;
               case '2' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (2 << shift));

                  index++;
                  break;
               case '3' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (3 << shift));

                  index++;
                  break;
               case '4' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (4 << shift));

                  index++;
                  break;
               case '5' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (5 << shift));

                  index++;
                  break;
               case '6' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (6 << shift));

                  index++;
                  break;
               case '7' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (7 << shift));

                  index++;
                  break;
               case '8' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (8 << shift));

                  index++;
                  break;
               case '9' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (9 << shift));

                  index++;
                  break;
               case 'a' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (10 << shift));

                  index++;
                  break;
               case 'b' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (11 << shift));

                  index++;
                  break;
               case 'c' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (12 << shift));

                  index++;
                  break;
               case 'd' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (13 << shift));

                  index++;
                  break;
               case 'e' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (14 << shift));

                  index++;
                  break;
               case 'f' :
                  temp [index >> 1] = ( byte ) (temp [index >> 1]
                                                | (15 << shift));

                  index++;
                  break;
            }
         }                   //end while
      }
      catch (Exception e)
      {
         return null;
      }

      if (size > 0)
         return temp;

      byte[] t = new byte [(index + 1) >> 1];

      System.arraycopy(temp, 0, t, 0, t.length);

      return t;
   }
}
