|
| 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 | +} |
0 commit comments