package de.persosim.simulator.protocols.pace;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import org.junit.Before;
import org.junit.Test;
import de.persosim.simulator.apdu.CommandApduFactory;
import de.persosim.simulator.cardobjects.AuthObjectIdentifier;
import de.persosim.simulator.cardobjects.CardObject;
import de.persosim.simulator.cardobjects.DomainParameterSetCardObject;
import de.persosim.simulator.cardobjects.DomainParameterSetIdentifier;
import de.persosim.simulator.cardobjects.Iso7816LifeCycleState;
import de.persosim.simulator.cardobjects.MasterFile;
import de.persosim.simulator.cardobjects.OidIdentifier;
import de.persosim.simulator.cardobjects.PasswordAuthObject;
import de.persosim.simulator.cardobjects.PasswordAuthObjectWithRetryCounter;
import de.persosim.simulator.crypto.DomainParameterSet;
import de.persosim.simulator.exception.LifeCycleChangeException;
import de.persosim.simulator.platform.CardStateAccessor;
import de.persosim.simulator.platform.Iso7816;
import de.persosim.simulator.processing.ProcessingData;
import de.persosim.simulator.protocols.ta.CertificateRole;
import de.persosim.simulator.protocols.ta.RelativeAuthorization;
import de.persosim.simulator.protocols.ta.TerminalType;
import de.persosim.simulator.seccondition.OrSecCondition;
import de.persosim.simulator.seccondition.PaceWithPasswordRunningSecurityCondition;
import de.persosim.simulator.seccondition.PaceWithPasswordSecurityCondition;
import de.persosim.simulator.seccondition.TaSecurityCondition;
import de.persosim.simulator.secstatus.PaceMechanism;
import de.persosim.simulator.secstatus.SecMechanism;
import de.persosim.simulator.secstatus.SecStatus.SecContext;
import de.persosim.simulator.test.PersoSimTestCase;
import de.persosim.simulator.tlv.TlvConstants;
import de.persosim.simulator.tlv.TlvDataObjectContainer;
import de.persosim.simulator.tlv.TlvValue;
import de.persosim.simulator.utils.BitField;
import de.persosim.simulator.utils.HexString;
import de.persosim.simulator.utils.Utils;
import mockit.Mocked;
import mockit.NonStrictExpectations;
public class PinManagementPaceBypassTest extends PersoSimTestCase {
@Mocked
MasterFile mockedMf;
@Mocked
CardStateAccessor mockedCardStateAccessor;
PaceBypassProtocol paceProtocol;
Collection<SecMechanism> csmEmpty, csmWithCan, csmWithPin;
PasswordAuthObject pwdaoWithCan;
PasswordAuthObjectWithRetryCounter pwdaoWithPinRc0Activated, pwdaoWithPinRc1Activated, pwdaoWithPinRc2Activated, pwdaoWithPinRc3Activated, pwdaoWithPinRc3Deactivated;
DomainParameterSet domainParameterSet0;
Collection<CardObject> domainParameterSet0Collection;
DomainParameterSetCardObject domainParameters0;
OidIdentifier oidIdentifier0;
/**
* Create the test environment.
* @throws LifeCycleChangeException
* @throws ReflectiveOperationException
*/
@Before
public void setUp() throws Exception {
AuthObjectIdentifier aoiCan = new AuthObjectIdentifier(new byte[]{(byte) 0x02});
AuthObjectIdentifier aoiPin = new AuthObjectIdentifier(new byte[]{(byte) 0x03});
pwdaoWithCan = new PasswordAuthObject(aoiCan, new byte[]{(byte) 0xFF});
TaSecurityCondition pinManagementCondition = new TaSecurityCondition(TerminalType.AT,
new RelativeAuthorization(CertificateRole.TERMINAL, new BitField(38).flipBit(5)));
pwdaoWithPinRc0Activated = new PasswordAuthObjectWithRetryCounter(aoiPin, new byte[] { (byte) 0xFF }, "PIN", 0,
16, 3, pinManagementCondition, new OrSecCondition(new PaceWithPasswordSecurityCondition("PIN"), new PaceWithPasswordSecurityCondition("PUK")),
new PaceWithPasswordSecurityCondition("PUK"),
new PaceWithPasswordRunningSecurityCondition("PIN"));
pwdaoWithPinRc0Activated.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED);
pwdaoWithPinRc0Activated.decrementRetryCounter();
pwdaoWithPinRc0Activated.decrementRetryCounter();
pwdaoWithPinRc0Activated.decrementRetryCounter();
pwdaoWithPinRc1Activated = new PasswordAuthObjectWithRetryCounter(aoiPin, new byte[] { (byte) 0xFF }, "PIN", 0,
16, 3, pinManagementCondition, new OrSecCondition(new PaceWithPasswordSecurityCondition("PIN"), new PaceWithPasswordSecurityCondition("PUK")),
new PaceWithPasswordSecurityCondition("PUK"),
new PaceWithPasswordRunningSecurityCondition("PIN"));
pwdaoWithPinRc1Activated.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED);
pwdaoWithPinRc1Activated.decrementRetryCounter();
pwdaoWithPinRc1Activated.decrementRetryCounter();
pwdaoWithPinRc2Activated = new PasswordAuthObjectWithRetryCounter(aoiPin, new byte[] { (byte) 0xFF }, "PIN", 0,
16, 3, pinManagementCondition, new OrSecCondition(new PaceWithPasswordSecurityCondition("PIN"), new PaceWithPasswordSecurityCondition("PUK")),
new PaceWithPasswordSecurityCondition("PUK"),
new PaceWithPasswordRunningSecurityCondition("PIN"));
pwdaoWithPinRc2Activated.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED);
pwdaoWithPinRc2Activated.decrementRetryCounter();
pwdaoWithPinRc3Activated = new PasswordAuthObjectWithRetryCounter(aoiPin, new byte[] { (byte) 0xFF }, "PIN", 0,
16, 3, pinManagementCondition, new OrSecCondition(new PaceWithPasswordSecurityCondition("PIN"), new PaceWithPasswordSecurityCondition("PUK")),
new PaceWithPasswordSecurityCondition("PUK"),
new PaceWithPasswordRunningSecurityCondition("PIN"));
pwdaoWithPinRc3Activated.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED);
pwdaoWithPinRc3Deactivated = new PasswordAuthObjectWithRetryCounter(aoiPin, new byte[] { (byte) 0xFF }, "PIN",
0, 16, 3, pinManagementCondition, new OrSecCondition(new PaceWithPasswordSecurityCondition("PIN"), new PaceWithPasswordSecurityCondition("PUK")),
new PaceWithPasswordSecurityCondition("PUK"),
new PaceWithPasswordRunningSecurityCondition("PIN"));
pwdaoWithPinRc3Deactivated.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED);
pwdaoWithPinRc3Deactivated.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_DEACTIVATED);
PaceMechanism paceMechanismWithCan = new PaceMechanism(pwdaoWithCan, null, null);
PaceMechanism paceMechanismWithPin = new PaceMechanism(pwdaoWithPinRc3Activated, null, null);
csmWithCan = new HashSet<SecMechanism>();
csmWithCan.add(paceMechanismWithCan);
csmWithPin = new HashSet<SecMechanism>();
csmWithPin.add(paceMechanismWithPin);
csmEmpty = new HashSet<SecMechanism>();
// create and init the object under test
paceProtocol = new PaceBypassProtocol();
paceProtocol.setCardStateAccessor(mockedCardStateAccessor);
domainParameters0 = new DomainParameterSetCardObject(domainParameterSet0, new DomainParameterSetIdentifier(0));
domainParameters0.addOidIdentifier(oidIdentifier0);
domainParameterSet0Collection = new ArrayList<CardObject>();
domainParameterSet0Collection.add(domainParameters0);
}
//ok
/**
* Positive test case: perform PACE with PIN. PIN retry counter is 3 (default), PIN activated.
*/
@Test
public void testSetAtPinRc3Act_NoPrevPwd(){
// prepare the mock
new NonStrictExpectations() {
{
mockedCardStateAccessor.getCurrentMechanisms(
withInstanceOf(SecContext.class),
null);
result = csmEmpty;
mockedCardStateAccessor.getMasterFile();
result = mockedMf;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class),
withInstanceOf(OidIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(withInstanceOf(AuthObjectIdentifier.class));
result = pwdaoWithPinRc3Activated;
}
};
// select Apdu
ProcessingData processingData = new ProcessingData();
byte[] apduBytes = HexString.toByteArray("FF 86 00 00 06 83 01 03 92 01 FF");
processingData.updateCommandApdu(this, "pseudo pace APDU",
CommandApduFactory.createCommandApdu(apduBytes));
// call mut
paceProtocol.process(processingData);
// check results
short sw = processingData.getResponseApdu().getStatusWord();
assertEquals("Statusword is not correct", HexString.hexifyShort(Iso7816.SW_9000_NO_ERROR), HexString.hexifyShort(sw));
}
//ok
/**
* Positive test case: perform PACE with PIN. PIN retry counter is 3 (default), PIN deactivated.
*/
@Test
public void testSetAtPinRc3Deact_NoPrevPwd(){
// prepare the mock
new NonStrictExpectations() {
{
mockedCardStateAccessor.getCurrentMechanisms(
withInstanceOf(SecContext.class),
null);
result = csmEmpty;
mockedCardStateAccessor.getMasterFile();
result = mockedMf;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class),
withInstanceOf(OidIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(withInstanceOf(AuthObjectIdentifier.class));
result = pwdaoWithPinRc3Deactivated;
}
};
// select Apdu
ProcessingData processingData = new ProcessingData();
byte[] apduBytes = HexString.toByteArray("FF 86 00 00 06 83 01 03 92 01 FF");
processingData.updateCommandApdu(this, "pseudo pace APDU",
CommandApduFactory.createCommandApdu(apduBytes));
// call mut
paceProtocol.process(processingData);
// check results
short sw = processingData.getResponseApdu().getStatusWord();
TlvValue data = processingData.getResponseApdu().getData();
assertTrue("Encoding is valid", data.isValidBerEncoding());
TlvDataObjectContainer container = new TlvDataObjectContainer(data);
assertEquals("setAt status word is not correct", HexString.hexifyShort(0x6283), HexString.hexifyShort(Utils.getShortFromUnsignedByteArray(container.getTlvDataObject(TlvConstants.TAG_80).getValueField())));
assertEquals("Statusword is not correct", HexString.hexifyShort(0x6984), HexString.hexifyShort(sw));
}
//ok
/**
* Positive test case: perform PACE with PIN. PIN retry counter is 2, PIN activated.
*/
@Test
public void testSetAtPinRc2Act_NoPrevPwd(){
// prepare the mock
new NonStrictExpectations() {
{
mockedCardStateAccessor.getCurrentMechanisms(
withInstanceOf(SecContext.class),
null);
// previously used password
result = csmEmpty;
mockedCardStateAccessor.getMasterFile();
result = mockedMf;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class),
withInstanceOf(OidIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(withInstanceOf(AuthObjectIdentifier.class));
result = pwdaoWithPinRc2Activated;
}
};
// select Apdu
ProcessingData processingData = new ProcessingData();
byte[] apduBytes = HexString.toByteArray("FF 86 00 00 06 83 01 03 92 01 FF");
processingData.updateCommandApdu(this, "pseudo pace APDU",
CommandApduFactory.createCommandApdu(apduBytes));
// call mut
paceProtocol.process(processingData);
// check results
short sw = processingData.getResponseApdu().getStatusWord();
TlvValue data = processingData.getResponseApdu().getData();
assertTrue("Encoding is valid", data.isValidBerEncoding());
TlvDataObjectContainer container = new TlvDataObjectContainer(data);
assertEquals("setAt status word is not correct", HexString.hexifyShort(0x63C2), HexString.hexifyShort(Utils.getShortFromUnsignedByteArray(container.getTlvDataObject(TlvConstants.TAG_80).getValueField())));
assertEquals("Statusword is not correct", HexString.hexifyShort(0x9000), HexString.hexifyShort(sw));
}
//ok
/**
* Positive test case: perform PACE with PIN. PIN retry counter is 1, PIN activated.
*/
@Test
public void testSetAtPinRc1Act_NoPrevPwd(){
// prepare the mock
new NonStrictExpectations() {
{
mockedCardStateAccessor.getCurrentMechanisms(
withInstanceOf(SecContext.class),
null);
result = csmWithCan;
mockedCardStateAccessor.getMasterFile();
result = mockedMf;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class),
withInstanceOf(OidIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(withInstanceOf(AuthObjectIdentifier.class));
result = pwdaoWithPinRc1Activated;
}
};
// select Apdu
ProcessingData processingData = new ProcessingData();
byte[] apduBytes = HexString.toByteArray("FF 86 00 00 06 83 01 03 92 01 FF");
processingData.updateCommandApdu(this, "pseudo pace APDU",
CommandApduFactory.createCommandApdu(apduBytes));
// call mut
paceProtocol.process(processingData);
// check results
short sw = processingData.getResponseApdu().getStatusWord();
TlvValue data = processingData.getResponseApdu().getData();
assertTrue("Encoding is valid", data.isValidBerEncoding());
TlvDataObjectContainer container = new TlvDataObjectContainer(data);
assertEquals("setAt status word is not correct", HexString.hexifyShort(0x63C1), HexString.hexifyShort(Utils.getShortFromUnsignedByteArray(container.getTlvDataObject(TlvConstants.TAG_80).getValueField())));
assertEquals("Statusword is not correct", HexString.hexifyShort(0x9000), HexString.hexifyShort(sw));
}
/**
* Positive test case: perform PACE with PIN. PIN retry counter is 1, PIN activated, previous CAN.
*/
@Test
public void testSetAtPinRc1Act_PrevCan(){
// prepare the mock
new NonStrictExpectations() {
{
mockedCardStateAccessor.getCurrentMechanisms(
withInstanceOf(SecContext.class),
null);
// previously used password
result = csmWithCan;
mockedCardStateAccessor.getMasterFile();
result = mockedMf;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class),
withInstanceOf(OidIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(withInstanceOf(AuthObjectIdentifier.class));
result = pwdaoWithPinRc1Activated;
}
};
// select Apdu
ProcessingData processingData = new ProcessingData();
byte[] apduBytes = HexString.toByteArray("FF 86 00 00 06 83 01 03 92 01 FF");
processingData.updateCommandApdu(this, "pseudo pace APDU",
CommandApduFactory.createCommandApdu(apduBytes));
// call mut
paceProtocol.process(processingData);
// check results
short sw = processingData.getResponseApdu().getStatusWord();
TlvValue data = processingData.getResponseApdu().getData();
assertTrue("Encoding is valid", data.isValidBerEncoding());
TlvDataObjectContainer container = new TlvDataObjectContainer(data);
assertEquals("setAt status word is not correct", HexString.hexifyShort(0x63C1), HexString.hexifyShort(Utils.getShortFromUnsignedByteArray(container.getTlvDataObject(TlvConstants.TAG_80).getValueField())));
assertEquals("Statusword is not correct", HexString.hexifyShort(0x9000), HexString.hexifyShort(sw));
}
/**
* Negative testcase: Perform PACE with Pin. Retry counter is 0, PIN activated
*/
@Test
public void testSetAtPinRc0Act_NoPrevPwd(){
// prepare the mock
new NonStrictExpectations() {
{
mockedCardStateAccessor.getCurrentMechanisms(
withInstanceOf(SecContext.class),
null);
// previously used password
result = csmEmpty;
mockedCardStateAccessor.getMasterFile();
result = mockedMf;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class),
withInstanceOf(OidIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(
withInstanceOf(DomainParameterSetIdentifier.class));
result = domainParameters0;
mockedMf.findChildren(withInstanceOf(AuthObjectIdentifier.class));
result = pwdaoWithPinRc0Activated;
}
};
// select Apdu
ProcessingData processingData = new ProcessingData();
byte[] apduBytes = HexString.toByteArray("FF 86 00 00 06 83 01 03 92 01 FF");
processingData.updateCommandApdu(this, "pseudo pace APDU",
CommandApduFactory.createCommandApdu(apduBytes));
// call mut
paceProtocol.process(processingData);
// check results
short sw = processingData.getResponseApdu().getStatusWord();
TlvValue data = processingData.getResponseApdu().getData();
assertTrue("Encoding is valid", data.isValidBerEncoding());
TlvDataObjectContainer container = new TlvDataObjectContainer(data);
assertEquals("setAt status word is not correct", HexString.hexifyShort(0x63C0), HexString.hexifyShort(Utils.getShortFromUnsignedByteArray(container.getTlvDataObject(TlvConstants.TAG_80).getValueField())));
assertEquals("Statusword is not correct", HexString.hexifyShort(0x6983), HexString.hexifyShort(sw));
}
}