com.dalsemi.onewire.container
Class SHAiButton

java.lang.Object
  |
  +--com.dalsemi.onewire.container.SHAiButton

public class SHAiButton
extends Object

High level class implementing transactions and authentications for a DS1963S, Sha iButton.

This class makes use of several performance enhancements for TINI. For instance, most methods are synchronized to access instance variable byte arrays rather than creating new byte arrays every time a transaction is performed. This could hurt performance in multi-threaded applications, but the usefulness of having several threads contending to talk to a single iButton is questionable since the methods in com.dalsemi.onewire.adapter.DSPortAdapter beginExclusive(boolean) and endExclusive() should be used.

A DS1963S SHA iButton can be a Coprocessor or a User. A Coprocessor iButton verifies signatures and signs data for User iButtons. A Coprocessor might be located inside a vending machine, where a person would bring their User iButton. When the User iButton is pressed to the Blue Dot to perform a transaction, the Coprocessor would first verify that this button belongs to the system, i.e. that it knows the same authentication secret (example: a Visa terminal making sure the iButton had a Visa account installed). Then the Coprocessor would verify the signed data, probably money, to make sure it was valid. If someone tried to overwrite the money file, even with a previously valid money file (an attempt to 'restore' a previous amount of money), the signed file would be invalid because the signature includes the write cycle counter, which is incremented every time a page is written to. The write cycle counter is read-only and does not roll over, so the previous amount of money could not be restored by rolling the write counter. The Coprocessor verifies the money, then signs a new data file that contains the new amount of money.

There are two secrets involved with the transaction process. The first secret is the authentication secret. It is used to validate a User iButton to a system. The Coprocessor iButton has the system authentication secret installed. On User iButtons, the system authentication secret is merged with binding data and the unique address of the User iButton to create a unique device authentication secret. The second secret is a signing secret. This secret only exists on the Coprocessor iButton, and is used to sign and verify data such as money. These secrets are inaccessible outside the iButton. Once they are installed, they cannot be retrieved.

Usage

  1. Initializing a Coprocessor iButton
  2. Initializing a User iButton
    1. Initializing a SHAiButton to act as a Coprocessor
    2. Using a Coprocessor to initialize a User iButton
  3. Performing an Authentication and a Transaction
Initializing a Coprocessor iButton

There are two main operations involved in initializing a DS1963S to be a Coprocessor iButton for transactions. First, it must have a Coprocessor file that defines parameters for the system. Second, it must install a master authentication secret and a signing secret.

Dallas Semiconductor has created a suggested format for Coprocessor files to help developers implement systems for authentication and transactions. The structure is as follows:

Field
# bytes
Sample data
Name of user account data file
5
{'D','L','S','M',102}
Signing page number
1
8
Authentication page number
1
7
Workspace page number
1
9
Version number
1
1
Installation data code
4
10182000
Binding data
32
32 0xff bytes
Binding code
7
7 0xff bytes
Signing challenge
3
3 0x00 bytes
Provider name length (LN)
1
20
Initial Signature length (LS)
1
20
Extra data length (LE)
1
0
Service provider name
LN
"Dallas Semiconductor"
Initial signature
LS
20 0x00 bytes
Extra data
LE
Encryption algorithm code
1
0

This class assumes the use of TMEX files. The code below shows how to set up a Coprocessor iButton using the TMEX file format and the service file format discussed above.


       //preformatted root directory page for TMEX
       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
       };

       . . . 

       //adapter is a DSPortAdapter that has just found a DS1963S
       OneWireContainer18 owc18 = new OneWireContainer18();
       owc18.setupContainer(adapter,adapter.getAddressAsLong());

       //we will control the speed of the 1-Wire bus, don't do speed check
       owc18.setSpeedCheck(false);
       SHAiButton sha = new SHAiButton(owc18);
       
       //first we need to accumulate data for our service file
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       baos.write("DLSM".getBytes());                 //coprocessor service file name
       baos.write(102);                               //coprocessor service file extension
       baos.write(8);                                 //signing page
       baos.write(7);                                 //authentication page
       baos.write(9);                                 //workspace page
       baos.write(1);                                 //version number
       baos.write(10);                                //date stamp: month
       baos.write(18);                                //date stamp: date
       baos.write(20);                                //date stamp: year / 100
       baos.write(0);                                 //date stamp: year % 100
       //32 bytes of bind data
       baos.write("01234567890123456789012345678901".getBytes()); 
       baos.write("binding".getBytes());              //bind code, 7 bytes
       baos.write(new byte[3]);                       //signing challenge, 3 bytes
       baos.write("Dallas Semiconductor".length());   //provider of the service
       baos.write(20);                                //initial signature length (20)
       baos.write(0);                                 //extra data length
       baos.write("Dallas Semiconductor".getBytes()); //provider of the service
       baos.write(new byte[20]);                      //initial signature
       baos.write("".getBytes());                     //any extra data
       baos.write(0);                                 //an encryption code
       byte[] data = baos.toByteArray();
        
       //start writing the page at page 1 of the Coprocessor,
       //since page 0 will be taken by the TMEX root directory file
       int page = 1;
       byte[] this_page = new byte[32];
       int index = 0;
       int length;
       while (index < data.length)
       {
           //this loop handles breaking the data into 
           //TMEX file structure compatible pages
           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//make sure this is an inverted CRC
           crc = (~crc) & 0x0ffff;
             
           this_page[length+2] = (byte) crc;
           this_page[length+3] = (byte) (crc >> 8);
              
           owc18.writeDataPage(page,this_page);
           page++;
       }
       //now we have written the entire COPR.0 file, now we 
       //need to fix up page 0, the root directory

       //make a bitmask of which pages we have used.  since we
       //have used all pages from 0 to (page-1), this trick 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 recalculate the CRC for the root directory
       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
       if ((!owc18.eraseScratchPad(0)) || (!owc18.writeDataPage(0,page0)))
       {
           System.out.println("Failed to write page 0");
           return;
       }

       //done with root directory, now install signing secret and an authentication secret
       byte[] sign_secret = "this is my signing secret".getBytes();
       byte[] auth_secret = "this is my authentication secret".getBytes();
       
       if (!owc18.installMasterSecret(8,sign_secret,0))
           System.out.println("Could not install master signing secret!");
       else 
           System.out.println("Signing secret installed.");
       if (!owc18.installMasterSecret(7,auth_secret,7))
           System.out.println("Could not install master authentication secret!");
       else 
           System.out.println("Authentication secret installed.");
 

In order to initialize a SHAiButton object to be used as a Coprocessor iButton, read the "COPR.0" file from the DS1963S using the readFile(int) command. Use the file data to make calls to setup this iButton as a Coprocessor. See isCoprocessor() for which parameters must be set to act as a Coprocessor. The next section also describes setting up a SHAiButton object to act as a Coprocessor.

Initializing a User iButton

Initializing a User iButton also requires two steps. The first is finding and gathering information from a Coprocessor iButton. The second is installing an authentication secret and an initial money page on the User iButton.

I. The following code shows how gather data from a Coprocessor iButton and setup a SHAiButton object to act as a Coprocessor.

       //adapter is a DSPortAdapter, ID is the byte[] address of the DS1963S
       // that has already been found
       SHAiButton copr = null;
       OneWireContainer18 ds1963S = (OneWireContainer18) adapter.getDeviceContainer(ID);
       ds1963S.setupContainer(adapter,ID);
       ds1963S.setSpeedCheck(false);
       SHAiButton sha = new SHAiButton(ds1963S);
       byte[] filebuffer = new byte[400];
       filelength = sha.readFile(0, filebuffer);

       //look for the file "COPR.000"
       byte[] file = "COPR".getBytes();
       int filepage = -1;

       for (i=0;i < filelength;i++)
       {
           if (filebuffer[i]==file[0])
             if (filebuffer[i+1]==file[1])
               if (filebuffer[i+2]==file[2])
                 if (filebuffer[i+3]==file[3])
                   if (filebuffer[i+4]==(byte)0)  //000 is the extension
                       filepage = filebuffer[i+5];
       }
       if (filepage==-1)
       {
           System.out.println("Could not find the file page!!!");
       }
       else
       {
           filelength = sha.readFile(filepage, filebuffer);
           sha.setFilename(filebuffer,0);
           sha.setSigningPageNumber(filebuffer[5]);
           sha.setAuthenticationPageNumber(filebuffer[6]);
           sha.setWorkspacePageNumber(filebuffer[7]);
           //note: not all Dallas Semiconductor demo's include the bind
           //  data and bind code in the COPR.000 file, which is why
           //  we check the file length here
           if (filelength >= 45)
               sha.setBindData(filebuffer,13);
           if (filelength >= 52)
               sha.setBindCode(filebuffer,45);
           if (sha.isCoprocessor())
           {
               copr = sha;
               System.out.println("Found a coprocessor for service \""+sha.getUserFileName()+"\"");
           }
       }                        
 

