/// <summary> /// Does the writing of transaction data to the user button as well /// as actually signing the data with the coprocessor. /// </summary> private bool writeTransactionData(SHAiButtonUser user, int transID, int balance, byte[] accountData) { //init local vars SHAiButtonCopr copr = this.copr; int acctPageNum = user.AccountPageNumber; byte[] scratchpad = this.writeTransactionData_scratchpad; // length of the TMEX file - 28 data, 1 cont. ptr accountData[I_FILE_LENGTH] = (byte)29; // transaction ID - 2 data bytes accountData[I_TRANSACTION_ID + 0] = (byte)transID; accountData[I_TRANSACTION_ID + 1] = (byte)((int)((uint)transID >> 8)); // conversion factor - 2 data bytes accountData[I_CONVERSION_FACTOR + 0] = 0x8B; accountData[I_CONVERSION_FACTOR + 1] = 0x48; // account balance - 3 data bytes Convert.toByteArray(balance, accountData, I_BALANCE, 3); // initial signature - 20 data bytes copr.getInitialSignature(accountData, I_SIGNATURE); // data type code - dynamic: 0x00, static: 0x01 accountData[I_DATA_TYPE_CODE] = 0x01; // continuation pointer for TMEX file accountData[I_CONTINUATION_PTR] = 0x00; // clear out the crc16 - 2 data bytes accountData[I_FILE_CRC16 + 0] = 0x00; accountData[I_FILE_CRC16 + 1] = 0x00; //we need to increment the writeCycleCounter since we will be writing to the part int wcc = user.WriteCycleCounter; if (wcc > 0) { //copy the write cycle counter into scratchpad Convert.toByteArray(wcc + 1, scratchpad, 8, 4); } else { if (user.hasWriteCycleCounter()) { // failed to read account data this.lastError = SHATransaction.USER_READ_AUTH_FAILED; return(false); } Array.Copy(ffBlock, 0, scratchpad, 8, 4); } // svcPageNumber, followed by address of device scratchpad[12] = (byte)acctPageNum; user.getAddress(scratchpad, 13, 7); // copy in the signing challenge copr.getSigningChallenge(scratchpad, 20); // sign the data, return the mac right into accountData copr.createDataSignature(accountData, scratchpad, accountData, I_SIGNATURE); //after signature make sure to dump in the inverted CRC int crc = ~CRC16.compute(accountData, 0, accountData[I_FILE_LENGTH] + 1, acctPageNum); //set the the crc16 bytes accountData[I_FILE_CRC16 + 0] = (byte)crc; accountData[I_FILE_CRC16 + 1] = (byte)(crc >> 8); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// OneWireEventSource.Log.Debug("------------------------------------"); OneWireEventSource.Log.Debug("writing transaction data"); OneWireEventSource.Log.Debug("acctPageNum: " + acctPageNum); OneWireEventSource.Log.Debug("accountData: " + Convert.toHexString(accountData)); OneWireEventSource.Log.Debug("------------------------------------"); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// // write it to the button try { if (user.writeAccountData(accountData, 0)) { return(true); } } catch (OneWireException owe) { OneWireEventSource.Log.Debug(owe.ToString()); } this.lastError = SHATransaction.USER_WRITE_DATA_FAILED; return(false); }
/// <summary> /// <P>Verifies user's account data. Account data is "verified" if and /// only if the account balance is greater than zero and the digital /// signature matches the signature recreated by the coprocessor.</P> /// /// <P><B>Flow of action: </B> /// <ul> /// <li> Read the account data from user </li> /// <li> Extract account balance </li> /// <li> Debit money from balance </li> /// <li> Insert the new balance </li> /// <li> Reset the digital signature </li> /// <li> Use coprocessor to sign account data </li> /// <li> Insert the new digital signature </li> /// <li> Write the account data to the user </li> /// </ul></P> /// /// <P>If previous steps have been executed, all "Read" commands on /// the user are reading from cached data.</P> /// </summary> /// <param name="user"> SHAiButtonUser upon which the transaction occurs. /// </param> /// <returns> <code>true</code> if and only if the account balance is /// greater than zero and digital signature matches the signature /// recreated by the coprocessor. /// </returns> /// <seealso cref= SHAiButtonUser#readAccountData(byte[],int) </seealso> /// <seealso cref= SHAiButtonCopr#verifySignature(byte[],byte[],byte[]) </seealso> /// <seealso cref= #getLastError() </seealso> public override bool verifyTransactionData(SHAiButtonUser user) { lock (this) { //clear any error this.lastError = NO_ERROR; //init local vars byte[] scratchpad = this.verifyData_scratchpad; byte[] accountData = this.verifyData_accountData; byte[] verify_mac = this.verifyData_mac; //if verifyUser was called, this is a read of cached data int wcc = user.WriteCycleCounter; //if verifyUser was called, this is a read of cached data user.readAccountData(accountData, 0); //get the user's balance from accountData int balance = Convert.toInt(accountData, I_BALANCE, 3); if (balance < 0) { this.lastError = USER_BAD_ACCOUNT_DATA; return(false); } this.userBalance = balance; //get the mac from the account data page Array.Copy(accountData, I_SIGNATURE, verify_mac, 0, 20); //now lets reset the mac copr.getInitialSignature(accountData, I_SIGNATURE); //and reset the CRC accountData[I_FILE_CRC16 + 0] = 0; accountData[I_FILE_CRC16 + 1] = 0; //now we also need to get things like wcc, user_page_number, user ID if (wcc < 0) { if (user.hasWriteCycleCounter()) { // failed to read account data this.lastError = USER_READ_AUTH_FAILED; return(false); } //has no write cycle counter Array.Copy(ffBlock, 0, scratchpad, 8, 4); } else { //copy the write cycle counter into scratchpad Convert.toByteArray(wcc, scratchpad, 8, 4); } scratchpad[12] = (byte)user.AccountPageNumber; user.getAddress(scratchpad, 13, 7); copr.getSigningChallenge(scratchpad, 20); if (!copr.verifySignature(accountData, scratchpad, verify_mac)) { this.lastError = COPROCESSOR_FAILURE; return(false); } return(true); } }