1 package org.bouncycastle.tsp.cms;
2 
3 import java.io.IOException;
4 import java.io.OutputStream;
5 
6 import org.bouncycastle.asn1.ASN1Encoding;
7 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
8 import org.bouncycastle.asn1.cms.AttributeTable;
9 import org.bouncycastle.asn1.cms.ContentInfo;
10 import org.bouncycastle.asn1.cms.Evidence;
11 import org.bouncycastle.asn1.cms.TimeStampAndCRL;
12 import org.bouncycastle.asn1.cms.TimeStampedData;
13 import org.bouncycastle.asn1.cms.TimeStampedDataParser;
14 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
15 import org.bouncycastle.cms.CMSException;
16 import org.bouncycastle.operator.DigestCalculator;
17 import org.bouncycastle.operator.DigestCalculatorProvider;
18 import org.bouncycastle.operator.OperatorCreationException;
19 import org.bouncycastle.tsp.TSPException;
20 import org.bouncycastle.tsp.TimeStampToken;
21 import org.bouncycastle.tsp.TimeStampTokenInfo;
22 import org.bouncycastle.util.Arrays;
23 
24 class TimeStampDataUtil
25 {
26     private final TimeStampAndCRL[] timeStamps;
27 
28     private final MetaDataUtil      metaDataUtil;
29 
TimeStampDataUtil(TimeStampedData timeStampedData)30     TimeStampDataUtil(TimeStampedData timeStampedData)
31     {
32         this.metaDataUtil = new MetaDataUtil(timeStampedData.getMetaData());
33 
34         Evidence evidence = timeStampedData.getTemporalEvidence();
35         this.timeStamps = evidence.getTstEvidence().toTimeStampAndCRLArray();
36     }
37 
TimeStampDataUtil(TimeStampedDataParser timeStampedData)38     TimeStampDataUtil(TimeStampedDataParser timeStampedData)
39         throws IOException
40     {
41         this.metaDataUtil = new MetaDataUtil(timeStampedData.getMetaData());
42 
43         Evidence evidence = timeStampedData.getTemporalEvidence();
44         this.timeStamps = evidence.getTstEvidence().toTimeStampAndCRLArray();
45     }
46 
getTimeStampToken(TimeStampAndCRL timeStampAndCRL)47     TimeStampToken getTimeStampToken(TimeStampAndCRL timeStampAndCRL)
48         throws CMSException
49     {
50         ContentInfo timeStampToken = timeStampAndCRL.getTimeStampToken();
51 
52         try
53         {
54             TimeStampToken token = new TimeStampToken(timeStampToken);
55             return token;
56         }
57         catch (IOException e)
58         {
59             throw new CMSException("unable to parse token data: " + e.getMessage(), e);
60         }
61         catch (TSPException e)
62         {
63             if (e.getCause() instanceof CMSException)
64             {
65                 throw (CMSException)e.getCause();
66             }
67 
68             throw new CMSException("token data invalid: " + e.getMessage(), e);
69         }
70         catch (IllegalArgumentException e)
71         {
72             throw new CMSException("token data invalid: " + e.getMessage(), e);
73         }
74     }
75 
initialiseMessageImprintDigestCalculator(DigestCalculator calculator)76     void initialiseMessageImprintDigestCalculator(DigestCalculator calculator)
77         throws CMSException
78     {
79         metaDataUtil.initialiseMessageImprintDigestCalculator(calculator);
80     }
81 
getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider)82     DigestCalculator getMessageImprintDigestCalculator(DigestCalculatorProvider calculatorProvider)
83         throws OperatorCreationException
84     {
85         TimeStampToken token;
86 
87         try
88         {
89             token = this.getTimeStampToken(timeStamps[0]);
90 
91             TimeStampTokenInfo info = token.getTimeStampInfo();
92             ASN1ObjectIdentifier algOID = info.getMessageImprintAlgOID();
93 
94             DigestCalculator calc = calculatorProvider.get(new AlgorithmIdentifier(algOID));
95 
96             initialiseMessageImprintDigestCalculator(calc);
97 
98             return calc;
99         }
100         catch (CMSException e)
101         {
102             throw new OperatorCreationException("unable to extract algorithm ID: " + e.getMessage(), e);
103         }
104     }
105 
getTimeStampTokens()106     TimeStampToken[] getTimeStampTokens()
107         throws CMSException
108     {
109         TimeStampToken[] tokens = new TimeStampToken[timeStamps.length];
110         for (int i = 0; i < timeStamps.length; i++)
111         {
112             tokens[i] = this.getTimeStampToken(timeStamps[i]);
113         }
114 
115         return tokens;
116     }
117 
getTimeStamps()118     TimeStampAndCRL[] getTimeStamps()
119     {
120         return timeStamps;
121     }
122 
calculateNextHash(DigestCalculator calculator)123     byte[] calculateNextHash(DigestCalculator calculator)
124         throws CMSException
125     {
126         TimeStampAndCRL tspToken = timeStamps[timeStamps.length - 1];
127 
128         OutputStream out = calculator.getOutputStream();
129 
130         try
131         {
132             out.write(tspToken.getEncoded(ASN1Encoding.DER));
133 
134             out.close();
135 
136             return calculator.getDigest();
137         }
138         catch (IOException e)
139         {
140             throw new CMSException("exception calculating hash: " + e.getMessage(), e);
141         }
142     }
143 
144     /**
145      * Validate the digests present in the TimeStampTokens contained in the CMSTimeStampedData.
146      */
validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest)147     void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest)
148         throws ImprintDigestInvalidException, CMSException
149     {
150         byte[] currentDigest = dataDigest;
151 
152         for (int i = 0; i < timeStamps.length; i++)
153         {
154             try
155             {
156                 TimeStampToken token = this.getTimeStampToken(timeStamps[i]);
157                 if (i > 0)
158                 {
159                     TimeStampTokenInfo info = token.getTimeStampInfo();
160                     DigestCalculator calculator = calculatorProvider.get(info.getHashAlgorithm());
161 
162                     calculator.getOutputStream().write(timeStamps[i - 1].getEncoded(ASN1Encoding.DER));
163 
164                     currentDigest = calculator.getDigest();
165                 }
166 
167                 this.compareDigest(token, currentDigest);
168             }
169             catch (IOException e)
170             {
171                 throw new CMSException("exception calculating hash: " + e.getMessage(), e);
172             }
173             catch (OperatorCreationException e)
174             {
175                 throw new CMSException("cannot create digest: " + e.getMessage(), e);
176             }
177         }
178     }
179 
validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken)180     void validate(DigestCalculatorProvider calculatorProvider, byte[] dataDigest, TimeStampToken timeStampToken)
181         throws ImprintDigestInvalidException, CMSException
182     {
183         byte[] currentDigest = dataDigest;
184         byte[] encToken;
185 
186         try
187         {
188             encToken = timeStampToken.getEncoded();
189         }
190         catch (IOException e)
191         {
192             throw new CMSException("exception encoding timeStampToken: " + e.getMessage(), e);
193         }
194 
195         for (int i = 0; i < timeStamps.length; i++)
196         {
197             try
198             {
199                 TimeStampToken token = this.getTimeStampToken(timeStamps[i]);
200                 if (i > 0)
201                 {
202                     TimeStampTokenInfo info = token.getTimeStampInfo();
203                     DigestCalculator calculator = calculatorProvider.get(info.getHashAlgorithm());
204 
205                     calculator.getOutputStream().write(timeStamps[i - 1].getEncoded(ASN1Encoding.DER));
206 
207                     currentDigest = calculator.getDigest();
208                 }
209 
210                 this.compareDigest(token, currentDigest);
211 
212                 if (Arrays.areEqual(token.getEncoded(), encToken))
213                 {
214                     return;
215                 }
216             }
217             catch (IOException e)
218             {
219                 throw new CMSException("exception calculating hash: " + e.getMessage(), e);
220             }
221             catch (OperatorCreationException e)
222             {
223                 throw new CMSException("cannot create digest: " + e.getMessage(), e);
224             }
225         }
226 
227         throw new ImprintDigestInvalidException("passed in token not associated with timestamps present", timeStampToken);
228     }
229 
compareDigest(TimeStampToken timeStampToken, byte[] digest)230     private void compareDigest(TimeStampToken timeStampToken, byte[] digest)
231         throws ImprintDigestInvalidException
232     {
233         TimeStampTokenInfo info = timeStampToken.getTimeStampInfo();
234         byte[] tsrMessageDigest = info.getMessageImprintDigest();
235 
236         if (!Arrays.areEqual(digest, tsrMessageDigest))
237         {
238             throw new ImprintDigestInvalidException("hash calculated is different from MessageImprintDigest found in TimeStampToken", timeStampToken);
239         }
240     }
241 
getFileName()242     String getFileName()
243     {
244         return metaDataUtil.getFileName();
245     }
246 
getMediaType()247     String getMediaType()
248     {
249         return metaDataUtil.getMediaType();
250     }
251 
getOtherMetaData()252     AttributeTable getOtherMetaData()
253     {
254         return new AttributeTable(metaDataUtil.getOtherMetaData());
255     }
256 }
257