II. Once the Coprocessor iButton has been set, any other DS1963S can be set as a User iButton. The following code shows how to create a User iButton that would be compatible with the Coprocessor iButton created in the section above.

       //byte[] page0 should be the same as in the Coprocessor initialization section
       //owc18 has already been set up to talk to a new DS1963S
       owc18.setSpeedCheck(false);
       
       SHAiButton sha = new SHAiButton(owc18);
 
       int auth_page = 13;   //put the money page and authentication data on page 13
       int tid = 0x1234;     //transaction ID, defined by credit/debit authority
       int fact = 0x8b48;    //ISO4217 monetary unit code and multiplier
       int data_type = 0;    //certificate type, defined by credit/debit authority
       
       byte[] auth_secret = "this is my authentication secret".getBytes();

       if (!owc18.installMasterSecret(auth_page,auth_secret,auth_page&7))
           System.out.println("Could not install master authentication secret!");
       else 
           System.out.println("Authentication secret installed.");

       //now we need to bind this data to the user iButton
       if (!owc18.bindSecretToiButton(auth_page,copr.getBindData(), copr.getBindCode(), auth_page & 7))
       {
           System.out.println("Failed to bind the secret to the iButton");
       }
       else
           System.out.println("Authentication secret bound to iButton");

       //now the authentication secret has been installed, 
       //we need to put some initial money on the page
       byte[] default_page = new byte[32];
       default_page[1] = (byte)0x34;
       default_page[2] = (byte)0x12;
       default_page[3] = (byte)0x48;
       default_page[4] = (byte)0x8b;

       sha.setUser(auth_page);
       //this puts $100.00 on the iButton (10000 pennies)
       //copr is defined in part I of this User initialization example
       if (copr.signDataFile(sha,10000,-1, default_page))
           System.out.println("Successfully set up user iButton");
       else
           System.out.println("Failed to set up user iButton");

       //we've put money on the iButton, now we need a root directory
       char[] name = copr.getUserFileName().toCharArray();
       int ext = copr.getUserFileExtension();
       page0[8] = (byte)name[0];    
       page0[9] = (byte)name[1];    
       page0[10] = (byte)name[2];    
       page0[11] = (byte)name[3];    
       page0[12] = (byte)ext;    
       page0[13] = (byte)auth_page;
       page0[14] = (byte)1;

       //the mask should represent that we are using page 0 and page auth_page
       int mask = 0x01 | (0x01 << auth_page);
           
       //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 redo the CRC and write it
       int CRC = ~CRC16.compute(page0,0,page0[0]+1);
       page0[page0[0]+1] = (byte)CRC;
       page0[page0[0]+2] = (byte)(CRC >> 8);

       //now put this new page 0 on the iButton as a root directory
       if ((!owc18.eraseScratchPad(0)) || (!owc18.writeDataPage(0,page0)))
       {
           System.out.println("Failed to write page 0");
           return;
       }   
 
Performing an Authentication and a Transaction

The code in OneWireContainer18 and this class has been written to make transactions fast in Java. While the example code for initializing Coprocessors and Users is not extremely fast, the code in the critical path of a transaction is fast. The critical path of a transaction can be defined as everything that happens from the time an iButton is inserted for a transaction until a new data file has been signed and installed on the User iButton. This means that code to find a Coprocessor and initialize a SHAiButton object to act as a Coprocessor is not in the critical path. The code below uses a SHAiButton object copr. It can be set up in the same manner as described in section I of "Initializing a User iButton".

       OneWireContainer18 owc = new OneWireContainer18();
       owc.setSpeedCheck(false);
       SHAiButton user = new SHAiButton();
       byte[] file     = copr.getUserFileName().getBytes();
       byte   ext      = ( byte ) copr.getUserFileExtension();

       //adapter is our DSPortAdapter object, it has just found
       // a DS1963S to transact with

       //CRITICAL PATH BEGINS HERE, let's time it
       long    t1      = System.currentTimeMillis();
       //first let's start talking in overdrive
       adapter.reset();
       adapter.putByte(0x3c);
       adapter.setSpeed(DSPortAdapter.SPEED_OVERDRIVE);
       adapter.reset();

       adapter.getAddress(ID);
       owc.setupContainer(adapter, ID);
       user.setiButton(owc);

       //now we need to find the file installed!
       //filebuffer is a previously defined, large byte array (~400 bytes)
       filelength = user.readFile(0, filebuffer);

       int filepage = -1;

       for (i = 0; i < filelength-5; i++)
       {
          if (filebuffer [i] == file [0])
             if (filebuffer [i + 1] == file [1])
                if (filebuffer [i + 2] == file [2])
                   if (filebuffer [i + 3] == file [3])
                      if (filebuffer [i + 4] == ext)
                      {
                         //we have found the service file on this iButton
                         filepage = filebuffer [i + 5];
                         i = filelength;
                      }
       }

       if (filepage == -1)
       {
          System.out.println("No service installed!");
       }
       else
       {
          //set up this iButton as a User iButton
          user.setUser(filepage);
          int wcc = copr.verifyAuthentication(user, pagedata);

          if (wcc > 0)
          {
             //verify that our money is valid
             if (copr.verifyUserMoney(pagedata, user,wcc))
             {
                //this is how the cash is stored in our money page
                int cash = (pagedata [5] & 0x0ff) | ((pagedata [6] & 0x0ff) << 8)
                           | ((pagedata [7] & 0x0ff) << 16);
                //amount is the amount to be deducted or added
                if (cash + amount >= 0)
                {
                   //put the new amount of money onto the iButton
                   if (copr.signDataFile(user, cash + amount, wcc, pagedata))
                   {
                      //CRITICAL PATH IS OVER, stop timing
                      long t2      =  System.currentTimeMillis();
                      int  newcash = cash + amount;
                      //now report our transaction

                      System.out.println("-----------------------");
                      System.out.println("| Device  : "+ Long.toHexString(t));
                       
                      //note that I am assuming the money is stored as US $0.01
                      //rather than reading the monetary unit code and multiplier from 
                      //the iButton
                      System.out.println("| Balance : $ " + (newcash / 100)+ "." + (newcash % 100) + " US");
                      System.out.println("| Transaction time : " + (t2 - t1) + " ms");
                      System.out.println("-----------------------\r\n");
                   }
                }
             }
          }
       }
       //put the 1-Wire bus back at normal speed
       adapter.setSpeed(DSPortAdapter.SPEED_REGULAR);
       adapter.reset();
 

