/** * Copyright (c) 1999 GEMPLUS group. All Rights Reserved. *------------------------------------------------------------------------------ * Project name: PACAP - cas d'étude - * * * Platform : Java virtual machine * Language : JAVA 1.1.x * Devl tool : Symantec VisualCafe * * @author: Thierry Bressure * @modified by Abdellah El Marouani, Ludovic Casset, Jean-Louis Lanet & Hugues Martin * @version 2.0.1 *------------------------------------------------------------------------------ */ package com.gemplus.pacap.purse; /* this class must be re-designed 8/11/00*/ import com.gemplus.pacap.utils.*; import com.gemplus.pacap.pacapinterfaces.*; import javacard.framework.AID; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.JCSystem; import javacard.framework.OwnerPIN; import javacard.framework.Shareable; import javacard.framework.Util; import javacard.security.Signature; import javacardx.crypto.Cipher; import javacard.framework.APDU; import visa.openplatform.OPSystem; //code added by Nestor CATANO import javacard.framework.SystemException ; import javacard.framework.TransactionException ; /*Functional description of the purse*/ public class Purse { /*@ invariant maxTransactionWOPIN == 3 ; invariant appletSN.length == 4 ; invariant maxTransactionCounter == 32000 ; invariant transactionWOPIN >= 0 && transactionWOPIN <= maxTransactionWOPIN ; invariant administrativeCounter >= 0 ; invariant transactionCounter >= 0; invariant CARDISSUER_AID != null ; invariant appletSN != null ; invariant balance != null ; invariant creditAmountWOAuthorization != null ; invariant currencyTable != null ; invariant transactionRecord != null ; invariant exchangeRecord != null ; invariant exchangeRate != null ; invariant loyaltiesTable != null ; invariant maxBalance != null ; invariant maxDebitWOPIN != null ; invariant maxDebitWOExternalAut != null ; invariant expirationDate != null ; invariant loyaltyInterface != null ; invariant invExchangeRate != null ; invariant aid != null ; invariant t1_4 != null ; invariant t2_4 != null ; invariant t3_8 != null ; invariant t4_5 != null ; invariant t5_16 != null ; invariant transactionInterface != null; invariant temp != null ; invariant transaction != null ; invariant exchangeSession != null ; invariant decimal1 != null ; invariant decimal2 != null ; invariant securite != null ; invariant applet != null; invariant applet.purse == this; */ //////////////////// ATTRIBUTES /////////////////////// //------------- Default constant values-------------------// /*The cardissuer AID*/ private static final byte[] CARDISSUER_AID = new byte[] { (byte)0xA0, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x18, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x02 }; /*the administrative code for the administrative and the system session*/ /*private static final byte[] DEFAULT_ADMINISTRATIVE_CODE = new byte[] { (byte)0x02, (byte)0x01 };*/ //------------- Specification of the attributes ---------------------// /*The applet Serial number: unique for each applet and different from the aid*/ private /*@ spec_public */ byte[] appletSN = new byte[4]; /*The electronic purse balance*/ private /*@ spec_public */ Decimal balance = new Decimal(); /*The credit amount without an authorisation*/ private /*@ spec_public */ Decimal creditAmountWOAuthorization = new Decimal((short)100); /*The table containing the different currencies supported by the purse*/ private /*@ spec_public */ Currencies currencyTable = new Currencies(); /*The record containing all the transactions performed by the purse*/ private /*@ spec_public */ TransactionRecord transactionRecord = new TransactionRecord(); /*The record containing all the currency changes*/ /* spec_public */ private ExchangeRecord exchangeRecord = new ExchangeRecord(); /*The current currency of the purse*/ private /*@ spec_public */ byte currency = Currencies.REFERENCE; /*The exchange rate related to the current currency*/ private /*@ spec_public */ Decimal exchangeRate = new Decimal((short)1); /*The list of all the loyalties attached with the purse*/ private /*@ spec_public */ LoyaltiesTable loyaltiesTable = new LoyaltiesTable(); /*The transaction counter*/ private /*@ spec_public */ short transactionCounter = (short)0; /*The maximum number of transaction*/ private /*@ spec_public */ short maxTransactionCounter = (short)32000; /*The maximum value of the purse balance */ private /*@ spec_public */ Decimal maxBalance = new Decimal((short)1000); /*The maximum debit allowed without entering the PINcode (that is being authenticated)*/ private /*@ spec_public */ Decimal maxDebitWOPIN = new Decimal((short)10); /*The maximum debit allowed without identifying the terminal (mutual authentication)*/ private /*@ spec_public */ Decimal maxDebitWOExternalAut = new Decimal((short)5); /*The number of transaction since the last PIN code authentication*/ private /*@ spec_public */ byte transactionWOPIN = (byte)0; /*The maximum number of transaction allowed since the last Pin code authentication*/ private /*@ spec_public */ byte maxTransactionWOPIN = (byte)3; /*The expiration date of the applet*/ private /*@ spec_public */ Date expirationDate = new Date(); /*The administrative session counter, used to monitor the administrative session.*/ private /*@ spec_public */ short administrativeCounter = (short)0; /*The administrator pin code: the card holder pin code is managed by the card manager and is the same for all applications on the card. However, each application can manage is own set of code as the administrator code for the purse.*/ // WARNING THE NUMBER OF TRIES IS 3 AND THE SIZE OF THE PIN CODE IS 2 // DIGITS /*private OwnerPIN administrativeCode = new OwnerPIN((byte)3, (byte)2);*/ //---------- Third parts interface links ------------// /*The loyalties interface*/ // ESC/Java warns that at this moment the class invariant for // purse might not yet have been established /*@ spec_public */ private LoyaltyInterfaceObject loyaltyInterface = new LoyaltyInterfaceObject(this); //------------------ The current session keys ---------------// /*The current session keys are computed during the init phase of all the commands of the purse. Their computation depends on a mother key that is chosen depending on which session is being executed. The goal of a session key is to be used only once and its computation is based on different elements that make this key unique.*/ /*The key used to do Secure Messaging. Note that the secure messaging is performed other an apdu and then it is done in the purseapplet class. That is this key is sent to the purseapplet class.*/ // private PacapKey SMKey = new PacapKey(JCSystem.CLEAR_ON_DESELECT); //---------------------- Others attributes -------------------// /*Performing a division is not really easy. So the inverse og the exchange rate is stored in the following variable. Then, it is just necessary to multiply instead of divide.*/ private /*@ spec_public */ Decimal invExchangeRate = new Decimal((short)1); /*The aid of the appelt*/ private byte[] aid; /*The reference other the object that controls this purse, that is the purseapplet*/ private PurseApplet applet; /*Temporary tables*/ private byte t1_4[] = JCSystem.makeTransientByteArray((short)4, JCSystem.CLEAR_ON_DESELECT); private byte t2_4[] = JCSystem.makeTransientByteArray((short)4, JCSystem.CLEAR_ON_DESELECT); private byte t3_8[] = JCSystem.makeTransientByteArray((short)8, JCSystem.CLEAR_ON_DESELECT); private byte t4_5[] = JCSystem.makeTransientByteArray((short)5, JCSystem.CLEAR_ON_DESELECT); private byte t5_16[] = JCSystem.makeTransientByteArray((short)16, JCSystem.CLEAR_ON_DESELECT); private byte temp[] = JCSystem.makeTransientByteArray((short)80, JCSystem.CLEAR_ON_DESELECT); /*Temporary Objects*/ /*Data sent during the agreement phase. These variables are used to temporary store a transaction or an exchange session that is being performed.*/ Transaction transaction = new Transaction(); ExchangeSession exchangeSession = new ExchangeSession(); /*A shareable object used to peform a link between the transaction history and the loyalties*/ /* spec_public */ TransactionInterfaceObject transactionInterface = new TransactionInterfaceObject(); Decimal decimal1 = new Decimal(); Decimal decimal2 = new Decimal(); // private byte ivNull[] = {0, 0, 0, 0, 0, 0, 0, 0}; /* The security object of the purse: enforce the security policy defined for the purse*/ // private Security securite; protected Security securite; /////////////// CONSTRUCTOR //////////////// /*@ requires p != null; ensures \fresh(this); ensures applet == p; ensures \fresh(securite); //exsures (Exception) false ; */ Purse(PurseApplet p) { super (); /*The adminsitrative code is initialized to its default value*/ //administrativeCode.update(DEFAULT_ADMINISTRATIVE_CODE, //(short)0, (byte)2); applet = p; securite = new Security(); } //////////////// METHODS /////////////// /*This method contains actions to perform each time the applet is selected*/ //code modified by Nestor CATANO //inclusion of throws clause /*@ modifies securite.terminalSessionType ; modifies securite.accessCondition.condition ; modifies securite.administrativeCode.flags, securite.administrativeCode.flags[OwnerPIN.VALIDATED], securite.administrativeCode.triesLeft[0] ; modifies securite.authPin ; requires true ; ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; ensures securite.accessCondition.condition == AccessCondition.FREE ; ensures securite.administrativeCode.flags != null ; ensures securite.authPin == false ; exsures (SystemException e) e._reason == SystemException.NO_TRANSIENT_SPACE; */ boolean select() throws SystemException { //The terminal session type is set to NOT_INITIALIZED. That is there //is no active current session securite.setTerminalSessionType(Security.NOT_INITIALIZED); //The accessCondition and the adminsitrative code are reset securite.resetAccessCondition(); securite.resetAdministrativeCode(); // securite.resetAdministrativeCode(); //The card holder is no longer authenticated securite.initAuthPin(); return true; } /*This method contains actions to perform each time the applet is deselected*/ //code modified by Nestor CATANO //inclusion of throws clause /*@ modifies securite.terminalSessionType ; modifies securite.administrativeCode.flags, securite.administrativeCode.flags[OwnerPIN.VALIDATED], securite.administrativeCode.triesLeft[0] ; requires true ; ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; ensures securite.administrativeCode.flags != null ; exsures (SystemException e) e._reason == SystemException.NO_TRANSIENT_SPACE; */ void deselect() throws SystemException { // Simply end administrative mode endAdminMode(); } //----------------------------------------------------------- // // Object sharing methods // //----------------------------------------------------------- /** * @param client: The aid of the client applet * @param type: indicates the requested interface * @return returns the interface if the request is accepted */ /*@ //modifies \nothing ; requires client != null ; ensures true ; //exsures (Exception) false ; */ Shareable getShareableInterfaceObject(AID client, byte type) { // There is only one interface return getLoyaltyInterface(client); } /*returns the interface dedicated to the loyalties, depending on the client aid */ /*@ //modifies \nothing ; requires client != null ; ensures true ; //exsures (Exception) false ; */ private Shareable getLoyaltyInterface(AID client) { Shareable resu = null; // check that the loyalty is in the purse list AllowedLoyalty al = loyaltiesTable.getAllowedLoyalty(client); if(al != null) { resu = loyaltyInterface; } //if it is the case returns the interface otherwise returns null return resu; } //---------------------------------------------------------------------- // // Methods with the terminal interface // //---------------------------------------------------------------------- /* Applicative methods */ /** * Debit initialisation. * * @param montant: amount of the transaction * @param monnaie: The currency of the amount * @param idCom: The merchant identifier (4 bytes) * @param randT: The random of the terminal (4 bytes) * @param ttc: the terminal transaction counter (4 bytes) * @param tsn: the terminal serial number (4 bytes) * @param buffer: the apdu buffer, used to check the Secure Messaging if necessary * @exception ISOException with the code * PurseApplet.NOT_CURRENT_CURRENCY * if the current currency is not the one the requested debit * @exception ISOException avec le code * PurseApplet.BAD_VALUE_FOR_AMOUNT * if the amount value (montant) is not correct */ //code modified by Nestor CATANO 24/10/01 //inclusion of throws clause /*@ //securite.verifyState modifies securite.State ; modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth ; modifies transactionWOPIN ; requires true; ensures true; ensures transactionWOPIN == \old(transactionWOPIN) + 1 ; exsures (ISOException e ) (e._reason == PurseApplet.ACCESS_CONDITION_ERR && Security.NOT_INITIALIZED!=securite.terminalSessionType) || e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || (e._reason == PurseApplet.TRANSACTION_COUNTER_OVERFLOW && transactionCounter >= maxTransactionCounter) || (e._reason == PurseApplet.NOT_CURRENT_CURRENCY && monnaie != currency) || e._reason == PurseApplet.BAD_VALUE_FOR_AMOUNT || (e._reason == PurseApplet.TOO_MUCH_TRANSACTION_WO_PIN && transactionWOPIN >= maxTransactionWOPIN ); exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; */ void appInitDebit( Decimal montant, byte monnaie, byte[] idCom, short typeProd, byte[] randT, byte[] ttc, byte[] tsn, APDU apdu ) throws ISOException,TransactionException, AccessConditionException { //byte buffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.APP_INIT_DEBIT); try { // Verification of the access condition if( securite.verifyAccessControlTable(PurseApplet.APP_INIT_DEBIT, Security.NOT_INITIALIZED, apdu)){ // increments the transaction counter of the purse // modified by LC 21/11/00. This operation is now performed in appDebit // when the transaction is being recorded //transactionCounter++; if(transactionCounter >= maxTransactionCounter) { ISOException.throwIt(PurseApplet.TRANSACTION_COUNTER_OVERFLOW); } //Check that the current currency and the transaction currency are the same if(monnaie != currency) { ISOException.throwIt(PurseApplet.NOT_CURRENT_CURRENCY); } //check the correctness of the amount value (montant) if((montant.isNull()) || montant.isGreaterThan(balance)) { ISOException.throwIt(PurseApplet.BAD_VALUE_FOR_AMOUNT); } // added by HM and LC 08/01/2001 // check if the cardholder must be authenticated or not //check if the card holder must re-enter its PIN code or not by comparing // transactionWOPIN and maxTransactionWOPIN if( montant.isGreaterEqualThan(maxDebitWOPIN)) verifyDurationOfPin(PurseApplet.APP_DEBIT); //Decide wether or not a terminal authentication is necessary. If montant is //less than maxDebitWOExternalAut, then there is no need to authenticate the //terminal. Otherwise, it is necessary to authenticate the terminal. byte authen = (byte)(choixAuthentification(tsn, montant)?1:0); // The data related to the debit transaction are stores within the //transaction variable. They will be necessary in the 2nd step of the debit. transaction.reset(); transaction.setMontant(montant); transaction.setDevise(monnaie); transaction.setPartnerID(idCom, (short)0); transaction.setNumber(transactionCounter); transaction.setTerminalSN(tsn,(short)0); transaction.setTerminalTC(ttc, (short)0); transaction.setType(Transaction.TYPE_DEBIT); transaction.setTypeProduit(typeProd); transaction.setMutualAuthentification(authen == 1); transaction.setTaux(exchangeRate); // This will help to verify the terminal signature Util.arrayCopyNonAtomic(randT, (short)0, t2_4, (short)0, (short)4); // change the terminal session type to indicate // that a debit is being performed securite.setTerminalSessionType(Security.DEBIT_SESSION); // generate the session keys Util.arrayCopyNonAtomic(securite.generateSessionKeys(tsn, ttc, randT, appletSN, transactionCounter), (short)0, t1_4, (short) 0, (short) 4); //obtain the aid AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length ); Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); /* generate the signature for the data that are sent back to the terminal. the signature is stored in t3_8.*/ Util.arrayCopyNonAtomic(securite.initSignature(tsn, ttc, randT, appletSN, transactionCounter, expirationDate, authen), (short)0, t3_8, (short)0, (short)8); // The answer of the appinitdebitcommand applet.appInitDebitResp( t4_5, appletSN, expirationDate, transactionCounter, t1_4, Cipher.ALG_DES_CBC_NOPAD, Signature.ALG_DES_MAC8_NOPAD, authen, t3_8, apdu); // compare the t3_8 value with the one built in the original purse } else { //the current state does not match the access condition ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); } } catch(ISOException e) { ISOException.throwIt(e.getReason()); } } /** * This command is allowed only if a appinitdebit has been performed before * @param date: the transaction date sent by the terminal * @param heure: the transaction time sent by the terminal * @param externalAuth: The terminal signature * @param buffer: the apdu buffer if verification of the secure messaging is necessary * @exception ISOException with the code ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED * if the terminal authentication fails */ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; //setTerminalSessionType modifies securite.terminalSessionType ; requires true; ensures true; //setTerminalSessionType ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appDebit(Date date, Heure heure, byte[] externalAuth, APDU apdu) throws ISOException { byte buffer[] = apdu.getBuffer(); // check if this command can be invoked regarding the state of the applet securite.verifyState(PurseApplet.APP_DEBIT); //check if this command can be executed securite.verifyAccessCondition(buffer, Security.DEBIT_SESSION, PurseApplet.APP_DEBIT); //The debit session is terminated so, the session is now NOT_INITIALIZED securite.setTerminalSessionType(Security.NOT_INITIALIZED); // Removed by LC and HM 08/01/2001 // check if the cardholder must be authenticated or not //check if the card holder must re-enter its PIN code or not by comparing // transactionWOPIN and maxTransactionWOPIN //Decimal montant = transaction.getMontant(); //if( montant.isGreaterEqualThan(maxDebitWOPIN)) // verifyDurationOfPin(PurseApplet.APP_DEBIT); try { transaction.setHeure(heure); transaction.setDate(date); // check if the card must authenticate the terminal if(transaction.getMutualAuthentification()) { // verification of the signature that authenticates the terminal // generation of the data to be signed AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length); // only the first 5 bytes are used Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); // check the terminal signature if (!securite.authenticateTerminalDebitSignature(transaction, expirationDate, date, heure, externalAuth, appletSN, transactionCounter)) { //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); transaction.setStatus(Transaction.CERTIFICATE_ERROR); transactionRecord.addTransaction(transaction); ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); } } //generation of the debit certificate. This certificate must be signed with //another key as the terminal must not be able to modify it. AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length ); // only the first 5 bytes are used Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); // generate the debit certificate Util.arrayCopyNonAtomic(securite.generateDebitCertificate(transaction, appletSN, transactionCounter), (short) 0, t3_8, (short) 0, (short) 8); // certifiate the transaction transaction.setCertificat(t3_8, (short)0); //Now the terminal is authenticated, the debit cerrtificate is generated //Then, the debit can be performed: the purse attributes can be changed. //in particular, the balance is modified // Decimal exception added 14/11/00 try{ balance.sub(transaction.getMontant()); } catch(DecimalException e){ ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW); } // the transaction is record in the history of all the transactions //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); transaction.setStatus(Transaction.WELL_TERMINATED); transactionRecord.addTransaction(transaction); // Check if the history of all transactions is full or not. If it is full, then // the purse will contact all the loyalty in order to send them the transaction //in which they are involved. Finally, the transaction history will be deleted. processLogFullIfNeeded(); //The purse response to the terminal applet.appDebitResp(balance, t3_8, apdu); } catch(DateException de) { //@ assert false; //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); // Normally, we never get here because the date is already constructed transaction.setStatus(Transaction.ABORTED); transactionRecord.addTransaction(transaction); ISOException.throwIt(ISO7816.SW_UNKNOWN); } catch(HeureException ee) { //@ assert false; //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); // Normally, we never get here because the date is already constructed transaction.setStatus(Transaction.ABORTED); transactionRecord.addTransaction(transaction); ISOException.throwIt(ISO7816.SW_UNKNOWN); } } /** * initialisation of a credit transaction. * * @param montant: amount of the transaction * @param monnaie: the currency of montant * @param idBnk: the credit operator identifier (4 bytes) * @param randT: a terminal random (4 bytes) * @param ttc: Terminal transaction counter (4 bytes) * @param tsn: Terminal serial number (4 bytes) * @param buffer: the apdu buffer, used to verify the secure messaging if necessary */ //code modified by Nestor CATANO 24/10/01 //addition of ISOException exception /*@ //verifyState modifies securite.State ; requires true; ensures true ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appInitCredit( Decimal montant, byte monnaie, byte[] idBnk, byte[] randT, byte[] ttc, byte[] tsn, APDU apdu ) throws ISOException { //byte buffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.APP_INIT_CREDIT); try { // verification of the access condition if(securite.verifyAccessControlTable(PurseApplet.APP_INIT_CREDIT, Security.NOT_INITIALIZED,apdu)) { if(transactionCounter >= maxTransactionCounter) { ISOException.throwIt(PurseApplet.TRANSACTION_COUNTER_OVERFLOW); } //check if the currency of the transaction is the same of the current //currency of the purse if(monnaie != currency) { ISOException.throwIt(PurseApplet.NOT_CURRENT_CURRENCY); } //check the correctness of the montant variable if(montant.isNull()) { ISOException.throwIt(PurseApplet.BAD_VALUE_FOR_AMOUNT); } // Decimal exception added 14/11/00 try{ decimal1.setValue(montant); decimal1.add(balance); if(decimal1.isGreaterThan(maxBalance)) { ISOException.throwIt(PurseApplet.BALANCE_OVERFLOW); } } catch(DecimalException e){ ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW); } // increase the transaction counter // modified by LC 21/11/00. It is now performed in appCredit //transactionCounter++; // Check if a bank authorisation is necessary or not. It depends on the value // of montant compared to creditAmountWOAuthorization. // 1 : a bank authorization is required // 0 : the bank authorization is not required byte authen = (byte)0; // Decimal exception added 14/11/00 if(montant.isGreaterThan(creditAmountWOAuthorization)) authen = (byte)1; //data related to the credit transaction are stored for a further used in //the credit command. transaction.reset(); transaction.setMontant(montant); transaction.setDevise(monnaie); transaction.setPartnerID(idBnk, (short)0); transaction.setNumber(transactionCounter); transaction.setTerminalSN(tsn,(short)0); transaction.setTerminalTC(ttc, (short)0); transaction.setType(Transaction.TYPE_CREDIT); transaction.setMutualAuthentification(true); transaction.setBankAuthorization(authen == 1); transaction.setTaux(exchangeRate); //the terminal random is stored because it will be used to authenticate the terminal Util.arrayCopyNonAtomic(randT, (short)0, t2_4, (short)0, (short)4); //check if the credit can be performed (balance must be less or equal to //maxBalance securite.setTerminalSessionType(Security.CREDIT_SESSION); //generation of the session key that will be used during this credit session // the card random is stored in the local temporary table t1_4 Util.arrayCopyNonAtomic(securite.generateSessionKeys(tsn, ttc, randT, appletSN, transactionCounter), (short) 0, t1_4, (short) 0, (short) 4); AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length); // Only the first 5 bytes are used. T4_5 contains the aid of the applet Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); Util.arrayCopyNonAtomic(securite.initSignature(tsn, ttc, randT, appletSN, transactionCounter, expirationDate, authen), (short) 0, t3_8, (short) 0, (short) 8); //the purse response is the following applet.appInitCreditResp( t4_5, appletSN, expirationDate, transactionCounter, t1_4, Cipher.ALG_DES_CBC_NOPAD, Signature.ALG_DES_MAC8_NOPAD, authen, t3_8, apdu); } else { // the current security state does not match the access condition ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); } } //catch(AccessConditionException ase) { //an error has occured during the verificationhe access condition // this error is internally treated //ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); catch(ISOException e) {ISOException.throwIt(e.getReason()); } } /** * The CREDIT command. This command can only be peformed if an initCREDIT command * has been performed just before. * @param date: the transaction date sent by the terminal * @param heure: the transaction time ent by the terminal * @param bankCert: the bank certificate that allow the credit * @param sign: the signature of the terminal (mutual authentication is mandatory) * @param buffer; the apdu buffer, used to verify the secure messaging */ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; //setTerminalSessionType modifies securite.terminalSessionType ; requires true; ensures true; //setTerminalSessionType ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appCredit(Date date, Heure heure, byte[] bankCert, byte[] sign, APDU apdu) throws ISOException { byte buffer[] = apdu.getBuffer(); // check if this command can be invoked regarding the state of the applet securite.verifyState(PurseApplet.APP_CREDIT); //check the access condition securite.verifyAccessCondition(buffer, Security.CREDIT_SESSION, PurseApplet.APP_CREDIT); securite.setTerminalSessionType(Security.NOT_INITIALIZED); //check if the card holder must re-enter its PIN code or not by comparing // transactionWOPIN and maxTransactionWOPIN verifyDurationOfPin(PurseApplet.APP_CREDIT); try { transaction.setHeure(heure); transaction.setDate(date); //re-allocated by LC and HM 09/01/2001 // verification of the terminal signature // first, the data to be signed are generated, they are stored in the transaction // variable AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length); // Only the first 5 bytes are used Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); //check the terminal Signature if( !securite.authenticateTerminalCreditSignature(transaction, expirationDate, date, heure, bankCert, sign, appletSN, transactionCounter)) { //added, previously, this error was not recorded. However, it must be recorded //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); transaction.setStatus(Transaction.CERTIFICATE_ERROR); transactionRecord.addTransaction(transaction); ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);} // Check the bank authorisation if required if(transaction.getBankAuthorization()) { // This functionalities is not described in the specification. // Therefore, it is not implemented yet: it has to be done but first specified } //generation of the credit certificate. This certificate is signed with the // creditSignatureKey that is only known by the purse and the card holder bank. // Therefore, the integrity of the certificate is ensured and the terminal cannot // modify it. AID monAid = JCSystem.getAID(); if(monAid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length); // Only the first 5 bytes are used Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); // Construction of the data of the certificate to be signed Util.arrayCopyNonAtomic(securite.generateCreditCertificate(transaction,appletSN,transactionCounter, balance), (short)0, t3_8, (short) 0, (short) 8); transaction.setCertificat(t3_8, (short)0); // The transaction can now be stored in the history file transaction.setStatus(Transaction.WELL_TERMINATED); // internal attributes of the purse can be modified // The balance is modified // Decimal exception added 14/11/00 try{ balance.add(transaction.getMontant()); } catch(DecimalException e){ ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW); } //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); // The transaction is added to the history file of the transactions transactionRecord.addTransaction(transaction); // Check if the history of all transactions is full or not. If it is full, then // the purse will contact all the loyalty in order to send them the transaction // in which they are involved. Finally, the transaction history will be deleted. processLogFullIfNeeded(); applet.appCreditResp(balance, t3_8, apdu); } catch(DateException de) { //added by LC 21/11/00 //@ assert false; transactionCounter++; transaction.setNumber(transactionCounter); // normally we never get here as the date is already constructed transaction.setStatus(Transaction.ABORTED); transactionRecord.addTransaction(transaction); ISOException.throwIt(ISO7816.SW_UNKNOWN); } catch(HeureException ee) { //@ assert false; //added by LC 21/11/00 transactionCounter++; transaction.setNumber(transactionCounter); // normally we never get here as the date is already constructed transaction.setStatus(Transaction.ABORTED); transactionRecord.addTransaction(transaction); ISOException.throwIt(ISO7816.SW_UNKNOWN); } } /*initiate an exchange session * @param monnaieCourante: the current currency * @param nouvelleMonnaie: the new currency * @param idBnk: the id of the bank * @param randT: the terminal random * @param ttc :the terminal transaction counter * @param tsn :the terminal serial number * @param buffer:the buffer used to verify the secure messaging if necessary */ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of ISOException exception /*@ //verifyState modifies securite.State ; requires true; ensures true ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appInitExchange( byte monnaieCourante, byte nouvelleMonnaie, byte[] idBnk, byte[] randT, byte[] ttc, byte[] tsn, APDU apdu ) throws ISOException { byte buffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.APP_INIT_EXCHANGE); try { if(securite.verifyAccessControlTable(PurseApplet.APP_INIT_EXCHANGE, Security.NOT_INITIALIZED,apdu)) { if(transactionCounter >= maxTransactionCounter) { ISOException.throwIt(PurseApplet.TRANSACTION_COUNTER_OVERFLOW); } if(monnaieCourante != currency) { ISOException.throwIt(PurseApplet.NOT_CURRENT_CURRENCY); } if(!currencyTable.contens(nouvelleMonnaie)) { ISOException.throwIt(PurseApplet.CURRENCY_NOT_SUPPORTED); } //increase the transaction counter // modified by LC 22/11/00. it is now performed in appExchange //transactionCounter++; // the data of the exchange session are temporarly stored in the exchangeSession // variable. It will be used later in the appExchange command exchangeSession.reset(); exchangeSession.setAncienneDevise(monnaieCourante); exchangeSession.setNouvelleDevise(nouvelleMonnaie); exchangeSession.setId(idBnk, (short)0); exchangeSession.setSessionNumber(transactionCounter); exchangeSession.setTerminalTC(ttc, (short)0); exchangeSession.setTerminalSN(tsn, (short)0); Util.arrayCopyNonAtomic(randT, (short)0, t2_4, (short)0, (short)4); securite.setTerminalSessionType( Security.EXCHANGE_SESSION); // generate the session keys and the purse random Util.arrayCopyNonAtomic(securite.generateSessionKeys(tsn, ttc, randT, appletSN, transactionCounter), (short) 0, t1_4, (short)0,(short)4); // get the purse provider aid AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length); // Only the first 5 bytes are used. T4_5 contains the aid of the applet Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); // WARNING: perhaps a signature is missing to allow the terminal to authenticate // the purse Util.arrayCopyNonAtomic(securite.initSignature(tsn, ttc, randT, appletSN, transactionCounter, expirationDate, (byte) 0), (short) 0, t3_8, (short) 0, (short) 8); // the purse response to the terminal applet.appInitExchangeResp( t4_5, appletSN, expirationDate, transactionCounter, t1_4, Cipher.ALG_DES_CBC_NOPAD, Signature.ALG_DES_MAC8_NOPAD, apdu ); } else { // the current state does not match the security requierements ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); } } catch(ISOException e) {ISOException.throwIt(e.getReason()); } } /** perform the change of the current currency * @param date: the transaction date * @param heure: the transaction time * @param bankCert: the bank certificate that certifies the exchange rate * @param termSign :the terminal signature to authenticate the terminal * @param buffer : the apdu buffer used to verify the secure messaging if necessary */ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; //setTerminalSessionType modifies securite.terminalSessionType ; requires true; ensures true; //setTerminalSessionType ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appExchangeCurrency( Date date, Heure heure, byte[] bankCert, byte[] termSign, APDU apdu ) throws ISOException { byte buffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.APP_EXCHANGE_CURRENCY); // check the access condition securite.verifyAccessCondition( buffer, Security.EXCHANGE_SESSION, PurseApplet.APP_EXCHANGE_CURRENCY); // now we have check that the session is a EXCHANGE_SESSION, we can set it to NOT_INITIALIZED securite.setTerminalSessionType(Security.NOT_INITIALIZED); try { exchangeSession.setHeure(heure); exchangeSession.setDate(date); // verification of the terminal signature // first, obtain the purse provider aid AID aid = JCSystem.getAID(); if(aid != null) aid.getBytes(temp, (short)0); else Util.arrayCopyNonAtomic( this.aid, (short)0, temp, (short)0, (short)this.aid.length ); // only the first 5 bytes are used Util.arrayCopyNonAtomic(temp, (short)0, t4_5, (short)0, (short)5); // authenticate the terminal if(!securite.authenticateTerminalExchangeSignature(exchangeSession, expirationDate, date, heure, bankCert, termSign, appletSN, transactionCounter)){ // added by LC and HM 08/01/01 transactionCounter++; exchangeSession.setSessionNumber(transactionCounter); //normally we never get here as the date is already constructed exchangeSession.setStatus(ExchangeSession.ABORTED); exchangeRecord.addExchangeSession(exchangeSession); ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); } // verification of the bank certificate try { securite.verifyBankCertificate(bankCert,appletSN,transactionCounter); } catch (ISOException e){ // Added by LC and HM 09/01/2001 transactionCounter++; exchangeSession.setSessionNumber(transactionCounter); // normally we never get here as the time is already constructed exchangeSession.setStatus(ExchangeSession.ABORTED); exchangeRecord.addExchangeSession(exchangeSession); ISOException.throwIt(PurseApplet.BANK_CERTIFICATE_ERR); } // computation of the new inverse rate and of the new balance try{ decimal1.setValue(bankCert, (short)1); decimal1.mul(invExchangeRate); decimal2.setValue(balance); decimal2.mul(decimal1);} catch(DecimalException e){ ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW); } // generate the signature to authenticate the purse Util.arrayCopyNonAtomic(securite.generateExchangeSignature(exchangeSession,decimal2),(short)0, t3_8,(short)0, (short)8); // modification of the purse attributes JCSystem.beginTransaction(); try{ balance.setValue(decimal2); //code modified by Nestor CATANO 12/10/01 //the method setValue(byte[], short); doesn't exist exchangeRate.setValue(bankCert, (short)1); //exchangeRate.setValue(bankCert, (short)1); invExchangeRate.setValue(bankCert, (short)5); maxBalance.mul(decimal1); maxDebitWOExternalAut.mul(decimal1); maxDebitWOPIN.mul(decimal1); creditAmountWOAuthorization.mul(decimal1); currency = exchangeSession.getNouvelleDevise();} catch(DecimalException e){ ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW); } JCSystem.commitTransaction(); // added by LC 22/11/00 transactionCounter++; exchangeSession.setSessionNumber(transactionCounter); // the transaction is now stored in the exchange sessions record exchangeSession.setStatus(ExchangeSession.WELL_TERMINATED); exchangeRecord.addExchangeSession(exchangeSession); // the loyalties are informed of the currency change informLoyaltiesOfExchangingRate(); applet.appExchangeCurrencyResp(balance, currency, t3_8, apdu); } catch (DateException de) { // added by LC 22/11/00 //@ assert false; transactionCounter++; exchangeSession.setSessionNumber(transactionCounter); //normally we never get here as the date is already constructed exchangeSession.setStatus(ExchangeSession.ABORTED); exchangeRecord.addExchangeSession(exchangeSession); ISOException.throwIt(ISO7816.SW_UNKNOWN); } catch (HeureException ee) { // added by LC 22/11/00 //@ assert false; transactionCounter++; exchangeSession.setSessionNumber(transactionCounter); // normally we never get here as the time is already constructed exchangeSession.setStatus(ExchangeSession.ABORTED); exchangeRecord.addExchangeSession(exchangeSession); ISOException.throwIt(ISO7816.SW_UNKNOWN); } } /** * initilisation of a verify PIN session * @param tsn: the terminal serial number * @param randT: the terminal random * @param buffer: the apdu buffer, used to check the secure messaging if necessary */ //code modified by Nestor CATANO 24/10/01 //addition of ISOException exception /*@ //verifyState modifies securite.State ; requires true; ensures true ; //verifyState exsures (ISOException e ) false; */ void appInitVerifyPin(byte[] tsn, byte[] randT, APDU apdu) throws ISOException { //byte buffer[] = apdu.getBuffer(); // verify the current state of the applet securite.verifyState(PurseApplet.APP_INIT_VERIFY_PIN); try { // verification of the access condition if( securite.verifyAccessControlTable(PurseApplet.APP_INIT_VERIFY_PIN, Security.NOT_INITIALIZED,apdu)) { // The current session is now a VERIFY PIN session securite.setTerminalSessionType(Security.VERIFY_PIN_SESSION); // Generation of the session keys Util.arrayCopyNonAtomic(securite.generateSessionPINKeys(tsn, randT, appletSN), (short)0, t1_4,(short) 0, (short) 4); // the purse response to the terminal: The applet serial number, the card // random, the encryption algorithm, the signature algorithm and the apdu // are sent back to the terminal applet.appInitVerifyPinResp( appletSN, t1_4, Cipher.ALG_DES_CBC_NOPAD, Signature.ALG_DES_MAC8_NOPAD, apdu ); } else { // The current state does not match the access condition ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); } } //catch(AccessConditionException ase) { // an error has occured during the verification of the access condition // This error is internally treated //ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); catch(ISOException e) {ISOException.throwIt(e.getReason()); } } /************************************************************************************/ /***************************** The Verify PIN session *******************************/ /************************************************************************************/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; //verifyAccessCondition requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appVerifyPin(APDU apdu) throws ISOException { byte[] buffer = apdu.getBuffer(); // check if this command can be invoked regarding the state of the applet securite.verifyState(PurseApplet.APP_VERIFY_PIN); /* verification of the current access condition*/ securite.verifyAccessCondition(buffer, Security.VERIFY_PIN_SESSION, PurseApplet.APP_VERIFY_PIN); /* the number of tries on the PIN code*/ byte tries; /* To check th PIN code, we use the functionnalities of the Open Platform*/ if(OPSystem.verifyPin(apdu, ISO7816.OFFSET_CDATA)) { // reset the transactionWOPIN transactionWOPIN = 0; // set the number of tries tries = -1; // indicates that the verification has been performed: the card holder is // authenticated securite.setAuthPin(true); } else { // indicate the number of remaining tries, we use the Open Platform // functionnalities tries = OPSystem.getTriesRemaining(); // the user is no longer authenticated LC 05/01/01 securite.setAuthPin(false); transactionWOPIN = 3; } // The verify pin is terminated. The current session is now NOT_INITIALIZED securite.setTerminalSessionType(Security.NOT_INITIALIZED); // the purse response to the terminal. It sends back the number of tres, the // cryptographic key for the secure messaging and the apdu applet.appVerifyPinResp(tries, apdu); } /* read the current purse balance in its current currency*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; //verifyAccessCondition requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void appGetBalance(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the state of the applet securite.verifyState(PurseApplet.APP_GET_BALANCE); // check if the access condition allows the execution of this command securite.verifyAccessCondition( apduBuffer, Security.NOT_INITIALIZED, PurseApplet.APP_GET_BALANCE); // The purse response to the terminal includes the balance and the currency applet.appGetBalanceResp(balance, currency, apdu); } /*****************************************************************************************/ /*********************** Administrative methods ***********************************/ /*****************************************************************************************/ /* initiate an administrative session. To do so, it requires several parameters: @param terminalSN: the terminal serial number @param terminalSC: the terminal session counter ???? @param randT: the terminal random @param type: the kind of administrative session: ADMINISTRATIVE or SYSTEM @param apdu: the apdu buffer, used to chech the Secure Messaging if necessary */ //code modified by Nestor CATANO 24/10/01 //addition of ISOException exception /*@ //verifyState modifies securite.State ; requires true; ensures true ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admInitAdministrativeMode( byte[] terminalSN, byte[] terminalSC, byte[] randT, byte type, APDU apdu ) throws PurseException, ISOException { //byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_INIT_ADMINISTRATIVE_MODE); try { // verification of the access condition if(securite.verifyAccessControlTable(PurseApplet.ADM_INIT_ADMINISTRATIVE_MODE, securite.NOT_INITIALIZED, apdu)) { // the behaviour depends on the session type // ADMINISTRATIVE is used to modify some purse parameters // SYSTEM is used to manage the cryptographic keys switch(type) { case Security.ADMINISTRATIVE_SESSION: // to access attributes securite.setTerminalSessionType(Security.ADMINISTRATIVE_SESSION); break; case Security.SYSTEM_SESSION : // to access cryptographic keys securite.setTerminalSessionType(Security.SYSTEM_SESSION); break; default: // the session must be one of the following byte t = PurseException.ADMINISTRATIVE_MODE_ERR; PurseException.throwIt(t); break; } // increase the counter of adminisitrative session administrativeCounter++; // generate the cryptographic session keys used to secure communication // and copy the card random into t1_4 Util.arrayCopyNonAtomic(securite.generateSessionKeys(terminalSN, terminalSC, randT, appletSN, administrativeCounter), (short) 0, t1_4, (short)0,(short) 4); // set the SMKey for this session //SMKey = securite.getSMKey(); // generate the signature of the data Util.arrayCopyNonAtomic(securite.initAdministrativeSignature(terminalSN, terminalSC,randT,type,appletSN, administrativeCounter), (short)0, t3_8, (short)0,(short)8); /*// prepare the data to be signed short offset = (short)0; // add the terminal serial number offset = Util.arrayCopyNonAtomic( terminalSN, (short)0, temp, offset, (short)terminalSN.length); // add the terminal session counter offset = Util.arrayCopyNonAtomic( terminalSC, (short)0, temp, offset, (short)terminalSC.length); // add the terminal random offset = Util.arrayCopyNonAtomic( randT, (short)0, temp, offset, (short)randT.length); // the type of the administrative session SYSTEM or ADMINISTRATIVE temp[offset++] = type; // add the applet serial number offset = Util.arrayCopyNonAtomic( appletSN, (short)0, temp, offset, (short)appletSN.length); // add the number of administrative session offset = Util.setShort(temp,offset, administrativeCounter); offset = Util.arrayCopyNonAtomic( t1_4, (short)0, temp, offset, (short)t1_4.length); // indicate the encryption algorithm used by the card temp[offset++] = Cipher.ALG_DES_CBC_NOPAD; // indicate the signature algorithm used by the card temp[offset++] = Signature.ALG_DES_MAC8_NOPAD; // generate the signature PacapSignature.sign( temp, (short)0, offset, signatureKey, t3_8, (short)0, ivNull, (short)0, (short)ivNull.length);*/ // the purse response to the terminal applet.admInitAdministrativeModeResp( appletSN, administrativeCounter, t1_4, Cipher.ALG_DES_CBC_NOPAD, Signature.ALG_DES_MAC8_NOPAD, t3_8, apdu); } else { // the current state does not match the access condition ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED); } } //catch(AccessConditionException ase) { // an error has occured during the verification of the access condition // this error is internaly treated // ISOException.throwIt(PurseApplet.ACCESS_CONDITION_ERR); catch(ISOException e){ISOException.throwIt(e.getReason()); } } /* verification of the administrative code. This code is different from the PIN code. It is used to authenticate the administrator and it is lmanaged by the purse. @param pin: the code to checked @apdu: the apdu buffer used to check the secure messaging if necessary */ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admVerifyAdmPin(short pin, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_VERIFY_ADM_PIN); // check if the current access condition allow the execution of this command securite.verifyAccessCondition(apduBuffer,Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_VERIFY_ADM_PIN); // conditions are accepted, the code can now be checked //Util.setShort(temp, (short)0, pin); //boolean resu = administrativeCode.check(temp, (short)0, (byte)2); boolean resu = securite.checkAdministrativeCode(pin); // if the code is not correct, it ends the adminsitrative session if(!resu) endAdminMode(); // the purse responds to the terminal. It indicates the check of the code and the // number of remaining tries applet.admVerifyAdmPinResp( resu, securite.triesAdministrativeCode(), apdu); } /* End a session by setting the TypeSession to NOT_INITIALIZED*/ //code modified by Nestor CATANO //inclusion of throws clause /*@ modifies securite.terminalSessionType ; modifies securite.administrativeCode.flags, securite.administrativeCode.flags[OwnerPIN.VALIDATED], securite.administrativeCode.triesLeft[0] ; requires true ; ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; ensures securite.administrativeCode.flags != null ; exsures (SystemException e) e._reason == SystemException.NO_TRANSIENT_SPACE; */ void admEndSession(APDU apdu) throws SystemException { endAdminMode(); applet.admEndSessionResp(apdu); } /* Read the Applet serial number. This number is unique for each applet. @param apdu: the apdu buffer, used to check the Secure Messaging if necessary */ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admGetAsn(APDU apdu) throws ISOException { byte buffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_ASN); // check the access condition mandatory to execute this command securite.verifyAccessCondition(buffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_ASN); // the purse response to the terminal includes the applet serial number applet.admGetAsnResp(appletSN, apdu); } /* This command sets the applet serial number. This command can only be performed in the not personalized state. @param sn: the new serial number */ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admSetAsn(byte[] sn, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_SET_ASN); // check the applet state using the Open Platform functionalities //if(OPSystem.getCardContentState() != OPSystem.APPLET_SELECTABLE) // ISOException.throwIt(ISO7816.SW_COMMAND_NOT_ALLOWED); // if the state is correct, then check the access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_ASN); // set the new applet serial number Util.arrayCopy(sn, (short)0, appletSN, (short)0, (short)4); // the purse response to the terminal applet.admSetAsnResp( apdu); } /* modify the applet state using the Open Platform functionnalities. @param state: the new applet state*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admSetState(byte state, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_STATE); // check if the current acces condition allows the execution of this command securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_STATE); // set the new applet state if (!(OPSystem.setCardContentState(state))) ISOException.throwIt(ISO7816.SW_UNKNOWN); // the purse response to the terminal applet.admSetStateResp( apdu); } /* read the current purse balance in its current currency*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admGetBalance(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_GET_BALANCE); // check if the access condition allows the execution of this command securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_BALANCE); // The purse response to the terminal includes the balance and the currency applet.admGetBalanceResp(balance, currency, apdu); } /* read a given transaction that is stored in TransactionRecord. @param index: the index of the transaction in the history file TransactionRecord*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admGetTransactionRecord(short index, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_TRANSACTION_RECORD); // check if the access condition allows the execution of the command securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_TRANSACTION_RECORD); // check the correctness of the index Transaction t = transactionRecord.getTransaction(index); if(t == null) { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } // indicate the number of remaing entries // Modified by HM 08/01/2001 // byte reste = (byte)(transactionRecord.getNbEntries() - (byte)(index - (byte)1)); byte reste = (byte)(transactionRecord.getNbEntries() - (byte)(index) - (byte)1 ); // extracting the data of the given transaction and sending them back to the // terminal applet.admGetTransactionRecordResp( t.getNumber(), t.getTerminalSN(), t.getTerminalTC(), t.getType(), t.getPartnerID(), t.getTypeProduit(), t.getDevise(), t.getTaux(), t.getMontant(), t.getDate(), t.getHeure(), t.getCertificat(), t.getMutualAuthentification(), t.getBankAuthorization(), t.isGiven(), t.getStatus(), t.isValid(), reste, apdu); } /* read the applet's expiration date*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admGetExpirationDate(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_GET_EXPIRATION_DATE); // check if the current access condition grants the execution of this command securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet. ADM_GET_EXPIRATION_DATE); // the purse response to the terminal includes the expiration date of the purse applet.admGetExpirationdateResp(expirationDate, apdu); } /* modify the expiration date of the purse. @param d: the expiration date of the purse.*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admSetExpirationDate(Date d, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_EXPIRATION_DATE); // check if the current access condition grants the execution of this command securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_EXPIRATION_DATE); // set the expiration date in the purse try { expirationDate.setDate(d); } catch(DateException de) {} //@ assert false; // the purse response to the terminal applet.admSetExpirationdateResp( apdu); } /* initialize or modify the cryptographic mother key used to secure the DEBIT session. @param bArray: a cryptogram containg the key.*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setDebitKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setDebitKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setDebitKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; */ void admSetDebitKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_DEBIT_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_DEBIT_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in DebitKey //decryptKey(bArray, (short)0, (byte)16, debitKey); // update the key in the security class securite.setDebitKey(bArray, apdu); // the purse response to the terminal applet.admSetDebitKeyResp( apdu); } /* initialize or modify the cryptographic mother key used to secure the CREDIT session. @param bArray: a cryptogram containg the key.*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setCreditKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setCreditKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setCreditKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; */ void admSetCreditKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_CREDIT_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_CREDIT_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in CreditKey //decryptKey(bArray, (short)0, (byte)16, creditKey); // update the key in the security class securite.setCreditKey(bArray, apdu); // the purse response to the terminal applet.admSetCreditKeyResp( apdu); } /* initialize or modify the cryptographic mother key used to secure the PIN session. @param bArray: a cryptogram containg the key.*/ //PERHAPS IT IS NO LONGER USEFULL BECAUSE WE USE THE FUNCTIONNALITIES OF THE // OPEN PLATFORM. CHECK THE VALIDITY OF THE ATTRIBUTE PINKEY //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setPINKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setPINKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setPINKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; */ void admSetPINKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException{ //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_PIN_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition(apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_PIN_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in PINKey //decryptKey(bArray, (short)0, (byte)16, PINKey); // update the key in the security class securite.setPINKey(bArray, apdu); // the purse response to the terminal applet.admSetPINKeyResp( apdu); } /* read the current state of the purse*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; //verifyAccessCondition requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetState(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_GET_STATE); // check if the access conditon grants the execution of this command securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_STATE); // the purse response to the terminal includes the current state of the applet // with the Open Platform functionnalities applet.admGetStateResp(OPSystem.getCardContentState(), apdu); } /* modify the cryptographic key used to authenticate a bank certificate obtained if the amount of the credit is too important. @param bArray: a cryptogram containing the cryptographic key*/ //code modified by Nestor CATANO 25/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setCreditAuthorizationKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setCreditAuthorizationKey, exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setCreditAuthorizationKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; */ void admSetCreditAuthorizationKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_SET_CREDIT_AUTHORIZATION_KEY); // check if the current access condition grants the execution of this comand //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_CREDIT_AUTHORIZATION_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in creditAuthorizationKey //securite.decryptSignatureKey(bArray,PurseApplet.ADM_SET_CREDIT_AUTHORIZATION_KEY); // update the key in the security class securite.setCreditAuthorizationKey(bArray, apdu); // the purse response to the terminal applet.admSetCreditAuthorizationKeyResp( apdu); } /* modify the cryptographic key used to sign the credit certificate generated by the purse after a CREDIT session. @param bArray: a cryptogram containing the cryptographic key*/ //code modified by Nestor CATANO 25/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setCreditSignatureKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setCreditSignatureKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setCreditSignatureKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; */ void admSetCreditSignatureKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_CREDIT_SIGNATURE_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_CREDIT_SIGNATURE_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in creditSignatureKey //decryptKey(bArray, (short)0, (byte)16, creditSignatureKey); // update the key in the security class securite.setCreditSignatureKey(bArray, apdu); // the purse response to the terminal applet.admSetCreditSignatureKeyResp( apdu); } /* modify the cryptographic key used to sign the debit certificate generated by the purse after a DEBIT session. @param bArray: a cryptogram containing the cryptographic key*/ //code modified by Nestor CATANO 25/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setDebitSignatureKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setDebitSignatureKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setDebitSignatureKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; */ void admSetDebitSignatureKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_DEBIT_SIGNATURE_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_DEBIT_SIGNATURE_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in debitSignatureKey //decryptKey(bArray, (short)0, (byte)16, debitSignatureKey); // update the key in the security class securite.setDebitSignatureKey(bArray, apdu); // the purse response to the terminal applet.admSetDebitSignatureKeyResp( apdu); } /* modify the cryptographic key use to generate the session key of an administrative session. @param bArray: the cryptogram containing the cryptographic key*/ //code modified by Nestor CATANO 25/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setAdministrativeKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, setAdministrativeKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; //setAdministrativeKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; */ void admSetAdministrativeKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException{ //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_ADMINISTRATIVE_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_ADMINISTRATIVE_KEY //); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in administrativeKey //decryptKey(bArray, (short)0, (byte)16, administrativeKey); // update the key in the security class securite.setAdministrativeKey(bArray, apdu); // the purse response to the terminal applet.admSetAdministrativeKeyResp( apdu); } /* modify the cryptographic key used to generate the session key of a SYSTEM session. @param bArray: the cryptogram containing the cryptographic key*/ //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setSystemKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, setSystemKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; //setSystemKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; */ void admSetSystemKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_SYSTEM_KEY); // check if the current access condition grants the execution of this command //securite.verifyAccessCondition( // apduBuffer, securite.SYSTEM_SESSION, PurseApplet.ADM_SET_SYSTEM_KEY); // decrypt the bArray. This key is encrypted by the purse provider because // a cryptographic key is confidential. The result is stored in systemKey //decryptKey(bArray, (short)0, (byte)16, systemKey); // update the key in the security class securite.setSystemKey(bArray, apdu); // the purse response to the terminal applet.admSetSystemKeyResp( apdu); } /* modify the cryptographic key used to generate the session keys of an Exchange session. @param bArray: the cryptogram containing the cryptographic key*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setExchangeKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //setExchangeKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; //verifyState, setExchangeKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED; */ void admSetExchangeKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException { //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_EXCHANGE_KEY); securite.setExchangeKey(bArray,apdu); /*verifyAccessCondition( apduBuffer, SYSTEM_SESSION, PurseApplet.ADM_SET_EXCHANGE_KEY ); decryptKey(bArray, (short)0, (byte)16, exchangeKey);*/ applet.admSetExchangeKeyResp( apdu); } /* modify the cryptographic key used to check the certificate of the exchange rate. @param bArray: the cryptogram containing the cryptographic key*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setExchangeRateKey modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, setExchangeRateKey exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED ; //setExchangeRateKey exsures (TransactionException e) e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1 ; */ void admSetExchangeRateKey(byte[] bArray, APDU apdu) throws ISOException, TransactionException{ //byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_EXCHANGE_RATE_KEY); securite.setExchangeRateKey(bArray, apdu); /*verifyAccessCondition( apduBuffer, SYSTEM_SESSION, PurseApplet.ADM_SET_EXCHANGE_RATE_KEY ); decryptKey(bArray, (short)0, (byte)16, exchangeRateKey);*/ applet.admSetExchangeKeyRateResp( apdu); } /* modify the adminsitrative code used to authenticate an administrator during a ADMINISTRATIVE or a SYSTEM session. @param newCode: the new code of the administrator*/ // WARNING THE ADMINISTRATIVE CODE MAY NEED TO BE IN THE SECURITY CLASS //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admSetAdministrativeCode( short newCode, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); securite.verifyState(PurseApplet.ADM_SET_ADMINISTRATIVE_CODE); // check if the current access condition grants the execution of this command securite.verifyAccessCondition( apduBuffer, Security.SYSTEM_SESSION, PurseApplet.ADM_SET_ADMINISTRATIVE_CODE); // format the new code //Util.setShort(t1_4,(short)0, newCode); // update the administrative code //administrativeCode.update(t1_4, (short)0, (byte)2); securite.updateAdministrativeCode(newCode); // the purse response to the terminal applet.admSetAdministrativeCodeResp( apdu); } /*****************************************************************************************/ /************* Methods of the loyalties inteface ****************/ /*****************************************************************************************/ /* check if the history file of the purse contains a transaction related with a given loyalty. @param aid: the aid of the loyalty @return resu: a boolean indicating if there is a transaction (TRUE) or not (FALSE)*/ /*@ //modifies \nothing ; requires aid != null ; ensures true ; //exsures (Exception) false ; */ boolean isThereTransaction(AID aid) { boolean resu = false; // verify if the loyalty is registered within the purse AllowedLoyalty al = loyaltiesTable.getAllowedLoyalty(aid); if(al != null) { // check if the loyalty has a transaction in the purse resu = (0 < transactionRecord.getNbTransactions(al)); } // indicates if the purse has a transaction for the loyalty aid return resu; } /* a loyalty asks to the purse its related transactions that have not been taken into account. @param aid: the aid of the loyalty*/ /*@ //modifies \nothing ; modifies JCSystem._transactionDepth; requires aid != null ; // requires that one of the salers in the allowed loyalty is // actually in transaction record, so that trans != null requires (\exists int i; (transactionRecord.firstIndex < transactionRecord.newIndex ? transactionRecord.firstIndex <= i && i < transactionRecord.newIndex : (0 <= i && i < transactionRecord.newIndex) || (transactionRecord.firstIndex <= i && i < transactionRecord.MAX_ENTRIES)) && transactionRecord.data[i] != null && transactionRecord.data[i].isValid && transactionRecord.data[i].type == Transaction.TYPE_DEBIT && ! transactionRecord.data[i].given && (\exists int m; 0 <= m & m < loyaltiesTable.nbLoyalties && (\forall int k; 0 <= k & k < loyaltiesTable.data[m].aid.length ==> loyaltiesTable.data[m].aid[k] == aid.theAID[k]) && (\exists int j; 0 <= j && j < AllowedLoyalty.MAX_SALERS && (\forall int l; 0 <= l && l < Transaction.PARTNER_ID_LENGTH ==> loyaltiesTable.data[m].data[j].data[l] == transactionRecord.data[i].id[l])))); ensures true ; exsures (ArrayIndexOutOfBoundsException) false ; exsures (NullPointerException) false ; exsures (ISOException) false ; exsures (TransactionException e) (\old(JCSystem._transactionDepth) == 1 && e._reason == TransactionException.IN_PROGRESS) || (\old(JCSystem._transactionDepth) == 0 && e._reason == TransactionException.NOT_IN_PROGRESS) ; */ // Code modified by Marieke HUISMAN 11/12/01 // inclusion of throws clause TransactionInterface getTransaction(AID aid) throws ArrayIndexOutOfBoundsException, NullPointerException, TransactionException, ISOException { TransactionInterface resu = null; // verify that the loyalty is registered within the purse AllowedLoyalty al = loyaltiesTable.getAllowedLoyalty(aid); if(al != null) { // indicates the number of transactions related to the loyalty short nb = transactionRecord.getNbTransactions(al); // check if there is some transactions if(nb > 0) { // extract one transaction Transaction trans = transactionRecord.getTransaction(al); // mark the transaction as sent to the loyalty trans.markAsGiven(); // precondition clone trans.terminalSN != terminalTC // can not be established: uniqueness annotation needed transactionInterface.clone(trans); // indicate the number of remaining transactions related to the loyalty //@ assert nb > 0; transactionInterface.setReste((byte)(nb - (byte)1)); resu = transactionInterface; } } return resu; } /* indicate to a loyalty the current inverse exchange rate. @return invExchangeRate: the inverse exchange rate.*/ /*@ //modifies \nothing ; requires true ; ensures \result == invExchangeRate ; //exsures (Exception) false ; */ Decimal getInvExchangeRate() { return invExchangeRate; } /********************Purse functionalities from the administrative session*****************/ /** Read the value of the amount of credit without the bank authorization. Return: this value and the current currency */ // readded by LC 21/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetCreditAmountWOAut(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the state of the purse securite.verifyState(PurseApplet.ADM_GET_CREDIT_AMOUNT_WO_AUT); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_CREDIT_AMOUNT_WO_AUT ); // the purse response to the terminal applet.admGetCreditAmountWOAutResp( creditAmountWOAuthorization, currency, apdu ); } /** * Modify the value of the maximum credit without a bank authorization * @param d the new value.This value is provided in the current currency */ // readded by LC 21/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admSetCreditAmountWOAut(Decimal d, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the state of the applet securite.verifyState(PurseApplet.ADM_SET_CREDIT_AMOUNT_WO_AUT); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_CREDIT_AMOUNT_WO_AUT); // The access conditions are correct, the value can be changed JCSystem.beginTransaction(); try{creditAmountWOAuthorization.setValue(d);} catch(DecimalException de) {ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW);} JCSystem.commitTransaction(); applet.admSetCreditAmountWOAutResp( apdu); } /* Read the max number of transaction allowed */ // re-added by LC 21/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetMaxTransactionCounter(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the state of the appelt securite.verifyState(PurseApplet.ADM_GET_MAX_TRANSACTION_COUNTER); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_MAX_TRANSACTION_COUNTER); applet.admGetMaxTransactionCounterResp(maxTransactionCounter, apdu); } /*Modify the maximum number of transaction allowed */ // re-added by LC 21/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admSetMaxTransactionCounter(short mt, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the state of the applet securite.verifyState(PurseApplet.ADM_SET_MAX_TRANSACTION_COUNTER); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_MAX_TRANSACTION_COUNTER); // check if the value is greater or equal than the previous one if(mt >= maxTransactionCounter) { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } maxTransactionCounter = mt; applet.admSetMaxTransactionCounterResp( apdu); } /* read the current currency of the purse*/ // re-added by LC 21/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetCurrency(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet of the state securite.verifyState(PurseApplet.ADM_GET_EXCHANGE_RATE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_EXCHANGE_RATE ); applet.admGetCurrencyResp(currency, apdu); } /* read the exchange rate */ // re-added by LC 21/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetExchangeRate(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the state of the applet securite.verifyState(PurseApplet.ADM_GET_EXCHANGE_RATE); // check the acces condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_EXCHANGE_RATE ); applet.admGetExchangeRateResp(exchangeRate, apdu); } /**read an entry in the loyalty table*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetLoyaltiesTable(byte index, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_LOYALTIES_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_LOYALTIES_TABLE ); AllowedLoyalty t = loyaltiesTable.getAllowedLoyalty(index); if(t == null) { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } byte reste = (byte)(loyaltiesTable.getNbLoyalties() - (byte)(index + (byte)1)); short offset = (short)0; byte max = t.MAX_SALERS; for(byte i = (byte)0;i < max;i++) { offset = Util.arrayCopyNonAtomic( t.getSaler(i).getBytes(), (short)0, temp, offset, SalerID.ID_LENGTH ); } applet.admGetLoyaltiesTableResp( t.getAID(), t.isToBeInformed(), temp, offset, reste, apdu ); } /**suppress an entry in the loyalty table*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admDelLoyaltiesTable (byte index, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_DEL_LOYALTIES_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_DEL_LOYALTIES_TABLE ); // Modified by L. CASSET and H. MARTIN 18/01/01 (test added) if((loyaltiesTable.getNbLoyalties() > index) && (index >= 0)) { JCSystem.beginTransaction(); loyaltiesTable.delLoyalty(index); JCSystem.commitTransaction(); } else { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } applet.admDelLoyaltiesTableResp(apdu); } /*add an entries in loyalty table*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admAddLoyaltiesTable(byte[] aid, boolean bool, byte[] salerId, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_ADD_LOYALTIES_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_ADD_LOYALTIES_TABLE ); try { loyaltiesTable.addLoyalty(aid, bool, salerId); } catch(LoyaltiesTableException lte) { ISOException.throwIt(PurseApplet.LOYALTIES_TABLE_FULL); } catch(AllowedLoyaltyException ale) { ISOException.throwIt(PurseApplet.SALERS_TABLE_FULL); } applet.admAddLoyaltiesTableResp( apdu); } /* read the maximum value for the balance*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetMaxBalance(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); //check the applet's state securite.verifyState(PurseApplet.ADM_GET_MAX_BALANCE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_MAX_BALANCE ); applet.admGetMaxBalanceResp(maxBalance, apdu); } /* modify the maximum value for the balance*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admSetMaxBalance(Decimal d, APDU apdu) throws ISOException{ byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_SET_MAX_BALANCE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_MAX_BALANCE ); if(d.isSmallerThan(balance)) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try{maxBalance.setValue(d);} catch(DecimalException de) {ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW);} applet.admSetMaxBalanceResp(apdu); } /* read the max debit without PIN value*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetMaxDebitWOPIN(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_MAX_DEBIT_WO_PIN); //check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_MAX_DEBIT_WO_PIN ); applet.admGetMaxDebitWOPINResp(maxDebitWOPIN, apdu); } /* modify the max debit without PIN value*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admSetMaxDebitWOPIN(Decimal d, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_SET_MAX_DEBIT_WO_PIN); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_MAX_DEBIT_WO_PIN ); if(d.isNegatif()) { ISOException.throwIt(ISO7816.SW_DATA_INVALID); } try{maxDebitWOPIN.setValue(d);} catch(DecimalException de) {ISOException.throwIt(PurseApplet.DECIMAL_OVERFLOW);} applet.admSetMaxDebitWOPINResp( apdu); } /* read the max number of transaction allowed without re-entering the pin code*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED */ void admGetMaxTransactionWOPIN(APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_MAX_TRANSACTION_WO_PIN); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_MAX_TRANSACTION_WO_PIN ); applet.admGetMaxTransactionWOPINResp(maxTransactionWOPIN, apdu); } /*modification of the max transaction without pin value*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admSetMaxTransactionWOPIN(byte n, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_SET_MAX_TRANSACTION_WO_PIN); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_MAX_TRANSACTION_WO_PIN); if(n < 0) ISOException.throwIt(ISO7816.SW_DATA_INVALID); maxTransactionWOPIN = n; applet.admSetMaxTransactionWOPINResp( apdu); } /** read the access condition for a given command * @param id: the identifier of the command. * @param apduBuffer : this buffer is used to verify the secure messaging */ // re-added by LC 22/11/00 /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; ensures true ; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admGetControlAccesTable(byte id, APDU apdu) throws ISOException { // check the applet's state securite.verifyState(PurseApplet.ADM_GET_CONTROL_ACCESS_TABLE); // this table is managed in the security class applet.admGetControlAccessTableResp(securite.getControlAccessTable(id,apdu),apdu); } /* modification of the access condition of a specific command @param id : the id of the command @param ac : the new access condition @param apduBuffer: used to check the secure messaging if necessary */ //re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //setControlAccessTable modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, setControlAccessTable exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || e._reason == ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED || e._reason == ISO7816.SW_WRONG_DATA ; //setControlAccessTable exsures (TransactionException e) ( e._reason == TransactionException.BUFFER_FULL && JCSystem._transactionDepth == 1) ; */ void admSetControlAccesTable(byte id, byte ac, APDU apdu) throws ISOException, TransactionException { // check the applet's state securite.verifyState(PurseApplet.ADM_SET_CONTROL_ACCESS_TABLE); // this table is managed in the security class securite.setControlAccessTable(id, ac, apdu); applet.admSetControlAccessTableResp(apdu); } /* read an entry of the table containing all the exchange performed by the purse*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED ; */ void admGetExchangeRecord(byte index, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_EXCHANGE_RECORD); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_EXCHANGE_RECORD); ExchangeSession t = exchangeRecord.getExchangeSession(index); if(t == null) { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } // Modified by LC and HM 10/01/2001 // byte reste = (byte)((exchangeRecord.getNbEntries() - (index + (byte)1))%10); byte reste = (byte)((exchangeRecord.getNbEntries() - (index + (byte)1))%10); applet.admGetExchangeRecordResp( t.getSessionNumber(),t.getDate(), t.getHeure(),t.getId(), t.getAncienneDevise(), t.getNouvelleDevise(), t.getStatus(), t.isValid(), reste, apdu); } /* read an entry in the currency table*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || (e._reason == ISO7816.SW_DATA_INVALID && currencyTable.nbData > index && index >= 0 ) ; */ void admGetCurrencyTable(byte index, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_GET_CURRENCY_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_GET_CURRENCY_TABLE); byte d = (byte)0; byte r = (byte)0; if((currencyTable.getNbData() > index) && (index >= 0)) { d = currencyTable.getData(index); r = (byte)(currencyTable.getNbData() - index); r--; } else { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } applet.admGetCurrencyTableResp(d, r, apdu); } /*modify a value in the currency table*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, if(...) exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || (e._reason == ISO7816.SW_DATA_INVALID && currencyTable.nbData > index && index >= 0 ) ; */ void admSetCurrencyTable(byte index, byte devise, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_SET_CURRENCY_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_SET_CURRENCY_TABLE); if((currencyTable.getNbData() > index) && (index >= 0)) { JCSystem.beginTransaction(); currencyTable.setData(index, devise); JCSystem.commitTransaction(); } else { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } applet.admSetCurrencyTableResp( apdu); } /* add a new entry in the currency table*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, if(...) exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED // beginTransaction ... */ void admAddCurrencyTable(byte devise, APDU apdu) throws ISOException { byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_ADD_CURRENCY_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_ADD_CURRENCY_TABLE); JCSystem.beginTransaction(); currencyTable.addCurrency(devise); JCSystem.commitTransaction(); applet.admAddCurrencyTableResp( apdu); } /* suppress an entry in the table of currencies*/ // re-added by LC 22/11/00 //code modified by Nestor CATANO 24/10/01 //addition of throws clause /*@ // not checked completely, because ESC/Java times out (> 300 s) //verifyState modifies securite.State ; //verifyAccessCondition modifies securite.temp[*], securite.SMKey ; modifies TransactionException.systemInstance._reason, JCSystem._transactionDepth, ISOException.systemInstance._reason ; requires true; ensures true; //verifyState, if(...) exsures (ISOException e ) e._reason == ISO7816.SW_COMMAND_NOT_ALLOWED || ( e._reason == ISO7816.SW_DATA_INVALID && currencyTable.nbData > index && index >= 0 ) */ void admDelCurrencyTable(byte index, APDU apdu) throws ISOException{ byte apduBuffer[] = apdu.getBuffer(); // check the applet's state securite.verifyState(PurseApplet.ADM_DEL_CURRENCY_TABLE); // check the current access condition securite.verifyAccessCondition( apduBuffer, Security.ADMINISTRATIVE_SESSION, PurseApplet.ADM_DEL_CURRENCY_TABLE); if((currencyTable.getNbData() > index) && (index >= 0)) { JCSystem.beginTransaction(); currencyTable.delCurrency(index); JCSystem.commitTransaction(); } else { short code = ISO7816.SW_DATA_INVALID; ISOException.throwIt(code); } applet.admDelCurrencyTableResp( apdu); } /*****************************************************************************************/ /************************ Private Methods of the purse *********************************/ /*****************************************************************************************/ /* Once the pin is verified, one can only perform a limited number of transactions before it is required to enter the pin code once more. The fololowing method check if the limited number of transactions is not overflown. If it is the case, an exception is thrown and caught by the JCRE that leaves the word status PurseApplet.TOO_MUCH_TRANSACTION_WO_PIN in the apdu buffer. @param m: the method that use the pin code authentication*/ //strange: the global variable "testCounter" is only used in the verifyDurationOfPin. //Besides this fact, verifyDurationOfPin doesn't take any decision taking "testCounter" //into account. The variable testCounter is a protected variable but it's not used //for any other class belongig to current package. byte testCounter = 0; //code modifies by Nestor CATANO 24/10/01 //inclusion of throws clause /*@ modifies transactionWOPIN ; requires true ; ensures transactionWOPIN == \old(transactionWOPIN) + 1; exsures (ISOException e)e._reason == PurseApplet.TOO_MUCH_TRANSACTION_WO_PIN && transactionWOPIN >= maxTransactionWOPIN ; */ private void verifyDurationOfPin(byte m) throws ISOException{ // check in case of a multiple transactions with the same PIN authentication AccessControl ac = securite.getAccessControl(m); // check if the transactionWOPIN is greater than the maxTransactionWOPIN testCounter += (byte)2; if(transactionWOPIN >= maxTransactionWOPIN) { // throws the exception short code = PurseApplet.TOO_MUCH_TRANSACTION_WO_PIN; testCounter++; ISOException.throwIt(code); } // increase the counter of the number of transaction without Pin transactionWOPIN++; } /* the certificate is made of 23 bytes: * 1 byte for the operation kind * 2*2 bytes: the exchange rate * 2*2 bytes: the inverse of the exchange rate * 4 bytes for the applet serial number * 2 bytes for the purse transaction counter * 8 bytes for the signature of the previous data*/ // WARNING THIS METHOD IS NOT ACTUALLY USED // IT MAY BE DEVOTED TO THE SECURITY CLASS /*void verifyBankCertificate(byte[] bankCert) { short offset = (short)0; offset = Util.arrayCopy(bankCert, (short)0, temp, (short)0, (short)15); // check the certificate with the exchangeRateKey if(!PacapCertificate.verify( temp, (short)0, offset, exchangeRateKey, bankCert, (short)15, ivNull )) { // throws an exception if the cetificate is not correct ISOException.throwIt(PurseApplet.BANK_CERTIFICATE_ERR); } // check the correctness of the certificate data byte op = (byte)0; byte asn = (byte)9; byte atc = (byte)13; if(bankCert[op] != securite.EXCHANGE_SESSION) // throws an exception if the session is not an EXCHANGE session ISOException.throwIt(PurseApplet.BANK_CERTIFICATE_ERR); // check the applet serial number provided by the certificate byte cmp = Util.arrayCompare( bankCert, (short)asn, appletSN, (short)0, (short)4 ); if(cmp != 0) ISOException.throwIt(PurseApplet.BANK_CERTIFICATE_ERR); // check the transaction counter provided by the certificate if(Util.getShort(bankCert,atc) != transactionCounter) ISOException.throwIt(PurseApplet.BANK_CERTIFICATE_ERR); }*/ /* test if the history file is full. If it is the case, the concerned loyalties must be informed if they have subscribed to this service.*/ /*@ //modifies \nothing ; requires true ; ensures true ; //exsures (Exception) false ; */ private void processLogFullIfNeeded() { if(transactionRecord.isFull()) { // Added by L. CASSET and H. MARTIN 22/01/01 // on cherche dans la table des loyalties ceux qu'il faut // prévenir byte borne = loyaltiesTable.getNbLoyalties(); for (byte index = (byte) 0; index < borne; index++) { AllowedLoyalty al = loyaltiesTable.getAllowedLoyalty(index); if ( al.isToBeInformed() ){ informIt(al.getAID()); } } // End of addition // look in the table the loyalties that must be informed // IT IS NOT IMPLEMENTED } } /* inform the loyalties registred in the purse that the currency is about to be changed*/ /*@ //modifies \nothing ; requires true ; ensures true ; //exsures (Exception) false ; // MH: loyaltiesTable.getAllowedLoyalty(index) might return null // should be tested for, before calling JCSystem.lookupAID // ESC/Java warns that there might be a cast error */ private void informLoyaltiesOfExchangingRate() { // Added by L. CASSET and H. MARTIN byte borne = loyaltiesTable.getNbLoyalties(); for (byte index = (byte) 0; index < borne; index++) { AllowedLoyalty al = loyaltiesTable.getAllowedLoyalty(index); // on doit cherche si l'applet existe bien AID aid = JCSystem.lookupAID(al.getAID(),(short) 0, (byte) 16); if ( aid == null )return; // le JCRE va demander au loyalty l'interface purse // que celui-ci doit implementer Shareable so = JCSystem.getAppletShareableInterfaceObject(aid, (byte) 1); // si le serveur ne retourne rien il ne faut pas adresser un "null" if ( so != null) { try{ // on transtype vers l'interface qu'on attend // c'est le purse qui a défini ette interface LoyaltyPurseInterface purseInterface = (LoyaltyPurseInterface) so; // on fait notre boulot d'avertissment purseInterface.exchangeRate(); } catch (ClassCastException cce){ // on n'a pas reussi à transtyper // on devrait le virer de notre liste // loyaltiesTable.delLoyalty(aid); } } } // End of addition // IT IS NOT IMPLEMENTED } /* send a logfull signal to the loyalty aid. @param aid: the aid of the loyalty to inform of the logfull*/ /*@ //modifies \nothing ; requires bArray != null ; requires 16 <= bArray.length ; ensures true ; // ESC/Java warns that there might be a cast error //exsures (Exception) false ; */ private void informIt(byte[] bArray) { // Added by L. CASSET and H. MARTIN // on doit cherche si l'applet existe bien AID aid = JCSystem.lookupAID(bArray,(short) 0, (byte) 16); if ( aid == null )return; // le JCRE va demander au loyalty l'interface purse que celui-ci doit // implementer Shareable so = JCSystem.getAppletShareableInterfaceObject(aid,(byte) 1); // si le serveur ne retourne rien il ne faut pas adresser un "null" if ( so != null) { try{ // on transtype vers l'interface qu'on attend // c'est le purse qui a défini ette interface LoyaltyPurseInterface purseInterface = (LoyaltyPurseInterface) so; // on fait notre boulot d'avertissment purseInterface.logFull(); } catch (ClassCastException cce){ // on n'a pas reussi à transtyper // on devrait le virer de notre liste // loyaltiesTable.delLoyalty(aid); // ou du moins lui retirer la notification // loyaltiesTable.removeNotification(aid); } } // End of addition // IT IS NOT IMPLEMENTED } /* end the administrative mode*/ //code modified by Nestor CATANO //inclusion of throws clause /*@ modifies securite.terminalSessionType ; modifies securite.administrativeCode.flags, securite.administrativeCode.flags[OwnerPIN.VALIDATED], securite.administrativeCode.triesLeft[0] ; requires true ; ensures securite.terminalSessionType == Security.NOT_INITIALIZED ; ensures securite.administrativeCode.flags != null ; exsures (SystemException e) e._reason == SystemException.NO_TRANSIENT_SPACE; */ private void endAdminMode() throws SystemException { // the session type is no longer ADMINISTRATIVE nor SYSTEM. It is NOT INITIALIZED securite.setTerminalSessionType(Security.NOT_INITIALIZED); // securite.resetAdministrativeCode(); } /* choice of the external authentication for a debit. It depends of the value of the debit amount. @param tsn: the terminal serial number @return: True if a mutual authentication is mandatory, False otherwise*/ /*@ // modifies nothing; requires montant != null ; ensures \result == (montant.intPart * Decimal.PRECISION + montant.decPart >= maxDebitWOExternalAut.intPart * Decimal.PRECISION + maxDebitWOExternalAut.decPart); // exsures (Exception) false; */ private boolean choixAuthentification(byte[] tsn, Decimal montant) { // we want a mutual authentication if the montant value is greater than the value // of maxDebitWOexternalAut return montant.isGreaterThan(maxDebitWOExternalAut); } }