package org.bouncycastle.tsp.cms;
import java.io.IOException;
import java.io.OutputStream;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.cms.AttributeTable;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.asn1.cms.Evidence;
import org.bouncycastle.asn1.cms.TimeStampAndCRL;
import org.bouncycastle.asn1.cms.TimeStampedData;
import org.bouncycastle.asn1.cms.TimeStampedDataParser;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.operator.DigestCalculator;
import org.bouncycastle.operator.DigestCalculatorProvider;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.tsp.TSPException;
import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.tsp.TimeStampTokenInfo;
import org.bouncycastle.util.Arrays;
class TimeStampDataUtil
{
private final TimeStampAndCRL[] timeStamps;
private final MetaDataUtil metaDataUtil;
TimeStampDataUtil(TimeStampedData timeStampedData)
{
this.metaDataUtil = new MetaDataUtil(timeStampedData.getMetaData());
Evidence evidence = timeStampedData.getTemporalEvidence();
this.timeStamps = evidence.getTstEvidence().toTimeStampAndCRLArray();
}
TimeStampDataUtil(TimeStampedDataParser timeStampedData)
throws IOException
{
this.metaDataUtil = new MetaDataUtil(timeStampedData.getMetaData());
Evidence evidence = timeStampedData.getTemporalEvidence();
this.timeStamps = evidence.getTstEvidence().toTimeStampAndCRLArray();
}
TimeStampToken getTimeStampToken(TimeStampAndCRL timeStampAndCRL)
throws CMSException
{
ContentInfo timeStampToken = timeStampAndCRL.getTimeStampToken();
try
{
TimeStampToken token = new TimeStampToken(timeStampToken);
return token;
}
catch (IOException e)
{
throw new CMSException("unable to parse token data: " + e.getMessage(), e);
}
catch (TSPException e)
{
if (e.getCause() instanceof CMSException)
{
throw (CMSException)e.getCause();
}
throw new CMSException("token data invalid: " + e.getMessage(), e);
}
catch (IllegalArgumentException e)
{
throw new CMSException("token data invalid: " + e.getMessage(), e);
}
}
void initialiseMessageImprintDigestCalculator(DigestCalculator calculator)
throws CMSException
{
metaDataUtil.initialiseMessageImprintDigestCalculator(calculator);
}
DigestCalculator getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider)
throws OperatorCreationException
{
TimeStampToken token;
try
{
token = this.getTimeStampToken(timeStamps[0]);
TimeStampTokenInfo info = token.getTimeStampInfo();
ASN1ObjectIdentifier algOID = info.getMessageImprintAlgOID();
DigestCalculator calc = calculatorProvider.get(new AlgorithmIdentifier(algOID));
initialiseMessageImprintDigestCalculator(calc);
return calc;
}
catch (CMSException e)
{
throw new OperatorCreationException("unable to extract algorithm ID: " + e.getMessage(), e);
}
}
TimeStampToken[] getTimeStampTokens()
throws CMSException
{
TimeStampToken[] tokens = new TimeStampToken[timeStamps.length];
for (int i = 0; i < timeStamps.length; i++)
{
tokens[i] = this.getTimeStampToken(timeStamps[i]);
}
return tokens;
}
TimeStampAndCRL[] getTimeStamps()
{
return timeStamps;
}
byte[] calculateNextHash(DigestCalculator calculator)
throws CMSException
{
TimeStampAndCRL tspToken = timeStamps[timeStamps.length - 1];
OutputStream out = calculator.getOutputStream();
try
{
out.write(tspToken.getEncoded(ASN1Encoding.DER));
out.close();
return calculator.getDigest();
}
catch (IOException e)
{
throw new CMSException("exception calculating hash: " + e.getMessage(), e);
}
}
void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest)
throws ImprintDigestInvalidException, CMSException
{
byte[] currentDigest = dataDigest;
for (int i = 0; i < timeStamps.length; i++)
{
try
{
TimeStampToken token = this.getTimeStampToken(timeStamps[i]);
if (i > 0)
{
TimeStampTokenInfo info = token.getTimeStampInfo();
DigestCalculator calculator = calculatorProvider.get(info.getHashAlgorithm());
calculator.getOutputStream().write(timeStamps[i - 1].getEncoded(ASN1Encoding.DER));
currentDigest = calculator.getDigest();
}
this.compareDigest(token, currentDigest);
}
catch (IOException e)
{
throw new CMSException("exception calculating hash: " + e.getMessage(), e);
}
catch (OperatorCreationException e)
{
throw new CMSException("cannot create digest: " + e.getMessage(), e);
}
}
}
void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken)
throws ImprintDigestInvalidException, CMSException
{
byte[] currentDigest = dataDigest;
byte[] encToken;
try
{
encToken = timeStampToken.getEncoded();
}
catch (IOException e)
{
throw new CMSException("exception encoding timeStampToken: " + e.getMessage(), e);
}
for (int i = 0; i < timeStamps.length; i++)
{
try
{
TimeStampToken token = this.getTimeStampToken(timeStamps[i]);
if (i > 0)
{
TimeStampTokenInfo info = token.getTimeStampInfo();
DigestCalculator calculator = calculatorProvider.get(info.getHashAlgorithm());
calculator.getOutputStream().write(timeStamps[i - 1].getEncoded(ASN1Encoding.DER));
currentDigest = calculator.getDigest();
}
this.compareDigest(token, currentDigest);
if (Arrays.areEqual(token.getEncoded(), encToken))
{
return;
}
}
catch (IOException e)
{
throw new CMSException("exception calculating hash: " + e.getMessage(), e);
}
catch (OperatorCreationException e)
{
throw new CMSException("cannot create digest: " + e.getMessage(), e);
}
}
throw new ImprintDigestInvalidException("passed in token not associated with timestamps present", timeStampToken);
}
private void compareDigest(TimeStampToken timeStampToken, byte[] digest)
throws ImprintDigestInvalidException
{
TimeStampTokenInfo info = timeStampToken.getTimeStampInfo();
byte[] tsrMessageDigest = info.getMessageImprintDigest();
if (!Arrays.areEqual(digest, tsrMessageDigest))
{
throw new ImprintDigestInvalidException("hash calculated is different from MessageImprintDigest found in TimeStampToken", timeStampToken);
}
}
String getFileName()
{
return metaDataUtil.getFileName();
}
String getMediaType()
{
return metaDataUtil.getMediaType();
}
AttributeTable getOtherMetaData()
{
return new AttributeTable(metaDataUtil.getOtherMetaData());
}
}