Relevant Links:

This class makes no calls to the adapter level, i.e. it does no direct talking to the device. It uses the methods in OneWireContainer18 to implement transactions.

Version:
0.00, 28 Aug 2000
See Also:
OneWireContainer18

Field Summary
static int AUTHENTICATION_FAILED_ERROR
          Useful constant for getLastError().
static int BIND_SECRET_ERROR
          Useful constant for getLastError().
static int CRC_ERROR
          Useful constant for getLastError().
static int ERASE_SCRATCHPAD_ERROR
          Useful constant for getLastError().
static int NO_COPROCESSOR_ERROR
          Useful constant for getLastError().
static int NO_ERROR
          Useful constant for getLastError().
static int NO_USER_ERROR
          Useful constant for getLastError().
static int READ_AUTHENTICATED_ERROR
          Useful constant for getLastError().
static int READ_MEMORY_PAGE_ERROR
          Useful constant for getLastError().
static int READ_SCRATCHPAD_ERROR
          Useful constant for getLastError().
static int SHA_FUNCTION_ERROR
          Useful constant for getLastError().
static int VERIFICATION_FAILED_ERROR
          Useful constant for getLastError().
static int WRITE_MEMORY_PAGE_ERROR
          Useful constant for getLastError().
static int WRITE_SCRATCHPAD_ERROR
          Useful constant for getLastError().
 
Constructor Summary
SHAiButton()
          Creates a new object to handle authentications and transactions with a DS1963S SHA iButton.
SHAiButton(OneWireContainer18 ibc)
          Creates a new object to handle authentications and transactions with a DS1963S SHA iButton.
 
Method Summary
 int answerChallenge(byte[] challenge, byte[] mac, byte[] pagedata)
          Answers a challenge from the Coprocessor iButton by signing a data page.
 boolean generateChallenge(int page_number, int offset, byte[] ch)
          Generates a 3 byte random challenge in the iButton, sufficient to be used as a challenge to be answered by a User iButton.
 byte[] getBindCode()
          Get the 7-byte binding code.
 byte[] getBindData()
          Gets the 32-byte binding data.
 int getLastError()
          Returns the last error that occurred.
 int getUserFileExtension()
          Returns the extension of the Coprocessor Service File.
 String getUserFileName()
          Returns the name of the Coprocessor Service File.
 boolean isCoprocessor()
          Determines if this SHAiButton has enough of its parameters set to act as a Coprocessor iButton.
 int readFile(int start_page, byte[] page)
          Reads a TMEX file starting at the specified page.
 void setAuthenticationPageNumber(int pg)
          Sets the page that the authentication secret is installed on in this Coprocessor iButton.
 void setBindCode(byte[] buf, int offset)
          Sets the binding code for this SHA iButton.
 void setBindData(byte[] buf, int offset)
          Sets the binding data for this SHA iButton.
 void setFilename(byte[] buf, int start)
          Sets the file name and extension for the Coprocessor Service File.
 void setiButton(OneWireContainer18 ibc)
          Sets the OneWireContainer18 object that this SHAiButton will use to communicate with a DS1963S.
 void setInitialSignature(byte[] sig_ini, int start)
          Sets the optional initial signature for a Coprocessor iButton.
 void setSigningChallenge(byte[] ch, int start)
          Sets the optional signing challenge for a Coprocessor iButton.
 void setSigningPageNumber(int pg)
          Sets the page used to generate signatures on the Coprocessor iButton.
 boolean setUser(int file_page_number)
          Sets up this SHAiButton as a User iButton.
 void setWorkspacePageNumber(int pg)
          Sets the page this Coprocessor iButton will use to recreate User iButton specific secrets.
 boolean signDataFile(SHAiButton user, int newbalance, int write_cycle_counter, byte[] pagedata)
          Makes this Coprocessor iButton produce a signature for a new amount of money for the User iButton, then places the new money file on user.
 String toString()
          Returns a java.lang.String with the unique address of the SHA iButton in hex.
 int verifyAuthentication(SHAiButton user, byte[] pagedata)
          Determines if the SHAiButton user belongs to the system defined by this Coprocessor iButton.
 boolean verifyUserMoney(byte[] userpage, SHAiButton user, int wcc)
          Verifies a User iButton's signed data on this Coprocessor iButton.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, wait, wait, wait
 

Field Detail

NO_ERROR

public static final int NO_ERROR
Useful constant for getLastError(). Means no error occurred.
See Also:
getLastError()

ERASE_SCRATCHPAD_ERROR

public static final int ERASE_SCRATCHPAD_ERROR
Useful constant for getLastError(). Means an error occurred during an Erase Scratch Pad activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

