package de.persosim.simulator.protocols.pin;
import static org.globaltester.logging.BasicLogger.DEBUG;
import static org.globaltester.logging.BasicLogger.log;
import java.util.Collection;
import java.util.Collections;
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.cardobjects.AuthObjectIdentifier;
import de.persosim.simulator.cardobjects.CardObject;
import de.persosim.simulator.cardobjects.CardObjectUtils;
import de.persosim.simulator.cardobjects.ChangeablePasswordAuthObject;
import de.persosim.simulator.cardobjects.Iso7816LifeCycleState;
import de.persosim.simulator.cardobjects.MasterFile;
import de.persosim.simulator.cardobjects.PasswordAuthObjectWithRetryCounter;
import de.persosim.simulator.exception.AccessDeniedException;
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.Protocol;
import de.persosim.simulator.protocols.SecInfoPublicity;
import de.persosim.simulator.protocols.Tr03110;
import de.persosim.simulator.tlv.TlvConstants;
import de.persosim.simulator.tlv.TlvDataObject;
import de.persosim.simulator.tlv.TlvValue;
import de.persosim.simulator.utils.HexString;
import de.persosim.simulator.utils.Utils;
public class PinProtocol implements Protocol, Iso7816, Tr03110, TlvConstants, ApduSpecificationConstants, InfoSource{
protected ApduSpecification apduSpecification;
protected CardStateAccessor cardState;
protected ProcessingData processingData;
public PinProtocol() {
}
@Override
public String getProtocolName() {
return "PIN";
}
@Override
public void setCardStateAccessor(CardStateAccessor cardState) {
this.cardState = cardState;
}
@Override
public Collection<? extends TlvDataObject> getSecInfos(
SecInfoPublicity publicity, MasterFile mf) {
return Collections.emptySet();
}
@Override
public void process(ProcessingData processingData) {
this.processingData = processingData;
if (processingData != null) {
byte cla = processingData.getCommandApdu().getCla();
byte ins = processingData.getCommandApdu().getIns();
byte p1 = processingData.getCommandApdu().getP1();
byte p2 = processingData.getCommandApdu().getP2();
if(cla == (byte) 0x00) {
switch(ins){
case 0x20:
processCommandVerifyPassword();
break;
case 0x2C:
/* Values for p2 must be less than 0x1F */
if (p1 == 0x02 && p2 < 0x1F) {
processCommandChangePassword();
break;
}
/* Because unblocking the CAN is not necessary regarding ISO 7816 p2 must be 0x03 */
else if (p1 == 0x03 && p2 == 0x03) {
processCommandUnblockPassword();
break;
}
else {
log(this, "APDU can not be processed, this protocol is not applicable.", DEBUG);
break;
}
case 0x44:
if (p1 == 0x10) {
processCommandActivatePassword();
break;
} else {
log(this, "APDU can not be processed, this protocol is not applicable.", DEBUG);
break;
}
case 0x04:
if (p1 == 0x10) {
processCommandDeactivatePassword();
break;
} else {
log(this, "APDU can not be processed, this protocol is not applicable.", DEBUG);
break;
}
default:
log(this, "APDU can not be processed, this protocol is not applicable.", DEBUG);
break;
}
}
}
else {
log(this, "APDU can not be processed, this protocol is not applicable.", DEBUG);
}
}
@Override
public void reset() {
}
@Override
public boolean isMoveToStackRequested() {
return false;
}
@Override
public String getIDString() {
return "Personal Identification Number";
}
private void processCommandActivatePassword() {
CommandApdu cApdu = processingData.getCommandApdu();
int identifier = Utils.maskUnsignedByteToInt(cApdu.getP2());
CardObject object = CardObjectUtils.findObject(cardState.getMasterFile(), new AuthObjectIdentifier(identifier));
if(!(object instanceof PasswordAuthObjectWithRetryCounter)) {
ResponseApdu resp = new ResponseApdu(SW_6984_REFERENCE_DATA_NOT_USABLE);
this.processingData.updateResponseAPDU(this, "PIN object not found", resp);
/* there is nothing more to be done here */
return;
}
PasswordAuthObjectWithRetryCounter passwordObject = ((PasswordAuthObjectWithRetryCounter) object);
if(!passwordObject.getLifeCycleState().equals(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED)) {
try {
passwordObject.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_ACTIVATED);
} catch (LifeCycleChangeException e) {
ResponseApdu resp = new ResponseApdu(SW_6985_CONDITIONS_OF_USE_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this, "PIN object transition from state " + e.getOldState() + " to " + e.getNewState() + " not possible", resp);
/* there is nothing more to be done here */
return;
} catch (AccessDeniedException e) {
ResponseApdu resp = new ResponseApdu(SW_6982_SECURITY_STATUS_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this, "Access conditions to activate password not met", resp);
/* there is nothing more to be done here */
return;
}
}
ResponseApdu resp = new ResponseApdu(SW_9000_NO_ERROR);
String passwordName = ((PasswordAuthObjectWithRetryCounter) object).getPasswordName();
this.processingData.updateResponseAPDU(this, passwordName + " successfully activated", resp);
log(this, "processed COMMAND_ACTIVATE_PASSWORD", DEBUG);
}
private void processCommandChangePassword() {
CommandApdu cApdu = processingData.getCommandApdu();
TlvValue tlvData = cApdu.getCommandData();
int identifier = Utils.maskUnsignedByteToInt(cApdu.getP2());
CardObject object = CardObjectUtils.findObject(cardState.getMasterFile(), new AuthObjectIdentifier(identifier));
if(!(object instanceof ChangeablePasswordAuthObject)) {
ResponseApdu resp = new ResponseApdu(SW_6982_SECURITY_STATUS_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this, "Password is not changeable", resp);
/* there is nothing more to be done here */
return;
}
ChangeablePasswordAuthObject passwordObject = (ChangeablePasswordAuthObject) object;
String passwordName = passwordObject.getPasswordName();
if(tlvData.isEmpty()) {
ResponseApdu resp = new ResponseApdu(SW_6A80_WRONG_DATA);
this.processingData.updateResponseAPDU(this, "no new " + passwordName + " data received", resp);
/* there is nothing more to be done here */
return;
}
byte[] newPasswordPlain = tlvData.toByteArray();
log(this, "received data of " + newPasswordPlain.length + " bytes length for new " + passwordName + " is: " + HexString.dump(newPasswordPlain), DEBUG);
log(this, "old " + passwordName + " is: " + HexString.dump(passwordObject.getPassword()), DEBUG);
try {
passwordObject.setPassword(newPasswordPlain);
} catch (IllegalArgumentException e) {
ResponseApdu resp = new ResponseApdu(SW_6984_REFERENCE_DATA_NOT_USABLE);
this.processingData.updateResponseAPDU(this, e.getMessage(), resp);
/* there is nothing more to be done here */
return;
} catch (IllegalStateException e) {
ResponseApdu resp = new ResponseApdu(SW_6283_SELECTED_FILE_DEACTIVATED);
this.processingData.updateResponseAPDU(this, e.getMessage(), resp);
/* there is nothing more to be done here */
return;
} catch (AccessDeniedException e) {
ResponseApdu resp = new ResponseApdu(SW_6982_SECURITY_STATUS_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this,
"Access conditions to change " + passwordObject.getPasswordName() + " not met",
resp);
/* there is nothing more to be done here */
return;
}
log(this, "new " + passwordName + " is: " + HexString.dump(newPasswordPlain), DEBUG);
ResponseApdu resp = new ResponseApdu(SW_9000_NO_ERROR);
this.processingData.updateResponseAPDU(this, passwordName + " successfully changed", resp);
log(this, "processed COMMAND_CHANGE_PASSWORD", DEBUG);
}
private void processCommandDeactivatePassword() {
CommandApdu cApdu = processingData.getCommandApdu();
int identifier = Utils.maskUnsignedByteToInt(cApdu.getP2());
CardObject object = CardObjectUtils.findObject(cardState.getMasterFile(), new AuthObjectIdentifier(identifier));
if(!(object instanceof PasswordAuthObjectWithRetryCounter)) {
ResponseApdu resp = new ResponseApdu(SW_6984_REFERENCE_DATA_NOT_USABLE);
this.processingData.updateResponseAPDU(this, "PIN object not found", resp);
/* there is nothing more to be done here */
return;
}
PasswordAuthObjectWithRetryCounter passwordObject = ((PasswordAuthObjectWithRetryCounter) object);
if(!passwordObject.getLifeCycleState().equals(Iso7816LifeCycleState.OPERATIONAL_DEACTIVATED)) {
try {
passwordObject.updateLifeCycleState(Iso7816LifeCycleState.OPERATIONAL_DEACTIVATED);
} catch (LifeCycleChangeException e) {
ResponseApdu resp = new ResponseApdu(SW_6985_CONDITIONS_OF_USE_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this, "PIN object transition from state " + e.getOldState() + " to " + e.getNewState() + " not possible", resp);
/* there is nothing more to be done here */
return;
} catch (AccessDeniedException e) {
ResponseApdu resp = new ResponseApdu(SW_6982_SECURITY_STATUS_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this, "Access conditions to deactivate password not met", resp);
/* there is nothing more to be done here */
return;
}
}
ResponseApdu resp = new ResponseApdu(SW_9000_NO_ERROR);
String passwordName = ((PasswordAuthObjectWithRetryCounter) object).getPasswordName();
this.processingData.updateResponseAPDU(this, passwordName + " successfully deactivated", resp);
log(this, "processed COMMAND_DEACTIVATE_PASSWORD" , DEBUG);
}
private void processCommandUnblockPassword() {
CommandApdu cApdu = processingData.getCommandApdu();
int identifier = Utils.maskUnsignedByteToInt(cApdu.getP2());
CardObject object = CardObjectUtils.findObject(cardState.getMasterFile(), new AuthObjectIdentifier(identifier));
if(!(object instanceof PasswordAuthObjectWithRetryCounter)) {
ResponseApdu resp = new ResponseApdu(SW_6984_REFERENCE_DATA_NOT_USABLE);
this.processingData.updateResponseAPDU(this, "PIN object not found", resp);
/* there is nothing more to be done here */
return;
}
PasswordAuthObjectWithRetryCounter pinObject = (PasswordAuthObjectWithRetryCounter) object;
String passwordName = pinObject.getPasswordName();
log(this, "old " + passwordName +" retry counter is: " + pinObject.getRetryCounterCurrentValue(), DEBUG);
try {
pinObject.resetRetryCounterToDefault();
} catch (IllegalArgumentException e) {
ResponseApdu resp = new ResponseApdu(SW_6984_REFERENCE_DATA_NOT_USABLE);
this.processingData.updateResponseAPDU(this, e.getMessage(), resp);
/* there is nothing more to be done here */
return;
} catch (IllegalStateException e) {
ResponseApdu resp = new ResponseApdu(SW_6283_SELECTED_FILE_DEACTIVATED);
this.processingData.updateResponseAPDU(this, e.getMessage(), resp);
/* there is nothing more to be done here */
return;
} catch (AccessDeniedException e) {
ResponseApdu resp = new ResponseApdu(SW_6982_SECURITY_STATUS_NOT_SATISFIED);
this.processingData.updateResponseAPDU(this,
"Access conditions to unblock " + pinObject.getPasswordName() + " not met",
resp);
/* there is nothing more to be done here */
return;
}
log(this, "new " + passwordName + " retry counter is: " + pinObject.getRetryCounterCurrentValue(), DEBUG);
ResponseApdu resp = new ResponseApdu(SW_9000_NO_ERROR);
this.processingData.updateResponseAPDU(this, passwordName +" successfully unblocked", resp);
log(this, "processed COMMAND_UNBLOCK_PASSWORD", DEBUG);
}
private void processCommandVerifyPassword() {
CommandApdu cApdu = processingData.getCommandApdu();
int identifier = Utils.maskUnsignedByteToInt(cApdu.getP2());
CardObject object = CardObjectUtils.findObject(cardState.getMasterFile(), new AuthObjectIdentifier(identifier));
if(!(object instanceof PasswordAuthObjectWithRetryCounter)) {
ResponseApdu resp = new ResponseApdu(SW_6984_REFERENCE_DATA_NOT_USABLE);
this.processingData.updateResponseAPDU(this, "PIN object not found", resp);
/* there is nothing more to be done here */
return;
}
PasswordAuthObjectWithRetryCounter pinObject = (PasswordAuthObjectWithRetryCounter) object;
String passwordName = pinObject.getPasswordName();
ResponseApdu resp = new ResponseApdu((short) (SW_63C0_COUNTER_IS_0 + (pinObject.getRetryCounterCurrentValue())));
this.processingData.updateResponseAPDU(this, passwordName + " retry counter is: " + pinObject.getRetryCounterCurrentValue(), resp);
log(this, "processed COMMAND_VERIFY_PASSWORD", DEBUG);
}
}