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.engines;
18
19 import org.bouncycastle.crypto.CipherParameters;
20 import org.bouncycastle.crypto.DataLengthException;
21 import org.bouncycastle.crypto.OutputLengthException;
22 import org.bouncycastle.crypto.StreamCipher;
23 import org.bouncycastle.crypto.params.KeyParameter;
24 import org.bouncycastle.crypto.params.ParametersWithIV;
25 import org.bouncycastle.util.Memoable;
26
27 import java.util.Arrays;
28
29
30
31
32
33
34 @SuppressWarnings("checkstyle:MagicNumber")
35 public class GordianRabbitEngine
36 implements StreamCipher, Memoable {
37
38
39
40 private static final int NUM_VARS = 8;
41
42
43
44
45 private static final int STREAM_LEN = 80;
46
47
48
49
50 static class GordianRabbitContext {
51
52
53
54 private int[] x = new int[NUM_VARS];
55
56
57
58
59 private int[] c = new int[NUM_VARS];
60
61
62
63
64 private int carry;
65
66
67
68
69
70
71 void copyFrom(final GordianRabbitContext pSource) {
72 System.arraycopy(pSource.x, 0, x, 0, NUM_VARS);
73 System.arraycopy(pSource.c, 0, c, 0, NUM_VARS);
74 carry = pSource.carry;
75 }
76 }
77
78
79
80
81 private GordianRabbitContext work = new GordianRabbitContext();
82
83
84
85
86 private GordianRabbitContext master = new GordianRabbitContext();
87
88
89
90
91 private int theIndex;
92
93
94
95
96 private final byte[] keyStream = new byte[STREAM_LEN];
97
98
99
100
101 private GordianRabbitEngine theResetState;
102
103
104
105
106 public GordianRabbitEngine() {
107 }
108
109
110
111
112
113
114 private GordianRabbitEngine(final GordianRabbitEngine pSource) {
115 reset(pSource);
116 }
117
118
119
120
121
122
123
124
125 public void init(final boolean forEncryption,
126 final CipherParameters params) {
127
128
129
130
131
132
133 CipherParameters myParams = params;
134 byte[] newKey = null;
135 byte[] newIV = null;
136 if ((myParams instanceof ParametersWithIV)) {
137 final ParametersWithIV ivParams = (ParametersWithIV) myParams;
138 newIV = ivParams.getIV();
139 myParams = ivParams.getParameters();
140 }
141 if (myParams instanceof KeyParameter) {
142 final KeyParameter keyParam = (KeyParameter) myParams;
143 newKey = keyParam.getKey();
144 }
145
146
147 theIndex = 0;
148 setKey(newKey);
149 setIV(newIV);
150 makeKeyStream();
151
152
153 theResetState = copy();
154 }
155
156 @Override
157 public String getAlgorithmName() {
158 return "Rabbit";
159 }
160
161 @Override
162 public int processBytes(final byte[] in,
163 final int inOff,
164 final int len,
165 final byte[] out,
166 final int outOff) {
167
168 if (theResetState == null) {
169 throw new IllegalStateException(getAlgorithmName() + " not initialised");
170 }
171 if ((inOff + len) > in.length) {
172 throw new DataLengthException("input buffer too short");
173 }
174 if ((outOff + len) > out.length) {
175 throw new OutputLengthException("output buffer too short");
176 }
177
178
179 for (int i = 0; i < len; i++) {
180 out[i + outOff] = returnByte(in[i + inOff]);
181 }
182 return len;
183 }
184
185 @Override
186 public void reset() {
187 if (theResetState != null) {
188 reset(theResetState);
189 }
190 }
191
192 @Override
193 public byte returnByte(final byte in) {
194 final byte out = (byte) (keyStream[theIndex] ^ in);
195 theIndex = (theIndex + 1) % STREAM_LEN;
196
197 if (theIndex == 0) {
198 makeKeyStream();
199 }
200 return out;
201 }
202
203
204
205
206
207
208
209
210 private static int decode32le(final byte[] buf, final int off) {
211 return (buf[off] & 0xFF)
212 | ((buf[off + 1] & 0xFF) << 8)
213 | ((buf[off + 2] & 0xFF) << 16)
214 | ((buf[off + 3] & 0xFF) << 24);
215 }
216
217
218
219
220
221
222
223
224 private static void encode32le(final int val, final byte[] buf, final int off) {
225 buf[off] = (byte) val;
226 buf[off + 1] = (byte) (val >> 8);
227 buf[off + 2] = (byte) (val >> 16);
228 buf[off + 3] = (byte) (val >> 24);
229 }
230
231
232
233 static int rabbitGFunc(final int x) {
234
235 final int a = x & 0xFFFF;
236 final int b = x >>> 16;
237
238
239 final int h = ((((a * a) >>> 17) + (a * b)) >>> 15) + b * b;
240 final int l = x * x;
241
242
243 return (h ^ l);
244 }
245
246
247 static void rabbitNextState(final GordianRabbitContext pContext) {
248
249 final int[] cOld = Arrays.copyOf(pContext.c, NUM_VARS);
250 final int[] g = new int[NUM_VARS];
251
252
253 pContext.c[0] = (pContext.c[0] + 0x4D34D34D + pContext.carry);
254 pContext.c[1] = (pContext.c[1] + 0xD34D34D3 + (Integer.compareUnsigned(pContext.c[0], cOld[0]) < 0 ? 1 : 0));
255 pContext.c[2] = (pContext.c[2] + 0x34D34D34 + (Integer.compareUnsigned(pContext.c[1], cOld[1]) < 0 ? 1 : 0));
256 pContext.c[3] = (pContext.c[3] + 0x4D34D34D + (Integer.compareUnsigned(pContext.c[2], cOld[2]) < 0 ? 1 : 0));
257 pContext.c[4] = (pContext.c[4] + 0xD34D34D3 + (Integer.compareUnsigned(pContext.c[3], cOld[3]) < 0 ? 1 : 0));
258 pContext.c[5] = (pContext.c[5] + 0x34D34D34 + (Integer.compareUnsigned(pContext.c[4], cOld[4]) < 0 ? 1 : 0));
259 pContext.c[6] = (pContext.c[6] + 0x4D34D34D + (Integer.compareUnsigned(pContext.c[5], cOld[5]) < 0 ? 1 : 0));
260 pContext.c[7] = (pContext.c[7] + 0xD34D34D3 + (Integer.compareUnsigned(pContext.c[6], cOld[6]) < 0 ? 1 : 0));
261 pContext.carry = Integer.compareUnsigned(pContext.c[7], cOld[7]) < 0 ? 1 : 0;
262
263
264 for (int i = 0; i < 8; i++) {
265 g[i] = rabbitGFunc(pContext.x[i] + pContext.c[i]);
266 }
267
268
269 pContext.x[0] = (g[0] + rotl32(g[7], 16) + rotl32(g[6], 16));
270 pContext.x[1] = (g[1] + rotl32(g[0], 8) + g[7]);
271 pContext.x[2] = (g[2] + rotl32(g[1], 16) + rotl32(g[0], 16));
272 pContext.x[3] = (g[3] + rotl32(g[2], 8) + g[1]);
273 pContext.x[4] = (g[4] + rotl32(g[3], 16) + rotl32(g[2], 16));
274 pContext.x[5] = (g[5] + rotl32(g[4], 8) + g[3]);
275 pContext.x[6] = (g[6] + rotl32(g[5], 16) + rotl32(g[4], 16));
276 pContext.x[7] = (g[7] + rotl32(g[6], 8) + g[5]);
277 }
278
279 static int rotl32(final int v, final int n) {
280 return (((v) << (n)) | ((v) >>> (32 - (n))));
281 }
282
283
284 void setKey(final byte[] key) {
285
286 if (key == null || key.length != 16) {
287 throw new IllegalArgumentException("A key of 16 bytes is needed");
288 }
289
290
291 final int k0 = decode32le(key, 0);
292 final int k1 = decode32le(key, 4);
293 final int k2 = decode32le(key, 8);
294 final int k3 = decode32le(key, 12);
295
296
297 master.x[0] = k0;
298 master.x[2] = k1;
299 master.x[4] = k2;
300 master.x[6] = k3;
301 master.x[1] = (k3 << 16) | (k2 >>> 16);
302 master.x[3] = (k0 << 16) | (k3 >>> 16);
303 master.x[5] = (k1 << 16) | (k0 >>> 16);
304 master.x[7] = (k2 << 16) | (k1 >>> 16);
305
306
307 master.c[0] = rotl32(k2, 16);
308 master.c[2] = rotl32(k3, 16);
309 master.c[4] = rotl32(k0, 16);
310 master.c[6] = rotl32(k1, 16);
311 master.c[1] = (k0 & 0xFFFF0000) | (k1 & 0xFFFF);
312 master.c[3] = (k1 & 0xFFFF0000) | (k2 & 0xFFFF);
313 master.c[5] = (k2 & 0xFFFF0000) | (k3 & 0xFFFF);
314 master.c[7] = (k3 & 0xFFFF0000) | (k0 & 0xFFFF);
315
316
317 master.carry = 0;
318
319
320 for (int i = 0; i < 4; i++) {
321 rabbitNextState(master);
322 }
323
324
325 for (int i = 0; i < NUM_VARS; i++) {
326 master.c[i] ^= master.x[(i + 4) & 0x7];
327 }
328
329
330 for (int i = 0; i < NUM_VARS; i++) {
331 work.x[i] = master.x[i];
332 work.c[i] = master.c[i];
333 }
334 work.carry = master.carry;
335 }
336
337
338
339
340 void setIV(final byte[] iv) {
341
342 if (iv == null || iv.length != 8) {
343 throw new IllegalArgumentException("An IV of 8 bytes is needed");
344 }
345
346
347 final int i0 = decode32le(iv, 0);
348 final int i2 = decode32le(iv, 4);
349 final int i1 = (i0 >>> 16) | (i2 & 0xFFFF0000);
350 final int i3 = (i2 << 16) | (i0 & 0x0000FFFF);
351
352
353 work.c[0] = master.c[0] ^ i0;
354 work.c[1] = master.c[1] ^ i1;
355 work.c[2] = master.c[2] ^ i2;
356 work.c[3] = master.c[3] ^ i3;
357 work.c[4] = master.c[4] ^ i0;
358 work.c[5] = master.c[5] ^ i1;
359 work.c[6] = master.c[6] ^ i2;
360 work.c[7] = master.c[7] ^ i3;
361
362
363 for (int i = 0; i < NUM_VARS; i++) {
364 work.x[i] = master.x[i];
365 }
366 work.carry = master.carry;
367
368
369 for (int i = 0; i < 4; i++) {
370 rabbitNextState(work);
371 }
372 }
373
374
375 void makeKeyStream() {
376
377 for (int i = 0; i < STREAM_LEN; i += 16) {
378
379 rabbitNextState(work);
380
381
382 encode32le(work.x[0] ^ (work.x[5] >>> 16) ^ (work.x[3] << 16), keyStream, i + 0);
383 encode32le(work.x[2] ^ (work.x[7] >>> 16) ^ (work.x[5] << 16), keyStream, i + 4);
384 encode32le(work.x[4] ^ (work.x[1] >>> 16) ^ (work.x[7] << 16), keyStream, i + 8);
385 encode32le(work.x[6] ^ (work.x[3] >>> 16) ^ (work.x[1] << 16), keyStream, i + 12);
386 }
387 }
388
389 @Override
390 public GordianRabbitEngine copy() {
391 return new GordianRabbitEngine(this);
392 }
393
394 @Override
395 public void reset(final Memoable pState) {
396 final GordianRabbitEngine e = (GordianRabbitEngine) pState;
397 work.copyFrom(e.work);
398 master.copyFrom(e.master);
399 System.arraycopy(e.keyStream, 0, keyStream, 0, STREAM_LEN);
400 theIndex = e.theIndex;
401 }
402 }