WRITE_SCRATCHPAD_ERROR

public static final int WRITE_SCRATCHPAD_ERROR
Useful constant for getLastError(). Means an error occurred during an Erase Scratch Pad activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

READ_SCRATCHPAD_ERROR

public static final int READ_SCRATCHPAD_ERROR
Useful constant for getLastError(). Means an error occurred during a Read Scratch Pad activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

READ_MEMORY_PAGE_ERROR

public static final int READ_MEMORY_PAGE_ERROR
Useful constant for getLastError(). Means an error occurred during a Read Memory Page activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

READ_AUTHENTICATED_ERROR

public static final int READ_AUTHENTICATED_ERROR
Useful constant for getLastError(). Means an error occurred during a Read Authenticated Memory Page activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

BIND_SECRET_ERROR

public static final int BIND_SECRET_ERROR
Useful constant for getLastError(). Means an error occurred during a Secret Binding activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

WRITE_MEMORY_PAGE_ERROR

public static final int WRITE_MEMORY_PAGE_ERROR
Useful constant for getLastError(). Means an error occurred during a Write Memory Page activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

SHA_FUNCTION_ERROR

public static final int SHA_FUNCTION_ERROR
Useful constant for getLastError(). Means an error occurred during a cryptographic activity with the DS1963S. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

CRC_ERROR

public static final int CRC_ERROR
Useful constant for getLastError(). Means a CRC from the DS1963S was invalid. Probably due to a communication error, meaning a retry might succeed.
See Also:
getLastError()

NO_COPROCESSOR_ERROR

public static final int NO_COPROCESSOR_ERROR
Useful constant for getLastError(). Means that the coprocessor SHAiButton object has not been successfully setup.
See Also:
getLastError()

NO_USER_ERROR

public static final int NO_USER_ERROR
Useful constant for getLastError(). Means that the user SHAiButton object has not been successfully setup.
See Also:
getLastError()

AUTHENTICATION_FAILED_ERROR

public static final int AUTHENTICATION_FAILED_ERROR
Useful constant for getLastError(). It is possible this error is due to a communication error, and that a retry will be successful. However, it can also mean that the user SHAiButton does not belong to the system (could not authenticate).
See Also:
getLastError()

VERIFICATION_FAILED_ERROR

public static final int VERIFICATION_FAILED_ERROR
Useful constant for getLastError(). It is possible this error is due to a communication error, and that a retry will be successful. However, it can also mean that the user SHAiButton's money data file is invalid (has been tampered with).
See Also:
getLastError()
Constructor Detail

SHAiButton

public SHAiButton()

Creates a new object to handle authentications and transactions with a DS1963S SHA iButton. This constructor does not initialize any parameters, but only creates allocation for several instance variables. Before doing any processing using this SHAiButton, call setiButton(OneWireContainer18) to initialize this object. This method is meant to be used outside of the 'critical path' of a transaction:

     //initialize variables to use in the transaction loop here
     . . .
     SHAiButton user = new SHAiButton();
     OneWireContainer18 owc18 = new OneWireContainer18();
     . . .
     while (allowingTransactions)
     {
         //here begins the 'critical path' of the transaction
         . . . 
         //find a new DS1963S iButton inserted
         // 'ID' is the 8 byte address of the device
         // 'adapter' is the DSPortAdapter object to talk to the device
         owc18.setupContainer(adapter, ID);
         user.setiButton(owc18);
         . . .
         //now perform transaction
     }
 
This is less expensive than creating a new SHAiButton in every instance of the loop.

See Also:
SHAiButton(OneWireContainer18), setiButton(OneWireContainer18)

SHAiButton

public SHAiButton(OneWireContainer18 ibc)
Creates a new object to handle authentications and transactions with a DS1963S SHA iButton. See the default constructor for speed considerations when creating a new SHAiButton object for a User iButton.
Parameters:
ibc - the initialized OneWireContainer object for the DS1963S that will belong to this SHAiButton
See Also:
SHAiButton()
Method Detail

setiButton

public void setiButton(OneWireContainer18 ibc)

Sets the OneWireContainer18 object that this SHAiButton will use to communicate with a DS1963S. This method should be used in conjunction with the constructor SHAiButton(). Note that the following two code snippets are functionally equivalent:

     . . . 
     //owc18 is a OneWireContainer18 already initialized
     SHAiButton sha = new SHAiButton(owc18);
     . . .
 
is functionally equivalent to
     . . . 
     //owc18 is a OneWireContainer18 already initialized
     SHAiButton sha = new SHAiButton();
     sha.setiButton(owc18);
     . . .
 
The reasons for using the latter code are discussed in the SHAiButton() documentation.

See Also:
SHAiButton(), SHAiButton(OneWireContainer18)

setBindData

public void setBindData(byte[] buf,
                        int offset)
