📄 shadebit.java
字号:
//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.getWriteCycleCounter(); //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 System.arraycopy(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] = (byte)0; accountData[I_FILE_CRC16+1] = (byte)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 System.arraycopy(ffBlock, 0, scratchpad, 8, 4); } else { //copy the write cycle counter into scratchpad Convert.toByteArray(wcc, scratchpad, 8, 4); } scratchpad[12] = (byte)user.getAccountPageNumber(); user.getAddress(scratchpad, 13, 7); copr.getSigningChallenge(scratchpad, 20); if(!copr.verifySignature(accountData, scratchpad, verify_mac)) { this.lastError = COPROCESSOR_FAILURE; return false; } return true; } //prevent malloc'ing in critical path private byte[] executeTransaction_accountData = new byte[32]; private byte[] executeTransaction_oldAcctData = new byte[32]; private byte[] executeTransaction_newAcctData = new byte[32]; //private byte[] executeTransaction_scratchpad = new byte[32]; /** * <P>Performs the signed debit, subtracting the debit amount from * the user's balance and storing the new, signed account data on the * user's iButton. The debit amount can be set using * <code>transaction.setParameter(SHADebit.DEBIT_AMOUNT, 50)</code>, * where the value is in units of cents (i.e. for 1 dollar, use 100).</P> * * <P><B>Flow of action: </B> * <ul> * <li> Read the account data from user </li> * <li> Extract account balance </li> * <li> Assert balance greater than debit amount </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> * * @param user SHAiButtonUser upon which the transaction occurs. * * @return <code>true</code> if and only if the user has enough in the * account balance to perform the requested debit AND a new digital * signature is successfully created AND the account data has been written * to the button. * * @see SHAiButtonUser#readAccountData(byte[],int) * @see SHAiButtonUser#writeAccountData(byte[],int) * @see SHAiButtonCopr#createDataSignature(byte[],byte[],byte[],int) * @see #getLastError() */ public synchronized boolean executeTransaction(SHAiButtonUser user, boolean verifySuccess) throws OneWireException, OneWireIOException { //clear any error this.lastError = NO_ERROR; //init local vars //holds the working copy of account data byte[] accountData = this.executeTransaction_accountData; //holds the backup copy of account data before writing byte[] oldAcctData = this.executeTransaction_oldAcctData; //holds the account data read back for checking byte[] newAcctData = this.executeTransaction_newAcctData; //just make the transaction ID a random number, so it changes int transID = rand.nextInt(); //if verifyUser was called, this is a read of cached data user.readAccountData(accountData,0); //before we update the account data array at all, let's make a backup copy System.arraycopy(accountData, 0, oldAcctData, 0, 32); //get the user's balance from accountData int balance = Convert.toInt(accountData, I_BALANCE, 3); //if there are insufficient funds if(this.debitAmount>balance) { this.lastError = USER_BAD_ACCOUNT_DATA; return false; } //update the user's balance this.userBalance = (balance - this.debitAmount); boolean success = writeTransactionData(user, transID, this.userBalance, accountData); //if write didn't succeeded or if we need to perform //a verification step anyways, let's double-check what //the user has on the button. if(verifySuccess || !success) { boolean dataOK = false; int cnt = MAX_RETRY_CNT; do { //calling verify user re-issues a challenge-response //and reloads the cached account data in the user object. if(verifyUser(user)) { //compare the user's account data against the working //copy and the backup copy. if(user.readAccountData(newAcctData,0)) { boolean isOld = true; boolean isCur = true; for(int i=0; i<32 && (isOld||isCur); i++) { //match the backup isOld = isOld && (newAcctData[i]==oldAcctData[i]); //match the working copy isCur = isCur && (newAcctData[i]==accountData[i]); } if(isOld) { //if it matches the backup copy, we didn't write anything //and the data is still okay, but we didn't do a debit dataOK = true; success = false; } else if(isCur) { dataOK = true; } else { //iBUTTON DATA IS TOTALLY HOSED //keep trying to get account data on the button success = writeTransactionData(user, transID, this.userBalance, accountData); } } } } while(!dataOK && cnt-->0); if(!dataOK) { //couldn't fix the data after 255 retries IOHelper.writeLine("Catastrophic Failure!"); success = false; } } return success; } private byte[] writeTransactionData_scratchpad = new byte[32]; /** * Does the writing of transaction data to the user button as well * as actually signing the data with the coprocessor. */ private final boolean writeTransactionData(SHAiButtonUser user, int transID, int balance, byte[] accountData) throws OneWireException, OneWireIOException { //init local vars SHAiButtonCopr copr = this.copr; int acctPageNum = user.getAccountPageNumber(); 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)(transID>>>8); // conversion factor - 2 data bytes accountData[I_CONVERSION_FACTOR+0] = (byte)0x8B; accountData[I_CONVERSION_FACTOR+1] = (byte)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.getWriteCycleCounter(); 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 = this.USER_READ_AUTH_FAILED; return false; } System.arraycopy(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); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// if(DEBUG) { IOHelper.writeLine("------------------------------------"); IOHelper.writeLine("writing transaction data"); IOHelper.writeLine("acctPageNum: " + acctPageNum); IOHelper.writeLine("accountData"); IOHelper.writeBytesHex(accountData); IOHelper.writeLine("------------------------------------"); } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// // write it to the button try { if(user.writeAccountData(accountData, 0)) return true; } catch(OneWireException owe) { if(DEBUG) IOHelper.writeLine(owe); } this.lastError = this.USER_WRITE_DATA_FAILED; return false; } /** * <P>Retrieves the value of a particular parameter for this * debit transaction.</P> * * <P><B>Valid Parameters</B> * <UL> * <LI><code>SHADebit.DEBIT_AMOUNT</code></LI> * <LI><code>SHADebit.INITIAL_AMOUNT</code></LI> * <LI><code>SHADebit.USER_BALANCE</code></LI> * </UL> * </P> * * <P>Note that the value of <code>SHADebit.USER_BALANCE</code> will * be set after calling <code>verifyTransactionData(SHAiButtonUser)</code> * and after calling <code>executeTransaction(SHAiButtonUser)</code>.</P> * * @return The value of the requested parameter. * * @throws IllegalArgumentException if an invalid parameter type * is requested. */ public synchronized int getParameter(int type) { switch(type) { case DEBIT_AMOUNT: return debitAmount; case INITIAL_AMOUNT: return initialAmount; case USER_BALANCE: return userBalance; default: throw new IllegalArgumentException("Invalid parameter type"); } } /** * <P>Sets the value of a particular parameter for this * debit transaction.</P> * * <P><B>Valid Parameters</B> * <UL> * <LI><code>SHADebit.DEBIT_AMOUNT</code></LI> * <LI><code>SHADebit.INITIAL_AMOUNT</code></LI> * </UL> * </P> * * @param type Specifies the parameter type (<code>SHADebit.DEBIT_AMOUNT</code> or * <code>SHADebit.INITIAL_AMOUNT</code>) * @return </code>true</code> if a valid parameter type was specified * and the value of the parameter is positive. * * @throws IllegalArgumentException if an invalid parameter type * is requested. */ public synchronized boolean setParameter(int type, int param) { if(param<=0) return false; switch(type) { case DEBIT_AMOUNT: debitAmount = param; break; case INITIAL_AMOUNT: initialAmount = param; break; default: return false; } return true; } /** * <p>Resets all transaction parameters to default values</p> */ public synchronized void resetParameters() { debitAmount = 50; //50 cents initialAmount = 10000; //100 dollars userBalance = 0; //0 dollars }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -