[Impressum] [E-Mail]

package generated.javacard.copycardMoneySecure.copycard;

import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.Util;

/**
 * Class for T(L)V encoding of messages and other classes
 */
public class Coding {

	/**
	 * offset into encodeDestination for writing/reading
	 */
	private short encoding_length;

	/**
	 * array used for encoding/decoding
	 */
	private byte[] encodeDestination;

	/**
	 * Singleton instance
	 */
	private static Coding the_instance;

	/**
	 * Singleton accessor
	 * 
	 * @return Singleton instance
	 */
	public static Coding getInstance() {
		if (the_instance == null)
			the_instance = new Coding();
		return the_instance;
	}

	/**
	 * return offset into encodeDestination
	 * 
	 * @return value of encoding_length
	 */
	public short getEncodingLength() {
		return encoding_length;
	}

	/**
	 * initializes encoding. This method must be called before encoding an object.
	 * The offset (encoding_length) is reset to 0 and encodeDestination is set to dest.
	 * 
	 * @param dest The (empty) destination array for encoding
	 */
	private void encodeInit(byte[] dest) {
		encodeDestination = dest;
		encoding_length = 0;
	}

	/**
	 * initializes decoding. The offset (encoding_length) is reset to 0.
	 */
	private void decodeInit() {
		encoding_length = 0;
	}

	/**
	 * initializes encoding and encodes an object.
	 * 
	 * @param o The object to encode
	 * @param dest destination array for the encoding
	 * @return length of the encoded object
	 */
	public short encode(Codeable o, byte[] dest) {
		encodeInit(dest);
		try {
			encode(o);
		} catch (ArrayIndexOutOfBoundsException e) {
			stop();
		}
		return encoding_length;
	}

	/**
	 * computes the encoding length of an object without actually encoding it.
	 * 
	 * @param o The object of which the encoding length should be computed
	 * @return encoding length
	 */
	private short computeSize(Codeable o) {
		encoding_length = 0;
		try {
			compSize(o);
			return encoding_length;
		} catch (Exception e) {
			return -1;
		}
	}

	private void encodeShort(short o) {
		encodeDestination[encoding_length++] = Code.SHORT;
		Util.setShort(encodeDestination, encoding_length, o);
		encoding_length += 2;
	}

	private void encodeByte(byte o) {
		encodeDestination[encoding_length++] = Code.BYTE;
		encodeDestination[encoding_length++] = o;
	}

	private void encodeBoolean(boolean o) {
		encodeDestination[encoding_length++] = Code.BOOLEAN;
		encodeDestination[encoding_length++] = (byte) (o ? 1 : 0);
	}

	private void compSizeShort(short o) {
		encoding_length += 3;
	}

	private void compSizeByte(byte o) {
		encoding_length += 2;
	}

	private void compSizeBoolean(boolean o) {
		encoding_length += 2;
	}

	private short decodeShort(byte[] in) {
		if (in[encoding_length++] != Code.SHORT)
			stop();
		short res = Util.getShort(in, encoding_length);
		encoding_length += 2;
		return res;
	}

	private byte decodeByte(byte[] in) {
		if (in[encoding_length++] != Code.BYTE)
			stop();
		byte res = in[encoding_length++];
		return res;
	}

	private boolean decodeBoolean(byte[] in) {
		if (in[encoding_length++] != Code.BOOLEAN)
			stop();
		boolean res = (in[encoding_length++] == 1 ? true : false);
		return res;
	}

	private void encodeByteArray(byte[] o) {
		encodeDestination[encoding_length++] = Code.BYTEARRAY;
		Util.setShort(encodeDestination, encoding_length, (short) (o.length));
		encoding_length += 2;
		Util.arrayCopy(o, (short) 0, encodeDestination, encoding_length,
				(short) o.length);
		encoding_length += o.length;
	}

	private void compSizeByteArray(byte[] o) {
		encoding_length += 3;
		encoding_length += o.length;
	}

	private void decodeByteArrayInto(byte[] in, byte[] into) {
		if (in[encoding_length++] != Code.BYTEARRAY)
			stop();
		short len = Util.getShort(in, encoding_length);
		encoding_length += 2;
		Util.arrayCopy(in, encoding_length, into, (short) 0, len);
		encoding_length += len;
	}

	private void encode(Codeable c) {
		switch (c.getCode()) {
			case Code.LOAD :
				encodeLoad((Load) c);
				break;
			case Code.REQUESTBALANCE :
				encodeRequestBalance((RequestBalance) c);
				break;
			case Code.PAY :
				encodePay((Pay) c);
				break;
			case Code.RESREQUESTBALANCE :
				encodeResRequestBalance((ResRequestBalance) c);
				break;
			case Code.RESPAY :
				encodeResPay((ResPay) c);
				break;
			case Code.RESAUTHENTICATE :
				encodeResAuthenticate((ResAuthenticate) c);
				break;
			case Code.AUTHENTICATE :
				encodeAuthenticate((Authenticate) c);
				break;
			case Code.HASHEDDATA :
				encodeHashedData((HashedData) c);
				break;
			case Code.AUTHDATA :
				encodeAuthData((AuthData) c);
				break;
			case Code.NONCE :
				encodeNonce((Nonce) c);
				break;
			case Code.SECRET :
				encodeSecret((Secret) c);
				break;
			// never reached
			default :
				stop();
				break;
		}
	}

	public Message decodeMessage(byte[] in, short offset, short expectedLength) {
		encoding_length = offset;
		Message m = null;
		try {
			switch (in[encoding_length]) {
				case Code.LOAD :
					m = Store.newLoad();
					decodeLoadInto(in, (Load) m);
					break;
				case Code.REQUESTBALANCE :
					m = Store.newRequestBalance();
					decodeRequestBalanceInto(in, (RequestBalance) m);
					break;
				case Code.PAY :
					m = Store.newPay();
					decodePayInto(in, (Pay) m);
					break;
				case Code.RESREQUESTBALANCE :
					m = Store.newResRequestBalance();
					decodeResRequestBalanceInto(in, (ResRequestBalance) m);
					break;
				case Code.RESPAY :
					m = Store.newResPay();
					decodeResPayInto(in, (ResPay) m);
					break;
				case Code.RESAUTHENTICATE :
					m = Store.newResAuthenticate();
					decodeResAuthenticateInto(in, (ResAuthenticate) m);
					break;
				case Code.AUTHENTICATE :
					m = Store.newAuthenticate();
					decodeAuthenticateInto(in, (Authenticate) m);
					break;
			}
		} catch (ArrayIndexOutOfBoundsException e) {
			stop();
		}
		if (m == null || encoding_length != (short) (expectedLength + offset))
			stop();
		return m;
	}

	private void compSize(Codeable c) {
		switch (c.getCode()) {
			case Code.LOAD :
				compSizeLoad((Load) c);
				break;
			case Code.REQUESTBALANCE :
				compSizeRequestBalance((RequestBalance) c);
				break;
			case Code.PAY :
				compSizePay((Pay) c);
				break;
			case Code.RESREQUESTBALANCE :
				compSizeResRequestBalance((ResRequestBalance) c);
				break;
			case Code.RESPAY :
				compSizeResPay((ResPay) c);
				break;
			case Code.RESAUTHENTICATE :
				compSizeResAuthenticate((ResAuthenticate) c);
				break;
			case Code.AUTHENTICATE :
				compSizeAuthenticate((Authenticate) c);
				break;
			case Code.HASHEDDATA :
				compSizeHashedData((HashedData) c);
				break;
			case Code.AUTHDATA :
				compSizeAuthData((AuthData) c);
				break;
			case Code.NONCE :
				compSizeNonce((Nonce) c);
				break;
			case Code.SECRET :
				compSizeSecret((Secret) c);
				break;
			// never reached
			default :
				stop();
				break;
		}
	}

	private void encodeLoad(Load c) {
		encodeDestination[encoding_length++] = Code.LOAD;
		encodeShort((short) c.amount);
		encode(c.authterminal);
	}

	private void decodeLoadInto(byte[] in, Load into) {
		if (in[encoding_length++] != Code.LOAD)
			stop();
		into.amount = decodeShort(in);
		decodeHashedDataInto(in, (HashedData) (into.authterminal));
	}

	private void compSizeLoad(Load c) {
		encoding_length++;
		compSizeShort(c.amount);
		compSize(c.authterminal);
	}

	private void encodeRequestBalance(RequestBalance c) {
		encodeDestination[encoding_length++] = Code.REQUESTBALANCE;
	}

	private void decodeRequestBalanceInto(byte[] in, RequestBalance into) {
		if (in[encoding_length++] != Code.REQUESTBALANCE)
			stop();
	}

	private void compSizeRequestBalance(RequestBalance c) {
		encoding_length++;
	}

	private void encodePay(Pay c) {
		encodeDestination[encoding_length++] = Code.PAY;
		encodeShort((short) c.amount);
		encode(c.terminalchallenge);
	}

	private void decodePayInto(byte[] in, Pay into) {
		if (in[encoding_length++] != Code.PAY)
			stop();
		into.amount = decodeShort(in);
		decodeNonceInto(in, (Nonce) (into.terminalchallenge));
	}

	private void compSizePay(Pay c) {
		encoding_length++;
		compSizeShort(c.amount);
		compSize(c.terminalchallenge);
	}