Sets the binding data for this SHA iButton. This should be 32 bytes. Anything shorter will result in an java.lang.ArrayIndexOutOfBoundsException. Data beyond the first 32 bytes will be ignored. This data is used to bind secrets to User iButtons and also to re-create user signatures in Coprocessor iButtons.
Parameters:
buf - buffer with the binding data
offset - offset to start reading from the buffer (32 bytes will be read from offset)
See Also:
getBindData(), setBindCode(byte[],int)

setBindCode

public void setBindCode(byte[] buf,
                        int offset)
Sets the binding code for this SHA iButton. This should be 7 bytes. Anything shorter will result in an java.lang.ArrayIndexOutOfBoundsException. Data beyond the first 7 bytes will be ignored. This data is written to the scratchpad for binding secrets to User iButtons and also to recreate User iButton signatures on Coprocessor iButtons.
Parameters:
buf - buffer with the binding data
offset - offset to start reading from the buffer (7 bytes will be read from offset)
See Also:
getBindCode(), setBindData(byte[],int)

getBindData

public byte[] getBindData()
Gets the 32-byte binding data. This data is used to bind secrets to User iButtons and also to re-create user signatures in Coprocessor iButtons.
Returns:
the binding data (length 32)
See Also:
getBindCode(), setBindData(byte[],int)

getBindCode

public byte[] getBindCode()
Get the 7-byte binding code. This data is written to the scratchpad for binding secrets to User iButtons and also to recreate User iButton signatures on Coprocessor iButtons.
Returns:
the binding code (length 7)
See Also:
getBindData(), setBindCode(byte[],int)

answerChallenge

public int answerChallenge(byte[] challenge,
                           byte[] mac,
                           byte[] pagedata)
                    throws OneWireIOException,
                           OneWireException

Answers a challenge from the Coprocessor iButton by signing a data page. This method is used by the User iButton.

To authenticate a User iButton to a system, the Coprocessor iButton generates a random challenge. This way, an attacker cannot simply memorize one successful response and fake its way into the system. The User iButton signs a data page with its system authentication secret. The random number generated by the Coprocessor and the write cycle counter of the user's data page are also part of the signature. If the Coprocessor can verify this signature, then the User iButton belongs to the system.

Note that authentication does not guarantee the validity of the data in the page. It must be verified using verifyUserMoney().

The only true input to this method is the random byte array challenge. The other byte arrays are used to return information that the Coprocessor iButton needs to verify the signature.

Parameters:
challenge - a 3 byte random challenge (generated by the Coprocessor DS1963S)
mac - buffer for the returned signature generated by the User DS1963S (must be at least 20 bytes long)
pagedata - buffer for the page data the User DS1963S signed (must be at least 32 bytes long)
Returns:
the write cycle counter value of the page returned in pagedata, of -1 if an error occurred (use getLastError() for more information on the type of error)
Throws:
OneWireIOException - on a 1-Wire communication error such as reading an incorrect CRC from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
OneWireException - on a communication or setup error with the 1-Wire adapter
See Also:
verifyUserMoney(byte[],SHAiButton,int), generateChallenge(int,int,byte[]), getLastError()

generateChallenge

public boolean generateChallenge(int page_number,
                                 int offset,
                                 byte[] ch)
                          throws OneWireIOException,
                                 OneWireException

Generates a 3 byte random challenge in the iButton, sufficient to be used as a challenge to be answered by a User iButton. This method will normally be called by a Coprocessor iButton.

The DS1963S will generate 20 bytes of pseudo random data, though only 3 bytes are needed for the challenge. Programs can add more 'randomness' by selecting different bytes from the 20 bytes of random data using the offset parameter.

The random number generator is actually the DS1963S's SHA engine, which requires page data to compute a hash. Select a page number with the page_number parameter.

Parameters:
page_number - page number that the random number generation will be based on (cannot be 0 or 8)
offset - offset into the 20 random bytes to draw random data from (must be in range 0-16)
ch - buffer for the challenge to be returned (must be of length 3 or more)
Returns:
true if successful, false if an error occurred (use getLastError() for more information on the type of error)
Throws:
OneWireIOException - on a 1-Wire communication error such as reading an incorrect CRC from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
OneWireException - on a communication or setup error with the 1-Wire adapter
See Also:
answerChallenge(byte[],byte[],byte[]), getLastError()

verifyAuthentication

public int verifyAuthentication(SHAiButton user,
                                byte[] pagedata)
                         throws OneWireException,
                                OneWireIOException

Determines if the SHAiButton user belongs to the system defined by this Coprocessor iButton. This method can only be called by a Coprocessor iButton. See the usage example in this class for initializing up a Coprocessor iButton.

The SHAiButton user must have previously been initialized with the setUser() method.

The TMEX formatted page with the money signature is returned in the parameter pagedata. pagedata must be at least 32 bytes long to hold this data. If the verification is successful, verify that the data in the returned page is valid with the verifyUserMoney() method.

Failure of this method does not necessarily mean that the User iButton does not belong to the system. It is possible that a communication disruption here could cause a CRC error that would be indistinguishable from a failed authentication. However, repeated attempts should reveal whether it was truly a communication problem or a User iButton that does not belong to the system.

