Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,22 @@
*/
package org.niis.xroad.proxy.core.messagelog;

import ee.ria.xroad.common.CodedException;
import ee.ria.xroad.common.messagelog.MessageLogProperties;

import jakarta.xml.bind.JAXBException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.xml.security.signature.XMLSignatureException;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampRequest;
import org.bouncycastle.tsp.TimeStampRequestGenerator;
import org.bouncycastle.tsp.TimeStampResponse;
import org.bouncycastle.tsp.TimeStampToken;
import org.niis.xroad.common.core.exception.ErrorCode;
import org.niis.xroad.common.core.exception.XrdRuntimeException;
import org.niis.xroad.globalconf.GlobalConfProvider;
import org.niis.xroad.globalconf.impl.signature.TimestampVerifier;
Expand All @@ -51,11 +51,18 @@
import java.io.InputStream;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static ee.ria.xroad.common.ErrorCodes.X_INTERNAL_ERROR;
import static ee.ria.xroad.common.crypto.Digests.calculateDigest;
import static ee.ria.xroad.common.crypto.Digests.getAlgorithmIdentifier;
import static org.niis.xroad.common.core.exception.ErrorCode.ADDING_SIGNATURE_TO_TS_TOKEN_FAILED;
import static org.niis.xroad.common.core.exception.ErrorCode.CALCULATING_MESSAGE_DIGEST_FAILED;
import static org.niis.xroad.common.core.exception.ErrorCode.TIMESTAMPING_FAILED;
import static org.niis.xroad.common.core.exception.ErrorCode.TIMESTAMP_RESPONSE_VALIDATION_FAILED;
import static org.niis.xroad.common.core.exception.ErrorCode.TIMESTAMP_TOKEN_ENCODING_FAILED;
import static org.niis.xroad.common.core.exception.ErrorCode.TSP_CERTIFICATE_NOT_FOUND;
import static org.niis.xroad.proxy.core.messagelog.TimestamperUtil.addSignerCertificate;
import static org.niis.xroad.proxy.core.messagelog.TimestamperUtil.getTimestampResponse;

