📄 shadebitunsigned.java
字号:
* @see SHAiButtonCopr#verifyAuthentication(byte[],byte[],byte[],byte[],byte) * @see SHAiButtonUser#readAccountData(byte[],int,byte[],int,byte[],int) * @see #getLastError() */ public synchronized boolean verifyUser(SHAiButtonUser user) throws OneWireException, OneWireIOException { //clear any error this.lastError = this.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 = this.USER_READ_AUTH_FAILED; return false; } System.arraycopy(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 System.arraycopy(fullBindCode, 4, scratchpad, 12, 8); //set the same challenge bytes System.arraycopy(chlg, 0, scratchpad, 20, 3); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// if(DEBUG) { IOHelper.writeLine("------------------------------------"); IOHelper.writeLine("Verifying user"); IOHelper.writeLine("chlg"); IOHelper.writeBytesHex(chlg); IOHelper.writeLine("accountData"); IOHelper.writeBytesHex(accountData); IOHelper.writeLine("mac"); IOHelper.writeBytesHex(mac); IOHelper.writeLine("wcc: " + user.getWriteCycleCounter()); IOHelper.writeLine("fullBindCode"); IOHelper.writeBytesHex(fullBindCode); IOHelper.writeLine("scratchpad"); IOHelper.writeBytesHex(scratchpad); IOHelper.writeLine("------------------------------------"); } //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// if(!copr.verifyAuthentication(fullBindCode, accountData, scratchpad, mac, user.getAuthorizationCommand())) { this.lastError = this.COPROCESSOR_FAILURE; return false; } return true; } //prevent malloc'ing in the critical path private byte[] verifyData_accountData = new byte[32]; /** * <P>Verifies user's account data. Account data is "verified" if and * only if the account balance is greater than zero. No digital * signature is checked by this transaction.</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> 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 account balance is * greater than zero. * * @see SHAiButtonUser#readAccountData(byte[],int) * @see #getLastError() */ public synchronized boolean verifyTransactionData(SHAiButtonUser user) throws OneWireException, OneWireIOException { //clear any error this.lastError = NO_ERROR; byte[] accountData = this.verifyData_accountData; //if verifyUser was called, this is a read of cached data user.readAccountData(accountData,0); // verify the A-B data scheme is valid boolean validPtr = false, validA = false, validB = false; byte fileLength = accountData[I_FILE_LENGTH]; int crc16 = CRC16.compute(accountData, 0, fileLength+3, user.getAccountPageNumber()); if(fileLength==RECORD_A_LENGTH || fileLength==RECORD_B_LENGTH) validPtr = true; // was the crc of the file correct? if(crc16==0xB001) { // if the header points to a valid record, we're done if(validPtr) { // nothing more to check for DS1961S/DS2432, since // it carries no signed data, we're finished return true; } // header points to neither record A nor B, but // crc is absolutely correct. that can only mean // we're looking at something that is the wrong // size from what was expected, but apparently is // exactly what was meant to be written. I'm done. this.lastError = USER_BAD_ACCOUNT_DATA; return false; } // restore the other header information accountData[I_DATA_TYPE_CODE] = 0x01; accountData[I_CONVERSION_FACTOR+0] = (byte)0x8B; accountData[I_CONVERSION_FACTOR+1] = (byte)0x48; // zero-out the don't care bytes accountData[I_DONT_CARE] = 0x00; accountData[I_DONT_CARE+1] = 0x00; accountData[I_DONT_CARE+2] = 0x00; accountData[I_DONT_CARE+3] = 0x00; // lets try Record A and check the crc accountData[I_FILE_LENGTH] = RECORD_A_LENGTH; crc16 = CRC16.compute(accountData, 0, RECORD_A_LENGTH+3, user.getAccountPageNumber()); if(crc16==0xB001) validA = true; // lets try Record B and check the crc accountData[I_FILE_LENGTH] = RECORD_B_LENGTH; crc16 = CRC16.compute(accountData, 0, RECORD_B_LENGTH+3, user.getAccountPageNumber()); if(crc16==0xB001) validB = true; if(validA && validB) { //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ if(DEBUG) System.out.println("Both A and B are valid"); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // Both A & B are valid! And we know that we can only // get here if the pointer or the header was not valid. // That means that B was the last updated one but the // header got hosed and the debit was not finished... // which means A is the last known good value, let's go with A. accountData[I_FILE_LENGTH] = RECORD_A_LENGTH; } else if(validA) { //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ if(DEBUG) System.out.println("A is valid not B"); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // B is invalid, A is valid. Means A is the last updated one, // but B is the last known good value. The header was not updated // to point to A before debit was aborted. Let's go with B accountData[I_FILE_LENGTH] = RECORD_B_LENGTH; } else if(validB) { //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ if(DEBUG) System.out.println("B is valid not A - impossible"); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // A is invalid, B is valid. Should never ever happen. Something // got completely hosed. What should happen here? this.lastError = USER_BAD_ACCOUNT_DATA; return false; } else { //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ if(DEBUG) System.out.println("Neither record has valid CRC"); //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // neither record contains a valid CRC. What should happen here? // probably got weak bit in ptr record, no telling which way to go this.lastError = USER_BAD_ACCOUNT_DATA; return false; } //get the user's balance from accountData int balance = -1; if(accountData[I_FILE_LENGTH]==RECORD_A_LENGTH ) balance = Convert.toInt(accountData, I_BALANCE_A, 3); else if(accountData[I_FILE_LENGTH]==RECORD_B_LENGTH) balance = Convert.toInt(accountData, I_BALANCE_B, 3); if(balance<0) { this.lastError = USER_BAD_ACCOUNT_DATA; return false; } this.userBalance = balance; int cnt = MAX_RETRY_CNT; do { if(user.refreshDevice() && writeTransactionData(user, -1, balance, accountData)) break; } while(--cnt>0); return false; } //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]; /** * <P>Performs the unsigned debit, subtracting the debit amount from * the user's balance and storing the new, unsigned 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> 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 the account data * has been written to the button. * * @see SHAiButtonUser#readAccountData(byte[],int) * @see SHAiButtonUser#writeAccountData(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 = -1; if(accountData[I_FILE_LENGTH]==RECORD_A_LENGTH) balance = Convert.toInt(accountData, I_BALANCE_A, 3); else if(accountData[I_FILE_LENGTH]==RECORD_B_LENGTH) balance = Convert.toInt(accountData, I_BALANCE_B, 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -