package anoncard;

import static org.junit.Assert.*;
import javacard.framework.APDU;
import javacard.framework.ISO7816;
import mixconfig.tools.dataretention.smartcard.ApduConstants;
import mixconfig.tools.dataretention.smartcard.Helpers;

import org.junit.Test;

public class ANONCardAppletSubTest_ProcessStatelessOperations extends AbtractANONCardAppletSubTest {

	public ANONCardAppletSubTest_ProcessStatelessOperations() {
		super();
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)}.
	 */
	@Test
	public void testProcessGetVersionAPDU() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_VERSION;
		assertProcessResponse(new byte[] { ANONCardApplet.APPLET_VERSION_MAJOR, ANONCardApplet.APPLET_VERSION_MINOR });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)}.
	 */
	@Test
	public void testProcessNOPAPDU() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_NOP;
		assertProcessResponse(new byte[] {});
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)}.
	 */
	@Test
	public void testProcessInvalidINSCodeAPDU() {
		apduBuffer[ISO7816.OFFSET_INS] = (byte) 0xFF;
		assertExceptionResponse(ISO7816.SW_INS_NOT_SUPPORTED);
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)}.
	 */
	@Test
	public void testProcessInvalidCLACodeAPDU() {
		apduBuffer[ISO7816.OFFSET_CLA]++;
		assertExceptionResponse(ISO7816.SW_CLA_NOT_SUPPORTED);
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#getNumberOfUsers(User[], APDU)}.
	 */
	@Test
	public void testProcessGetNumberOfAdministrators() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NUMBER_OF_ADMINISTRATORS;
		getConfiguration().administrators = null;
		assertProcessResponse(new byte[] { 0x00 });

		getConfiguration().administrators = new User[1];
		assertProcessResponse(new byte[] { 0x01 });

		getConfiguration().administrators = new User[15];
		assertProcessResponse(new byte[] { 0x0F });

		getConfiguration().administrators = new User[255];
		assertProcessResponse(new byte[] { (byte) 0xFF });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#getNameOfUsers(User[], short, APDU)}.
	 */
	@Test
	public void testProcessGetNameOfAdministrator() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NAME_OF_ADMINISTRATOR;
		for (int i = 0; i < getConfiguration().administrators.length; i++) {
			apduBuffer[ISO7816.OFFSET_P1] = (byte) i;
			assertProcessResponse(getConfiguration().administrators[i].getUsername());
		}

		apduBuffer[ISO7816.OFFSET_P1] = -1;
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		apduBuffer[ISO7816.OFFSET_P1] = (byte) (getConfiguration().administrators.length + 1);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#getNumberOfUsers(User[], APDU)}.
	 */
	@Test
	public void testProcessGetNumberOfOperators() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NUMBER_OF_OPERATORS;
		getConfiguration().operators = null;
		assertProcessResponse(new byte[] { 0x00 });

		getConfiguration().operators = new User[2];
		assertProcessResponse(new byte[] { 0x02 });

		getConfiguration().operators = new User[16];
		assertProcessResponse(new byte[] { 0x10 });

		getConfiguration().operators = new User[255];
		assertProcessResponse(new byte[] { (byte) 0xFF });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#getNameOfUsers(User[], short, APDU)}.
	 */
	@Test
	public void testProcessGetNameOperator() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NAME_OF_OPERATOR;
		for (int i = 0; i < getConfiguration().operators.length; i++) {
			apduBuffer[ISO7816.OFFSET_P1] = (byte) i;
			assertProcessResponse(getConfiguration().operators[i].getUsername());
		}

		apduBuffer[ISO7816.OFFSET_P1] = -1;
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		apduBuffer[ISO7816.OFFSET_P1] = (byte) (getConfiguration().operators.length + 1);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#getNumberOfTimeservers(APDU)}.
	 */
	@Test
	public void testProcessGetNumberOfTimeServers() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NUMBER_OF_TIMESERVERS;
		getConfiguration().timeservers = null;
		assertProcessResponse(new byte[] { 0x00 });

		getConfiguration().timeservers = new Timeserver[2];
		assertProcessResponse(new byte[] { 0x02 });

		getConfiguration().timeservers = new Timeserver[16];
		assertProcessResponse(new byte[] { 0x10 });

		getConfiguration().timeservers = new Timeserver[255];
		assertProcessResponse(new byte[] { (byte) 0xFF });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#getDomainOfTimeserver(short, APDU)}.
	 */
	@Test
	public void testProcessGetDomainOfTimeserver() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NAME_OF_TIMESERVER;
		for (int i = 0; i < getConfiguration().timeservers.length; i++) {
			apduBuffer[ISO7816.OFFSET_P1] = (byte) i;
			assertProcessResponse(getConfiguration().timeservers[i].getDomain());
		}

		apduBuffer[ISO7816.OFFSET_P1] = -1;
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		apduBuffer[ISO7816.OFFSET_P1] = (byte) (getConfiguration().timeservers.length + 1);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#isAllowedToModifyAdministrators(APDU)}.
	 */
	@Test
	public void testProcessIsAllowedToModifyAdministrators() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_IS_ALLOWED_TO_MODIFY_ADMINISTRATORS;
		byte expect = ApduConstants.TRUE;
		getConfiguration().isAllowedToModifyAdministrators = true;
		assertProcessResponse(new byte[] { expect });

		expect = ApduConstants.FALSE;
		getConfiguration().isAllowedToModifyAdministrators = false;
		assertProcessResponse(new byte[] { expect });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#isAllowedToModifyOperators(APDU)}.
	 */
	@Test
	public void testProcessIsAllowedToModifyOperators() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_IS_ALLOWED_TO_MODIFY_OPERATORS;
		byte expect = ApduConstants.TRUE;
		getConfiguration().isAllowedToModifyOperators = true;
		assertProcessResponse(new byte[] { expect });

		expect = ApduConstants.FALSE;
		getConfiguration().isAllowedToModifyOperators = false;
		assertProcessResponse(new byte[] { expect });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#isAllowedToModifyTimeservers(APDU)}.
	 */
	@Test
	public void testProcessIsAllowedToModifyTimeServers() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_IS_ALLOWED_TO_MODIFY_TIMESERVERS;
		byte expect = ApduConstants.TRUE;
		getConfiguration().isAllowedToModifyTimeservers = true;
		assertProcessResponse(new byte[] { expect });

		expect = ApduConstants.FALSE;
		getConfiguration().isAllowedToModifyTimeservers = false;
		assertProcessResponse(new byte[] { expect });
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#changeAdministratorPin(APDU)}.
	 */
	@Test
	public void testProcessChangeAdministratorPin() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_CHANGE_ADMINISTRATOR_PIN;
		byte[] userPin = userToBytes(getConfiguration().administrators[0]);
		byte[] newPin = new byte[ApduConstants.LENGTH_OF_ADMINISTRATOR_PIN];
		byte[] newPin2 = new byte[ApduConstants.LENGTH_OF_ADMINISTRATOR_PIN];
		for (int i = 0; i < newPin.length; i++) {
			newPin[i] = (byte) (2 * i);
			newPin2[i] = (byte) i;
		}

		// illegal data package
		setAPDUData(new byte[] {});
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
		setAPDUData(new byte[] { 3, 0 });
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
		setAPDUData(new byte[] { 3, 1, 2, 3, 4 });
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
		setAPDUData(Arrays.copyOf(userPin, (short) 0, (short) (userPin.length - 1)));
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// no such admin
		byte[] nemoPin = Arrays.copyOf(userPin);
		nemoPin[1]++;
		assertFalse("Stupid test data", exists(new User(Arrays.copyOf(nemoPin, (short) 1, nemoPin[0]), Arrays.copyOf(nemoPin, (short) (nemoPin[0] + 1), (short) nemoPin.length)),
				getConfiguration().administrators));
		setAPDUData(nemoPin);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// wrong PIN
		byte[] userWrongPin = Arrays.copyOf(userPin);
		userWrongPin[userWrongPin.length - 1]++;
		setAPDUData(Helpers.concatenate(userWrongPin, newPin));
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// wrong length of old/new PIN
		setAPDUData(Helpers.concatenate(userPin, new byte[ApduConstants.LENGTH_OF_ADMINISTRATOR_PIN + 1]));
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// benign no transaction open
		setAPDUData(Helpers.concatenate(userPin, newPin));
		assertProcessResponse(new byte[] {});
		assertArrayEquals("Pin not set in configuration", newPin, getConfiguration().administrators[0].getPassword());

		// benign transaction open
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_START_TRANSACTION;
		assertProcessResponse(new byte[] {});

		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_CHANGE_ADMINISTRATOR_PIN;
		userPin = userToBytes(getConfiguration().administrators[0]);
		setAPDUData(Helpers.concatenate(userPin, newPin2));
		assertProcessResponse(new byte[] {});
		assertArrayEquals("Pin2 not set in configuration", newPin2, getConfiguration().administrators[0].getPassword());
		assertArrayEquals("Pin2 not set in furture configuration", newPin2, getConfiguration().newConfiguration.administrators[0].getPassword());
	}

	/**
	 * Test method for
	 * {@link anoncard.ANONCardApplet#process(javacard.framework.APDU)} and
	 * {@link ANONCardApplet#changeOperatorPin(APDU)}.
	 */
	@Test
	public void testProcessChangeOperatorPin() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_CHANGE_OPERATOR_PIN;
		byte[] userPin = userToBytes(getConfiguration().operators[0]);
		byte[] newPin = new byte[ApduConstants.LENGTH_OF_OPERATOR_PIN];
		byte[] newPin2 = new byte[ApduConstants.LENGTH_OF_OPERATOR_PIN];
		for (int i = 0; i < newPin.length; i++) {
			newPin[i] = (byte) (2 * i);
			newPin2[i] = (byte) i;
		}

		// illegal data package
		setAPDUData(new byte[] {});
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
		setAPDUData(new byte[] { 3, 0 });
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
		setAPDUData(new byte[] { 3, 1, 2, 3, 4 });
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
		setAPDUData(Arrays.copyOf(userPin, (short) 0, (short) (userPin.length - 1)));
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// no such admin
		byte[] nemoPin = Arrays.copyOf(userPin);
		nemoPin[1]++;
		assertFalse("Stupid test data", exists(new User(Arrays.copyOf(nemoPin, (short) 1, nemoPin[0]), Arrays.copyOf(nemoPin, (short) (nemoPin[0] + 1), (short) nemoPin.length)),
				getConfiguration().operators));
		setAPDUData(nemoPin);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// wrong PIN
		byte[] userWrongPin = Arrays.copyOf(userPin);
		userWrongPin[userWrongPin.length - 1]++;
		setAPDUData(Helpers.concatenate(userWrongPin, newPin));
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// wrong length of old/new PIN
		setAPDUData(Helpers.concatenate(userPin, new byte[ApduConstants.LENGTH_OF_ADMINISTRATOR_PIN + 1]));
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		// benign no transaction open
		setAPDUData(Helpers.concatenate(userPin, newPin));
		assertProcessResponse(new byte[] {});
		assertArrayEquals("Pin not set in configuration", newPin, getConfiguration().operators[0].getPassword());

		// benign transaction open
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_START_TRANSACTION;
		assertProcessResponse(new byte[] {});

		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_CHANGE_OPERATOR_PIN;
		userPin = userToBytes(getConfiguration().operators[0]);
		setAPDUData(Helpers.concatenate(userPin, newPin2));
		assertProcessResponse(new byte[] {});
		assertArrayEquals("Pin2 not set in configuration", newPin2, getConfiguration().operators[0].getPassword());
		assertArrayEquals("Pin2 not set in furture configuration", newPin2, getConfiguration().newConfiguration.operators[0].getPassword());
	}

	/**
	 * Test method for
	 * {@link ANONCardApplet#getNeededNumberOfAdministrators(APDU)} and
	 * {@link ANONCardApplet#process(APDU)}.
	 */
	@Test
	public void testGetNeededNumberOfAdministrators() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NEEDED_NUMBER_OF_ADMINISTRATORS;
		assertProcessResponse(new byte[] { getConfiguration().neededNumberOfAdministrators });
	}

	/**
	 * Test method for {@link ANONCardApplet#getNeededNumberOfOperators(APDU)}
	 * and {@link ANONCardApplet#process(APDU)}.
	 */
	@Test
	public void testGetNeededNumberOfOperators() {
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_NEEDED_NUMBER_OF_OPERATORS;
		assertProcessResponse(new byte[] { getConfiguration().neededNumberOfOperators });

	}

	/**
	 * Test method for {@link ANONCardApplet#process(APDU)} and
	 * {@link ANONCardApplet#getDate(APDU)}
	 */
	@Test
	public void testGetDate() {
		byte[] date = new byte[] { 01, 02, 20, 10 };
		getDate().setDate(date);
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_GET_DATE;
		assertProcessResponse(date);

		getDate().reset();
		assertExceptionResponse(ApduConstants.EXCEPTION_DATE_NOT_SET);
	}

	/**
	 * Test method for {@link ANONCardApplet#process(APDU)} and
	 * {@link ANONCardApplet#setDate(APDU)}
	 */
	@Test
	public void testSetDate() {
		byte[] date = new byte[] { 01, 02, 20, 10 };
		apduBuffer[ISO7816.OFFSET_INS] = ApduConstants.INSTRUCTION_SET_DATE;
		setAPDUData(date);
		assertProcessResponse(new byte[] {});

		date = new byte[] {};
		setAPDUData(date);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);

		date = new byte[] { 1, 2, 3, 4, 5 };
		setAPDUData(date);
		assertExceptionResponse(ApduConstants.EXCEPTION_ILLEGAL_ARGUMENT);
	}
}