Expand Down Expand Up @@ -89,62 +96,115 @@ public static class TsRequest {

protected Timestamper.TimestampResult makeTsRequest(TimeStampRequest tsRequest, List<String> tspUrls) {
log.debug("tspUrls: {}", tspUrls);
Map<String, Exception> errorsByUrl = new HashMap<>();
for (String url : tspUrls) {
try {
log.debug("Sending time-stamp request to {}", url);

TsRequest req = new TsRequest(TimestamperUtil.makeTsRequest(tsRequest, url), url);
TsRequest req = new TsRequest(getTsRequestInputStream(tsRequest, url), url);

TimeStampResponse tsResponse = getTimestampResponse(req.getInputStream());
log.info("tsresponse {}", tsResponse);

verify(tsRequest, tsResponse);

return result(tsResponse, url);
Timestamper.TimestampResult result = result(tsResponse, url);
result.setErrorsByUrl(errorsByUrl);
return result;

} catch (Exception ex) {
log.error("Failed to get time stamp from " + url, ex);
errorsByUrl.put(url, ex);
}
}

// All the URLs failed. Throw exception.
throw XrdRuntimeException.systemInternalError("Failed to get time stamp from any time-stamping providers");
Timestamper.TimestampFailed timestampFailed = new Timestamper.TimestampFailed(logRecords,
XrdRuntimeException.systemException(TIMESTAMPING_FAILED)
.details("Failed to get time stamp from any time-stamping providers")
.build()
);
timestampFailed.setErrorsByUrl(errorsByUrl);
return timestampFailed;
}

private TimeStampRequest createTimestampRequest(byte[] data) throws IOException {
private static InputStream getTsRequestInputStream(TimeStampRequest tsRequest, String url) {
try {
return TimestamperUtil.makeTsRequest(tsRequest, url);
} catch (IOException e) {
throw XrdRuntimeException.systemException(ErrorCode.TIMESTAMP_PROVIDER_CONNECTION_FAILED)
.details("Could not get response from TSP")
.build();
}
}

private TimeStampRequest createTimestampRequest(byte[] data) {
TimeStampRequestGenerator reqgen = new TimeStampRequestGenerator();

var tsaHashAlg = MessageLogProperties.getHashAlg();

log.trace("Creating time-stamp request (algorithm: {})", tsaHashAlg);

byte[] digest = calculateDigest(tsaHashAlg, data);
byte[] digest;
try {
digest = calculateDigest(tsaHashAlg, data);
} catch (IOException e) {
throw XrdRuntimeException.systemException(CALCULATING_MESSAGE_DIGEST_FAILED)
.cause(e)
.build();
}

ASN1ObjectIdentifier algorithm =
getAlgorithmIdentifier(tsaHashAlg).getAlgorithm();
ASN1ObjectIdentifier algorithm = getAlgorithmIdentifier(tsaHashAlg).getAlgorithm();

return reqgen.generate(algorithm, digest);
}

protected byte[] getTimestampDer(TimeStampResponse tsResponse)
throws CertificateEncodingException, IOException, TSPException, CMSException {
X509Certificate signerCertificate =
TimestampVerifier.getSignerCertificate(
tsResponse.getTimeStampToken(),
globalConfProvider.getTspCertificates());
protected byte[] getTimestampDer(TimeStampResponse tsResponse) {
X509Certificate signerCertificate = getSignerCertificate(tsResponse);
TimeStampToken token = getTimeStampToken(tsResponse, signerCertificate);
try {
return token.getEncoded(ASN1Encoding.DER);
} catch (IOException e) {
throw XrdRuntimeException.systemException(TIMESTAMP_TOKEN_ENCODING_FAILED)
.details("Timestamp token der encoding failed")
.cause(e)
.build();
}
}

private X509Certificate getSignerCertificate(TimeStampResponse tsResponse) {
X509Certificate signerCertificate;
signerCertificate = TimestampVerifier.getSignerCertificate(
tsResponse.getTimeStampToken(),
globalConfProvider.getTspCertificates());
if (signerCertificate == null) {
throw new CodedException(X_INTERNAL_ERROR,
"Could not find signer certificate");
throw XrdRuntimeException.systemException(TSP_CERTIFICATE_NOT_FOUND)
.details("Could not find signer certificate")
.build();
}
return signerCertificate;
}

TimeStampToken token =
addSignerCertificate(tsResponse, signerCertificate);
return token.getEncoded();
private static TimeStampToken getTimeStampToken(TimeStampResponse tsResponse, X509Certificate signerCertificate) {
try {
return addSignerCertificate(tsResponse, signerCertificate);
} catch (CMSException | TSPException | CertificateEncodingException | IOException e) {
throw XrdRuntimeException.systemException(ADDING_SIGNATURE_TO_TS_TOKEN_FAILED)
.details("Adding signer certificate to timestamp token failed")
.cause(e)
.build();
}
}

protected void verify(TimeStampRequest request, TimeStampResponse response)
throws TSPException, CertificateEncodingException, IOException, OperatorCreationException, CMSException {
response.validate(request);
protected void verify(TimeStampRequest request, TimeStampResponse response) {
try {
response.validate(request);
} catch (TSPException e) {
throw XrdRuntimeException.systemException(TIMESTAMP_RESPONSE_VALIDATION_FAILED)
.details("Timestamp response validation against the request failed")
.metadataItems(e.getMessage())
.cause(e)
.build();
}

TimeStampToken token = response.getTimeStampToken();
TimestampVerifier.verify(token, globalConfProvider.getTspCertificates());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,18 @@
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampResponse;
import org.niis.xroad.common.core.exception.XrdRuntimeException;
import org.niis.xroad.globalconf.GlobalConfProvider;

import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.util.Arrays;

import static ee.ria.xroad.common.util.EncoderUtils.decodeBase64;
import static ee.ria.xroad.common.util.MessageFileNames.SIGNATURE;
import static ee.ria.xroad.common.util.MessageFileNames.TS_HASH_CHAIN;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.niis.xroad.common.core.exception.ErrorCode.FAILED_TO_BUILD_SIGNATURE_HASH_CHAIN;

class BatchTimestampRequest extends AbstractTimestampRequest {

Expand All @@ -57,10 +60,17 @@ class BatchTimestampRequest extends AbstractTimestampRequest {
}

@Override
byte[] getRequestData() throws JAXBException, IOException {
HashChainBuilder hcBuilder = buildHashChain(signatureHashes);
hashChainResult = hcBuilder.getHashChainResult(TS_HASH_CHAIN);
hashChains = hcBuilder.getHashChains(SIGNATURE);
byte[] getRequestData() {
try {
HashChainBuilder hcBuilder = buildHashChain(signatureHashes);
hashChainResult = hcBuilder.getHashChainResult(TS_HASH_CHAIN);
hashChains = hcBuilder.getHashChains(SIGNATURE);
} catch (IOException | JAXBException e) {
throw XrdRuntimeException.systemException(FAILED_TO_BUILD_SIGNATURE_HASH_CHAIN)
.details("Failed to build hash chain for log records " + Arrays.toString(logRecords))
.cause(e)
.build();
}
return hashChainResult.getBytes(UTF_8);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
package org.niis.xroad.proxy.core.messagelog;

import ee.ria.xroad.common.CodedException;
import ee.ria.xroad.common.DiagnosticsErrorCodes;
import ee.ria.xroad.common.DiagnosticStatus;
import ee.ria.xroad.common.DiagnosticsStatus;
import ee.ria.xroad.common.DiagnosticsUtils;
import ee.ria.xroad.common.message.AttachmentStream;
import ee.ria.xroad.common.messagelog.AbstractLogManager;
Expand All @@ -44,9 +45,9 @@
import jakarta.xml.soap.SOAPException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.input.BoundedInputStream;
import org.niis.xroad.common.core.exception.ErrorCode;
import org.niis.xroad.common.core.exception.XrdRuntimeException;
import org.niis.xroad.globalconf.GlobalConfProvider;
import org.niis.xroad.globalconf.status.DiagnosticsStatus;
import org.niis.xroad.serverconf.ServerConfProvider;

import java.io.IOException;
Expand All @@ -61,7 +62,6 @@
import java.util.concurrent.ScheduledFuture;

import static ee.ria.xroad.common.ErrorCodes.X_LOGGING_FAILED_X;
import static ee.ria.xroad.common.ErrorCodes.X_MLOG_TIMESTAMPER_FAILED;
import static ee.ria.xroad.common.crypto.Digests.calculateDigest;
import static ee.ria.xroad.common.messagelog.MessageLogProperties.getAcceptableTimestampFailurePeriodSeconds;
import static ee.ria.xroad.common.messagelog.MessageLogProperties.getHashAlg;
Expand All @@ -70,6 +70,8 @@
import static ee.ria.xroad.common.util.EncoderUtils.encodeBase64;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.niis.xroad.common.core.exception.ErrorCode.NO_TIMESTAMPING_PROVIDER_FOUND;
import static org.niis.xroad.common.core.exception.ErrorCode.TIMESTAMPING_FAILED;

/**
* Message log manager. Sets up the whole logging system components.
Expand Down Expand Up @@ -194,7 +196,7 @@ private TimestampRecord timestampImmediately(MessageRecord logRecord) {
case Timestamper.TimestampFailed ttf:
Exception e = ttf.getCause();
log.error("Timestamping failed", e);
putStatusMapFailures(e);
putStatusMapFailures(ttf);
throw XrdRuntimeException.systemException(e);
default:
throw XrdRuntimeException.systemInternalError("Unexpected result from Timestamper: " + result.getClass());
Expand Down Expand Up @@ -312,25 +314,19 @@ static TimestampRecord saveTimestampRecord(Timestamper.TimestampSucceeded messag
* @param url url of timestamper which stamped successfully
*/
static void putStatusMapSuccess(String url) {
statusMap.put(url, new DiagnosticsStatus(DiagnosticsErrorCodes.RETURN_SUCCESS, TimeUtils.offsetDateTimeNow()));
statusMap.put(url, new DiagnosticsStatus(DiagnosticStatus.OK, TimeUtils.offsetDateTimeNow()));
}

/**
* Put failure state into statusMap (used for diagnostics).
* Timestamping ({@link AbstractTimestampRequest} attempts to use all TSAs, and failure means that
* all were tried and failed, so all TSAs will be marked with failed status
*
* @param e exception which is used to determine diagnostics error code
*/
void putStatusMapFailures(Exception e) {
int errorCode = DiagnosticsUtils.getErrorCode(e);
for (String tspUrl : serverConfProvider.getTspUrl()) {
statusMap.put(tspUrl,
new DiagnosticsStatus(errorCode, TimeUtils.offsetDateTimeNow(), tspUrl));
}
void putStatusMapFailures(Timestamper.TimestampFailed timestampFailedResult) {
timestampFailedResult.getErrorsByUrl().forEach((tspUrl, ex) -> {
ErrorCode errorCode = DiagnosticsUtils.getErrorCode(ex);
DiagnosticsStatus diagnosticsStatus =
new DiagnosticsStatus(DiagnosticStatus.ERROR, TimeUtils.offsetDateTimeNow(), tspUrl, errorCode);
diagnosticsStatus.setErrorCodeMetadata(DiagnosticsUtils.getErrorCodeMetadata(ex));
statusMap.put(tspUrl, diagnosticsStatus);
});
}


private static TimestampRecord createTimestampRecord(Timestamper.TimestampSucceeded message) {
TimestampRecord timestampRecord = new TimestampRecord();
timestampRecord.setTime(new Date().getTime());
Expand All @@ -353,7 +349,7 @@ void setTimestampingStatus(SetTimestampingStatusMessage statusMessage) {
}

/**
* Only externally use this method from tests. Otherwise send message to this actor.
* Only externally use this method from tests. Otherwise, send message to this actor.
*/
void setTimestampSucceeded() {
if (timestampFailed != null) {
Expand All @@ -371,8 +367,9 @@ void setTimestampFailed(Instant atTime) {

private void verifyCanLogMessage(boolean shouldTimestampImmediately) {
if (serverConfProvider.getTspUrl().isEmpty()) {
throw new CodedException(X_MLOG_TIMESTAMPER_FAILED,
"Cannot time-stamp messages: no timestamping services configured");
throw XrdRuntimeException.systemException(NO_TIMESTAMPING_PROVIDER_FOUND)
.details("Cannot time-stamp messages: no timestamping services configured")
.build();
}

if (!shouldTimestampImmediately) {
Expand All @@ -384,7 +381,9 @@ private void verifyCanLogMessage(boolean shouldTimestampImmediately) {

if (isTimestampFailed()) {
if (TimeUtils.now().minusSeconds(period).isAfter(timestampFailed)) {
throw new CodedException(X_MLOG_TIMESTAMPER_FAILED, "Cannot time-stamp messages");
throw XrdRuntimeException.systemException(TIMESTAMPING_FAILED)
.details("Cannot time-stamp messages")
.build();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Session;
import org.hibernate.query.MutationQuery;
import org.niis.xroad.common.core.exception.ErrorCode;
import org.niis.xroad.common.core.exception.XrdRuntimeException;

import java.sql.Connection;
Expand Down Expand Up @@ -182,12 +183,19 @@ static void updateMessageRecordSignature(MessageRecord messageRecord, String old
*/
static void saveTimestampRecord(TimestampRecord timestampRecord, Long[]
timestampedLogRecords, String[] hashChains) {
doInTransaction(session -> {
timestampRecord.setId(getNextRecordId(session));
save(session, timestampRecord);
setMessageRecordsTimestamped(session, timestampedLogRecords, timestampRecord, hashChains);
return null;
});
try {
doInTransaction(session -> {
timestampRecord.setId(getNextRecordId(session));
save(session, timestampRecord);
setMessageRecordsTimestamped(session, timestampedLogRecords, timestampRecord, hashChains);
return null;
});
} catch (Exception e) {
throw XrdRuntimeException.systemException(ErrorCode.TIMESTAMP_RECORD_SAVE_FAILURE)
.details("Failed to save timestamp record to database")
.cause(e)
.build();
}
}

/**
Expand Down Expand Up @@ -265,7 +273,7 @@ private static void setMessageRecordsTimestamped(Long[] messageRecords, Timestam
}

private static AbstractLogRecordEntity getLogRecord(Session session, Long number) {
return session.get(AbstractLogRecordEntity.class, number);
return session.find(AbstractLogRecordEntity.class, number);
}

private static MessageRecordEntity getMessageRecord(Session session, String queryId, ClientId clientId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ public static DatabaseCtx get() {
* @param <T> the type of result.
* @param callback the callback.
* @return the result.
* @throws Exception if an error occurs.
*/
public static <T> T doInTransaction(TransactionCallback<T> callback) {
return CTX.doInTransaction(callback);
Expand Down
Loading