	private void encodeResRequestBalance(ResRequestBalance c) {
		encodeDestination[encoding_length++] = Code.RESREQUESTBALANCE;
		encodeShort((short) c.balance);
	}

	private void decodeResRequestBalanceInto(byte[] in, ResRequestBalance into) {
		if (in[encoding_length++] != Code.RESREQUESTBALANCE)
			stop();
		into.balance = decodeShort(in);
	}

	private void compSizeResRequestBalance(ResRequestBalance c) {
		encoding_length++;
		compSizeShort(c.balance);
	}

	private void encodeResPay(ResPay c) {
		encodeDestination[encoding_length++] = Code.RESPAY;
		encode(c.authcard);
	}

	private void decodeResPayInto(byte[] in, ResPay into) {
		if (in[encoding_length++] != Code.RESPAY)
			stop();
		decodeHashedDataInto(in, (HashedData) (into.authcard));
	}

	private void compSizeResPay(ResPay c) {
		encoding_length++;
		compSize(c.authcard);
	}

	private void encodeResAuthenticate(ResAuthenticate c) {
		encodeDestination[encoding_length++] = Code.RESAUTHENTICATE;
		encode(c.cardletchallenge);
	}

	private void decodeResAuthenticateInto(byte[] in, ResAuthenticate into) {
		if (in[encoding_length++] != Code.RESAUTHENTICATE)
			stop();
		decodeNonceInto(in, (Nonce) (into.cardletchallenge));
	}

	private void compSizeResAuthenticate(ResAuthenticate c) {
		encoding_length++;
		compSize(c.cardletchallenge);
	}

	private void encodeAuthenticate(Authenticate c) {
		encodeDestination[encoding_length++] = Code.AUTHENTICATE;
	}

	private void decodeAuthenticateInto(byte[] in, Authenticate into) {
		if (in[encoding_length++] != Code.AUTHENTICATE)
			stop();
	}

	private void compSizeAuthenticate(Authenticate c) {
		encoding_length++;
	}

	private void encodeHashedData(HashedData c) {
		encodeDestination[encoding_length++] = Code.HASHEDDATA;
		encodeByteArray((byte[]) c.hashed);
	}

	private void decodeHashedDataInto(byte[] in, HashedData into) {
		if (in[encoding_length++] != Code.HASHEDDATA)
			stop();
		decodeByteArrayInto(in, into.hashed);
	}

	private void compSizeHashedData(HashedData c) {
		encoding_length++;
		compSizeByteArray(c.hashed);
	}

	private void encodeAuthData(AuthData c) {
		encodeDestination[encoding_length++] = Code.AUTHDATA;
		encodeShort((short) c.instruction);
		encode(c.passphrase);
		encode(c.challenge);
		encodeShort((short) c.amount);
	}

	private void decodeAuthDataInto(byte[] in, AuthData into) {
		if (in[encoding_length++] != Code.AUTHDATA)
			stop();
		into.instruction = decodeShort(in);
		decodeSecretInto(in, (Secret) (into.passphrase));
		decodeNonceInto(in, (Nonce) (into.challenge));
		into.amount = decodeShort(in);
	}

	private void compSizeAuthData(AuthData c) {
		encoding_length++;
		compSizeShort(c.instruction);
		compSize(c.passphrase);
		compSize(c.challenge);
		compSizeShort(c.amount);
	}

	private void encodeNonce(Nonce c) {
		encodeDestination[encoding_length++] = Code.NONCE;
		encodeByteArray((byte[]) c.nonce);
	}

	private void decodeNonceInto(byte[] in, Nonce into) {
		if (in[encoding_length++] != Code.NONCE)
			stop();
		decodeByteArrayInto(in, into.nonce);
	}

	private void compSizeNonce(Nonce c) {
		encoding_length++;
		compSizeByteArray(c.nonce);
	}

	private void encodeSecret(Secret c) {
		encodeDestination[encoding_length++] = Code.SECRET;
		encodeByteArray((byte[]) c.secret);
	}

	private void decodeSecretInto(byte[] in, Secret into) {
		if (in[encoding_length++] != Code.SECRET)
			stop();
		decodeByteArrayInto(in, into.secret);
	}

	private void compSizeSecret(Secret c) {
		encoding_length++;
		compSizeByteArray(c.secret);
	}

	public void decodeCopycard(byte[] in, short offset, Copycard thisCopycard) {
		encoding_length = offset;
		if (in[encoding_length++] != Code.COPYCARD)
			stop();

		if (in[encoding_length] != Code.IGNORE) {
			decodeSecretInto(in, thisCopycard.passphrase);
		} else
			encoding_length++;

		if (in[encoding_length] != Code.IGNORE) {
			thisCopycard.state = decodeByte(in);
		} else
			encoding_length++;
	}

	private void stop() {
		ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
	}
}