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 ivParams)) {
137 newIV = ivParams.getIV();
138 myParams = ivParams.getParameters();
139 }
140 if (myParams instanceof KeyParameter keyParam) {
141 newKey = keyParam.getKey();
142 }
143
144
145 theIndex = 0;
146 setKey(newKey);
147 setIV(newIV);
148 makeKeyStream();
149
150
151 theResetState = copy();
152 }
153
154 @Override
155 public String getAlgorithmName() {
156 return "Rabbit";
157 }
158
159 @Override
160 public int processBytes(final byte[] in,
161 final int inOff,
162 final int len,
163 final byte[] out,
164 final int outOff) {
165
166 if (theResetState == null) {
167 throw new IllegalStateException(getAlgorithmName() + " not initialised");
168 }
169 if ((inOff + len) > in.length) {
170 throw new DataLengthException("input buffer too short");
171 }
172 if ((outOff + len) > out.length) {
173 throw new OutputLengthException("output buffer too short");
174 }
175
176
177 for (int i = 0; i < len; i++) {
178 out[i + outOff] = returnByte(in[i + inOff]);
179 }
180 return len;
181 }
182
183 @Override
184 public void reset() {
185 if (theResetState != null) {
186 reset(theResetState);
187 }
188 }
189
190 @Override
191 public byte returnByte(final byte in) {
192 final byte out = (byte) (keyStream[theIndex] ^ in);
193 theIndex = (theIndex + 1) % STREAM_LEN;
194
195 if (theIndex == 0) {
196 makeKeyStream();
197 }
198 return out;
199 }
200
201
202
203
204
205
206
207
208 private static int decode32le(final byte[] buf, final int off) {
209 return (buf[off] & 0xFF)
210 | ((buf[off + 1] & 0xFF) << 8)
211 | ((buf[off + 2] & 0xFF) << 16)
212 | ((buf[off + 3] & 0xFF) << 24);
213 }
214
215
216
217
218
219
220
221
222 private static void encode32le(final int val, final byte[] buf, final int off) {
223 buf[off] = (byte) val;
224 buf[off + 1] = (byte) (val >> 8);
225 buf[off + 2] = (byte) (val >> 16);
226 buf[off + 3] = (byte) (val >> 24);
227 }
228
229
230
231 static int rabbitGFunc(final int x) {
232
233 final int a = x & 0xFFFF;
234 final int b = x >>> 16;
235
236
237 final int h = ((((a * a) >>> 17) + (a * b)) >>> 15) + b * b;
238 final int l = x * x;
239
240
241 return (h ^ l);
242 }
243
244
245 static void rabbitNextState(final GordianRabbitContext pContext) {
246
247 final int[] cOld = Arrays.copyOf(pContext.c, NUM_VARS);
248 final int[] g = new int[NUM_VARS];
249
250
251 pContext.c[0] = (pContext.c[0] + 0x4D34D34D + pContext.carry);
252 pContext.c[1] = (pContext.c[1] + 0xD34D34D3 + (Integer.compareUnsigned(pContext.c[0], cOld[0]) < 0 ? 1 : 0));
253 pContext.c[2] = (pContext.c[2] + 0x34D34D34 + (Integer.compareUnsigned(pContext.c[1], cOld[1]) < 0 ? 1 : 0));
254 pContext.c[3] = (pContext.c[3] + 0x4D34D34D + (Integer.compareUnsigned(pContext.c[2], cOld[2]) < 0 ? 1 : 0));
255 pContext.c[4] = (pContext.c[4] + 0xD34D34D3 + (Integer.compareUnsigned(pContext.c[3], cOld[3]) < 0 ? 1 : 0));
256 pContext.c[5] = (pContext.c[5] + 0x34D34D34 + (Integer.compareUnsigned(pContext.c[4], cOld[4]) < 0 ? 1 : 0));
257 pContext.c[6] = (pContext.c[6] + 0x4D34D34D + (Integer.compareUnsigned(pContext.c[5], cOld[5]) < 0 ? 1 : 0));
258 pContext.c[7] = (pContext.c[7] + 0xD34D34D3 + (Integer.compareUnsigned(pContext.c[6], cOld[6]) < 0 ? 1 : 0));
259 pContext.carry = Integer.compareUnsigned(pContext.c[7], cOld[7]) < 0 ? 1 : 0;
260
261
262 for (int i = 0; i < 8; i++) {
263 g[i] = rabbitGFunc(pContext.x[i] + pContext.c[i]);
264 }
265
266
267 pContext.x[0] = (g[0] + rotl32(g[7], 16) + rotl32(g[6], 16));
268 pContext.x[1] = (g[1] + rotl32(g[0], 8) + g[7]);
269 pContext.x[2] = (g[2] + rotl32(g[1], 16) + rotl32(g[0], 16));
270 pContext.x[3] = (g[3] + rotl32(g[2], 8) + g[1]);
271 pContext.x[4] = (g[4] + rotl32(g[3], 16) + rotl32(g[2], 16));
272 pContext.x[5] = (g[5] + rotl32(g[4], 8) + g[3]);
273 pContext.x[6] = (g[6] + rotl32(g[5], 16) + rotl32(g[4], 16));
274 pContext.x[7] = (g[7] + rotl32(g[6], 8) + g[5]);
275 }
276
277 static int rotl32(final int v, final int n) {
278 return (((v) << (n)) | ((v) >>> (32 - (n))));
279 }
280
281
282 void setKey(final byte[] key) {
283
284 if (key == null || key.length != 16) {
285 throw new IllegalArgumentException("A key of 16 bytes is needed");
286 }
287
288
289 final int k0 = decode32le(key, 0);
290 final int k1 = decode32le(key, 4);
291 final int k2 = decode32le(key, 8);
292 final int k3 = decode32le(key, 12);
293
294
295 master.x[0] = k0;
296 master.x[2] = k1;
297 master.x[4] = k2;
298 master.x[6] = k3;
299 master.x[1] = (k3 << 16) | (k2 >>> 16);
300 master.x[3] = (k0 << 16) | (k3 >>> 16);
301 master.x[5] = (k1 << 16) | (k0 >>> 16);
302 master.x[7] = (k2 << 16) | (k1 >>> 16);
303
304
305 master.c[0] = rotl32(k2, 16);
306 master.c[2] = rotl32(k3, 16);
307 master.c[4] = rotl32(k0, 16);
308 master.c[6] = rotl32(k1, 16);
309 master.c[1] = (k0 & 0xFFFF0000) | (k1 & 0xFFFF);
310 master.c[3] = (k1 & 0xFFFF0000) | (k2 & 0xFFFF);
311 master.c[5] = (k2 & 0xFFFF0000) | (k3 & 0xFFFF);
312 master.c[7] = (k3 & 0xFFFF0000) | (k0 & 0xFFFF);
313
314
315 master.carry = 0;
316
317
318 for (int i = 0; i < 4; i++) {
319 rabbitNextState(master);
320 }
321
322
323 for (int i = 0; i < NUM_VARS; i++) {
324 master.c[i] ^= master.x[(i + 4) & 0x7];
325 }
326
327
328 for (int i = 0; i < NUM_VARS; i++) {
329 work.x[i] = master.x[i];
330 work.c[i] = master.c[i];
331 }
332 work.carry = master.carry;
333 }
334
335
336
337
338 void setIV(final byte[] iv) {
339
340 if (iv == null || iv.length != 8) {
341 throw new IllegalArgumentException("An IV of 8 bytes is needed");
342 }
343
344
345 final int i0 = decode32le(iv, 0);
346 final int i2 = decode32le(iv, 4);
347 final int i1 = (i0 >>> 16) | (i2 & 0xFFFF0000);
348 final int i3 = (i2 << 16) | (i0 & 0x0000FFFF);
349
350
351 work.c[0] = master.c[0] ^ i0;
352 work.c[1] = master.c[1] ^ i1;
353 work.c[2] = master.c[2] ^ i2;
354 work.c[3] = master.c[3] ^ i3;
355 work.c[4] = master.c[4] ^ i0;
356 work.c[5] = master.c[5] ^ i1;
357 work.c[6] = master.c[6] ^ i2;
358 work.c[7] = master.c[7] ^ i3;
359
360
361 for (int i = 0; i < NUM_VARS; i++) {
362 work.x[i] = master.x[i];
363 }
364 work.carry = master.carry;
365
366
367 for (int i = 0; i < 4; i++) {
368 rabbitNextState(work);
369 }
370 }
371
372
373 void makeKeyStream() {
374
375 for (int i = 0; i < STREAM_LEN; i += 16) {
376
377 rabbitNextState(work);
378
379
380 encode32le(work.x[0] ^ (work.x[5] >>> 16) ^ (work.x[3] << 16), keyStream, i + 0);
381 encode32le(work.x[2] ^ (work.x[7] >>> 16) ^ (work.x[5] << 16), keyStream, i + 4);
382 encode32le(work.x[4] ^ (work.x[1] >>> 16) ^ (work.x[7] << 16), keyStream, i + 8);
383 encode32le(work.x[6] ^ (work.x[3] >>> 16) ^ (work.x[1] << 16), keyStream, i + 12);
384 }
385 }
386
387 @Override
388 public GordianRabbitEngine copy() {
389 return new GordianRabbitEngine(this);
390 }
391
392 @Override
393 public void reset(final Memoable pState) {
394 final GordianRabbitEngine e = (GordianRabbitEngine) pState;
395 work.copyFrom(e.work);
396 master.copyFrom(e.master);
397 System.arraycopy(e.keyStream, 0, keyStream, 0, STREAM_LEN);
398 theIndex = e.theIndex;
399 }
400 }