This method starts by generating a random challenge on the Coprocessor iButton. The User iButton then answers that challenge by signing a specified data page. The Coprocessor then recreates the User iButton's secret, and signs the same data that the User signed. If the signatures match, the authentication succeeded.

Parameters:
user - the SHAiButton that will answer the challenge and be authenticated, previously setup with setUser()
pagedata - buffer for the data page that will be used by user to generate the authentication signature
Returns:
write cycle counter of the page used in authentication, or -1 if it could not authenticate (use getLastError() for more information on the type of error)
Throws:
OneWireIOException - on a 1-Wire communication error such as reading an incorrect CRC from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
OneWireException - on a communication or setup error with the 1-Wire adapter
See Also:
generateChallenge(int,int,byte[]), answerChallenge(byte[],byte[],byte[]), OneWireContainer18.bindSecretToiButton(int,byte[],byte[],int), OneWireContainer18.SHAFunction(byte,int), OneWireContainer18.matchScratchPad(byte[]), verifyUserMoney(byte[],SHAiButton,int), getLastError(), setUser(int)

isCoprocessor

public boolean isCoprocessor()

Determines if this SHAiButton has enough of its parameters set to act as a Coprocessor iButton. To be a Coprocessor, the following must be set:

The Coprocessor uses other parameters, but these have default and do not need to be set unless required by the system. These parameters are:

Returns:
true if this iButton can act as a Coprocessor
See Also:
setFilename(byte[],int), setSigningPageNumber(int), setAuthenticationPageNumber(int), setWorkspacePageNumber(int), setBindCode(byte[],int), setBindData(byte[],int), setSigningChallenge(byte[],int), setInitialSignature(byte[],int)

toString

public String toString()
Returns a java.lang.String with the unique address of the SHA iButton in hex. This is only a convenience method that calls OneWireContainer.getAddressAsString() on the OneWireContainer18 object this SHAiButton uses.
Overrides:
toString in class Object
Returns:
unique address of the SHA iButton
See Also:
OneWireContainer.getAddressAsString()

setUser

public boolean setUser(int file_page_number)

Sets up this SHAiButton as a User iButton. To act as a User iButton, this object only needs to know what page number its service file is located in. This is the page that contains signed data and/or is used for authentication. Methods that take a User SHAiButton as a parameter check to make sure that the User has been set up using this method.

See the usage section for an example on how to discover the page number where the service file is located on a User iButton.

Parameters:
file_page_number - page number that contains signed data
Returns:
true if successful

setInitialSignature

public void setInitialSignature(byte[] sig_ini,
                                int start)

Sets the optional initial signature for a Coprocessor iButton. When a data page is signed, the place in the page where the signature will be stored is initialized to this value.

Up to 20 bytes will be copied from sig_ini if its length permits. The initial signature defaults to all 0's. If sig_ini is not long enough to copy 20 bytes, the excess in the initial signature will remain at all 0's.

Parameters:
sig_ini - new initial signature
start - offset to start reading the new initial signature from sig_ini

setSigningChallenge

public void setSigningChallenge(byte[] ch,
                                int start)

Sets the optional signing challenge for a Coprocessor iButton. All signatures on the Coprocessor have a location for challenges. This may optionally be set. Note that this is not the same as the challenge in the authentication sequence. This is data that will be included with the Coprocessor's signature of a data (money) file.

Up to 3 bytes will be copied from ch if its length permits. The signing challenge defaults to all 0's. If ch is not long enough to copy 3 bytes, the excess in the signing challenge will remain at all 0's.

Parameters:
ch - Byte new random challenge
start - offset to start reading the new signing challenge from ch

setFilename

public void setFilename(byte[] buf,
                        int start)

Sets the file name and extension for the Coprocessor Service File. This file will appear on User iButtons and will contain data signed by the Coprocessor.

The parameter buf should have 5 bytes to represent the Coprocessor Service File name and extension. For example, if the service file is named "DLSM.102" (extension = 102), buf should look like:

   buf[start + 0] = (byte)'D';
   buf[start + 1] = (byte)'L';
   buf[start + 2] = (byte)'S';
   buf[start + 3] = (byte)'M';
   buf[start + 4] = (byte)102;
 

This function must be called to initialize a Coprocessor iButton.

Parameters:
buf - the file name and extension
start - index to start copying from buf
See Also:
isCoprocessor(), getUserFileName(), getUserFileExtension()

setSigningPageNumber

public void setSigningPageNumber(int pg)

Sets the page used to generate signatures on the Coprocessor iButton. The hardware of the DS1963S allows only page 0 or page 8 to be used for this function. Since this class assumes the TMEX file structure (requiring page 0 to be a directory page), this method should be called setting the signing page to page 8.

This function must be called to initialize a Coprocessor iButton.

Parameters:
pg - page number for generating signatures
See Also:
isCoprocessor()

setAuthenticationPageNumber

public void setAuthenticationPageNumber(int pg)

Sets the page that the authentication secret is installed on in this Coprocessor iButton. This secret will be used to recreate User iButton specific authentication secrets during the authentication process.

