package de.persosim.simulator.protocols;
import static org.globaltester.logging.BasicLogger.logException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.HashSet;
import org.globaltester.cryptoprovider.Crypto;
import org.globaltester.logging.InfoSource;
import de.persosim.simulator.cardobjects.CardObject;
import de.persosim.simulator.cardobjects.CardObjectIdentifier;
import de.persosim.simulator.cardobjects.DedicatedFile;
import de.persosim.simulator.cardobjects.DedicatedFileIdentifier;
import de.persosim.simulator.cardobjects.ElementaryFile;
import de.persosim.simulator.cardobjects.FileIdentifier;
import de.persosim.simulator.cardobjects.MasterFile;
import de.persosim.simulator.cardobjects.ShortFileIdentifier;
import de.persosim.simulator.exception.AccessDeniedException;
import de.persosim.simulator.exception.ProcessingException;
import de.persosim.simulator.perso.DefaultPersoGt;
import de.persosim.simulator.platform.CardStateAccessor;
import de.persosim.simulator.platform.Iso7816;
import de.persosim.simulator.processing.ProcessingData;
import de.persosim.simulator.tlv.ConstructedTlvDataObject;
import de.persosim.simulator.tlv.PrimitiveTlvDataObject;
import de.persosim.simulator.tlv.TlvConstants;
import de.persosim.simulator.tlv.TlvDataObject;
import de.persosim.simulator.utils.HexString;
import de.persosim.simulator.utils.Utils;
public class NpaProtocol implements Protocol, Iso7816, InfoSource, TlvConstants {
@Override
public String getProtocolName() {
return "nPA";
}
@Override
public void setCardStateAccessor(CardStateAccessor cardState) {
//card state not needed by this protocol
}
@Override
public Collection<TlvDataObject> getSecInfos(SecInfoPublicity publicity, MasterFile mf) {
HashSet<TlvDataObject> secInfos = new HashSet<TlvDataObject>();
//add CardInfoLocator
ConstructedTlvDataObject cardInfoLocator = new ConstructedTlvDataObject(TAG_SEQUENCE);
cardInfoLocator.addTlvDataObject(new PrimitiveTlvDataObject(TAG_OID, Tr03110.id_CI));
cardInfoLocator.addTlvDataObject(new PrimitiveTlvDataObject(TAG_IA5_STRING,
HexString.toByteArray("68 74 74 70 3A 2F 2F 62 73 69 2E 62 75 6E 64 2E 64 65 2F 63 69 66 2F 6E 70 61 2E 78 6D 6C")));
secInfos.add(cardInfoLocator);
//add eidSecurityInfos
if (publicity == SecInfoPublicity.PRIVILEGED) {
secInfos.add(getEidSecurityInfo(mf));
}
return secInfos;
}
private TlvDataObject getEidSecurityInfo(MasterFile mf) {
ConstructedTlvDataObject eidSecInfo = new ConstructedTlvDataObject(TAG_SEQUENCE);
eidSecInfo.addTlvDataObject(new PrimitiveTlvDataObject(TAG_OID, Tr03110.id_eIDSecurity));
//add eIDSecurityObject
ConstructedTlvDataObject eidSecurityObject = new ConstructedTlvDataObject(TAG_SEQUENCE);
eidSecInfo.addTlvDataObject(eidSecurityObject);
//eIDVersionInfo is optional, so skip it for simplicity
//add hashAlgorithm to eIDSecurityObject
eidSecurityObject.addTlvDataObject(new ConstructedTlvDataObject(TAG_SEQUENCE,
new PrimitiveTlvDataObject(TAG_OID, HexString
.toByteArray("60 86 48 01 65 03 04 02 04"))));
String hashAlg = "SHA224";
//add the sequence of hash values
ConstructedTlvDataObject dgHashes = new ConstructedTlvDataObject(TAG_SEQUENCE);
eidSecurityObject.addTlvDataObject(dgHashes);
//add the concrete hashes
DedicatedFileIdentifier eidAppIdentifier = new DedicatedFileIdentifier(
HexString.toByteArray(DefaultPersoGt.AID_EID));
Collection<CardObject> apps = mf.findChildren(eidAppIdentifier);
DedicatedFile eidApp = (DedicatedFile) apps.iterator().next();
MessageDigest md;
try {
md = MessageDigest.getInstance(hashAlg, Crypto.getCryptoProvider());
createDgHashes(md, dgHashes, eidApp, null);
} catch (NoSuchAlgorithmException e) {
logException(getClass(), e);
}
return eidSecInfo;
}
public static void createDgHashes(MessageDigest md, ConstructedTlvDataObject dgHashes, DedicatedFile dedicatedFile, Collection<Integer> dgs) {
try {
Collection<CardObject> eidObjects = dedicatedFile.getChildren();
for (CardObject curObject : eidObjects) {
if (!(curObject instanceof ElementaryFile)) continue;
ElementaryFile curFile = (ElementaryFile) curObject;
//get DG number
Integer dgNumber = null;
for (CardObjectIdentifier curIdentifier : curFile.getAllIdentifiers()) {
if (curIdentifier instanceof FileIdentifier) {
int fidInteger = ((FileIdentifier) curIdentifier).getFileIdentifier();
String fidHex = HexString.encode(Utils.removeLeadingZeroBytes(Utils.toUnsignedByteArray(fidInteger)));
dgNumber = Integer.parseInt(fidHex.substring(fidHex.toString().length()-2), 16);
break;
} else if (curIdentifier instanceof ShortFileIdentifier) {
dgNumber = ((ShortFileIdentifier) curIdentifier).getShortFileIdentifier();
break;
}
}
if (dgNumber == null || (dgs != null && !dgs.contains(dgNumber))) continue;
//read fileContent (bypassing access control enforcement)
byte[] fileContent = null;
try {
fileContent = curFile.getContent();
} catch (AccessDeniedException e1) {
throw new ProcessingException(SW_6982_SECURITY_STATUS_NOT_SATISFIED, "access denied to "+curFile);
}
if (fileContent == null) continue;
//calculate hash
md.reset();
byte[] digest = md.digest(fileContent);
ConstructedTlvDataObject currentDgHash = new ConstructedTlvDataObject(TAG_SEQUENCE);
currentDgHash.addTlvDataObject(new PrimitiveTlvDataObject(TAG_INTEGER, Utils.removeLeadingZeroBytes(Utils.toUnsignedByteArray(dgNumber))));
currentDgHash.addTlvDataObject(new PrimitiveTlvDataObject(TAG_OCTET_STRING, digest));
dgHashes.addTlvDataObject(currentDgHash);
}
} catch (NullPointerException e) {
//abort adding DG hashes if not possible
logException(NpaProtocol.class, e);
}
}
@Override
public void process(ProcessingData processingData) {
//nothing to process
}
@Override
public void reset() {
//nothing to reset
}
@Override
public String getIDString() {
return "nPA protocol";
}
@Override
public boolean isMoveToStackRequested() {
return false;
}
}