/*
* @(#)JavaCardCCProxyService.java 1.0 09/06/1999
*
* Copyright (c) 1994-1999 Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
*/
/*
* JavaCardCCProxyService.java --
*
* Copyright (c) 1998-1999 Sun Microsystem, Inc.
* All rights reserved, all wrongs reversed.
*
* SCCS: @(#) JavaCardCCProxyService.java 1.22 99/03/26 01:40:51
*/
package sunlabs.oc.javacard.proxies;
import sunlabs.sts.ByteArray;
import sunlabs.oc.ChainHashtable;
import sunlabs.oc.ISO7816Status;
import sunlabs.oc.purse.PurseCardService;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import opencard.core.OpenCardException;
import opencard.core.service.CardChannel;
import opencard.core.service.CardService;
import opencard.core.service.CardServiceException;
import opencard.core.service.CardServiceInabilityException;
import opencard.core.service.CardServiceOperationFailedException;
import opencard.core.service.CardServiceScheduler;
import opencard.core.service.SmartCard;
import opencard.core.terminal.CardID;
import opencard.core.terminal.ResponseAPDU;
import opencard.core.util.HexString;
import opencard.opt.service.CardServiceObjectNotAvailableException;
import opencard.opt.terminal.ISOCommandAPDU;
/**
* The JavaCardCCProxy provides a set of functions that the caller can
* use to access the PurseCardService on a Smart card, in addition
* to function to access CorporateCard services like the BusinessCard
* store.
*
* @author Rinaldo Di Giorgio (rinaldo.digiorgio@Sun.com)
* @author Colin Stevens (colin.stevens@sun.com)
* @version 1.22, 99/03/26
*
* @see sunlabs.brazil.handler.SecureTokenDeviceHandler
*/
public class JavaCardCCProxyService extends CardService implements PurseCardService {
private static final String corporateCardName = "CorporateCard";
private static final byte[] aidGemPlus = {
(byte) 0xa0, 0, 0, 0, (byte) 0x18, (byte) 0xff, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1
};
final static byte BZ = (byte) 0x00;
final static byte BFF = (byte) 0xFF;
// Main CLA supported by the CorporateCard applet
final static byte CC_CLA = (byte) 0x80;
final static byte SHA1_HASH = (byte) 0x80;
final static byte XOR_HASH = (byte) 0x82;
final static byte XOR_CHALLENGE_RESPONSE = (byte) 0x50;
final static byte XOR_VERIFY_RESPONSE = (byte) 0x52;
final static byte SHA1_CHALLENGE_RESPONSE = (byte) 0x54;
final static byte SHA1_VERIFY_RESPONSE = (byte) 0x4E;
// P2 byte for Master PIN in Verify
final static byte USER_PIN = (byte) 0x82;
final static byte USER_PIN_LENGTH = (byte) 8;
// INS --Business Card Instructions supported by the CorporateCard applet
final static byte CC_STORE = (byte) 0x3C;
final static byte CC_GETSIZE = (byte) 0x3E;
final static byte CC_RETRIEVE = (byte) 0x40;
final static byte CC_RETRIEVE_NEXT = (byte) 0x42;
// Paramters for CC_INS_STORE
final static byte CC_P1_SET_LENGTH = (byte) 0x01;
final static byte CC_P1_NEXT_PACKET = (byte) 0x02;
final static byte CC_P1_LAST_PACKET = (byte) 0x03;
final static short CC_SIZE = (short) 0x1FE;
final static short CC_STORE_SIZE = (short) 0x100;
final static byte APDU_XFER_MAX = 40;
static final byte NOT_DECIPHER = (byte) 0x5A;
static final byte RC4_DECIPHER = (byte) 0x7E;
static final byte HASH_SIZE = (byte) 0x14;
private Hashtable cardInfo =
new Hashtable(); // Will fail for multiple cards XXX
private Properties atrMap = new Properties();
private boolean isSelected = false;
private boolean verbose = true;
String purseID;
private static final JavaCardCCStatus ccStatus = new JavaCardCCStatus();
/**
* Perform class initializations if any.
*
*
*/
public JavaCardCCProxyService() throws IOException {}
/**
* Implement the required Open Card method.
*
*
* @param sched
* @param card
* @param block
*
* @exception OpenCardException
*
*/
protected void initialize(CardServiceScheduler sched, SmartCard card,
boolean block) throws OpenCardException {
super.initialize(sched, card, block);
/*
* Make sure that the application is really on the card.
*/
try {
setup();
}
finally {
releaseCardChannel();
}
}
/**
* Helper procedure that must be called before sending a CorporateCard
* APDU to the smartcard. It allocates a CardChannel to talk to the
* smartcard and ensures that the CorporateCard cardlet is present.
* <p>
* The CardChannel allocated by this function must eventually be
* released by the caller.
*
* @exception OpenCardException
* if there was an error selecting the CorporateCard.
*/
private void setup() throws OpenCardException {
allocateCardChannel();
if (isSelected == false) {
ISOCommandAPDU cmd;
select();
isSelected = true;
cmd = new ISOCommandAPDU(CC_CLA, 0x56, 0x00, 0x00, 16);
purseID = new String(execute(cmd));
System.out.println("Select complete");
cardInfo.put("id", purseID);
}
}
/**
* Sends an APDU and get a response APDU
*
*
* @param cmd
*
* @return
*
* @exception OpenCardException
*
*/
public ResponseAPDU sendAPDU(ISOCommandAPDU cmd)
throws OpenCardException {
CardChannel chan = getCardChannel();
ResponseAPDU resp = chan.sendCommandAPDU(cmd);
if (resp.sw1() == (byte) 0x9f) {
cmd = new ISOCommandAPDU(0x00, 0xC0, 0x00, 0x00, resp.sw2());
resp = chan.sendCommandAPDU(cmd);
}
return resp;
}
/**
* Execute a command APDU and return the response as byte[] for analysis.
*
*
* @param cmd
*
* @return
*
* @exception OpenCardException
*
*/
public byte[] execute(ISOCommandAPDU cmd) throws OpenCardException {
int le = cmd.getLE();
System.out.println(" In:" + cmd);
ResponseAPDU resp = sendAPDU(cmd);
System.out.println("Response:" + resp);
ccStatus.check(resp.sw(), cmd);
if ((le > 0) && (le != resp.getLength() - 2)) {
ccStatus.check(0x6700 + le, cmd);
}
return resp.data();
}
/**
* Gets the account id that is on the card.
* See the PurseCardService documentation for further details.
*
* @return A string that represents the id.
*
* @exception OpenCardException
* if there was an error talking to the card.
*
* @implements PurseCardService#purseId
*/
public String purseId() throws OpenCardException {
try {
setup();
return purseID;
}
finally {
releaseCardChannel();
}
}
/**
* Gets information about the funds that are on the card.
* See the PurseCardService documentation for further details.
*
* @param result
* The funds information is stored in this hashtable.
*
* @exception OpenCardException
* if there was an error talking to the card.
*
* @implements PurseCardService#purseQuantities
*/
public void purseQuantities(Hashtable result) throws OpenCardException {
try {
setup();
ISOCommandAPDU cmd = new ISOCommandAPDU(CC_CLA, 0x34, 0x00, 0x00,
0x02);
byte[] amt = execute(cmd);
int uh = amt[0];
int lh = amt[1];
int amount = ((uh & 0xff) << 8) + (lh & 0xff);
dbg("Amount in purse quantities:" + amount);
result.put("id", purseID);
result.put("currencies", "USD");
result.put("USD.amount", Long.toString(amount));
result.put("USD.minorPlaces", "2");
result.put("USD.localCurrency", "DukeDollars");
}
finally {
releaseCardChannel();
}
}
/**
* Gets a bag of information containing whatever properties this card
* service wants to return.
* See the PurseCardService documentation for further details.
*
* @param result
* The information is stored in this hashtable.
*
* @exception OpenCardException
* if there was an error talking to the card.
*
* @implements PurseCardService#purseInfo
*/
public void purseInfo(Hashtable result) throws OpenCardException {
try {
setup();
Enumeration e = cardInfo.keys();
while (e.hasMoreElements()) {
Object key = e.nextElement();
result.put(key, cardInfo.get(key));
}
result.put("pinstate", getLockState());
}
finally {
releaseCardChannel();
}
}
/**
* Sign the random challenge using the given message digest algorithm
* to generate a digital signature.
* See the PurseCardService documentation for further details.
*
* @param pin
* The PIN, which should be the PIN for the card.
*
* @param challenge
* Currently, this must be an array of 14 bytes to be
* signed by the purse.
*
* @param digestType
* The message digest algorithm name.
*
* @return The signature.
*
* @exception CardServiceOperationFailedException
* if the wrong PIN was provided
*
* @exception CardServiceInabilityException
* if an unsupported or invalid digest type was requested.
*
* @exception OpenCardException
* if there was any other error talking to the card.
*
* @implements PurseCardService#purseCheckPin
*/
public byte[] purseCheckPin(String pin, byte[] challenge,
String digestType) throws OpenCardException {
try {
setup();
ResponseAPDU resp;
ISOCommandAPDU cmd;
int ins = 0;
if ((digestType == null) || digestType.equals("XOR")) {
ins = XOR_CHALLENGE_RESPONSE;
} else if (digestType.equals("SHA1")) {
ins = SHA1_CHALLENGE_RESPONSE;
} else {
throw new CardServiceInabilityException("No support for:"
+ digestType);
}
byte[] buf = new byte[15];
System.arraycopy(pin.getBytes(), 0, buf, 0,
Math.min(pin.length(), 8));
System.arraycopy(challenge, 0, buf, 8,
Math.min(challenge.length, 7));
cmd = new ISOCommandAPDU(CC_CLA, ins, USER_PIN_LENGTH, USER_PIN,
buf, HASH_SIZE);
return execute(cmd);
}
finally {
releaseCardChannel();
}
}
/**
* Uses the signature produced by <code>purseCheckPin</code> to
* check whether the cardholder knew the pin of the purse and to
* verify the purse id.
*
* @param purse
* The purseID aka card number which has been signed
*
* @param challenge
* The same challenge that was sent to the other purse.
*
* @param digestType
* The message digest algorithm used to sign the challenge.
*
* @param signature
* The signature generated by the purse.
*
* @exception CardServiceOperationFailedException
* if the signature was not verified.
*
* @exception OpenCardException
* if there was any other error talking to the card.
*
* @implements PurseCardService#purseVerifyPin
*/
public byte[] purseVerifyPin(byte purse[], byte[] challenge,
String digestType,
byte[] signature) throws OpenCardException {
byte[] input = new byte[challenge.length + purse.length];
System.arraycopy(challenge, 0, input, 0, challenge.length);
System.arraycopy(purse, 0, input, challenge.length, purse.length);
System.out.print("Validate this Signature:");
System.out.println(ByteArray.toHexString(signature));
System.out.print("For this data:");
System.out.println(ByteArray.toHexString(input));
try {
setup();
ResponseAPDU resp;
ISOCommandAPDU cmd;
int ins = 0;
if ((digestType == null) || digestType.equals("XOR")) {
ins = XOR_VERIFY_RESPONSE;
} else if (digestType.equals("SHA1")) {
ins = SHA1_VERIFY_RESPONSE;
} else {
throw new CardServiceInabilityException("No support for:"
+ digestType);
}
cmd = new ISOCommandAPDU(CC_CLA, ins, 0, 0, input, HASH_SIZE);
byte data[] = execute(cmd);
if (!ByteArray.equals(data, signature)) {
throw new CardServiceException("Signatures do not match");
}
return purse;
}
finally {
releaseCardChannel();
}
}
/**
* Perform a value transfer eitehr upload or download.
*
*
* @param args
* @param result
*
* @exception OpenCardException
*
*/
public void purseValueTransfer(Hashtable args, Hashtable result)
throws OpenCardException {
StringBuffer sb = new StringBuffer();
String currencySymbol = null;
String actionString = null;
String amtRequested = null;
boolean credit, debit;
int step = 0;
long amount;
if (!args.containsKey("Step")) {
throw new CardServiceException("Step not specified");
}
step = Integer.parseInt((String) args.get("Step"));
if (!args.containsKey("TransactionData")
&& (1 != step && 2 != step)) {
throw new CardServiceException("TransactionData not specified");
}
switch (step) {
case 1: {
// Step one is always done on the server for this simple scheme
result.put("error", "out of sequence");
break;
}
case 2: {
System.out.println("JavaCardReal:" + args);
currencySymbol = (String) args.get("CurrencySymbol");
actionString = (String) args.get("Action");
amtRequested = (String) args.get("Amount");
amount = Long.parseLong(amtRequested);
if (actionString == null || currencySymbol == null
|| amtRequested == null) {
result
.put("error",
"Action, Amount and CurrencySymbol not specified for step 1");
return;
}
credit = (actionString.toLowerCase().compareTo("credit") == 0);
debit = (actionString.toLowerCase().compareTo("debit") == 0);
if (!(credit ^ debit)) {
result.put("error", "Step 2 is not a credit or debit");
return;
}
if (!args.containsKey("Pin")) {
result.put("error", "Pin Required");
return;
}
byte pin[] = ((String) args.get("Pin")).getBytes();
if (credit) {
purseDeposit(pin, (short) amount);
} else {
purseWithDrawal(pin, (short) amount);
}
// Success or failure
// Look at this and use proper signatures and secrets
Long d = new Long((long) (Math.random() * 1000000.00));
String tid = d.toString();
String id = (String) args.get("id");
result.put("Step", "3");
result.put("Signature", tid);
result.put("id", id);
break;
}
default: {
result.put("error", "Unknown step");
}
}
}
/**
* Deposit money on to the card.
*
*
* @param corporateCardPin
* @param depositAmount
*
* @exception OpenCardException
*
*/
private void purseDeposit(byte[] corporateCardPin,
short depositAmount) throws OpenCardException {
try {
setup();
transfer(0x30, corporateCardPin, depositAmount);
}
finally {
releaseCardChannel();
}
}
/**
* Withdraw some money from the card.
*
*
* @param corporateCardPin
* @param withDrawalAmount
*
* @exception OpenCardException
*
*/
private void purseWithDrawal(byte[] corporateCardPin, short withDrawalAmount)
throws OpenCardException {
try {
setup();
transfer(0x7C, corporateCardPin, withDrawalAmount);
}
finally {
releaseCardChannel();
}
}
/**
* Transfer funds.
*
*
* @param cCode
* @param ccPin
* @param depAmount
*
* @exception OpenCardException
*
*/
private void transfer(int cCode, byte[] ccPin,
short depAmount) throws OpenCardException {
ISOCommandAPDU cmd = new ISOCommandAPDU(CC_CLA, cCode,
(byte) ccPin.length, 0x82,
ccPin.length + 2, 2);
cmd.append(ccPin);
cmd.append((byte) (depAmount >> 8));
cmd.append((byte) depAmount);
execute(cmd);
}
/**
* Hash the Data using the algorithm specified.
*
*
* @param data -- data to be hashed
* @param hashType the type of SHA1, XOR, MD2
*
* @return the hash
*
* @exception OpenCardException other more general OpenCard exceptions
*
* @see http://www.rsa.com for more information on MD2
* @see FIPS PUB 180-1
*/
public byte[] hash(byte data[],
String hashType) throws OpenCardException {
byte hash[] = null;
ISOCommandAPDU cpc;
byte ins;
if (hashType.equals("SHA1")) {
ins = SHA1_HASH;
} else if (hashType.equals("XOR")) {
ins = XOR_HASH;
} else if (hashType.equals("RSA")) {
String error = "Encryption type not supported";
throw new CardServiceOperationFailedException(error);
} else {
String error = "Encryption type not supported";
throw new CardServiceOperationFailedException(error);
}
byte len = (byte) data.length;
cpc = new ISOCommandAPDU(CC_CLA, ins, 0x00, 0x00, data, HASH_SIZE);
try {
setup();
return (execute(cpc));
}
finally {
releaseCardChannel();
}
}
/**
* Decipher the data using the specified algorithm.
*
*
* @param data -- data to be encrypted.
* @param cipherType the type of cipher, NOT, RC4
*
* @return the decrypted input data
*
* @exception CardServiceOperationFailedException cipherType not supported
* @exception OpenCardException other more general OpenCard exceptions
*
* @see http://www.rsa.com for more information on RC4
*/
public byte[] decipher(byte data[],
String cipherType) throws OpenCardException {
ISOCommandAPDU cpc;
byte ins;
if (cipherType.equals("NOT")) {
ins = NOT_DECIPHER;
} else if (cipherType.equals("RC4")) {
ins = RC4_DECIPHER;
} else {
String error = "Encryption type not supported";
throw new CardServiceOperationFailedException(error);
}
byte len = (byte) data.length;
cpc = new ISOCommandAPDU(CC_CLA, ins, 0x00, 0x00, data, len);
try {
setup();
return (execute(cpc));
}
finally {
releaseCardChannel();
}
}
/**
* Encipher the data using the specified algorithm.
*
*
* @param data -- data to be encrypted.
* @param cipherType the type of cipher, NOT, RC4
*
* @return the decrypted input data
*
* @exception CardServiceOperationFailedException cipherType not supported
* @exception OpenCardException other more general OpenCard exceptions
*
* @see http://www.rsa.com for more information on RC4
*/
public byte[] encipher(byte data[],
String cipherType) throws OpenCardException {
String error = "Encipher Unsupported Due to space limitations:";
throw new CardServiceOperationFailedException(error + cipherType);
}
/**
* Get the Application Identifier.
*
*
* @param appName
*
* @return
*
* @exception CardServiceObjectNotAvailableException
*
*/
private byte[] getAid(String appName)
throws CardServiceObjectNotAvailableException {
CardID cardID = getCard().getCardID();
String atr = (ByteArray.toHexString(getATR())).toUpperCase();
cardInfo.put("atr", HexString.hexify(getATR()));
// First look the string up
String cardType = System.getProperty("OpenCard.atr." + atr);
System.out.println("The cardType is:" + cardType);
if (cardType == null) {
throw new CardServiceObjectNotAvailableException();
}
String aid = System.getProperty("OpenCard.aid." + appName);
if (aid == null) {
throw new CardServiceObjectNotAvailableException("Check Config file");
}
cardInfo.put("cardType", cardType);
if (cardType.equals("JavaiButton")
|| cardType.equals("JavaOneRing")) {
aid = aid.substring(0, 30);
}
return (ByteArray.parseHexString(aid));
}
/**
* Select the device using an AID.
* XXX the Dallas parts cannot handle an AID that is 16 bytes
* long so we truncate it. The Schlumberger card also has a
* similar problem but it is related to uniqueness.
*/
private void select() throws OpenCardException {
dbg("Selecting by AID:");
try {
sendSelect((byte) 0x00, getAid("CorporateCard"));
System.out.println("NON CRYPTO CARD SELECTED");
} catch (Exception noCorporateCard) {
try {
sendSelect((byte) 0x00, getAid("CorporateCardSha1"));
System.out.println("CRYPTO CARD SELECTED");
} catch (Exception noCorporateCardSha1) {
throw new CardServiceObjectNotAvailableException("NO CC/SHA1");
}
}
}
/**
* Send a select sequence.
*
*
* @param cla
* @param sD
*
* @exception OpenCardException
*
*/
private void sendSelect(byte cla, byte sD[]) throws OpenCardException {
dbg("Selecting Application");
execute(new ISOCommandAPDU(cla, 0xa4, 0x04, 0x00, sD, -1));
}
/**
* Read the contents of the secure personal storage area
*/
public byte[] readCC() throws Exception {
try {
setup();
ISOCommandAPDU cmd;
byte[] data;
cmd = new ISOCommandAPDU(CC_CLA, CC_GETSIZE, 0x00, 0x00, 2);
data = execute(cmd);
int sizeBCard = ((data[0] & 0xff) << 8) | (data[1] & 0xff);
if (sizeBCard == 0) {
return null;
}
dbg("Read: " + sizeBCard + " bytes from card.");
ByteArrayOutputStream out = new ByteArrayOutputStream();
int nBlocks = sizeBCard / APDU_XFER_MAX;
int lastBlock = sizeBCard % APDU_XFER_MAX;
int blockNumber = 0;
/*
* This seems inconvenient. Perhaps it should be done using a
* CASE 2E APDU?
*/
cmd = new ISOCommandAPDU(CC_CLA, CC_RETRIEVE, APDU_XFER_MAX,
0x00, APDU_XFER_MAX);
do {
out.write(execute(cmd));
cmd.setByte(ISOCommandAPDU.INS, CC_RETRIEVE_NEXT);
} while (++blockNumber < nBlocks);
if (lastBlock > 0) {
cmd = new ISOCommandAPDU(CC_CLA, CC_RETRIEVE_NEXT,
(byte) lastBlock, 0x00, lastBlock);
out.write(execute(cmd));
}
dbg("Returning buffer of size: " + out.size());
return (out.toByteArray());
}
finally {
releaseCardChannel();
}
}
/**
* Write business card fields to the BusinessCard applet on the iButton.
*/
public void storeCC(byte[] bcData) throws Exception {
try {
setup();
// Set the total length of the business card data
byte[] buffer = new byte[2];
buffer[0] = (byte) ((bcData.length & 0xFF00) >>> 8);
buffer[1] = (byte) (bcData.length & 0xFF);
// first APDU send tells the button what length of data to expect
ISOCommandAPDU setLengthAPDU = new ISOCommandAPDU(CC_CLA,
CC_STORE, CC_P1_SET_LENGTH, BZ, buffer, -1);
ResponseAPDU rapdu = sendAPDU(setLengthAPDU);
int offset = 0;
int bytesRemaining = bcData.length;
int packetBytes;
// remaining APDU sends transmit data to the button in packets
while (bytesRemaining > 0) {
byte p1;
if (bytesRemaining > APDU_XFER_MAX) {
packetBytes = APDU_XFER_MAX;
p1 = CC_P1_NEXT_PACKET;
} else {
packetBytes = bytesRemaining;
// signals the final transmission
p1 = CC_P1_LAST_PACKET;
}
byte[] packetData = new byte[packetBytes];
for (int j = 0; j < packetBytes; j++) {
packetData[j] = bcData[j + offset];
}
offset += packetBytes;
ISOCommandAPDU storeAPDU = new ISOCommandAPDU(CC_CLA,
(byte) CC_STORE, p1, BZ, packetData, (byte) -1);
rapdu = sendAPDU(storeAPDU);
bytesRemaining -= packetBytes;
}
}
finally {
releaseCardChannel();
}
}
/**
* Get the ATR for the current card.
*
*
* @return
*
*/
public byte[] getATR() {
return ((getCard().getCardID()).getATR());
}
/**
* Get Properties for this card.
*
*
* @return
*
*/
public Hashtable getCardProperties() {
System.out.println(" returning card info: " + cardInfo);
return (cardInfo);
}
/**
* Debug support
*
*
* @param s
*
*/
public void dbg(String s) {
if (verbose) {
System.err.println(s);
}
}
/**
* Is changing of a pin allowed?
*/
public boolean canChangePersonalCode() throws OpenCardException {
return (true);
// throw new CardServiceOperationFailedException("canChangePersonalCode");
}
/**
* Changes the Personal Code
*
* @param currentPin
* The PIN, which should be the PIN for the card.
*
* @param newPin
* The new pin which is desired.
*
* @exception CardServiceOperationFailedException
* if the wrong PIN was provided
*
* @exception OpenCardException
* if there was any other error talking to the card.
* @exception OpenCardException
* if the pin could not be changed for any reason
*
* @implements PurseCardService#changePersonalCode
*/
public void changePersonalCode(String currentPin,
String newPin) throws OpenCardException {
System.out.println(currentPin);
System.out.println(newPin);
try {
setup();
int cLen = currentPin.length();
int nLen = newPin.length();
ISOCommandAPDU cmd = new ISOCommandAPDU(CC_CLA, 0x38, BZ, BZ,
cLen + nLen + 2, -1);
cmd.append((byte) cLen);
cmd.append(currentPin.getBytes());
cmd.append((byte) nLen);
cmd.append(newPin.getBytes());
execute(cmd);
}
finally {
releaseCardChannel();
}
}
/**
* Status of Pin locking.
*
*
* @return
*
* @exception OpenCardException
*
*/
public String getLockState() throws OpenCardException {
// XXX later check for locked out -- to many failues
return ("Locked");
}
/**
* Set a personal pin code
*
*
* @param newCode
*
* @exception OpenCardException
*
*/
public void setPersonalCode(String newCode) throws OpenCardException {
throw new CardServiceOperationFailedException("setPersonalCode");
}
}
/**
* An object for holding status information
*
*
*
* @author
* @version 1.23, 06/27/99
*/
class JavaCardCCStatus extends ISO7816Status {
/**
* Return error messages in a hash table
*
*
* @return
*
*/
public Hashtable getTable() {
return msgs;
}
/*
* Extend the standard set of ISO7816 errors.
* the last niblle contains additional information in
* some cases.
*/
static final Hashtable msgs = new ChainHashtable(ISO7816Status.msgs);
static {
put(msgs, 0x9101, "invalid pin", Failed);
put(msgs, 0x9102, "invalid pin", Failed);
put(msgs, 0x9103, "insufficient funds", Failed);
put(msgs, 0x9104, "deposit would exceed purse limit", Failed);
put(msgs, 0x9105, "bad sequence", Invalid);
}
}