package de.persosim.simulator.secstatus;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map.Entry;
import java.util.Set;
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import org.junit.Before;
import org.junit.Test;
import de.persosim.simulator.apdu.ResponseApdu;
import de.persosim.simulator.cardobjects.Iso7816LifeCycleState;
import de.persosim.simulator.platform.Iso7816;
import de.persosim.simulator.platform.ProtocolMechanism;
import de.persosim.simulator.processing.ProcessingData;
import de.persosim.simulator.processing.UpdatePropagation;
import de.persosim.simulator.protocols.file.FileProtocol;
import de.persosim.simulator.seccondition.SecCondition;
import de.persosim.simulator.secstatus.SecStatus.SecContext;
import de.persosim.simulator.securemessaging.SmDataProvider;
import de.persosim.simulator.securemessaging.SmDataProviderGenerator;
import de.persosim.simulator.test.PersoSimTestCase;
import mockit.Mocked;
public class SecStatusTest extends PersoSimTestCase{
SecStatus securityStatus;
@Mocked SecMechanism mechanism;
@Before
public void setUp(){
securityStatus = new SecStatus();
}
//TODO define tests for SecStatus
/**
* Positive test case: This test checks that a session is stored correctly
*/
@Test
public void testSecStatus_StoreSession()
{
// store SecMechanism in SecStatus
ProtocolMechanism protocolMechanism = new ProtocolMechanism(FileProtocol.class);
SecStatusMechanismUpdatePropagation myMechanism = new SecStatusMechanismUpdatePropagation(SecContext.APPLICATION, protocolMechanism);
securityStatus.updateMechanisms(myMechanism);
// create processing data
ProcessingData processingData = new ProcessingData();
processingData.updateResponseAPDU(this, "Session context successful stored", new ResponseApdu(SW_9000_NO_ERROR));
// send update propagation to store the security status
processingData.addUpdatePropagation(this, "Inform the SecStatus to store the security status",
new SecStatusStoreUpdatePropagation(SecurityEvent.STORE_SESSION_CONTEXT, 1));
// update the security status and check that it has been stored
securityStatus.updateSecStatus(processingData);
// check that a session context has been stored
assertEquals(securityStatus.storedSecStatusContents.size(), 1);
// get the current stored mechanism from the stored security status
EnumMap<SecContext, HashMap<Class<? extends SecMechanism>, SecMechanism>> contexts = securityStatus.storedSecStatusContents.values().iterator().next();
Set<Entry<SecContext, HashMap<Class<? extends SecMechanism>, SecMechanism>>> contextSet = contexts.entrySet();
SecMechanism storedMechanism = null;
for( Entry<SecContext, HashMap<Class<? extends SecMechanism>, SecMechanism>> context : contextSet){
Set<Entry<Class<? extends SecMechanism>, SecMechanism>> test = context.getValue().entrySet();
if (!test.isEmpty()){
storedMechanism = test.iterator().next().getValue();
}
}
// checks that the stored security mechanism is equal to the one created
assertEquals(protocolMechanism, storedMechanism);
//ensure that the response has not been modified
assertEquals("Statusword is not 9000", Iso7816.SW_9000_NO_ERROR, processingData.getResponseApdu()
.getStatusWord());
}
/**
* Negative test case: This test tries to restore a session with an not existing session identifier
*/
@Test
public void testSecStatus_RestoreNotExistingSession()
{
ProcessingData processingData = new ProcessingData();
processingData.updateResponseAPDU(this, "Session context successful stored", new ResponseApdu(SW_9000_NO_ERROR));
processingData.addUpdatePropagation(this, "Inform the SecStatus to restore the security status",
new SecStatusStoreUpdatePropagation(SecurityEvent.RESTORE_SESSION_CONTEXT, 1));
securityStatus.updateSecStatus(processingData);
assertEquals(securityStatus.storedSecStatusContents.size(), 0);
assertEquals("Statusword is not 6A88", Iso7816.SW_6A88_REFERENCE_DATA_NOT_FOUND, processingData.getResponseApdu()
.getStatusWord());
}
/**
* Positive test case: This test stores a session and restores a session
*/
@Test
public void testSecStatus_StoreRestoreSession()
{
// test cases set up
final SmDataProvider smDataProvider = new SmDataProvider(){
@Override
public Class<? extends UpdatePropagation> getKey() {
return SmDataProvider.class;
}
@Override
public void init(SmDataProvider prev) {}
@Override
public void nextIncoming() {}
@Override
public void nextOutgoing() {}
@Override
public Cipher getCipher() {return null;}
@Override
public IvParameterSpec getCipherIv() {return null;}
@Override
public SecretKey getKeyEnc() {return null;}
@Override
public Mac getMac() {return null;}
@Override
public byte[] getMacAuxiliaryData() {return null;}
@Override
public SecretKey getKeyMac() {return null;}
@Override
public Integer getMacLength() {return null;}
@Override
public SmDataProviderGenerator getSmDataProviderGenerator() {return null;}
};
SmDataProviderGenerator dataProviderGenerator = new SmDataProviderGenerator(){
@Override
public boolean needsDeletionInCaseOf(SecurityEvent event) {
return false;
}
@Override
public Class<? extends SecMechanism> getKey() {
return SmDataProviderGenerator.class;
}
@Override
public SmDataProvider generateSmDataProvider() {
return smDataProvider;
}
};
SecStatusMechanismUpdatePropagation mySecMechanism = new SecStatusMechanismUpdatePropagation(SecContext.APPLICATION, dataProviderGenerator);
securityStatus.updateMechanisms(mySecMechanism);
// create processing data
ProcessingData processingData = new ProcessingData();
processingData.updateResponseAPDU(this, "Session context successful stored", new ResponseApdu(SW_9000_NO_ERROR));
// call store security status update propagation
processingData.addUpdatePropagation(this, "Inform the SecStatus to store the security status",
new SecStatusStoreUpdatePropagation(SecurityEvent.STORE_SESSION_CONTEXT, 1));
// update the security status
securityStatus.updateSecStatus(processingData);
assertEquals("Statusword is not 9000", Iso7816.SW_9000_NO_ERROR, processingData.getResponseApdu()
.getStatusWord());
// get the stored security mechanism
// EnumMap<SecContext, HashMap<Class<? extends SecMechanism>, SecMechanism>> contexts = securityStatus.storedSecStatusContents.values().iterator().next();
// Set<Entry<SecContext, HashMap<Class<? extends SecMechanism>, SecMechanism>>> contextSet = contexts.entrySet();
//
// SecMechanism storedSecMechanism = null;
// for( Entry<SecContext, HashMap<Class<? extends SecMechanism>, SecMechanism>> context : contextSet){
//
// Set<Entry<Class<? extends SecMechanism>, SecMechanism>> secMechanisms = context.getValue().entrySet();
//
// if (!secMechanisms.isEmpty()){
// Object object = secMechanisms.iterator().next().getValue();
// if (object instanceof SmDataProviderGenerator) {
// storedSecMechanism = (SecMechanism) object;
// }
//
// }
// }
//
// // compare that the stored security status and the one created before are equal
// assertEquals(dataProviderGenerator, storedSecMechanism);
// assertEquals(securityStatus.storedSecStatusContents.size(), 1);
// insert a new protocol mechanism
ProtocolMechanism protocolMechanism = new ProtocolMechanism(FileProtocol.class);
SecStatusMechanismUpdatePropagation myMechanism = new SecStatusMechanismUpdatePropagation(SecContext.APPLICATION, protocolMechanism);
securityStatus.updateMechanisms(myMechanism);
//test the restore
processingData = new ProcessingData();
processingData.updateResponseAPDU(this, "Session context successful restored", new ResponseApdu(SW_9000_NO_ERROR));
processingData.addUpdatePropagation(this, "Inform the SecStatus to restore the security status",
new SecStatusStoreUpdatePropagation(SecurityEvent.RESTORE_SESSION_CONTEXT, 1));
securityStatus.updateSecStatus(processingData);
// check if restore security status propagation has been processed
LinkedList<UpdatePropagation> secStoreUpdatePropagations = processingData.getUpdatePropagations(SecStatusStoreUpdatePropagation.class);
assertEquals(1, secStoreUpdatePropagations.size());
// ensure that the SmDataProviderGenerator mechanism is correctly restored
HashSet<Class<? extends SecMechanism>> smDataMechanisms = new HashSet<>();
smDataMechanisms.add(SmDataProviderGenerator.class);
Collection<SecMechanism> storedSmDataMechanism = securityStatus.getCurrentMechanisms(SecContext.APPLICATION, smDataMechanisms);
assertEquals(1, storedSmDataMechanism.size());
// ensure that the mechanism inserted before the restore got removed
HashSet<Class<? extends SecMechanism>> protocolMechanisms = new HashSet<>();
protocolMechanisms.add(ProtocolMechanism.class);
Collection<SecMechanism> storedProtocolMechanism = securityStatus.getCurrentMechanisms(SecContext.APPLICATION, protocolMechanisms);
assertEquals(0, storedProtocolMechanism.size());
// ensure that the correct SmDataUpgradePropagation is fired
LinkedList<UpdatePropagation> smDataProviders = processingData.getUpdatePropagations(SmDataProvider.class);
assertEquals(1, smDataProviders.size());
assertEquals(smDataProvider, smDataProviders.getFirst());
// ensure that the status word is not modified by the process
assertEquals("Statusword is not 9000", Iso7816.SW_9000_NO_ERROR, processingData.getResponseApdu()
.getStatusWord());
}
/**
* Positive test case: check the updateSecStatus method in the SecStatus class.
*/
@Test
public void testUpdateSecStatus_Input_Is_ProcessingData_Object()
{
SecStatus test = new SecStatus();
ProcessingData lol = new ProcessingData();
test.updateSecStatus(lol);
}
/**
* Positive test case: check that the getCurrentMechanisms method correctly finds mechanisms
*/
@Test
public void testGetCurrentMechanisms()
{
SecMechanism mechanismToFind = new AbstractSecMechanism() {
@Override
public boolean needsDeletionInCaseOf(SecurityEvent event) {
return false;
}
};
populateSecStatus(SecContext.APPLICATION, mechanismToFind);
Collection<Class<? extends SecMechanism>> previousMechanisms = new HashSet<>();
previousMechanisms.add(mechanismToFind.getClass());
Collection<SecMechanism> foundMechanisms = securityStatus.getCurrentMechanisms(SecContext.APPLICATION, previousMechanisms);
assertEquals(1, foundMechanisms.size());
assertSame(mechanismToFind, foundMechanisms.iterator().next());
}
/**
* Adds a mechanism to the {@link SecStatus} by wrapping it in a {@link SecStatusMechanismUpdatePropagation}
* @param context
* @param mechanisms
*/
private void populateSecStatus(SecContext context, SecMechanism ...mechanisms){
for (SecMechanism mechanism : mechanisms){
SecStatusMechanismUpdatePropagation updatePropagation = new SecStatusMechanismUpdatePropagation(context, mechanism);
securityStatus.updateMechanisms(updatePropagation);
}
}
/**
* Check that restoring using an incorrect ID causes the correct exception
*/
@Test(expected = IllegalArgumentException.class)
public void testRestoreNotExisting(){
securityStatus.restoreSecStatus(10);
}
@Test
public void testStoreAndRestoreStatus(){
SecMechanism beforeStoring = new AbstractSecMechanism() {
@Override
public boolean needsDeletionInCaseOf(SecurityEvent event) {
return false;
}
};
SecMechanism afterStoring = new AbstractSecMechanism() {
@Override
public boolean needsDeletionInCaseOf(SecurityEvent event) {
return true;
}
};
securityStatus.storeSecStatus(0);
populateSecStatus(SecStatus.SecContext.GLOBAL, beforeStoring);
Collection<Class<? extends SecMechanism>> previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(beforeStoring.getClass());
Collection<SecMechanism> mechanismsBeforeStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(1, mechanismsBeforeStore.size());
assertSame(beforeStoring, mechanismsBeforeStore.iterator().next());
securityStatus.storeSecStatus(1);
populateSecStatus(SecStatus.SecContext.GLOBAL, afterStoring);
previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(beforeStoring.getClass());
Collection<SecMechanism> mechanismsAfterStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(1, mechanismsAfterStore.size());
previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(afterStoring.getClass());
mechanismsAfterStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(1, mechanismsAfterStore.size());
securityStatus.restoreSecStatus(1);
previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(beforeStoring.getClass());
mechanismsAfterStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(1, mechanismsAfterStore.size());
previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(afterStoring.getClass());
mechanismsAfterStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(0, mechanismsAfterStore.size());
securityStatus.restoreSecStatus(0);
previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(beforeStoring.getClass());
mechanismsAfterStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(0, mechanismsAfterStore.size());
previousMechanisms = new HashSet<Class<? extends SecMechanism>>();
previousMechanisms.add(afterStoring.getClass());
mechanismsAfterStore = securityStatus.getCurrentMechanisms(SecContext.GLOBAL, previousMechanisms);
assertEquals(0, mechanismsAfterStore.size());
}
/**
* Positive test case checking the life cycle allowing access even if the
* security conditions do not match.
*/
@Test
public void testCheckAccessConditionsLifecycleAllows() {
Iso7816LifeCycleState state = Iso7816LifeCycleState.CREATION;
SecStatus securityStatus = new SecStatus();
SecCondition secCondition = new SecCondition() {
@Override
public Collection<Class<? extends SecMechanism>> getNeededMechanisms() {
return Collections.emptySet();
}
@Override
public boolean check(Collection<SecMechanism> mechanisms) {
return false;
}
};
assertTrue(securityStatus.checkAccessConditions(state, secCondition, SecContext.APPLICATION));
}
/**
* Positive test case checking whether the security conditions prohibit access.
*/
@Test
public void testCheckAccessConditionsSecurityConditionsProhibit(){
Iso7816LifeCycleState state = Iso7816LifeCycleState.OPERATIONAL_ACTIVATED;
SecStatus securityStatus = new SecStatus();
SecCondition secCondition = new SecCondition() {
@Override
public Collection<Class<? extends SecMechanism>> getNeededMechanisms() {
return Collections.emptySet();
}
@Override
public boolean check(Collection<SecMechanism> mechanisms) {
return false;
}
};
assertFalse(securityStatus.checkAccessConditions(state, secCondition, SecContext.APPLICATION));
}
/**
* Positive test case checking whether the security conditions can allow access.
*/
@Test
public void testCheckAccessConditionsSecurityConditionsAllow(){
Iso7816LifeCycleState state = Iso7816LifeCycleState.OPERATIONAL_ACTIVATED;
SecStatus securityStatus = new SecStatus();
SecCondition secCondition = new SecCondition() {
@Override
public Collection<Class<? extends SecMechanism>> getNeededMechanisms() {
return Collections.emptySet();
}
@Override
public boolean check(Collection<SecMechanism> mechanisms) {
return true;
}
};
assertTrue(securityStatus.checkAccessConditions(state, secCondition, SecContext.APPLICATION));
}
}