package de.persosim.simulator.platform; import java.util.ArrayList; import java.util.List; import org.globaltester.logging.InfoSource; import org.junit.Before; import org.junit.Test; import de.persosim.simulator.apdu.CommandApduFactory; import de.persosim.simulator.apdu.ResponseApdu; import de.persosim.simulator.cardobjects.MasterFile; import de.persosim.simulator.exception.AccessDeniedException; import de.persosim.simulator.processing.ProcessingData; import de.persosim.simulator.protocols.AbstractProtocolStateMachine; import de.persosim.simulator.protocols.Protocol; import de.persosim.simulator.protocols.ProtocolUpdate; import de.persosim.simulator.test.PersoSimTestCase; import de.persosim.simulator.tlv.TlvValuePlain; import mockit.Delegate; import mockit.Invocation; import mockit.Mocked; import mockit.NonStrictExpectations; import mockit.Verifications; import mockit.VerificationsInOrder; public class CommandProcessorTest extends PersoSimTestCase { @Mocked Protocol mockedProtocol; CommandProcessor commandProcessor; InfoSource source = new InfoSource() { @Override public String getIDString() { return "TestSource"; } }; /** * Instantiate and initialize the object under test * {@link #commandProcessor} with the mocked personalization. * @throws AccessDeniedException * */ @Before public void setUp() throws AccessDeniedException { MasterFile mf = new MasterFile(); List<Protocol> protocols = new ArrayList<>(); protocols.add(mockedProtocol); commandProcessor = new CommandProcessor(protocols, mf); commandProcessor.init(); } /** * Expected behavior for the {@link CommandProcessor} is that a * {@link Protocol} that has not been active yet is reset before every APDU * that it is requested to process. */ @Test public void testProcessProcessingData_ResetProtocol() { // provide simple APDU ProcessingData pData = new ProcessingData(); byte[] apduBytes = new byte[] { 0x00, (byte) 0x84, 0x00, 0x00, 0x08 }; pData.updateCommandApdu(this, "test command APDU", CommandApduFactory.createCommandApdu( apduBytes)); // call mut commandProcessor.processAscending(pData); // make sure reset has been called new Verifications() { { mockedProtocol.reset(); } }; } /** * It is expected that an active Protocol (e.g. a protocol on the stack, * that means a Protocol that already processed an APDU but did not * announce that it is finished) is not reset before the following APDU is * transmitted for processing. */ @Test public void testProcessProcessingData_NoResetForActiveProtocol() { // prepare the mocked protocol new NonStrictExpectations() {{ mockedProtocol.process(withInstanceOf(ProcessingData.class)); result = new Delegate<AbstractProtocolStateMachine>() { @SuppressWarnings("unused") // JMockit public void process(Invocation inv, ProcessingData processingData) { TlvValuePlain responseData = new TlvValuePlain(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }); ResponseApdu resp = new ResponseApdu(responseData, Iso7816.SW_9000_NO_ERROR); processingData.updateResponseAPDU(source, "GetNonce processed successfully", resp); } }; mockedProtocol.getProtocolName(); result = "MockedProtocol"; }}; // transmit first APDU final ProcessingData pData1 = new ProcessingData(); byte[] apduBytes = new byte[] { 0x00, (byte) 0x84, 0x00, 0x00, 0x08 }; pData1.updateCommandApdu(this, "1st test APDU", CommandApduFactory.createCommandApdu( apduBytes)); commandProcessor.processAscending(pData1); // transmit second APDU final ProcessingData pData2 = new ProcessingData(); pData2.updateCommandApdu(this, "2nd test APDU", CommandApduFactory.createCommandApdu( apduBytes)); commandProcessor.processAscending(pData2); // make sure reset has been called only once (implicitly known to be before the first APDU) new Verifications() {{ mockedProtocol.reset(); times = 1; mockedProtocol.process(pData1); mockedProtocol.process(pData2); }}; } /** * It is expected that a formerly active Protocol (e.g. a protocol that already processed an APDU and announced that it is finished) * is not reset before the following APDU is transmitted for processing. */ @Test public void testProcessProcessingData_ResetFormerlyActiveProtocol() { // prepare the mocked protocol new NonStrictExpectations() {{ mockedProtocol.process(withInstanceOf(ProcessingData.class)); result = new Delegate<AbstractProtocolStateMachine>() { @SuppressWarnings("unused") // JMockit public void process(Invocation inv, ProcessingData processingData) { TlvValuePlain responseData = new TlvValuePlain(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }); ResponseApdu resp = new ResponseApdu(responseData, Iso7816.SW_9000_NO_ERROR); processingData.updateResponseAPDU(source, "GetNonce processed successfully", resp); processingData.addUpdatePropagation(source, "mocked protocol completed", new ProtocolUpdate(true)); } }; mockedProtocol.getProtocolName(); result = "MockedProtocol"; }}; // transmit first APDU final ProcessingData pData1 = new ProcessingData(); byte[] apduBytes = new byte[] { 0x00, (byte) 0x84, 0x00, 0x00, 0x08 }; pData1.updateCommandApdu(this, "1st test APDU", CommandApduFactory.createCommandApdu( apduBytes)); commandProcessor.processAscending(pData1); // transmit second APDU final ProcessingData pData2 = new ProcessingData(); pData2.updateCommandApdu(this, "2nd test APDU", CommandApduFactory.createCommandApdu( apduBytes)); commandProcessor.processAscending(pData2); // make sure reset has been called twice (once before every APDU) new VerificationsInOrder() {{ mockedProtocol.reset(); mockedProtocol.process(pData1); mockedProtocol.reset(); mockedProtocol.process(pData2); }}; } /** * It is expected that all active Protocols (e.g. a protocol on the stack, * that means Protocols that already processed an APDU but did not announce * that it is finished) is removed from the protocol stack during powerOn * and thus reset before the following APDU is transmitted for processing. */ @Test public void testPowerOn_ResetForActiveProtocols() { // prepare the mocked protocol new NonStrictExpectations() {{ mockedProtocol.process(withInstanceOf(ProcessingData.class)); result = new Delegate<AbstractProtocolStateMachine>() { @SuppressWarnings("unused") // JMockit public void process(Invocation inv, ProcessingData processingData) { TlvValuePlain responseData = new TlvValuePlain(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }); ResponseApdu resp = new ResponseApdu(responseData, Iso7816.SW_9000_NO_ERROR); processingData.updateResponseAPDU(source, "GetNonce processed successfully", resp); } }; mockedProtocol.getProtocolName(); result = "MockedProtocol"; }}; // transmit first APDU final ProcessingData pData1 = new ProcessingData(); byte[] apduBytes = new byte[] { 0x00, (byte) 0x84, 0x00, 0x00, 0x08 }; pData1.updateCommandApdu(this, "1st test APDU", CommandApduFactory.createCommandApdu( apduBytes)); commandProcessor.processAscending(pData1); //mut, repower the card commandProcessor.powerOn(); // transmit second APDU final ProcessingData pData2 = new ProcessingData(); pData2.updateCommandApdu(this, "2nd test APDU", CommandApduFactory.createCommandApdu( apduBytes)); commandProcessor.processAscending(pData2); // make sure reset has been called once (implicitly known to be before the first APDU) new VerificationsInOrder() {{ mockedProtocol.reset(); mockedProtocol.process(pData1); mockedProtocol.reset(); mockedProtocol.process(pData2); }}; } }