1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.impl.ext.digests;
18
19 import org.bouncycastle.crypto.ExtendedDigest;
20 import org.bouncycastle.util.Memoable;
21
22 import java.util.Arrays;
23
24
25
26
27 @SuppressWarnings("checkstyle:MagicNumber")
28 public class GordianCubeHashDigest
29 implements ExtendedDigest, Memoable {
30
31
32
33 private static final int STATELEN = 32;
34
35
36
37
38 private static final int SWAPLEN = STATELEN / 2;
39
40
41
42
43 private final int[] theState = new int[STATELEN];
44
45
46
47
48 private final int[] theSwap = new int[SWAPLEN];
49
50
51
52
53 private final int[] theInitState;
54
55
56
57
58 private final int theBlockLen;
59
60
61
62
63 private final int theHashLen;
64
65
66
67
68 private final int theNumRounds;
69
70
71
72
73 private final int theNumFinalRounds;
74
75
76
77
78 private final byte[] theInputBuffer;
79
80
81
82
83 private int theByteIndex;
84
85
86
87
88
89
90 public GordianCubeHashDigest(final int pHashLen) {
91 this(pHashLen, 32, 16, 16, 32);
92 }
93
94
95
96
97
98
99 private GordianCubeHashDigest(final GordianCubeHashDigest pSource) {
100
101 theHashLen = pSource.theHashLen;
102 theBlockLen = pSource.theBlockLen;
103 theNumRounds = pSource.theNumRounds;
104 theNumFinalRounds = pSource.theNumFinalRounds;
105
106
107 theInputBuffer = new byte[theBlockLen];
108
109
110 System.arraycopy(pSource.theState, 0, theState, 0, theState.length);
111 System.arraycopy(pSource.theInputBuffer, 0, theInputBuffer, 0, theBlockLen);
112 theByteIndex = pSource.theByteIndex;
113 theInitState = Arrays.copyOf(pSource.theInitState, theState.length);
114 }
115
116
117
118
119
120
121
122
123
124
125 private GordianCubeHashDigest(final int pHashLen,
126 final int pBlockLen,
127 final int pNumRounds,
128 final int pInitRounds,
129 final int pFinalRounds) {
130
131 theHashLen = pHashLen / Byte.SIZE;
132 theBlockLen = pBlockLen;
133 theNumRounds = pNumRounds;
134 theNumFinalRounds = pFinalRounds;
135
136
137 theInputBuffer = new byte[pBlockLen];
138
139
140 theState[0] = theHashLen;
141 theState[1] = pBlockLen;
142 theState[2] = pNumRounds;
143 performRounds(pInitRounds);
144
145
146 theInitState = Arrays.copyOf(theState, theState.length);
147 }
148
149 @Override
150 public String getAlgorithmName() {
151 return "CubeHash-" + theHashLen * Byte.SIZE;
152 }
153
154 @Override
155 public int getDigestSize() {
156 return theHashLen;
157 }
158
159 @Override
160 public int getByteLength() {
161 return theHashLen;
162 }
163
164 @Override
165 public void update(final byte pByte) {
166 theInputBuffer[theByteIndex++] = pByte;
167 if (theByteIndex == theBlockLen) {
168 processBlock();
169 theByteIndex = 0;
170 }
171 }
172
173 @Override
174 public void update(final byte[] pData, final int pOffset, final int pLength) {
175 for (int i = 0; i < pLength; i++) {
176 update(pData[pOffset + i]);
177 }
178 }
179
180 @Override
181 public int doFinal(final byte[] pHash, final int pOffset) {
182 finaliseHash();
183 outputHash(pHash, pOffset);
184 return getDigestSize();
185 }
186
187 @Override
188 public void reset() {
189 System.arraycopy(theInitState, 0, theState, 0, theState.length);
190 theByteIndex = 0;
191 }
192
193 @Override
194 public GordianCubeHashDigest copy() {
195 return new GordianCubeHashDigest(this);
196 }
197
198 @Override
199 public void reset(final Memoable pState) {
200 final GordianCubeHashDigest d = (GordianCubeHashDigest) pState;
201
202
203 System.arraycopy(d.theState, 0, theState, 0, theState.length);
204 System.arraycopy(d.theInputBuffer, 0, theInputBuffer, 0, theBlockLen);
205 theByteIndex = d.theByteIndex;
206 }
207
208
209
210
211
212
213
214
215 private static int decode32le(final byte[] buf,
216 final int off) {
217 return (buf[off] & 0xFF)
218 | ((buf[off + 1] & 0xFF) << 8)
219 | ((buf[off + 2] & 0xFF) << 16)
220 | ((buf[off + 3] & 0xFF) << 24);
221 }
222
223
224
225
226
227
228
229
230 private static void encode32le(final int val,
231 final byte[] buf,
232 final int off) {
233 buf[off] = (byte) val;
234 buf[off + 1] = (byte) (val >> 8);
235 buf[off + 2] = (byte) (val >> 16);
236 buf[off + 3] = (byte) (val >> 24);
237 }
238
239
240
241
242 private void processBlock() {
243
244 for (int i = 0, j = 0; j < theBlockLen; i++, j += Integer.BYTES) {
245 theState[i] ^= decode32le(theInputBuffer, j);
246 }
247
248
249 performRounds(theNumRounds);
250 }
251
252
253
254
255 private void finaliseHash() {
256
257 theInputBuffer[theByteIndex++] = (byte) 0x80;
258
259
260 while (theByteIndex < theBlockLen) {
261 theInputBuffer[theByteIndex++] = 0;
262 }
263
264
265 processBlock();
266
267
268 theState[STATELEN - 1] ^= 1;
269
270
271 performRounds(theNumFinalRounds);
272 }
273
274
275
276
277
278
279
280 private void outputHash(final byte[] pOutput,
281 final int pOffSet) {
282
283 for (int i = 0, j = 0; j < theHashLen; i++, j += Integer.BYTES) {
284 encode32le(theState[i], pOutput, j + pOffSet);
285 }
286
287
288 reset();
289 }
290
291
292
293
294
295
296 private void performRounds(final int pNumRounds) {
297
298 for (int i = 0; i < pNumRounds; i++) {
299 performRound();
300 }
301 }
302
303
304
305
306 private void performRound() {
307
308 for (int i = 0; i < SWAPLEN; i++) {
309 theState[i + SWAPLEN] += theState[i];
310 }
311
312
313 for (int i = 0; i < SWAPLEN; i++) {
314 theSwap[i] = theState[i] << 7 | theState[i] >>> 25;
315 }
316
317
318 for (int i = 0; i < SWAPLEN; i++) {
319 theState[i] = theSwap[i ^ 8];
320 }
321
322
323 for (int i = 0; i < SWAPLEN; i++) {
324 theState[i] ^= theState[i + SWAPLEN];
325 }
326
327
328 for (int i = 0; i < SWAPLEN; i++) {
329 theSwap[i] = theState[i + SWAPLEN];
330 }
331 for (int i = 0; i < SWAPLEN; i++) {
332 theState[i + SWAPLEN] = theSwap[i ^ 2];
333 }
334
335
336 for (int i = 0; i < SWAPLEN; i++) {
337 theState[i + SWAPLEN] += theState[i];
338 }
339
340
341 for (int i = 0; i < SWAPLEN; i++) {
342 theSwap[i] = theState[i] << 11 | theState[i] >>> 21;
343 }
344
345
346 for (int i = 0; i < SWAPLEN; i++) {
347 theState[i] = theSwap[i ^ 4];
348 }
349
350
351 for (int i = 0; i < SWAPLEN; i++) {
352 theState[i] ^= theState[i + SWAPLEN];
353 }
354
355
356 for (int i = 0; i < SWAPLEN; i++) {
357 theSwap[i] = theState[i + SWAPLEN];
358 }
359 for (int i = 0; i < SWAPLEN; i++) {
360 theState[i + SWAPLEN] = theSwap[i ^ 1];
361 }
362 }
363 }