Skip to content

Commit bebf38b

Browse files
committed
further work on RFC 4998
1 parent 7cbe9f1 commit bebf38b

11 files changed

Lines changed: 285 additions & 8 deletions

core/src/main/java/org/bouncycastle/asn1/cms/ArchiveTimeStamp.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStamp.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import org.bouncycastle.asn1.ASN1EncodableVector;
44
import org.bouncycastle.asn1.ASN1Object;
@@ -7,8 +7,11 @@
77
import org.bouncycastle.asn1.ASN1TaggedObject;
88
import org.bouncycastle.asn1.DERSequence;
99
import org.bouncycastle.asn1.DERTaggedObject;
10+
import org.bouncycastle.asn1.cms.Attributes;
11+
import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
12+
import org.bouncycastle.asn1.cms.ContentInfo;
13+
import org.bouncycastle.asn1.cms.SignedData;
1014
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
11-
import org.bouncycastle.asn1.tsp.TSTInfo;
1215
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
1316

1417
/**
@@ -161,6 +164,11 @@ public AlgorithmIdentifier getDigestAlgorithm()
161164

162165
public PartialHashtree[] getReducedHashTree()
163166
{
167+
if (reducedHashTree == null)
168+
{
169+
return null;
170+
}
171+
164172
PartialHashtree[] rv = new PartialHashtree[reducedHashTree.size()];
165173

166174
for (int i = 0; i != rv.length; i++)

core/src/main/java/org/bouncycastle/asn1/cms/ArchiveTimeStampChain.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampChain.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import java.util.Enumeration;
44

core/src/main/java/org/bouncycastle/asn1/cms/ArchiveTimeStampSequence.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/ArchiveTimeStampSequence.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import java.util.Enumeration;
44

core/src/main/java/org/bouncycastle/asn1/cms/CryptoInfos.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/CryptoInfos.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import org.bouncycastle.asn1.ASN1Object;
44
import org.bouncycastle.asn1.ASN1Primitive;
55
import org.bouncycastle.asn1.ASN1Sequence;
66
import org.bouncycastle.asn1.ASN1TaggedObject;
77
import org.bouncycastle.asn1.DERSequence;
8+
import org.bouncycastle.asn1.cms.Attribute;
89

910
/**
1011
* Implementation of the CryptoInfos element defined in RFC 4998:

core/src/main/java/org/bouncycastle/asn1/cms/EncryptionInfo.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/EncryptionInfo.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import org.bouncycastle.asn1.ASN1Encodable;
44
import org.bouncycastle.asn1.ASN1EncodableVector;

core/src/main/java/org/bouncycastle/asn1/cms/EvidenceRecord.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/EvidenceRecord.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import java.util.Enumeration;
44

core/src/main/java/org/bouncycastle/asn1/cms/PartialHashtree.java renamed to core/src/main/java/org/bouncycastle/asn1/tsp/PartialHashtree.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.bouncycastle.asn1.cms;
1+
package org.bouncycastle.asn1.tsp;
22

33
import org.bouncycastle.asn1.ASN1EncodableVector;
44
import org.bouncycastle.asn1.ASN1Object;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.bouncycastle.tsp;
2+
3+
/**
4+
* Exception thrown if an Archive TimeStamp according to RFC4998 fails to containsHashValue.
5+
* <p>
6+
* {@see <a href="https://tools.ietf.org/html/rfc4998">RFC4998</a>}
7+
*/
8+
9+
public class ArchiveTimeStampValidationException
10+
extends Exception
11+
{
12+
13+
public ArchiveTimeStampValidationException(final String message)
14+
{
15+
super(message);
16+
}
17+
}
18+
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package org.bouncycastle.tsp;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
import java.util.ArrayList;
6+
import java.util.Comparator;
7+
import java.util.Iterator;
8+
import java.util.List;
9+
import java.util.TreeSet;
10+
11+
import org.bouncycastle.operator.DigestCalculator;
12+
import org.bouncycastle.util.Arrays;
13+
14+
/**
15+
* Representation of data groups according to the description provided in RFC4998.
16+
* <p>
17+
* Such data groups represent a set of one or more data objects (e.g. electronic documents) for
18+
* which an Evidence Record should be generated.
19+
*/
20+
public class DataGroup
21+
{
22+
private List<byte[]> dataObjects;
23+
private byte[] groupHash;
24+
private TreeSet<byte[]> hashes;
25+
26+
public DataGroup(final List<byte[]> dataObjects)
27+
{
28+
this.dataObjects = dataObjects;
29+
}
30+
31+
public DataGroup(final byte[] dataObject)
32+
{
33+
this.dataObjects = new ArrayList();
34+
dataObjects.add(dataObject);
35+
}
36+
37+
/**
38+
* Generates hashes for all the data objects included in the data group.
39+
*
40+
* @param digestCalculator the {@link DigestCalculator} to use for computing the hashes
41+
* @return the set of hashes, in ascending order
42+
*/
43+
public TreeSet<byte[]> getHashes(DigestCalculator digestCalculator)
44+
{
45+
return getHashes(digestCalculator, null);
46+
}
47+
48+
/**
49+
* Generates hashes for all the data objects included in the data group.
50+
*
51+
* @param digestCalculator the {@link DigestCalculator} to use for computing the hashes
52+
* @param ha a preceding hash, can be null.
53+
* @return the set of hashes, in ascending order
54+
*/
55+
private TreeSet<byte[]> getHashes(
56+
final DigestCalculator digestCalculator,
57+
final byte[] ha)
58+
{
59+
if (hashes == null)
60+
{
61+
hashes = new TreeSet(new ByteArrayComparator());
62+
63+
for (int i = 0; i != dataObjects.size(); i++)
64+
{
65+
byte[] dataObject = dataObjects.get(i);
66+
if (ha != null)
67+
{
68+
hashes.add(calcDigest(digestCalculator, Arrays.concatenate(calcDigest(digestCalculator, dataObject), ha)));
69+
}
70+
else
71+
{
72+
hashes.add(calcDigest(digestCalculator, dataObject));
73+
}
74+
}
75+
}
76+
77+
return hashes;
78+
}
79+
80+
/**
81+
* Generates a hash for the whole DataGroup.
82+
*
83+
* @param digestCalculator the {@link DigestCalculator} to use for computing the hash
84+
* @return a hash that is representative of the whole DataGroup
85+
*/
86+
public byte[] getHash(DigestCalculator digestCalculator)
87+
{
88+
if (groupHash == null)
89+
{
90+
TreeSet<byte[]> hashes = getHashes(digestCalculator);
91+
92+
if (hashes.size() > 1)
93+
{
94+
byte[] concat = new byte[0];
95+
Iterator<byte[]> iterator = hashes.iterator();
96+
97+
while (iterator.hasNext())
98+
{
99+
concat = Arrays.concatenate(concat, iterator.next());
100+
}
101+
102+
groupHash = calcDigest(digestCalculator, concat);
103+
}
104+
else
105+
{
106+
groupHash = hashes.first();
107+
}
108+
}
109+
110+
return groupHash;
111+
}
112+
113+
/**
114+
* Comparator for byte arrays
115+
*/
116+
private class ByteArrayComparator
117+
implements Comparator<byte[]>
118+
{
119+
public int compare(final byte[] left, final byte[] right)
120+
{
121+
int len = left.length < right.length ? left.length : right.length;
122+
123+
for (int i = 0; i != len; i++)
124+
{
125+
int a = (left[i] & 0xff);
126+
int b = (right[i] & 0xff);
127+
128+
if (a != b)
129+
{
130+
return a - b;
131+
}
132+
}
133+
134+
return left.length - right.length;
135+
}
136+
}
137+
138+
static byte[] calcDigest(DigestCalculator digCalc, byte[] data)
139+
{
140+
try
141+
{
142+
OutputStream dOut = digCalc.getOutputStream();
143+
144+
dOut.write(data);
145+
146+
dOut.close();
147+
148+
return digCalc.getDigest();
149+
}
150+
catch (IOException e)
151+
{
152+
throw new IllegalStateException("digest calculator failure: " + e.getMessage());
153+
}
154+
}
155+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package org.bouncycastle.tsp;
2+
3+
import java.io.IOException;
4+
import java.io.OutputStream;
5+
6+
import org.bouncycastle.asn1.tsp.PartialHashtree;
7+
import org.bouncycastle.operator.DigestCalculator;
8+
import org.bouncycastle.util.Arrays;
9+
10+
public class PartialHashTreeProcessor
11+
{
12+
private final byte[][] values;
13+
14+
public PartialHashTreeProcessor(PartialHashtree tree)
15+
{
16+
this.values = tree.getValues();
17+
}
18+
19+
/**
20+
* Compute a hash over the whole partialHashTree:
21+
* - Concatenate all the hashes contained in the partial hash tree;
22+
* - Generate a hash over the concatenated hashes, using a provided {@link DigestCalculator}.
23+
*
24+
* @param digestCalculator the {@link DigestCalculator} to use in order to generate the hash
25+
* @return a hash value that is representative of the whole partial hash tree.
26+
*/
27+
public byte[] getHash(DigestCalculator digestCalculator)
28+
{
29+
if (values.length == 1)
30+
{
31+
return values[0];
32+
}
33+
34+
try
35+
{
36+
OutputStream dOut = digestCalculator.getOutputStream();
37+
38+
for (int i = 1; i != values.length; i++)
39+
{
40+
dOut.write(values[i]);
41+
}
42+
43+
return digestCalculator.getDigest();
44+
}
45+
catch (IOException e)
46+
{
47+
throw new IllegalStateException("calculator failed: " + e.getMessage());
48+
}
49+
}
50+
51+
/**
52+
* Checks whether a PartialHashtree (RFC4998) contains a given hash.
53+
*
54+
* @param hash the hash to check
55+
* @throws PartialHashTreeVerificationException if the hash is not present in the
56+
* PartialHashtree
57+
*/
58+
public void verifyContainsHash(final byte[] hash)
59+
throws PartialHashTreeVerificationException
60+
{
61+
if (!containsHash(hash))
62+
{
63+
throw new PartialHashTreeVerificationException("calculated hash is not present in " + "partial hash tree");
64+
}
65+
}
66+
67+
/**
68+
* Checks whether a PartialHashtree (RFC4998) contains a given hash.
69+
*
70+
* @param hash the hash to check
71+
* @return true if the hash is present within the PartialHashtree's set of values, false
72+
* otherwise.
73+
*/
74+
public boolean containsHash(final byte[] hash)
75+
{
76+
for (int i = 1; i != values.length; i++)
77+
{
78+
if (Arrays.areEqual(hash, values[i]))
79+
{
80+
return true;
81+
}
82+
}
83+
84+
return false;
85+
}
86+
}

0 commit comments

Comments
 (0)