package eu.europa.esig.dss.validation.process.bbb.sav.checks;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.esig.dss.jaxb.detailedreport.XmlConstraintsConclusion;
import eu.europa.esig.dss.utils.Utils;
import eu.europa.esig.dss.validation.policy.rules.Indication;
import eu.europa.esig.dss.validation.policy.rules.SubIndication;
import eu.europa.esig.dss.validation.process.AdditionalInfo;
import eu.europa.esig.dss.validation.process.ChainItem;
import eu.europa.esig.dss.validation.process.MessageTag;
import eu.europa.esig.dss.validation.reports.wrapper.TokenProxy;
import eu.europa.esig.jaxb.policy.Algo;
import eu.europa.esig.jaxb.policy.AlgoExpirationDate;
import eu.europa.esig.jaxb.policy.CryptographicConstraint;
import eu.europa.esig.jaxb.policy.ListAlgo;
public class CryptographicCheck<T extends XmlConstraintsConclusion> extends ChainItem<T> {
private static final Logger logger = LoggerFactory.getLogger(CryptographicCheck.class);
private static final String DATE_FORMAT = "yyyy-MM-dd";
private final Date validationDate;
private final TokenProxy token;
private final CryptographicConstraint constraint;
private MessageTag errorMessage = MessageTag.EMPTY;
public CryptographicCheck(T result, TokenProxy token, Date currentTime, CryptographicConstraint constraint) {
super(result, constraint);
this.validationDate = currentTime;
this.token = token;
this.constraint = constraint;
}
@Override
protected boolean process() {
// Check encryption algorithm
ListAlgo acceptableEncryptionAlgo = constraint.getAcceptableEncryptionAlgo();
if ((acceptableEncryptionAlgo != null) && Utils.isCollectionNotEmpty(acceptableEncryptionAlgo.getAlgo())) {
if (!isIn(token.getEncryptionAlgoUsedToSignThisToken(), acceptableEncryptionAlgo.getAlgo())) {
errorMessage = MessageTag.ASCCM_ANS_1;
return false;
}
}
// Check digest algorithm
ListAlgo acceptableDigestAlgo = constraint.getAcceptableDigestAlgo();
if ((acceptableDigestAlgo != null) && Utils.isCollectionNotEmpty(acceptableDigestAlgo.getAlgo())) {
if (!isIn(token.getDigestAlgoUsedToSignThisToken(), acceptableDigestAlgo.getAlgo())) {
errorMessage = MessageTag.ASCCM_ANS_2;
return false;
}
}
// Check public key size
ListAlgo miniPublicKeySize = constraint.getMiniPublicKeySize();
if ((miniPublicKeySize != null) && Utils.isCollectionNotEmpty(miniPublicKeySize.getAlgo())) {
String keySize = token.getKeyLengthUsedToSignThisToken();
int tokenKeySize = 0;
if (Utils.isStringDigits(keySize)) {
tokenKeySize = Integer.parseInt(keySize);
}
int expectedMinimumKeySize = getExpectedKeySize(token.getEncryptionAlgoUsedToSignThisToken(), miniPublicKeySize.getAlgo());
if (tokenKeySize < expectedMinimumKeySize) {
errorMessage = MessageTag.ASCCM_ANS_3;
return false;
}
}
// Check algorithm expiration date
AlgoExpirationDate algoExpirationDate = constraint.getAlgoExpirationDate();
if ((algoExpirationDate != null) && Utils.isCollectionNotEmpty(algoExpirationDate.getAlgo())) {
// Digest algorithm
Date expirationDate = getExpirationDate(token.getDigestAlgoUsedToSignThisToken(), algoExpirationDate.getAlgo(), algoExpirationDate.getFormat());
if (expirationDate == null) {
errorMessage = MessageTag.ASCCM_ANS_4;
return false;
}
if (expirationDate.before(validationDate)) {
errorMessage = MessageTag.ASCCM_ANS_5;
return false;
}
// Encryption algorithm
String algoToFind = token.getEncryptionAlgoUsedToSignThisToken() + token.getKeyLengthUsedToSignThisToken();
expirationDate = getExpirationDate(algoToFind, algoExpirationDate.getAlgo(), algoExpirationDate.getFormat());
if (expirationDate == null) {
errorMessage = MessageTag.ASCCM_ANS_4;
return false;
}
if (expirationDate.before(validationDate)) {
errorMessage = MessageTag.ASCCM_ANS_5;
return false;
}
}
return true;
}
private Date getExpirationDate(String algoToFind, List<Algo> algos, String format) {
SimpleDateFormat dateFormat = new SimpleDateFormat(Utils.isStringEmpty(format) ? DATE_FORMAT : format);
Date result = null;
for (Algo algo : algos) {
if (Utils.areStringsEqual(algoToFind, algo.getValue()) && Utils.isStringNotEmpty(algo.getDate())) {
try {
result = dateFormat.parse(algo.getDate());
} catch (Exception e) {
logger.warn("Unable to parse date with pattern '" + dateFormat.toPattern() + "' : " + e.getMessage());
}
}
}
return result;
}
private int getExpectedKeySize(String encryptionAlgo, List<Algo> algos) {
int expectedSize = 0;
for (Algo algo : algos) {
if (Utils.areStringsEqual(algo.getValue(), encryptionAlgo)) {
String size = algo.getSize();
if (Utils.isStringDigits(size)) {
expectedSize = Integer.parseInt(size);
}
}
}
return expectedSize;
}
private boolean isIn(String algoToFind, List<Algo> algos) {
for (Algo algo : algos) {
if (Utils.areStringsEqual(algo.getValue(), algoToFind)) {
return true;
}
}
return false;
}
@Override
protected MessageTag getMessageTag() {
return MessageTag.ASCCM;
}
@Override
protected MessageTag getErrorMessageTag() {
return errorMessage;
}
@Override
protected Indication getFailedIndicationForConclusion() {
return Indication.INDETERMINATE;
}
@Override
protected SubIndication getFailedSubIndicationForConclusion() {
return SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE;
}
@Override
protected String getAdditionalInfo() {
SimpleDateFormat sdf = new SimpleDateFormat(AdditionalInfo.DATE_FORMAT);
Object[] params = new Object[] { sdf.format(validationDate) };
return MessageFormat.format(AdditionalInfo.VALIDATION_TIME, params);
}
}