package de.persosim.simulator.protocols;
import static org.globaltester.logging.BasicLogger.DEBUG;
import static org.globaltester.logging.BasicLogger.log;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import org.globaltester.logging.InfoSource;
import de.persosim.simulator.apdu.CommandApdu;
import de.persosim.simulator.apdu.ResponseApdu;
import de.persosim.simulator.apdumatching.ApduSpecification;
import de.persosim.simulator.apdumatching.ApduSpecificationConstants;
import de.persosim.simulator.apdumatching.TlvSpecification;
import de.persosim.simulator.cardobjects.MasterFile;
import de.persosim.simulator.platform.CardStateAccessor;
import de.persosim.simulator.platform.Iso7816;
import de.persosim.simulator.platform.Iso7816Lib;
import de.persosim.simulator.platform.PlatformUtil;
import de.persosim.simulator.processing.ProcessingData;
import de.persosim.simulator.statemachine.AbstractStateMachine;
import de.persosim.simulator.tlv.TlvDataObject;
import de.persosim.simulator.tlv.TlvPath;
import de.persosim.simulator.tlv.TlvTag;
/**
* Generic super class for {@link Protocol} implementations with state machine code.
*
* @author amay
*
*/
public abstract class AbstractProtocolStateMachine extends AbstractStateMachine implements ProtocolStateMachine, Iso7816, ApduSpecificationConstants, InfoSource {
protected String protocolName;
protected ProcessingData processingData;
protected boolean continueProcessing;
protected HashMap<String, ApduSpecification> apdus = new HashMap<>();
protected ApduSpecification apduSpecification;
protected TlvSpecification tagSpecification;
protected TlvPath path;
protected CardStateAccessor cardState;
public AbstractProtocolStateMachine(String protocolName) {
this.protocolName = protocolName;
}
@Override
public String getProtocolName() {
return protocolName;
}
// -------------------------------------------------------
// Methods/fields to implement {@link InfoSource} functionality
// -------------------------------------------------------
@Override
public String getIDString() {
return protocolName + " protocol";
}
// -------------------------------------------------------
// Methods to implement {@link Protocol} functionality
// -------------------------------------------------------
@Override
public void setCardStateAccessor(CardStateAccessor cardState){
this.cardState = cardState;
}
@Override
public Collection<TlvDataObject> getSecInfos(SecInfoPublicity publicity, MasterFile mf) {
return new HashSet<>();
}
@Override
public void process(ProcessingData processingData) {
this.processingData = processingData;
if (processingData.isReportingError()) {
/*
* Due to a processing error there is no guarantee that there is
* anything like an INS byte that could be set. Set dummy INS byte
* 0xFF instead;
*/
this.processEvent((byte) 0xFF);
} else {
this.processEvent(processingData.getCommandApdu().getIns());
}
}
// -------------------------------------------------------
// Methods to implement {@link ProtocolStateMachine} functionality
// -------------------------------------------------------
@Override
public void registerApduSpecification(ApduSpecification apduSpecification) {
this.apdus.put(apduSpecification.getId(), apduSpecification);
}
@Override
public void logs(String state) {
log(this, "State changed to " + state, DEBUG);
}
@Override
public void returnResult() {
this.continueProcessing = false;
}
@Override
public boolean apduHasBeenProcessed() {
return this.processingData.isProcessingFinished();
}
@Override
public boolean isStatusWord(short sw) {
ResponseApdu resp = this.processingData.getResponseApdu();
if (resp != null) {
return resp.getStatusWord() == sw;
} else {
return false;
}
}
@Override
public boolean isStatusWord_63CX_Counter() {
ResponseApdu resp = this.processingData.getResponseApdu();
if (resp != null) {
short swtmp = resp.getStatusWord();
swtmp &= (short) 0xFFF0;
return swtmp == (short) 0x63C0;
} else {
return false;
}
}
@Override
public boolean warningOrErrorOccurredDuringProcessing() {
if (processingData != null) {
ResponseApdu responseApdu = processingData.getResponseApdu();
if (responseApdu != null) {
short sw = responseApdu.getStatusWord();
return (Iso7816Lib.isReportingError(sw) || Iso7816Lib.isReportingWarning(sw) || PlatformUtil.is4xxxStatusWord(sw));
}
return false;
}
return false;
}
@Override
public boolean isAPDU(String apduId) {
CommandApdu apdu;
ApduSpecification apduSpec = apdus.get(apduId);
if(apduSpec == null) {
log(this, "APDU matching failed due to command \"" + apduId + "\" being unknown", DEBUG);
return false;
}
if(processingData == null) {
log(this, "APDU matching failed due to missing processing data", DEBUG);
return false;
}
apdu = processingData.getCommandApdu();
boolean match = apduSpec.matchesFullApdu(apdu);
if(match) {
log(this, "received APDU matches definition of command \"" + apduId + "\"", DEBUG);
}
return match;
}
@Override
public boolean isMoveToStackRequested() {
return false;
}
public void createNewApduSpecification(String id) {
this.apduSpecification = new ApduSpecification(id);
apduSpecification.getTags().setStrictOrder(ARBITRARY_ORDER);
}
public void createNewTagSpecification(TlvTag tag) {
this.tagSpecification = new TlvSpecification(tag);
}
public void createNewPath() {
this.path = new TlvPath();
}
}