package eu.europa.esig.dss.validation.process.vpfltvd; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.europa.esig.dss.jaxb.detailedreport.XmlBasicBuildingBlocks; import eu.europa.esig.dss.jaxb.detailedreport.XmlConclusion; import eu.europa.esig.dss.jaxb.detailedreport.XmlConstraint; import eu.europa.esig.dss.jaxb.detailedreport.XmlConstraintsConclusion; import eu.europa.esig.dss.jaxb.detailedreport.XmlSignature; import eu.europa.esig.dss.jaxb.detailedreport.XmlStatus; import eu.europa.esig.dss.jaxb.detailedreport.XmlValidationProcessLongTermData; import eu.europa.esig.dss.jaxb.detailedreport.XmlValidationProcessTimestamps; import eu.europa.esig.dss.utils.Utils; import eu.europa.esig.dss.validation.policy.Context; import eu.europa.esig.dss.validation.policy.ValidationPolicy; 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.Chain; import eu.europa.esig.dss.validation.process.ChainItem; import eu.europa.esig.dss.validation.process.bbb.sav.checks.CryptographicCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.AcceptableBasicSignatureValidationCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.BestSignatureTimeNotBeforeCertificateIssuanceCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.RevocationBasicBuildingBlocksCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.RevocationDateAfterBestSignatureTimeCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.SigningTimeAttributePresentCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.TimestampCoherenceOrderCheck; import eu.europa.esig.dss.validation.process.vpfltvd.checks.TimestampDelayCheck; import eu.europa.esig.dss.validation.reports.wrapper.CertificateWrapper; import eu.europa.esig.dss.validation.reports.wrapper.DiagnosticData; import eu.europa.esig.dss.validation.reports.wrapper.RevocationWrapper; import eu.europa.esig.dss.validation.reports.wrapper.SignatureWrapper; import eu.europa.esig.dss.validation.reports.wrapper.TimestampWrapper; /** * 5.5 Validation process for Signatures with Time and Signatures with Long-Term Validation Data */ public class ValidationProcessForSignaturesWithLongTermValidationData extends Chain<XmlValidationProcessLongTermData> { private static final Logger logger = LoggerFactory.getLogger(ValidationProcessForSignaturesWithLongTermValidationData.class); private final XmlConstraintsConclusion basicSignatureValidation; private final List<XmlValidationProcessTimestamps> timestampValidations; private final DiagnosticData diagnosticData; private final SignatureWrapper currentSignature; private final Map<String, XmlBasicBuildingBlocks> bbbs; private final ValidationPolicy policy; private final Date currentDate; public ValidationProcessForSignaturesWithLongTermValidationData(XmlSignature signatureAnalysis, DiagnosticData diagnosticData, SignatureWrapper currentSignature, Map<String, XmlBasicBuildingBlocks> bbbs, ValidationPolicy policy, Date currentDate) { super(new XmlValidationProcessLongTermData()); this.basicSignatureValidation = signatureAnalysis.getValidationProcessBasicSignatures(); this.timestampValidations = signatureAnalysis.getValidationProcessTimestamps(); this.diagnosticData = diagnosticData; this.currentSignature = currentSignature; this.bbbs = bbbs; this.policy = policy; this.currentDate = currentDate; } @Override protected void initChain() { /* * 1) The process shall initialize the set of signature time-stamp tokens from the signature time-stamp * attributes present in the signature and shall initialize the best-signature-time to the current time. * NOTE 1: Best-signature-time is an internal variable for the algorithm denoting the earliest time when it can * be proven that a signature has existed. */ Date bestSignatureTime = currentDate; /* * 5.5.4 2) Signature validation: the process shall perform the validation process for Basic Signatures as per * clause 5.3 with all the inputs, including the processing of any signed attributes as specified. If the * Signature contains long-term validation data, this data shall be passed to the validation process for Basic * Signatures. * * If this validation returns PASSED, INDETERMINATE/CRYPTO_CONSTRAINTS_FAILURE_NO_POE, * INDETERMINATE/REVOKED_NO_POE or INDETERMINATE/OUT_OF_BOUNDS_NO_POE, the SVA * shall go to the next step. Otherwise, the process shall return the status and information returned by the * validation process for Basic Signatures. */ ChainItem<XmlValidationProcessLongTermData> item = firstItem = isAcceptableBasicSignatureValidation(); Set<RevocationWrapper> revocationData = getLinkedRevocationData(); if (Utils.isCollectionNotEmpty(revocationData)) { for (RevocationWrapper revocation : revocationData) { XmlBasicBuildingBlocks revocationBBB = bbbs.get(revocation.getId()); if (revocationBBB != null) { item = item.setNextItem(revocationBasicBuildingBlocksValid(revocationBBB)); } else { logger.warn("No BBB found for revocation " + revocation.getId()); } } } /* * 3) Signature time-stamp validation: * a) For each time-stamp token in the set of signature time-stamp tokens, the process shall check that the * message imprint has been generated according to the corresponding signature format specification * verification. If the verification fails, the process shall remove the token from the set. */ Set<TimestampWrapper> allowedTimestamps = filterInvalidTimestamps(currentSignature.getTimestampList()); if (Utils.isCollectionNotEmpty(allowedTimestamps)) { /* * b) Time-stamp token validation: For each time-stamp token remaining in the set of signature time-stamp * tokens, the process shall perform the time-stamp validation process as per clause 5.4: * * If PASSED is returned and if the returned generation time is before best-signature-time, the process * shall set best-signature-time to this date and shall try the next token. */ for (TimestampWrapper timestampWrapper : allowedTimestamps) { Date productionTime = timestampWrapper.getProductionTime(); if (productionTime.before(bestSignatureTime)) { bestSignatureTime = productionTime; } } } /* * 4) Comparing times: * a) If step 2 returned the indication INDETERMINATE with the sub-indication REVOKED_NO_POE: If the * returned revocation time is posterior to best-signature-time, the process shall perform step 4d. * Otherwise, the process shall return the indication INDETERMINATE with the sub-indication REVOKED_NO_POE. */ XmlConclusion bsConclusion = basicSignatureValidation.getConclusion(); if (Indication.INDETERMINATE.equals(bsConclusion.getIndication()) && SubIndication.REVOKED_NO_POE.equals(bsConclusion.getSubIndication())) { item = item.setNextItem(revocationDateAfterBestSignatureDate(bestSignatureTime)); } /* * b) If step 2 returned the indication INDETERMINATE with the sub-indication * OUT_OF_BOUNDS_NO_POE: If best-signature-time is before the issuance date of the signing * certificate, the process shall return the indication FAILED with the sub-indication NOT_YET_VALID. * Otherwise, the process shall return the indication INDETERMINATE with the sub-indication * OUT_OF_BOUNDS_NO_POE. */ if (Indication.INDETERMINATE.equals(bsConclusion.getIndication()) && SubIndication.OUT_OF_BOUNDS_NO_POE.equals(bsConclusion.getSubIndication())) { item = item.setNextItem(bestSignatureTimeNotBeforeCertificateIssuance(bestSignatureTime)); } /* * c) If step 2 returned INDETERMINATE with the sub-indication CRYPTO_CONSTRAINTS_FAILURE_NO_POE and the * material concerned by this failure is the signature value or a signed attribute: If the algorithm(s) * concerned were still considered reliable at best-signature-time, the process shall continue with step d. * Otherwise, the process shall return the indication INDETERMINATE with the sub-indication * CRYPTO_CONSTRAINTS_FAILURE_NO_POE. */ if (Indication.INDETERMINATE.equals(bsConclusion.getIndication()) && SubIndication.CRYPTO_CONSTRAINTS_FAILURE_NO_POE.equals(bsConclusion.getSubIndication())) { item = item.setNextItem(algorithmReliableAtBestSignatureTime(bestSignatureTime)); } if (Utils.isCollectionNotEmpty(allowedTimestamps)) { /* * d) For each time-stamp token remaining in the set of signature time-stamp tokens, the process shall check * the coherence in the values of the times indicated in the time-stamp tokens. They shall be posterior to * the times indicated in any time-stamp token computed on the signed data. The process shall apply the * rules * specified in IETF RFC 3161 [3], clause 2.4.2 regarding the order of time-stamp tokens generated by the * same or different TSAs given the accuracy and ordering fields' values of the TSTInfo field, * unless stated differently by the signature validation constraints. If all the checks end successfully, * the process shall go to the next step. Otherwise the process shall return the indication FAILED with the * sub-indication TIMESTAMP_ORDER_FAILURE. */ item = item.setNextItem(timestampCoherenceOrder(allowedTimestamps)); /* * 5) Handling Time-stamp delay: If the validation constraints specify a time-stamp delay: * a) If no signing-time property/attribute is present, the process shall return the indication * INDETERMINATE with the sub-indication SIG_CONSTRAINTS_FAILURE. */ item = item.setNextItem(signingTimeAttributePresent()); /* * b) If a signing-time property/attribute is present, the process shall check that the claimed time in the * attribute plus the time-stamp delay is after the best-signature-time. If the check is successful, the * process shall go to the next step. Otherwise, the process shall return the indication FAILED with the * sub-indication SIG_CONSTRAINTS_FAILURE. */ item = item.setNextItem(timestampDelay(bestSignatureTime)); } } private Set<RevocationWrapper> getLinkedRevocationData() { Set<RevocationWrapper> result = new HashSet<RevocationWrapper>(); extractRevocationDataFromCertificateChain(result, currentSignature.getCertificateChainIds()); List<TimestampWrapper> timestampList = currentSignature.getTimestampList(); for (TimestampWrapper timestamp : timestampList) { extractRevocationDataFromCertificateChain(result, timestamp.getCertificateChainIds()); } return result; } private void extractRevocationDataFromCertificateChain(Set<RevocationWrapper> result, List<String> certificateChainIds) { for (String certificateId : certificateChainIds) { CertificateWrapper certificate = diagnosticData.getUsedCertificateById(certificateId); if (certificate != null && certificate.getRevocationData() != null) { result.addAll(certificate.getRevocationData()); } } } private Set<TimestampWrapper> filterInvalidTimestamps(List<TimestampWrapper> allTimestamps) { Set<TimestampWrapper> result = new HashSet<TimestampWrapper>(); for (TimestampWrapper timestampWrapper : allTimestamps) { boolean foundValidationTSP = false; for (XmlValidationProcessTimestamps timestampValidation : timestampValidations) { List<XmlConstraint> constraints = timestampValidation.getConstraint(); for (XmlConstraint tspValidation : constraints) { if (Utils.areStringsEqual(timestampWrapper.getId(), tspValidation.getId())) { foundValidationTSP = true; // PVA : if OK message imprint is validated in SVA of timestamp (depending of constraint.xml) if (XmlStatus.OK.equals(tspValidation.getStatus())) { result.add(timestampWrapper); break; } } } } if (!foundValidationTSP) { logger.warn("Cannot find tsp validation info for tsp " + timestampWrapper.getId()); } } return result; } private ChainItem<XmlValidationProcessLongTermData> isAcceptableBasicSignatureValidation() { return new AcceptableBasicSignatureValidationCheck(result, basicSignatureValidation, getFailLevelConstraint()); } private ChainItem<XmlValidationProcessLongTermData> revocationBasicBuildingBlocksValid(XmlBasicBuildingBlocks revocationBBB) { return new RevocationBasicBuildingBlocksCheck(result, revocationBBB, getFailLevelConstraint()); } private ChainItem<XmlValidationProcessLongTermData> revocationDateAfterBestSignatureDate(Date bestSignatureTime) { CertificateWrapper signingCertificate = diagnosticData.getUsedCertificateById(currentSignature.getSigningCertificateId()); return new RevocationDateAfterBestSignatureTimeCheck(result, signingCertificate, bestSignatureTime, getFailLevelConstraint()); } private ChainItem<XmlValidationProcessLongTermData> bestSignatureTimeNotBeforeCertificateIssuance(Date bestSignatureTime) { CertificateWrapper signingCertificate = diagnosticData.getUsedCertificateById(currentSignature.getSigningCertificateId()); return new BestSignatureTimeNotBeforeCertificateIssuanceCheck(result, bestSignatureTime, signingCertificate, policy.getBestSignatureTimeBeforeIssuanceDateOfSigningCertificateConstraint()); } private ChainItem<XmlValidationProcessLongTermData> timestampCoherenceOrder(Set<TimestampWrapper> allowedTimestamps) { return new TimestampCoherenceOrderCheck(result, allowedTimestamps, policy.getTimestampCoherenceConstraint()); } private ChainItem<XmlValidationProcessLongTermData> signingTimeAttributePresent() { return new SigningTimeAttributePresentCheck(result, currentSignature, policy.getSigningTimeConstraint()); } private ChainItem<XmlValidationProcessLongTermData> timestampDelay(Date bestSignatureTime) { return new TimestampDelayCheck(result, currentSignature, bestSignatureTime, policy.getTimestampDelaySigningTimePropertyConstraint()); } private ChainItem<XmlValidationProcessLongTermData> algorithmReliableAtBestSignatureTime(Date bestSignatureTime) { return new CryptographicCheck<XmlValidationProcessLongTermData>(result, currentSignature, bestSignatureTime, policy.getSignatureCryptographicConstraint(Context.SIGNATURE)); } }