/// <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 authentication response. User is "authenticated" if /// and only if the digital signature generated the user iButton matches /// the digital signature generated by the coprocessor after the user's /// unique secret has been recreated on the coprocessor.</P> /// /// <P><B>Flow of action: </B> /// <ul> /// <li> Generate 3-byte "challenge" on coprocessor </li> /// <li> Write challenge to scratchpad of user </li> /// <li> Read account data page with signature </li> /// <li> Attempt to match user's signature with the coprocessor </li> /// </ul></P> /// </summary> /// <param name="user"> SHAiButtonUser upon which the transaction occurs. /// </param> /// <seealso cref= SHAiButtonCopr#generateChallenge(int,byte[],int) </seealso> /// <seealso cref= SHAiButtonCopr#verifyAuthentication(byte[],byte[],byte[],byte[],byte) </seealso> /// <seealso cref= SHAiButtonUser#readAccountData(byte[],int,byte[],int,byte[],int) </seealso> /// <seealso cref= #getLastError() </seealso> public override bool verifyUser(SHAiButtonUser user) { lock (this) { //clear any error this.lastError = SHATransaction.NO_ERROR; //local vars byte[] fullBindCode = this.verifyUser_fullBindCode; byte[] scratchpad = this.verifyUser_scratchpad; byte[] accountData = this.verifyUser_accountData; byte[] mac = this.verifyUser_mac; byte[] chlg = this.verifyUser_chlg; int wcc; //Generate random challenge. This must be done on the //coprocessor, otherwise flags aren't setup for VALIDATE_PAGE. if (!copr.generateChallenge(0, chlg, 0)) { lastError = COPR_COMPUTE_CHALLENGE_FAILED; return(false); } //have user answer the challenge wcc = user.readAccountData(chlg, 0, accountData, 0, mac, 0); if (wcc < 0) { if (user.hasWriteCycleCounter()) { // failed to read account data this.lastError = SHATransaction.USER_READ_AUTH_FAILED; return(false); } Array.Copy(ffBlock, 0, scratchpad, 8, 4); } else { //copy the write cycle counter into scratchpad Convert.toByteArray(wcc, scratchpad, 8, 4); } //get the user's fullBindCode, formatted for user device user.getFullBindCode(fullBindCode, 0); //get the user address and page num from fullBindCode Array.Copy(fullBindCode, 4, scratchpad, 12, 8); //set the same challenge bytes Array.Copy(chlg, 0, scratchpad, 20, 3); OneWireEventSource.Log.Debug("------------------------------------"); OneWireEventSource.Log.Debug("Verifying user"); OneWireEventSource.Log.Debug("chlg: " + Convert.toHexString(chlg)); OneWireEventSource.Log.Debug("accountData: " + Convert.toHexString(accountData)); OneWireEventSource.Log.Debug("mac: " + Convert.toHexString(mac)); OneWireEventSource.Log.Debug("wcc: " + user.WriteCycleCounter); OneWireEventSource.Log.Debug("fullBindCode: " + Convert.toHexString(fullBindCode)); OneWireEventSource.Log.Debug("scratchpad: " + Convert.toHexString(scratchpad)); OneWireEventSource.Log.Debug("------------------------------------"); if (!copr.verifyAuthentication(fullBindCode, accountData, scratchpad, mac, user.AuthorizationCommand)) { this.lastError = SHATransaction.COPROCESSOR_FAILURE; return(false); } return(true); } }
/// <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); } }