/*
* 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;
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.exception.DSSException;
import eu.europa.ec.markt.dss.validation102853.policy.ProcessParameters;
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.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;
/**
* 6 Basic Validation Process<br>
* This clause describes a validation process for basic short-term signature validation that is appropriate for
* validating basic signatures (e.g. time-stamps, CRLs, etc.) as well as AdES-BES and AdES-EPES electronic signatures.
* The process is built on the building blocks described in the previous clause.
*
* @author bielecro
*/
public class BasicValidation implements Indication, SubIndication, NodeName, NodeValue, AttributeName, AttributeValue, ExceptionMessage {
private static final Logger LOG = LoggerFactory.getLogger(BasicValidation.class);
// Secondary inputs
/**
* See {@link eu.europa.ec.markt.dss.validation102853.policy.ProcessParameters#getBasicBuildingBlocksReport()}
*
* @return
*/
private XmlDom basicBuildingBlocksReport;
private XmlDom contentTimestampsAdESTValidationData;
private void prepareParameters(final XmlNode mainNode, final ProcessParameters params) {
this.basicBuildingBlocksReport = params.getBasicBuildingBlocksReport();
isInitialised(mainNode, params);
}
private void isInitialised(final XmlNode mainNode, final ProcessParameters params) {
if (basicBuildingBlocksReport == null) {
/**
* The execution of the Basic Building Blocks validation process which creates the validation data.<br>
*/
final BasicBuildingBlocks basicBuildingBlocks = new BasicBuildingBlocks();
basicBuildingBlocksReport = basicBuildingBlocks.run(mainNode, params);
}
}
/**
* This method runs the Basic validation process.
* <p/>
* 6.2 Inputs<br>
* Signature ..................... Mandatory<br>
* Signed data object (s) ........ Optional<br>
* Signer's Certificate .......... Optional<br>
* Trusted-status Service Lists .. Optional<br>
* Signature Validation Policies . Optional<br>
* Local configuration ...........Optional<br>
* <p/>
* 6.3 Outputs<br>
* The main output of the signature validation is a status indicating the validity of the signature. This status may
* be accompanied by additional information (see clause 4).<br>
* <p/>
* 6.4 Processing<br>
* NOTE 1: Since processing is largely implementation dependent, the steps listed in this clause are not necessarily
* to be processed exactly in the order given. Any ordering that produces the same results can be used, even parallel
* processing is possible.<br>
* <p/>
* The following steps shall be performed:
*
* @param params
* @return
*/
public XmlDom run(final XmlNode mainNode, final ProcessParameters params) {
prepareParameters(mainNode, params);
LOG.debug(this.getClass().getSimpleName() + ": start.");
final XmlNode basicValidationData = mainNode.addChild(BASIC_VALIDATION_DATA);
final List<XmlDom> signatures = basicBuildingBlocksReport.getElements("./Signature");
for (final XmlDom signature : signatures) {
final String signatureId = signature.getValue("./@Id");
final String type = signature.getValue("./@Type");
if (COUNTERSIGNATURE.equals(type)) {
params.setCurrentValidationPolicy(params.getCountersignatureValidationPolicy());
} else {
params.setCurrentValidationPolicy(params.getValidationPolicy());
}
final XmlNode signatureNode = basicValidationData.addChild(SIGNATURE);
signatureNode.setAttribute(ID, signatureId);
final XmlNode conclusionNode = signatureNode.addChild(CONCLUSION);
final boolean valid = process(signature, signatureId, conclusionNode);
if (valid) {
final XmlDom mainConclusion = signature.getElement("./Conclusion");
if (mainConclusion == null) {
throw new DSSException(EXCEPTION_TWUEIVP);
}
conclusionNode.addChildrenOf(mainConclusion);
}
}
final XmlDom bvDom = basicValidationData.toXmlDom();
params.setBvData(bvDom);
return bvDom;
}
/**
* @param signature depicts the basic building blocks detailed validation report for a signature.
* @param signatureId signature identifier
* @param conclusionNode {@code XmlNode} which is used to store the conclusion
* @return
*/
private boolean process(final XmlDom signature, final String signatureId, final XmlNode conclusionNode) {
/**
* 1) Identify the signer's certificate: Perform the Signer's Certificate Identification process (see clause 5.1)
* with the signature and the signer's certificate, if provided as a parameter. If it returns INDETERMINATE,
* terminate with INDETERMINATE and associated information, otherwise go to the next step.
*
* TODO: (***) The ICS process can also return INVALID.FORMAT_FAILURE. This is not mentioned in the BVP process.
*/
final XmlDom iscConclusion = signature.getElement("./ISC/Conclusion");
final String iscIndication = iscConclusion.getValue("./Indication/text()");
if (!VALID.equals(iscIndication)) {
conclusionNode.addChildrenOf(iscConclusion);
return false;
}
/**
* 2) Initialise the validation constraints and parameters: Perform the Validation Context Initialisation process
* (see clause 5.2).
*/
final XmlDom vciConclusion = signature.getElement("./VCI/Conclusion");
final String vciIndication = vciConclusion.getValue("./Indication/text()");
if (!VALID.equals(vciIndication)) {
conclusionNode.addChildrenOf(vciConclusion);
return false;
}
/**
* 4) Verify the cryptographic signature value: Perform the Cryptographic Verification process with the following
* inputs:
*
* a) The signature.
*
* b) The certificate chain returned in the previous step.
*
* c) The signed data object(s).
*
* If the process returns VALID, go to the next step. Otherwise, terminate with the returned indication and
* associated information.
*
* --> We do this first to not be oblige to redo it at LTV process.
*/
final XmlDom cvConclusion = signature.getElement("./CV/Conclusion");
final String cvIndication = cvConclusion.getValue("./Indication/text()");
if (!VALID.equals(cvIndication)) {
conclusionNode.addChildrenOf(cvConclusion);
return false;
}
/**
* 5) Apply the validation constraints: Perform the Signature Acceptance Validation process with the following
* inputs:
*
* a) The signature.
*
* b) The Cryptographic Constraints.
*
* c) The Signature Constraints.
*
* If the process returns VALID, go to the next step.
*
* If the process returns INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE and the material concerned by this
* failure is the signature value: If the signature contains a content-timestamp attribute, perform the Validation
* Process for AdES Time-Stamps as defined in clause 7. If it returns VALID and the algorithm(s) concerned were no
* longer considered reliable at the generation time of the time-stamp token, terminate with
* INVALID/CRYPTO_CONSTRAINTS_FAILURE. In all other cases, terminate with
* INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE.
*
* NOTE 2: The content time-stamp is a signed attribute and hence proves that the signature value was produced
* after the generation time of the time-stamp token.
*
* NOTE 3: In case this clause returns INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE, LTV can be used to
* validate the signature, if other POE (e.g. from a trusted archive) exist.
*
* In all other cases, terminate with the returned indication and associated information.
*
* --> We do this first to not be oblige to redo it at LTV process.
*/
final XmlDom savConclusion = signature.getElement("./SAV/Conclusion");
if (savConclusion == null) {
throw new DSSException(EXCEPTION_TWUEIVP);
}
final String savIndication = savConclusion.getValue("./Indication/text()");
final String savSubIndication = savConclusion.getValue("./SubIndication/text()");
if (INDETERMINATE.equals(savIndication) && CRYPTO_CONSTRAINTS_FAILURE_NO_POE.equals(savSubIndication)) {
List<XmlDom> contentTimestamps = signature.getElements("./ContentTimestamps/ProductionTime");
if (contentTimestamps.isEmpty()) {
conclusionNode.addChildrenOf(savConclusion);
return false;
}
// To carry out content-timestamp AdES-T validation process.
final XmlDom adestConclusion = contentTimestampsAdESTValidationData.getElement("/ContentTimestampsAdesTValidationData/Signature[@Id='%s']/Conclusion", signatureId);
final String adestIndication = adestConclusion.getValue("./Indication/text()");
if (!VALID.equals(adestIndication)) {
conclusionNode.addChild(INDICATION, INDETERMINATE);
conclusionNode.addChild(SUB_INDICATION, CRYPTO_CONSTRAINTS_FAILURE_NO_POE);
return false;
}
boolean ok = true;
final List<XmlDom> infoList = savConclusion.getElements("./Info");
for (XmlDom info : infoList) {
final String field = info.getValue("./@Field");
if (field.contains("/AlgoExpirationDate/")) {
final String expirationDateString = info.getValue("./text()");
if (!ALGORITHM_NOT_FOUND.equals(expirationDateString)) {
// TODO: to be adapted to "./Info[@Field='TimestampProductionTime']/text()"
final Date bestSignatureTime = adestConclusion.getTimeValue("./Info/@BestSignatureTime");
final Date expirationDate = DSSUtils.parseDate(DSSUtils.DEFAULT_DATE_FORMAT, expirationDateString);
if (expirationDate.before(bestSignatureTime)) {
ok = false;
}
} else {
ok = false;
}
break;
}
}
if (ok) {
conclusionNode.addChild(INDICATION, INDETERMINATE);
conclusionNode.addChild(SUB_INDICATION, EXPIRED);
} else {
conclusionNode.addChild(INDICATION, INVALID);
conclusionNode.addChild(SUB_INDICATION, CRYPTO_CONSTRAINTS_FAILURE_NO_POE);
}
return false;
}
if (!VALID.equals(savIndication)) {
conclusionNode.addChildrenOf(savConclusion);
return false;
}
/**
* 3) Validate the signer's certificate: Perform the X.509 Certificate Validation process (see clause 5.3) with
* the following inputs:
*
* a) The signature.<br>
*
* b) The signer's certificate obtained in step 1.<br>
*
* c) X.509 Validation Parameters, Certificate meta-data, Chain Constraints and Cryptographic Constraints obtained
* in step 2:<br>
*/
final XmlDom xcvConclusion = signature.getElement("./XCV/Conclusion");
final String xcvIndication = xcvConclusion.getValue("./Indication/text()");
final String xcvSubIndication = xcvConclusion.getValue("./SubIndication/text()");
/**
* If the process returns VALID, go to the next step.
*
* If the process returns INDETERMINATE/REVOKED_NO_POE: If the signature contains a content-time-stamp
* attribute, perform the Validation Process for AdES Time-Stamps as defined in clause 7. If it returns VALID and
* the generation time of the time-stamp token is after the revocation time, terminate with INVALID/REVOKED. In
* all other cases, terminate with INDETERMINATE/REVOKED_NO_POE.
*/
if (INDETERMINATE.equals(xcvIndication) && REVOKED_NO_POE.equals(xcvSubIndication)) {
XmlDom contentTimestamps = signature.getElement("./ContentTimestamps/ProductionTime");
if (contentTimestamps != null) {
final XmlDom adestConclusion = contentTimestampsAdESTValidationData.getElement("/ContentTimestampsAdesTValidationData/Signature[@Id='%s']/Conclusion", signatureId);
final String adestIndication = adestConclusion.getValue("./Indication/text()");
if (VALID.equals(adestIndication)) {
final Date revocationTime = xcvConclusion.getTimeValue("./Info/@RevocationTime");
final Date bestSignatureTime = adestConclusion.getTimeValue("./Info/@BestSignatureTime");
if (bestSignatureTime.after(revocationTime)) {
conclusionNode.addChild(INDICATION, INVALID);
conclusionNode.addChild(SUB_INDICATION, REVOKED);
return false;
}
}
}
}
/**
* If the process returns INDETERMINATE/OUT_OF_BOUNDS_NO_POE: If the signature contains a content-time-stamp
* attribute, perform the Validation Process for AdES Time-Stamps as defined in clause 7. If it returns VALID and
* the generation time of the time-stamp token is after the expiration date of the signer's certificate, terminate
* with INVALID/EXPIRED. In all other cases, terminate with INDETERMINATE/OUT_OF_BOUNDS_NO_POE.
*/
if (INDETERMINATE.equals(xcvIndication) && OUT_OF_BOUNDS_NO_POE.equals(xcvSubIndication)) {
XmlDom contentTimestamps = signature.getElement("./ContentTimestamps/ProductionTime");
if (contentTimestamps != null) {
final XmlDom adestConclusionDom = contentTimestampsAdESTValidationData
.getElement("/ContentTimestampsAdesTValidationData/Signature[@Id='%s']/Conclusion", signatureId);
final String adestIndication = adestConclusionDom.getValue("./Indication/text()");
if (VALID.equals(adestIndication)) {
final Date bestSignatureTime = adestConclusionDom.getTimeValue("./Info/@BestSignatureTime");
final Date notAfter = xcvConclusion.getTimeValue("./Info/@NotAfter");
if (bestSignatureTime.after(notAfter)) {
conclusionNode.addChild(INDICATION, INVALID);
conclusionNode.addChild(SUB_INDICATION, EXPIRED);
return false;
}
}
}
}
/**
* In all other cases, terminate with the returned indication and associated information.
*/
if (!VALID.equals(xcvIndication)) {
conclusionNode.addChildrenOf(xcvConclusion);
return false;
}
/**
* 6) Data extraction: the SVA shall return the success indication VALID. In addition, the SVA should return
* additional information extracted from the signature and/or used by the intermediate steps. In particular, the
* SVA should provide to the DA all information related to signed and unsigned properties/attributes, including
* those which were not processed during the validation process. What the DA shall do with this information is out
* of the scope of the present document.
*/
return true;
}
}