/*
* DSS - Digital Signature Services
*
* Copyright (C) 2013 European Commission, Directorate-General Internal Market and Services (DG MARKT), B-1049 Bruxelles/Brussel
*
* Developed by: 2013 ARHS Developments S.A. (rue Nicolas Bové 2B, L-1253 Luxembourg) http://www.arhs-developments.com
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* "DSS - Digital Signature Services" is free software: you can redistribute it and/or modify it under the terms of
* the GNU Lesser General Public License as published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* DSS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* "DSS - Digital Signature Services". If not, see <http://www.gnu.org/licenses/>.
*/
package eu.europa.ec.markt.dss.validation102853.processes.ltv;
import java.util.Date;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import eu.europa.ec.markt.dss.DSSUtils;
import eu.europa.ec.markt.dss.validation102853.policy.ProcessParameters;
import eu.europa.ec.markt.dss.validation102853.processes.subprocesses.EtsiPOEExtraction;
import eu.europa.ec.markt.dss.validation102853.rules.AttributeName;
import eu.europa.ec.markt.dss.validation102853.rules.AttributeValue;
import eu.europa.ec.markt.dss.validation102853.rules.ExceptionMessage;
import eu.europa.ec.markt.dss.validation102853.rules.Indication;
import eu.europa.ec.markt.dss.validation102853.rules.MessageTag;
import eu.europa.ec.markt.dss.validation102853.rules.NodeName;
import eu.europa.ec.markt.dss.validation102853.rules.NodeValue;
import eu.europa.ec.markt.dss.validation102853.rules.SubIndication;
import eu.europa.ec.markt.dss.validation102853.xml.XmlDom;
import eu.europa.ec.markt.dss.validation102853.xml.XmlNode;
import static eu.europa.ec.markt.dss.validation102853.engine.rules.wrapper.XPathSignature.getSigningCertificateId;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.PSV_IPCVC;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.PSV_IPCVC_ANS;
import static eu.europa.ec.markt.dss.validation102853.rules.MessageTag.PSV_ITPOSVAOBCT;
/**
* 9.2.4 Past signature validation process<br>
* <p/>
* 9.2.4.1 Description<br>
* <p/>
* This process is used when validation of a signature (or a time-stamp token) fails at the current time with an
* INDETERMINATE status such that the provided proofs of existence may help to go to a determined status.
*
* @author bielecro
*/
public class PastSignatureValidation implements Indication, SubIndication, NodeName, NodeValue, AttributeName, AttributeValue, ExceptionMessage {
private static final Logger LOG = LoggerFactory.getLogger(PastSignatureValidation.class);
/**
* // TODO: (Bob: 2014 Mar 12)
*/
private String contextName;
private EtsiPOEExtraction poe;
// returned data
private XmlNode pastSignatureValidationData;
private void prepareParameters(ProcessParameters params) {
this.poe = (EtsiPOEExtraction) params.getPOE();
isInitialised(params);
}
private void isInitialised(ProcessParameters params) {
if (poe == null) {
poe = new EtsiPOEExtraction();
params.setPOE(poe);
}
}
/**
* This method carry out the Past Signature Validation process.
* <p/>
* 9.2.1.2 Input<br>
* <p/>
* - Signature or time-stamp token . Mandatory<br>
* - Target certificate ............ Mandatory<br>
* - X.509 Validation Parameters ... Mandatory<br>
* - A set of POEs ................. Mandatory<br>
* - Certificate meta-data ......... Optional<br>
* - Chain Constraints ............. Optional<br>
* - Cryptographic Constraints ..... Optional<br>
*
* @param params
* @param signature Can be the document or the timestamp signature
* @param currentTimeSignatureConclusion
* @param context
*/
public PastSignatureValidationConclusion run(final ProcessParameters params, final XmlDom signature, final XmlDom currentTimeSignatureConclusion, final String context) {
this.contextName = context;
prepareParameters(params);
LOG.debug(this.getClass().getSimpleName() + ": start.");
pastSignatureValidationData = new XmlNode(PAST_SIGNATURE_VALIDATION_DATA);
pastSignatureValidationData.setNameSpace(XmlDom.NAMESPACE);
final PastSignatureValidationConclusion conclusion = process(params, signature, currentTimeSignatureConclusion);
conclusion.setValidationData(pastSignatureValidationData);
return conclusion;
}
private PastSignatureValidationConclusion process(final ProcessParameters params, final XmlDom signature, final XmlDom currentTimeSignatureConclusion) {
final PastSignatureValidationConclusion conclusion = new PastSignatureValidationConclusion();
final String signatureId = signature.getValue("./@Id");
pastSignatureValidationData.setAttribute(ID, signatureId);
final String currentTimeIndication = currentTimeSignatureConclusion.getValue("./Indication/text()");
final String currentTimeSubIndication = currentTimeSignatureConclusion.getValue("./SubIndication/text()");
/**
* 9.2.4.4 Processing<br>
*
* 1) Perform the past certificate validation process with the following inputs:<br>
* - the signature,<br>
* - the target certificate,<br>
* - the X.509 validation parameters,<br>
* - certificate meta-data,<br>
* - chain constraints,<br>
* - cryptographic constraints and<br>
* - the set of POEs.
*/
// --> run the past certificate validation
final PastCertificateValidation pcv = new PastCertificateValidation();
final PastCertificateValidationConclusion pcvConclusion = pcv.run(params, signature, contextName);
pastSignatureValidationData.addChild(pcvConclusion.getValidationData());
final Date controlTime = pcvConclusion.getControlTime();
XmlNode constraintNode = addConstraint(PSV_IPCVC);
boolean ok = VALID.equals(pcvConclusion.getIndication());
constraintNode.addChild(STATUS, ok ? OK : KO);
final XmlNode returnedPcvIndication;
if (ok) {
returnedPcvIndication = constraintNode.addChild(INFO);
} else {
returnedPcvIndication = constraintNode.addChild(ERROR, PSV_IPCVC_ANS);
}
returnedPcvIndication.setAttribute(INDICATION, pcvConclusion.getIndication());
final String pcvSubIndication = pcvConclusion.getSubIndication();
if (pcvSubIndication != null) {
returnedPcvIndication.setAttribute(SUB_INDICATION, pcvSubIndication);
}
if (controlTime != null) {
final String formatedControlTime = DSSUtils.formatDate(controlTime);
returnedPcvIndication.setAttribute(CONTROL_TIME, formatedControlTime);
}
/**
* If it returns VALID/control-time, go to the next step. Otherwise, return the current time status and
* sub-indication with an explanation of the failure.<br>
*/
if (!ok) {
conclusion.setIndication(currentTimeIndication);
conclusion.setSubIndication(currentTimeSubIndication);
conclusion.copyBasicInfo(returnedPcvIndication);//Info(pcvConclusion);
return conclusion;
}
/**
* 2) If there is a POE of the signature value at (or before) control-time do the following:<br>
*/
constraintNode = addConstraint(PSV_ITPOSVAOBCT);
final Date bestSignatureTime = poe.getLowestSignaturePOE(signatureId, controlTime);
ok = bestSignatureTime != null;
constraintNode.addChild(STATUS, ok ? OK : KO);
if (ok) {
final String formatedBestSignatureTime = DSSUtils.formatDate(bestSignatureTime);
constraintNode.addChild(INFO).setAttribute(BEST_SIGNATURE_TIME, formatedBestSignatureTime);
/**
* -- If current time indication/sub-indication is INDETERMINATE/REVOKED_NO_POE or INDETERMINATE/
* REVOKED_CA_NO_POE, return VALID.<br>
*/
if (INDETERMINATE.equals(currentTimeIndication) && (REVOKED_NO_POE.equals(currentTimeSubIndication) || REVOKED_CA_NO_POE.equals(currentTimeSubIndication))) {
conclusion.setIndication(VALID);
return conclusion;
}
/**
* -- If current time indication/sub-indication is INDETERMINATE/OUT_OF_BOUNDS_NO_POE:<br>
*/
if (INDETERMINATE.equals(currentTimeIndication) && OUT_OF_BOUNDS_NO_POE.equals(currentTimeSubIndication)) {
/**
* say best-signature-time is the lowest time at which there exists a POE for the signature value in the set
* of POEs:<br>
*
* --- a) If best-signature-time is before the issuance date of the signer's certificate (notBefore field),
* terminate with INVALID/NOT_YET_VALID.<br>
*/
final int signingCertId = getSigningCertificateId(signature);
final XmlDom signingCert = params.getCertificate(signingCertId);
final Date notBefore = signingCert.getTimeValue("./NotBefore/text()");
if (bestSignatureTime.before(notBefore)) {
conclusion.setIndication(INVALID);
conclusion.setSubIndication(NOT_YET_VALID);
return conclusion;
} else {
/**
* --- b) If best-signature-time is after the issuance date of the signer's certificate, return VALID.<br>
*/
conclusion.setIndication(VALID);
return conclusion;
}
}
/**
* -- If current time indication/sub-indication is INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE and for each
* algorithm (or key size) in the list concerned by the failure, there is a POE for the material that uses this
* algorithm (or key size) at a time before to the time up to which the algorithm in question was considered
* secure, return VALID.<br>
*/
if (INDETERMINATE.equals(currentTimeIndication) && CRYPTO_CONSTRAINTS_FAILURE_NO_POE.equals(currentTimeSubIndication)) {
boolean poeExists = true;
final List<XmlDom> infoList = currentTimeSignatureConclusion.getElements("./Info");
for (final XmlDom info : infoList) {
final String field = info.getValue("./@Field");
if (!field.contains("/AlgoExpirationDate")) {
poeExists = false;
continue;
}
final String expirationDateString = info.getValue("./text()");
if (ALGORITHM_NOT_FOUND.equals(expirationDateString)) {
poeExists = false;
continue;
}
final Date expirationDate = DSSUtils.parseDate(DSSUtils.DEFAULT_DATE_FORMAT, expirationDateString);
final String context = info.getValue("./@Context");
if (SIGNATURE.equals(context)) {
Date poeDate_ = poe.getSignaturePOE(signatureId, expirationDate);
if (poeDate_ == null) {
poeExists = false;
continue;
}
} else if (SIGNATURE.equals(context)) {
//TODO:
}
}
if (poeExists) {
conclusion.setIndication(VALID);
return conclusion;
} else {
conclusion.addInfo(infoList);
}
}
}
/**
* In all other cases, return current time indication/sub-indication together with an explanation of the failure.
*/
conclusion.setIndication(currentTimeIndication);
conclusion.setSubIndication(currentTimeSubIndication);
return conclusion;
}
/**
* @param messageTag
* @return
*/
private XmlNode addConstraint(final MessageTag messageTag) {
XmlNode constraintNode = pastSignatureValidationData.addChild(CONSTRAINT);
constraintNode.addChild(NAME, messageTag.getMessage()).setAttribute(NAME_ID, messageTag.name());
return constraintNode;
}
}