This function must be called to initialize a Coprocessor iButton.

Parameters:
pg - page the installation secret is installed on
See Also:
isCoprocessor()

setWorkspacePageNumber

public void setWorkspacePageNumber(int pg)

Sets the page this Coprocessor iButton will use to recreate User iButton specific secrets. No data should be stored here, as it will be overridden when the system tries to perform an authentication.

This function must be called to initialize a Coprocessor iButton.

Parameters:
pg - free page that can be used as workspace
See Also:
isCoprocessor()

getUserFileName

public String getUserFileName()
Returns the name of the Coprocessor Service File. Throws a java.lang.NullPointerException if the file name has not been set. If the file name is 'DLSM.102', this returns the java.lang.String "DLSM".
Returns:
the name of the Coprocessor Service File
See Also:
setFilename(byte[],int)

getUserFileExtension

public int getUserFileExtension()
Returns the extension of the Coprocessor Service File. Throws a java.lang.NullPointerException if the file name has not been set. If the file name is 'DLSM.102', this returns the int 102.
Returns:
the extension of the Coprocessor Service File
See Also:
setFilename(byte[],int)

signDataFile

public boolean signDataFile(SHAiButton user,
                            int newbalance,
                            int write_cycle_counter,
                            byte[] pagedata)
                     throws OneWireIOException,
                            OneWireException

Makes this Coprocessor iButton produce a signature for a new amount of money for the User iButton, then places the new money file on user.

The write cycle counter is part of a valid signature. The Coprocessor iButton uses it to verify a signature of a signed data file. It should be returned in a normal transaction from the method verifyAuthentication(). However, if this method fails or if this is the initialization of a User iButton, use -1 for the write cycle counter. Then, the code will determine the correct write cycle counter value directly from the User iButton. However, do not do this in a normal transaction sequence as it takes longer.

Note that this method specifically handles TMEX file formats.

Parameters:
user - SHAiButton object that has been initialized as a user with setUser(int)
newbalance - new balance to be stored on the User iButton
write_cycle_counter - the current write cycle counter of the User iButton's money page
pagedata - the money data page (from verifyAuthentication())
Returns:
true if successful, false if an error occurred (use getLastError() for more information on the type of error)
Throws:
OneWireIOException - on a 1-Wire communication error such as reading an incorrect CRC from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
OneWireException - on a communication or setup error with the 1-Wire adapter
See Also:
verifyUserMoney(byte[],SHAiButton,int), getLastError(), verifyAuthentication(SHAiButton,byte[])

verifyUserMoney

public boolean verifyUserMoney(byte[] userpage,
                               SHAiButton user,
                               int wcc)
                        throws OneWireIOException,
                               OneWireException
Verifies a User iButton's signed data on this Coprocessor iButton. The Coprocessor must recreate the signature based on the data in the file and the write cycle counter of the file, and then match that with the signature embedded in the file from the User iButton.
Parameters:
userpage - the full 32 byte TMEX file from the User iButton (from verifyAuthentication())
user - SHAiButton object that has been initialized as a user with setUser(int)
wcc - write cycle counter of the user's money page (from verifyAuthentication())
Returns:
true if the data file is valid, false if an error occurred (use getLastError() for more information on the type of error)
Throws:
OneWireIOException - on a 1-Wire communication error such as reading an incorrect CRC from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
OneWireException - on a communication or setup error with the 1-Wire adapter
See Also:
verifyAuthentication(SHAiButton,byte[]), getLastError()

readFile

public int readFile(int start_page,
                    byte[] page)
             throws OneWireIOException,
                    OneWireException

Reads a TMEX file starting at the specified page. The parameter page will store the file, or up to page.length bytes of the file. This method performs CRC checks to ensure the validity of the file.

Call readFile(0) to read the root directory.

Parameters:
start_page - page of the DS1963S to start reading from
page - buffer for the file data
Returns:
length of data read
Throws:
OneWireIOException - on a 1-Wire communication error such as reading an incorrect CRC from a 1-Wire device. This could be caused by a physical interruption in the 1-Wire Network due to shorts or a newly arriving 1-Wire device issuing a 'presence pulse'.
OneWireException - on a communication or setup error with the 1-Wire adapter

getLastError

public int getLastError()
Returns the last error that occurred. Most errors indicate a communication or setup problem, though some errors, if repeated, can indicate that an iButton does not belong to the system (cannot authenticate) or has an invalid money file (cannot verify signed data).
Returns:
the last error
See Also:
NO_ERROR, ERASE_SCRATCHPAD_ERROR, WRITE_SCRATCHPAD_ERROR, READ_SCRATCHPAD_ERROR, READ_MEMORY_PAGE_ERROR, READ_AUTHENTICATED_ERROR, BIND_SECRET_ERROR, WRITE_MEMORY_PAGE_ERROR, SHA_FUNCTION_ERROR, CRC_ERROR, NO_COPROCESSOR_ERROR, NO_USER_ERROR, AUTHENTICATION_FAILED_ERROR, VERIFICATION_FAILED_ERROR