/*
* @(#)SecureTokenDeviceHandler.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.
*
*/
import javacard.framework.*;
import javacardx.crypto.MessageDigest;
import javacardx.crypto.Sha1MessageDigest;
/**
* Common purse, authentication and storage methods
*
* This code requires modification for commercial use.
*
* @author Rinaldo Di Giorgio
* @author Moshe Levy
* @author Dallas Semiconductor
*
* @author
* @version 1.32, 08/23/99
* <H2>Revision History</H2>
* <li>09-07-99 RDG ADD RC4, SHA1, and reduce size </li>
* <li>10-29-98 RDG Correct authenticate offset error</li>
* <li>10-29-98 RDG Reduce transmit size b-card read (Gemplus limitation)</li>
*
*/
public class CorporateCard extends Applet {
// Offset in buffer where data begins
final static byte BUFFER_DATA_OFFSET = (byte) 5;
// Main CLA supported by the CorporateCard applet
final static byte CC_CLA = (byte) 0x80;
// APDU header stuff used during applet selection
final static byte SELECT_CLA = 0x00;
final static byte SELECT_INS = (byte) 0xA4;
final static short SZ = (short) 0;
final static byte BZ = 0x00;
// INS -- Instructions our purse comprehends
final static byte PURSE_DEPOSIT = (byte) 0x30;
final static byte PURSE_WITHDRAWL = (byte) 0x7C;
final static byte PURSE_BALANCE_CHECK = (byte) 0x34;
final static byte PURSE_GET_LOG_ENTRY = (byte) 0x36;
final static byte PURSE_SET_USER_PIN = (byte) 0x38;
final static byte PURSE_SET_ISSUER_PIN = (byte) 0x3A;
// INS --Business Card Instructions supported by the CorporateCard applet
final static byte BC_INS_STORE = (byte) 0x3C;
final static byte BC_INS_GETSIZE = (byte) 0x3E;
final static byte BC_INS_RETRIEVE = (byte) 0x40;
final static byte BC_INS_RETRIEVE_NEXT = (byte) 0x42;
// INS -- Certificate Instructions
final static byte STORE_CERTIFICATE = (byte) 0x44;
final static byte GET__CERTIFICATE = (byte) 0x46;
// INS -- Certificate Instructions
final static byte VERIFY_PERSONAL_CODE = (byte) 0x48;
final static byte CHECK_PERSONAL_CODE = (byte) 0x4A;
final static byte SET_SECRET = (byte) 0x4C;
// INS -- Challenge response (PIN+Challenge)
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;
// INS -- Get the Device Indentifier
final static byte GETID = (byte) 0x56;
final static byte SETID = (byte) 0x58;
final static byte ID_LENGTH = (byte) 0x10;
// INS
static final short NOT_DECIPHER = (byte) 0x5A;
static final short RC4_DECIPHER = (byte) 0x7E;
// INS -- PIN administration
final static byte UNBLOCK_ISSUER_PIN = (byte) 0x74; // WAS ISUUER
final static byte UNBLOCK_USER_PIN = (byte) 0x76;
final static byte RESET_ISSUER_PIN = (byte) 0x78; // WAS ISUUER
final static byte RESET_USER_PIN = (byte) 0x7A;
// PX
final static byte ISSUER_PIN =
(byte) 0x81; // P2 byte for Master PIN in Verify
final static byte USER_PIN = (byte) 0x82; // P2 byte for User PIN in Verify
// Paramters for BC_INS_STORE
final static byte BC_P1_SET_LENGTH = (byte) 0x01;
final static byte BC_P1_NEXT_PACKET = (byte) 0x02;
final static byte BC_P1_LAST_PACKET = (byte) 0x03;
final static short BC_SIZE = (short) 0x80;
// Constants to define PIN characteristics
final static byte ISSUER_PIN_LENGTH = (byte) 8;
final static byte ISSUER_TRIES = (byte) 3;
final static byte USER_PIN_LENGTH = (byte) 8;
final static byte USER_TRIES = (byte) 3;
final static byte SHARED_SECRET_LENGTH = (byte) 8;
// Cap the amount of money this purse can hold
final static short PURSE_MAX_BALANCE = (short) 10000;
// Offsets for extracting the PIN from the command APDU body
final static short OFFSET_PIN_LENGTH = (short) ISO.OFFSET_CDATA;
final static short OFFSET_PIN_DATA = (short) (OFFSET_PIN_LENGTH + 1);
final static short SW_BAD_USER_PIN = (short) 0x9101;
final static short SW_BAD_ISSUER_PIN = (short) 0x9102;
final static short SW_INSUFFICIENT_FUNDS = (short) 0x9103;
final static short SW_BAD_DEPOSIT = (short) 0x9104;
final static short SW_BAD_INS_SEQUENCE = (short) 0x9105;
final static short SW_CRYPTOSIGS_DIFFERENT = (short) 0x9106;
/*
* The following instance variables are used to hold state across
* possible multiple APDUs for BC_INS_STORE.
*/
final static short BcOutgoingLimit = 0x007f;
final static short SCRATCH_LENGTH = (short) 256;
final static byte HASH_SIZE = (byte) 20;
short Offset;
short totalLength;
short BcDataLength;
byte[] scratch;
byte hashSize = HASH_SIZE;
// Array to hold the business card info
byte[] bcData;
/*
* Required for ordinary user transactions such as balance check,
* withdrawl and deposit.
*/
OwnerPIN userPIN;
/*
* Required for purse configuration APDUs such as setting the clock
* offset and the secret used in generating hash signatures.
*/
OwnerPIN issuerPIN;
/*
* Shared secKey
*/
byte secKey[] = {
(byte) 'J', (byte) 'a', (byte) 'v', (byte) 'a', (byte) 'O',
(byte) 'n', (byte) 'e', (byte) 43
};
byte initialPIN[] = {
(byte) '$', (byte) '$', (byte) '$', (byte) '$', (byte) 'j',
(byte) 'a', (byte) 'v', (byte) 'a'
};
byte idNumber[] = {
(byte) '1', (byte) '0', (byte) '0', (byte) '0', (byte) '2',
(byte) '0', (byte) '0', (byte) '0', (byte) '3', (byte) '0',
(byte) '0', (byte) '0', (byte) '4', (byte) '0', (byte) '0', (byte) '0'
};
short purseBalance; // Current monetary balance
byte[] hash = new byte[HASH_SIZE];
byte[] ba;
APDU apdu;
short count;
byte[] buffer;
short rc4x, rc4y;
Sha1MessageDigest sha1;
/*
* Contruct an instance of CorporateCard.
*/
/**
* Undocumented Class Constructor.
*
*
* @see
*/
public CorporateCard() {
scratch = new byte[SCRATCH_LENGTH]; // share this
userPIN = new OwnerPIN((byte) USER_TRIES, (byte) USER_PIN_LENGTH);
issuerPIN = new OwnerPIN((byte) ISSUER_TRIES,
(byte) ISSUER_PIN_LENGTH);
userPIN.updateAndUnblock(initialPIN, SZ, (byte) initialPIN.length);
issuerPIN.updateAndUnblock(initialPIN, SZ, (byte) initialPIN.length);
ba = new byte[4];
bcData = new byte[BC_SIZE]; // Business card data
sha1 = new Sha1MessageDigest();
register();
}
/**
* Initialize an RC4 Cipher
*
*
* @param key
*
*/
private void initRC4(byte[] key) {
short index1, index2, counter;
byte temp;
for (counter = 0; counter < 256; ++counter) {
scratch[counter] = (byte) counter;
}
rc4x = 0;
rc4y = 0;
index1 = 0;
index2 = 0;
for (counter = 0; counter < 256; ++counter) {
index2 = (short) ((key[index1] + scratch[counter] + index2)
& 0xff);
temp = scratch[counter];
scratch[counter] = scratch[index2];
scratch[index2] = temp;
index1 = (byte) ((index1 + 1) % key.length);
}
}
/**
* Make this applet as the currently selected applet in the Java iButton.
* @param apdu APDU
*/
// public boolean select(APDU apdu)
public boolean select() {
return true;
}
/**
* Install the JiBlet on the "Card"
* @param apdu APDU
*/
public static void install(APDU apdu) {
new CorporateCard();
}
/**
* Dispatch APDUs
* @param apdu APDU
*/
public void process(APDU apdu) {
this.apdu = apdu;
this.buffer = apdu.getBuffer();
if ((buffer[ISO.OFFSET_CLA] == SELECT_CLA)
&& (buffer[ISO.OFFSET_INS] == SELECT_INS)) {
return;
}
// We don't know what to do with this CLA.
if (buffer[ISO.OFFSET_CLA] != CC_CLA) {
ISOException.throwIt(ISO.SW_CLA_NOT_SUPPORTED);
}
if (dispatch()) {
ISOException.throwIt(ISO.SW_INS_NOT_SUPPORTED);
}
}
/**
* Dispatch the APDU request from a user.
*
* @return false if processed true it more processing required
*
*/
public boolean dispatch() {
switch (buffer[ISO.OFFSET_INS]) {
case PURSE_DEPOSIT:
purseDeposit();
break;
case PURSE_WITHDRAWL:
purseWithdrawl();
break;
case PURSE_BALANCE_CHECK:
sendBalance();
break;
case PURSE_SET_USER_PIN:
purseSetPIN(apdu, userPIN, true);
break;
case PURSE_SET_ISSUER_PIN:
purseSetPIN(apdu, issuerPIN, false);
break;
case BC_INS_GETSIZE:
businessCardGetSize(apdu);
break;
case BC_INS_STORE:
businessCardStore(apdu);
break;
case BC_INS_RETRIEVE:
BcDataLength = (short) totalLength;
Offset = SZ;
businessCardRetrieve(apdu);
break;
case BC_INS_RETRIEVE_NEXT:
businessCardRetrieve(apdu);
break;
case GETID:
sendId(apdu);
break;
case SETID:
setId(apdu);
break;
case UNBLOCK_ISSUER_PIN:
updateAndUnblockPIN(apdu, issuerPIN);
break;
case UNBLOCK_USER_PIN:
updateAndUnblockPIN(apdu, userPIN);
break;
case RESET_ISSUER_PIN:
resetAndUnblockPIN(issuerPIN);
break;
case RESET_USER_PIN:
resetAndUnblockPIN(userPIN);
break;
case RC4_DECIPHER:
case NOT_DECIPHER:
decipher();
break;
case SHA1_HASH:
case XOR_HASH:
hash(buffer[ISO.OFFSET_INS]);
break;
case SHA1_CHALLENGE_RESPONSE:
case XOR_CHALLENGE_RESPONSE:
signCardID(buffer[ISO.OFFSET_INS]);
break;
case XOR_VERIFY_RESPONSE:
case SHA1_VERIFY_RESPONSE:
checkCardIdSignature(buffer[ISO.OFFSET_INS]);
break;
default:
return true;
}
return false;
}
/**
* NOT Encryption
* CLA INS P1 P2 LC Data
* +------+--------------+----+----+--+-------+
* |CC_CLA|NOT_DECIPHER| NA | NA |LC| Data |
* +------+--------------+----+----+--+-------+
*
*/
protected void decipher() {
// get expected data
short totalBytes = (short) (buffer[ISO.OFFSET_LC] & 0x00FF);
short bytesToRead = apdu.setIncomingAndReceive();
short offset = 0;
byte temp[] = new byte[64];
while (bytesToRead > 0) {
Util.arrayCopyNonAtomic(buffer, ISO.OFFSET_CDATA, temp, offset,
bytesToRead);
offset += bytesToRead;
bytesToRead = apdu.receiveBytes(ISO.OFFSET_CDATA);
}
if (buffer[ISO.OFFSET_INS] == NOT_DECIPHER) {
for (byte i = 0; i < totalBytes; i++) {
temp[i] = (byte) (~temp[i]);
}
} else if (buffer[ISO.OFFSET_INS] == RC4_DECIPHER) {
initRC4(secKey);
for (short i = 0; i < totalBytes; ++i) {
temp[i] = (byte) (temp[i] ^ scratch[computeNextStateRC4()]);
}
} else {
ISOException.throwIt(ISO.SW_INS_NOT_SUPPORTED);
}
sendMessage(apdu, temp, SZ, totalBytes);
}
/**
* Compute the next state for RC$
*
*
* @return
*
*/
private short computeNextStateRC4() {
byte temp;
rc4x = (short) ((rc4x + 1) & 0x00ff);
rc4y = (short) ((rc4y + scratch[rc4x]) & 0xff);
temp = scratch[rc4x];
scratch[rc4x] = scratch[rc4y];
scratch[rc4y] = temp;
return ((short) ((scratch[rc4x] + scratch[rc4y]) & 0xff));
}
/**
* Send the ID number of the card
*
* Response APDU body:
* -------------------
* | 16 Digit String |
* -------------------
*
*
* @param apdu APDU
*/
protected void sendId(APDU apdu) {
sendMessage(apdu, idNumber, SZ, (short) ID_LENGTH);
}
/**
* Set the ID number of the card
*
* Command APDU body:
* --------------------+-----------------+----------+-----------------+
* | CLA INS P1 P2 0x10| Issuer PIN Len | PIN data |16 bytes of DATA |
* --------------------+-----------------+----------+-----------------+
*
*
* @param apdu APDU
*/
protected void setId(APDU apdu) {
short bytesToRead = apdu.setIncomingAndReceive();
if (issuerPIN.check(buffer, OFFSET_PIN_DATA,
buffer[OFFSET_PIN_LENGTH])) {
short IdDataOffset = (short) (OFFSET_PIN_LENGTH
+ buffer[OFFSET_PIN_LENGTH] + 1);
Util.arrayCopyNonAtomic(buffer, IdDataOffset, idNumber, SZ,
(short) ID_LENGTH);
} else {
ISOException.throwIt(SW_BAD_ISSUER_PIN);
}
}
/**
* Perform a deposit using the issuer PIN for basic authentication
*
* Command APDU body:
* -------------------------------------
* | IPL | IPD | Amount |
* -------------------------------------
*
* Response APDU body:
* -----------
* | Balance |
* -----------
*
* with:
* IPL = Issuer PIN length
* IPD = Issuer PIN data (UPL bytes in length)
* Amount = Amount to deposit
* Balance = previous balance + Amount
*
* @param apdu APDU
*/
protected void purseDeposit() {
short amount = getAmount();
if ((amount > 0) && ((purseBalance + amount) < PURSE_MAX_BALANCE)) {
purseBalance += amount;
} else {
ISOException.throwIt(SW_BAD_DEPOSIT);
}
// Return the new balace
sendBalance();
}
/**
* Get the purse Amount
*
*
* @return
*
*/
protected short getAmount() {
short bytesToRead = apdu.setIncomingAndReceive();
byte pinLength = checkPin();
return (byteArrayToShort(buffer,
(short) (ISO.OFFSET_CDATA + pinLength)));
}
/**
* Perform a withdraw using the user PIN for basic authentication
*
* Command APDU body:
* -------------------------------------
* | UPL | UPD | Amount |
* -------------------------------------
*
* Response APDU body:
* -----------
* | Balance |
* -----------
*
* with:
* UPL = User PIN length
* UPD = User PIN data (UPL bytes in length)
* Amount = Amount to withdraw
* Balance = previous balance - Amount
*
* @param apdu APDU
*/
protected void purseWithdrawl() {
short amount = getAmount();
if ((amount > 0) && ((purseBalance - amount) > 0)) {
purseBalance -= amount;
} else {
ISOException.throwIt(SW_INSUFFICIENT_FUNDS);
}
// Return the new balace
sendBalance();
}
/**
* Perform a balance check
*
* Command APDU body:
*
* Response APDU body:
* -----------
* | Balance |
* -----------
*
* Balance = Current balance
*
* @param apdu APDU
* HA1.
*/
protected void sendBalance() {
sendMessage(apdu, shortToByteArray(purseBalance), SZ, (short) 2);
}
/**
* Changes a PIN.
*
* Command APDU body:
* -----------------------------------------------
* | UPL | UPD | NPL | NPD |
* -----------------------------------------------
*
* with:
* UPL = User PIN length
* UPD = User PIN data (UPL bytes in length)
* NPL = New user PIN length
* NPD = New user PIN data (UPL bytes in length)
*
* @param apdu APDU
*/
private void purseSetPIN(APDU apdu, OwnerPIN oldPIN, boolean isUserPIN) {
short bytesToRead = apdu.setIncomingAndReceive();
if (oldPIN.check(buffer, OFFSET_PIN_DATA,
buffer[OFFSET_PIN_LENGTH])) {
short newPINLengthOffset = (short) (OFFSET_PIN_DATA
+ buffer[OFFSET_PIN_LENGTH]);
oldPIN.updateAndUnblock(buffer, (short) (newPINLengthOffset + 1),
buffer[newPINLengthOffset]);
} else {
ISOException.throwIt(isUserPIN ? SW_BAD_USER_PIN
: SW_BAD_ISSUER_PIN);
}
}
/**
* Convert an integer to a 2 byte array
*/
protected byte[] shortToByteArray(short i) {
// Fill byte array using big-endian byte ordering
ba[0] = (byte) ((i & (short) 0xFF00) >>> 8);
ba[1] = (byte) (i & (short) 0x00FF);
return ba;
}
/**
* Convert a byte array to an integer
*/
protected short byteArrayToShort(byte[] ba, short offset) {
short i;
// Byte array should be using big-endian byte ordering
i = (short) ((ba[offset] << 8) & (short) 0xFF00);
i |= (short) (ba[offset + 1] & (short) 0x00FF);
return i;
}
/**
* Store new business card info
* This method copies business card data into a scratchorary byte
* array in pieces using receiveBytes. Once we have all the business
* card data in scratch it will be atomically copied to the bcData array.
* This way we should never have partial data in bcData.
*
* @param apdu APDU
*/
protected void businessCardStore(APDU apdu) {
short bytesToRead = apdu.setIncomingAndReceive();
// byte error[] = {(byte)buffer[ISO.OFFSET_P1],(byte)BC_P1_SET_LENGTH};
// byte error[] = {(byte)ISO.OFFSET_P1,(byte)BC_P1_SET_LENGTH};
// sendMessage(apdu, error, (byte)0, (short)2);
switch (buffer[ISO.OFFSET_P1]) {
case BC_P1_SET_LENGTH:
// Reset offset into scratch array
// byte error[] = {(byte)buffer[ISO.OFFSET_LC],(byte)bytesToRead};
// sendMessage(apdu, error, (byte)0, (short)2);
Offset = 0;
// We're expecting a 2 byte length
if (buffer[ISO.OFFSET_LC] == 2) {
totalLength = (short) ((buffer[ISO.OFFSET_CDATA] << 8)
& (short) 0xFF00);
totalLength |= (short) ((buffer[ISO.OFFSET_CDATA + 1]
& (short) 0x00FF));
} else {
ISOException.throwIt(ISO.SW_DATA_INVALID);
}
break;
case BC_P1_LAST_PACKET:
// Do an atomic copy of the APDU body data to the bcData array
// Util.arrayCopy(scratch, SZ, bcData,
// SZ, (short) totalLength);
case BC_P1_NEXT_PACKET:
if (totalLength > 0) {
while (bytesToRead > 0) {
Util.arrayCopyNonAtomic(buffer, ISO.OFFSET_CDATA, bcData,
Offset, bytesToRead);
Offset += bytesToRead;
// Get the next chunk of business card info
bytesToRead = apdu.receiveBytes(ISO.OFFSET_CDATA);
}
} else {
ISOException.throwIt(SW_BAD_INS_SEQUENCE);
}
break;
default:
ISOException.throwIt(ISO.SW_WRONG_P1P2);
}
}
/**
* Retrieve business card length
* @param apdu APDU
*/
protected void businessCardGetSize(APDU apdu) {
byte b[] = shortToByteArray(totalLength);
sendMessage(apdu, b, (byte) 0, (byte) 2);
}
/**
* Retrieve business card info
* @param apdu APDU
* Sepcify P1 to get small block amounts
*/
protected void businessCardRetrieve(APDU apdu) {
byte amtToSend = buffer[ISO.OFFSET_P1];
short len = 0;
if (bcData != null) {
// Return business card data to host
if (amtToSend == 0) {
len = BcDataLength > BcOutgoingLimit ? BcOutgoingLimit
: BcDataLength;
} else {
len = amtToSend;
}
sendMessage(apdu, bcData, (short) Offset, (short) len);
Offset += len;
BcDataLength -= (short) len;
}
}
/**
* Copy two arrays.
*
*
* @param a
* @param aOff
* @param aLen
* @param b
* @param bOff
* @param bLen
*
* @return
*
*/
short twoArrayCpy(byte a[], short aOff, short aLen, byte b[], short bOff,
short bLen) {
Util.arrayCopyNonAtomic(a, aOff, scratch, SZ, aLen);
Util.arrayCopyNonAtomic(b, bOff, scratch, aLen, bLen);
return ((short) (aLen + bLen));
}
/**
* Check the pin
*
*
* @param buffer containing pin and other data
* with P1 being the pin length
*
* @return
*
*/
byte checkPin() {
if (buffer[ISO.OFFSET_P1] != USER_PIN_LENGTH) {
ISOException.throwIt(ISO.SW_WRONG_LENGTH);
}
if (buffer[ISO.OFFSET_P2] == USER_PIN) {
if (!userPIN.check(buffer, (short) (ISO.OFFSET_CDATA),
buffer[ISO.OFFSET_P1])) {
// The last nibble of return code is number of remaining tries
ISOException.throwIt(SW_BAD_USER_PIN);
}
} else {
ISOException.throwIt(ISO.SW_INCORRECT_P1P2);
}
return (buffer[ISO.OFFSET_P1]);
}
/**
* Copy secret, Challenge and Id to scratch buffer
*
*
* @param lPin length of Pin
* @param lChallenge Length 0f data
*
* @return
*
*/
short createDataForSigning() {
short count = apdu.setIncomingAndReceive();
byte lPin = checkPin();
// Copy secKey then challenge then idNumber
short cStart = (short) (ISO.OFFSET_CDATA + lPin);
// Copy secret and challenge to scratch buffer
short len = (short) twoArrayCpy(secKey, SZ, (short) secKey.length,
buffer, cStart,
(short) (count - lPin));
// Copy idNumber to scratch buffer
Util.arrayCopyNonAtomic(idNumber, SZ, scratch, len,
(short) idNumber.length);
return ((short) (len + idNumber.length));
}
/**
* This method sets a new value for the PIN and resets the PIN try
* counter to the value of the PIN try limit. It also resets the validated flag.
* CLA INS P1 P2 Lc Data
* +----+---------+----+----+----+----+------------------+
* |0x00|0x90/0x92|0x00|0x00|0x09|0x08| Pin data |
* +----+---------+----+----+----+----+------------------+
*
* pin the bytearray containing the new pin value
* offset the starting offset in the pin array
* length the length of the new pin.
* PINException.ILLEGAL_VALUE on illegal parameter
*/
protected void updateAndUnblockPIN(APDU apdu, OwnerPIN pin) {
short count = apdu.setIncomingAndReceive();
byte DataLen = (byte) count;
if (--DataLen > ISSUER_PIN_LENGTH) {
ISOException.throwIt(ISO.SW_WRONG_LENGTH);
}
pin.updateAndUnblock(buffer, (short) (ISO.OFFSET_CDATA + 1),
buffer[ISO.OFFSET_CDATA]);
return;
}
/**
* This method resets the validated flag and
* resets the PIN try counter to the value of the PIN try limit.
* This method is used by the owner to re-enable the blocked PIN.
* CLA INS P1 P2
* +----+---------+----+----+
* |0x00|0x94/0x96|0x00|0x00|
* +----+---------+----+----+
*
*/
protected void resetAndUnblockPIN(OwnerPIN pin) {
pin.resetAndUnblock();
return;
}
/**
* Send data back to the user.
*
*
* @param apdu
* @param buff
* @param offset
* @param length
*
*/
protected void sendMessage(APDU apdu, byte buff[], short offset,
short length) {
apdu.setOutgoing();
apdu.setOutgoingLength((short) length);
apdu.sendBytesLong(buff, (short) offset, (short) length);
}
/**
* Sign the data
*
*
* @param bufferLength
*
*/
void signData(short bufferLength, byte alg) {
scratch[bufferLength] = (byte) 0x80;
for (byte i = (byte) (bufferLength + 1); i < sha1.blockSize(); i++) {
scratch[i] = (byte) 0x00;
}
byte bitL[] =
shortToByteArray((short) (8 * bufferLength)); // 60 bytes max
scratch[sha1.blockSize() - 2] = bitL[0];
scratch[sha1.blockSize() - 1] = bitL[1];
if ((alg == SHA1_HASH) || (alg == SHA1_CHALLENGE_RESPONSE)) {
hashSize = (byte) sha1.hashSize();
sha1.generateDigest(scratch, (short) 0, bufferLength, hash,
(short) 0);
} else if ((alg == XOR_HASH) || (alg == XOR_CHALLENGE_RESPONSE)) {
hashSize = 20;
toyXOR(sha1.blockSize());
}
}
/**
* A very simple digest operation
*
*
* @param bufferLength
*
*/
private void toyXOR(short bufferLength) {
for (byte i = 0; i < 20; i++) {
hash[i] = (byte) i;
}
for (byte i = 0; i < bufferLength; i++) {
hash[i % hashSize] ^= (byte) scratch[i];
}
}
/**
* SHA1 Challenge/Response check
*
* The input contains the challenge and the purse as well
* as a signature.
*
*/
void checkCardIdSignature(byte alg) {
short count = apdu.setIncomingAndReceive();
short challengePurseLength = count;
short dataLength = 0;
Util.arrayCopyNonAtomic(secKey, SZ, scratch, SZ,
(short) secKey.length);
dataLength += secKey.length;
Util.arrayCopyNonAtomic(buffer, ISO.OFFSET_CDATA, scratch,
(short) secKey.length, challengePurseLength);
dataLength += challengePurseLength;
signData(dataLength, alg);
sendMessage(apdu, hash, (short) 0, hashSize);
}
/**
* SHA1 Challenge/Response
* CLA INS P1 P2 LC Data
* +------+------------------+--------------+--------+--+-------+---------+
* |CC_CLA|CHALLENGE_RESPONSE|userPIN length|PIN type|LC|userPIN|CHALLENGE|
* +------+------------------+--------------+--------+--+-------+---------+
*
* The input data will be padded with zeros
*/
void signCardID(byte alg) {
signData(createDataForSigning(), alg);
sendMessage(apdu, hash, (short) 0, hashSize);
}
/**
* XOR hash -- hash the supplied data using "TOY" XOR hash
*
*/
void hash(byte alg) {
short count = apdu.setIncomingAndReceive();
Util.arrayCopyNonAtomic(buffer, ISO.OFFSET_CDATA, scratch, (short) 0,
count);
signData(count, alg);
sendMessage(apdu, hash, (short) 0, hashSize);
}
}