Skip to content

Commit 9b8a523

Browse files
committed
SIVA-760 Validate 'not granted ASiC-S timestamp tokens at POE time' as valid but with warnings
1 parent 42db8b9 commit 9b8a523

File tree

11 files changed

+736
-75
lines changed

11 files changed

+736
-75
lines changed

siva-parent/siva-validation-proxy/src/main/java/ee/openeid/siva/proxy/ContainerValidationProxy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ private SimpleReport mergeReports(SimpleReport timeStampTokenReport, SimpleRepor
189189
dataFileReport.getValidationConclusion().setSignatureForm(timeStampTokenReport.getValidationConclusion().getSignatureForm());
190190
dataFileReport.getValidationConclusion().setValidatedDocument(timeStampTokenReport.getValidationConclusion().getValidatedDocument());
191191
dataFileReport.getValidationConclusion().setValidationTime(timeStampTokenReport.getValidationConclusion().getValidationTime());
192+
dataFileReport.getValidationConclusion().setValidationWarnings(timeStampTokenReport.getValidationConclusion().getValidationWarnings());
192193
return dataFileReport;
193194
}
194195
return timeStampTokenReport;

validation-services-parent/timestamptoken-validation-service/src/main/java/ee/openeid/validation/service/timestamptoken/TimeStampTokenValidationService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import ee.openeid.siva.validation.service.signature.policy.properties.ConstraintDefinedPolicy;
2929
import ee.openeid.tsl.configuration.AlwaysFailingCRLSource;
3030
import ee.openeid.tsl.configuration.AlwaysFailingOCSPSource;
31+
import ee.openeid.validation.service.timestamptoken.util.TimestampNotGrantedValidationUtils;
3132
import ee.openeid.validation.service.timestamptoken.validator.report.TimeStampTokenValidationReportBuilder;
3233
import eu.europa.esig.dss.asic.cades.validation.ASiCContainerWithCAdESValidator;
3334
import eu.europa.esig.dss.enumerations.MimeType;
@@ -88,6 +89,7 @@ public Reports validateDocument(ValidationDocument validationDocument) {
8889

8990
final ConstraintDefinedPolicy policy = signaturePolicyService.getPolicy(validationDocument.getSignaturePolicy());
9091
final eu.europa.esig.dss.validation.reports.Reports reports = validator.validateDocument(policy.getConstraintDataStream());
92+
TimestampNotGrantedValidationUtils.convertNotGrantedErrorsToWarnings(reports);
9193

9294
return new TimeStampTokenValidationReportBuilder(
9395
reports,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright 2024 Riigi Infosüsteemi Amet
3+
*
4+
* Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
5+
* the European Commission - subsequent versions of the EUPL (the "Licence");
6+
* You may not use this work except in compliance with the Licence.
7+
* You may obtain a copy of the Licence at:
8+
*
9+
* https://joinup.ec.europa.eu/software/page/eupl
10+
*
11+
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
12+
* distributed on an "AS IS" basis,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the Licence for the specific language governing permissions and limitations under the Licence.
15+
*/
16+
17+
package ee.openeid.validation.service.timestamptoken.util;
18+
19+
import ee.openeid.siva.validation.document.report.ValidationWarning;
20+
import ee.openeid.siva.validation.document.report.Warning;
21+
import eu.europa.esig.dss.enumerations.Indication;
22+
import eu.europa.esig.dss.i18n.I18nProvider;
23+
import eu.europa.esig.dss.i18n.MessageTag;
24+
import eu.europa.esig.dss.simplereport.jaxb.XmlDetails;
25+
import eu.europa.esig.dss.simplereport.jaxb.XmlMessage;
26+
import eu.europa.esig.dss.simplereport.jaxb.XmlTimestamp;
27+
import eu.europa.esig.dss.simplereport.jaxb.XmlToken;
28+
import eu.europa.esig.dss.validation.reports.Reports;
29+
import lombok.AccessLevel;
30+
import lombok.NoArgsConstructor;
31+
import org.apache.commons.collections4.CollectionUtils;
32+
33+
import java.util.Collection;
34+
import java.util.Iterator;
35+
import java.util.List;
36+
import java.util.Objects;
37+
import java.util.function.Consumer;
38+
import java.util.function.Function;
39+
import java.util.function.Predicate;
40+
41+
@NoArgsConstructor(access = AccessLevel.PRIVATE)
42+
public final class TimestampNotGrantedValidationUtils {
43+
private static final String NOT_GRANTED_AT_MESSAGE_TAG = MessageTag.QUAL_HAS_GRANTED_AT_ANS.getId();
44+
private static final String NOT_GRANTED_AT_TST_POE_TIME_MESSAGE_TEXT = new I18nProvider()
45+
.getMessage(MessageTag.QUAL_HAS_GRANTED_AT_ANS, MessageTag.VT_TST_POE_TIME);
46+
47+
private static final Predicate<String> NOT_GRANTED_TEXT_PREDICATE = NOT_GRANTED_AT_TST_POE_TIME_MESSAGE_TEXT::equals;
48+
private static final Predicate<XmlMessage> NOT_GRANTED_MESSAGE_PREDICATE = message -> message != null &&
49+
NOT_GRANTED_AT_MESSAGE_TAG.equals(message.getKey()) && NOT_GRANTED_TEXT_PREDICATE.test(message.getValue());
50+
51+
private static final String NOT_GRANTED_CONTAINER_WARNING = "Found a timestamp token not related to granted status."
52+
+ " If not yet covered with a fresh timestamp token, this container might become invalid in the future.";
53+
54+
public static void convertNotGrantedErrorsToWarnings(Reports reports) {
55+
doForEachTimestampInSimpleReportJaxb(reports, timestamp -> {
56+
if (isValidWithOneQualificationError(timestamp)) {
57+
convertErrorsToWarnings(timestamp.getQualificationDetails(), NOT_GRANTED_MESSAGE_PREDICATE);
58+
}
59+
});
60+
}
61+
62+
public static ValidationWarning getValidationWarningIfNotGrantedTimestampExists(List<Warning> timestampWarnings) {
63+
if (containsMessage(timestampWarnings, Warning::getContent, NOT_GRANTED_TEXT_PREDICATE)) {
64+
return new ValidationWarning(NOT_GRANTED_CONTAINER_WARNING);
65+
} else {
66+
return null;
67+
}
68+
}
69+
70+
private static void doForEachTimestampInSimpleReportJaxb(Reports reports, Consumer<XmlTimestamp> timestampProcessor) {
71+
for (XmlToken token : reports.getSimpleReportJaxb().getSignatureOrTimestampOrEvidenceRecord()) {
72+
if (token instanceof XmlTimestamp) {
73+
timestampProcessor.accept((XmlTimestamp) token);
74+
}
75+
}
76+
}
77+
78+
private static boolean isValidWithOneQualificationError(XmlTimestamp timestamp) {
79+
return Indication.PASSED.equals(timestamp.getIndication()) &&
80+
hasNoErrors(timestamp.getAdESValidationDetails()) &&
81+
getErrorCount(timestamp.getQualificationDetails()) == 1;
82+
}
83+
84+
private static void convertErrorsToWarnings(XmlDetails details, Predicate<XmlMessage> messageFilter) {
85+
if (details == null || CollectionUtils.isEmpty(details.getError())) {
86+
return;
87+
}
88+
89+
Iterator<XmlMessage> errorsIterator = details.getError().iterator();
90+
91+
while (errorsIterator.hasNext()) {
92+
XmlMessage errorMessage = errorsIterator.next();
93+
94+
if (messageFilter.test(errorMessage)) {
95+
errorsIterator.remove();
96+
details.getWarning().add(errorMessage);
97+
}
98+
}
99+
}
100+
101+
private static boolean hasErrors(XmlDetails details) {
102+
return (details != null) && CollectionUtils.isNotEmpty(details.getError());
103+
}
104+
105+
private static boolean hasNoErrors(XmlDetails details) {
106+
return !hasErrors(details);
107+
}
108+
109+
private static int getErrorCount(XmlDetails details) {
110+
return (details != null)
111+
? CollectionUtils.size(details.getError())
112+
: 0;
113+
}
114+
115+
private static <E> boolean containsMessage(
116+
Collection<E> collection,
117+
Function<E, String> messageMapper,
118+
Predicate<String> messageFilter
119+
) {
120+
return CollectionUtils.isNotEmpty(collection) && collection.stream()
121+
.filter(Objects::nonNull)
122+
.map(messageMapper)
123+
.filter(Objects::nonNull)
124+
.anyMatch(messageFilter);
125+
}
126+
}

validation-services-parent/timestamptoken-validation-service/src/main/java/ee/openeid/validation/service/timestamptoken/validator/report/TimeStampTokenValidationReportBuilder.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,22 @@
1717
package ee.openeid.validation.service.timestamptoken.validator.report;
1818

1919
import ee.openeid.siva.validation.document.ValidationDocument;
20+
import ee.openeid.siva.validation.document.report.Certificate;
21+
import ee.openeid.siva.validation.document.report.CertificateType;
22+
import ee.openeid.siva.validation.document.report.DetailedReport;
23+
import ee.openeid.siva.validation.document.report.DiagnosticReport;
2024
import ee.openeid.siva.validation.document.report.Error;
21-
import ee.openeid.siva.validation.document.report.*;
25+
import ee.openeid.siva.validation.document.report.Reports;
26+
import ee.openeid.siva.validation.document.report.Scope;
27+
import ee.openeid.siva.validation.document.report.SimpleReport;
28+
import ee.openeid.siva.validation.document.report.TimeStampTokenValidationData;
29+
import ee.openeid.siva.validation.document.report.ValidationConclusion;
30+
import ee.openeid.siva.validation.document.report.ValidationWarning;
31+
import ee.openeid.siva.validation.document.report.Warning;
2232
import ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils;
2333
import ee.openeid.siva.validation.service.signature.policy.properties.ValidationPolicy;
2434
import ee.openeid.siva.validation.util.CertUtil;
35+
import ee.openeid.validation.service.timestamptoken.util.TimestampNotGrantedValidationUtils;
2536
import eu.europa.esig.dss.diagnostic.CertificateWrapper;
2637
import eu.europa.esig.dss.diagnostic.SignerDataWrapper;
2738
import eu.europa.esig.dss.diagnostic.TimestampWrapper;
@@ -46,7 +57,11 @@
4657
import java.util.stream.Collectors;
4758
import java.util.stream.Stream;
4859

49-
import static ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils.*;
60+
import static ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils.WARNING_MSG_DATAFILE_NOT_COVERED_BY_TS;
61+
import static ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils.createReportPolicy;
62+
import static ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils.emptyWhenNull;
63+
import static ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils.getDateFormatterWithGMTZone;
64+
import static ee.openeid.siva.validation.document.report.builder.ReportBuilderUtils.getValidationTime;
5065

5166
public class TimeStampTokenValidationReportBuilder {
5267

@@ -92,6 +107,7 @@ private ValidationConclusion getValidationConclusion() {
92107
validationConclusion.setSignatureForm(ASICS_SIGNATURE_FORMAT);
93108
validationConclusion.setTimeStampTokens(generateTimeStampTokenData());
94109
validationConclusion.setValidatedDocument(ReportBuilderUtils.createValidatedDocument(isReportSignatureEnabled, validationDocument.getName(), validationDocument.getBytes()));
110+
validationConclusion.setValidationWarnings(addValidationWarningsIfNotGrantedTimestampExists(validationConclusion.getTimeStampTokens()));
95111
return validationConclusion;
96112
}
97113

@@ -134,6 +150,16 @@ private List<TimeStampTokenValidationData> generateTimeStampTokenData() {
134150
return timeStampTokenValidationDataList;
135151
}
136152

153+
private List<ValidationWarning> addValidationWarningsIfNotGrantedTimestampExists(List<TimeStampTokenValidationData> validationDataList) {
154+
return validationDataList.stream()
155+
.map(TimeStampTokenValidationData::getWarning)
156+
.map(TimestampNotGrantedValidationUtils::getValidationWarningIfNotGrantedTimestampExists)
157+
.filter(Objects::nonNull)
158+
.findFirst()
159+
.map(warning -> new ArrayList<>(List.of(warning)))
160+
.orElse(null);
161+
}
162+
137163
private List<Scope> getTimestampScopes(TimestampWrapper ts) {
138164
return dssReports.getDiagnosticData().getTimestampById(ts.getId()).getTimestampScopes()
139165
.stream()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package ee.openeid.validation.service.timestamptoken;
2+
3+
import ee.openeid.siva.validation.configuration.ReportConfigurationProperties;
4+
import ee.openeid.siva.validation.document.ValidationDocument;
5+
import ee.openeid.siva.validation.service.signature.policy.ConstraintLoadingSignaturePolicyService;
6+
import ee.openeid.tsl.TSLLoader;
7+
import ee.openeid.tsl.TSLRefresher;
8+
import ee.openeid.tsl.TSLValidationJobFactory;
9+
import ee.openeid.tsl.configuration.TSLLoaderConfiguration;
10+
import ee.openeid.validation.service.generic.configuration.GenericValidationServiceConfiguration;
11+
import ee.openeid.validation.service.timestamptoken.configuration.TimeStampTokenSignaturePolicyProperties;
12+
import eu.europa.esig.dss.service.http.proxy.ProxyConfig;
13+
import eu.europa.esig.dss.spi.tsl.TrustedListsCertificateSource;
14+
import org.junit.jupiter.api.BeforeEach;
15+
import org.springframework.beans.factory.annotation.Autowired;
16+
import org.springframework.boot.test.context.SpringBootTest;
17+
import org.springframework.context.annotation.Bean;
18+
import org.springframework.context.annotation.Import;
19+
20+
@SpringBootTest(classes = {BaseTimeStampTokenValidationServiceTest.TestConfiguration.class})
21+
abstract class BaseTimeStampTokenValidationServiceTest {
22+
TimeStampTokenValidationService validationService;
23+
final TimeStampTokenSignaturePolicyProperties policyProperties = new TimeStampTokenSignaturePolicyProperties();
24+
25+
@Autowired
26+
TrustedListsCertificateSource trustedListsCertificateSource;
27+
28+
@BeforeEach
29+
void setUp() {
30+
validationService = new TimeStampTokenValidationService();
31+
policyProperties.initPolicySettings();
32+
ConstraintLoadingSignaturePolicyService signaturePolicyService = new ConstraintLoadingSignaturePolicyService(policyProperties);
33+
validationService.setSignaturePolicyService(signaturePolicyService);
34+
validationService.setTrustedListsCertificateSource(trustedListsCertificateSource);
35+
ReportConfigurationProperties reportConfigurationProperties = new ReportConfigurationProperties(true);
36+
validationService.setReportConfigurationProperties(reportConfigurationProperties);
37+
}
38+
39+
static ValidationDocument buildValidationDocument(String testFile) {
40+
return TestUtils.buildValidationDocument(TestUtils.TEST_FILES_LOCATION, testFile);
41+
}
42+
43+
@Import({
44+
TSLLoaderConfiguration.class,
45+
GenericValidationServiceConfiguration.class
46+
})
47+
public static class TestConfiguration {
48+
49+
@Bean
50+
public ProxyConfig proxyConfig() {
51+
return new ProxyConfig();
52+
}
53+
54+
@Bean
55+
public TSLLoader tslLoader() {
56+
return new TSLLoader("testName");
57+
}
58+
59+
@Bean
60+
public TSLRefresher tslRefresher() {
61+
return new TSLRefresher();
62+
}
63+
64+
@Bean
65+
public TSLValidationJobFactory tslValidationJobFactory() {
66+
return new TSLValidationJobFactory();
67+
}
68+
}
69+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2024 Riigi Infosüsteemi Amet
3+
*
4+
* Licensed under the EUPL, Version 1.1 or – as soon they will be approved by
5+
* the European Commission - subsequent versions of the EUPL (the "Licence");
6+
* You may not use this work except in compliance with the Licence.
7+
* You may obtain a copy of the Licence at:
8+
*
9+
* https://joinup.ec.europa.eu/software/page/eupl
10+
*
11+
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
12+
* distributed on an "AS IS" basis,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the Licence for the specific language governing permissions and limitations under the Licence.
15+
*/
16+
17+
package ee.openeid.validation.service.timestamptoken;
18+
19+
import ee.openeid.siva.validation.document.ValidationDocument;
20+
import ee.openeid.siva.validation.document.builder.DummyValidationDocumentBuilder;
21+
22+
public class TestUtils {
23+
24+
public static final String TEST_FILES_LOCATION = "test-files/";
25+
public static final String NOT_GRANTED_WARNING = "Found a timestamp token not related to granted status."
26+
+ " If not yet covered with a fresh timestamp token, this container might become invalid in the future.";
27+
public static final String POE_TIME_MESSAGE_TEXT = "The certificate is not related to a granted status at time-stamp lowest POE time!";
28+
29+
public static ValidationDocument buildValidationDocument(String testFileLocation, String testFile) {
30+
return DummyValidationDocumentBuilder
31+
.aValidationDocument()
32+
.withDocument(testFileLocation + testFile)
33+
.withName(testFile)
34+
.build();
35+
}
36+
}

0 commit comments

Comments
 (0)