View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
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   * StreamCipher implementation based on the SosemanukFast java implementation.
31   * <p>
32   * Based on http://www.ecrypt.eu.org/stream/e2-rabbit.html.
33   */
34  @SuppressWarnings("checkstyle:MagicNumber")
35  public class GordianRabbitEngine
36          implements StreamCipher, Memoable {
37      /**
38       * Number of variables.
39       */
40      private static final int NUM_VARS = 8;
41  
42      /**
43       * Advanced stream length.
44       */
45      private static final int STREAM_LEN = 80;
46  
47      /**
48       * context.
49       */
50      static class GordianRabbitContext {
51          /**
52           * X state.
53           */
54          private int[] x = new int[NUM_VARS];
55  
56          /**
57           * c stat.
58           */
59          private int[] c = new int[NUM_VARS];
60  
61          /**
62           * carry state.
63           */
64          private int carry;
65  
66          /**
67           * Copy from.
68           *
69           * @param pSource the source context
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       * Work context.
80       */
81      private GordianRabbitContext work = new GordianRabbitContext();
82  
83      /**
84       * master context.
85       */
86      private GordianRabbitContext master = new GordianRabbitContext();
87  
88      /**
89       * index of next byte in keyStream.
90       */
91      private int theIndex;
92  
93      /**
94       * Advanced stream.
95       */
96      private final byte[] keyStream = new byte[STREAM_LEN];
97  
98      /**
99       * Reset state.
100      */
101     private GordianRabbitEngine theResetState;
102 
103     /**
104      * Constructor.
105      */
106     public GordianRabbitEngine() {
107     }
108 
109     /**
110      * Constructor.
111      *
112      * @param pSource the source engine
113      */
114     private GordianRabbitEngine(final GordianRabbitEngine pSource) {
115         reset(pSource);
116     }
117 
118     /**
119      * initialise a Rabbit cipher.
120      *
121      * @param forEncryption whether or not we are for encryption.
122      * @param params        the parameters required to set up the cipher.
123      * @throws IllegalArgumentException if the params argument is inappropriate.
124      */
125     public void init(final boolean forEncryption,
126                      final CipherParameters params) {
127         /*
128          * encryption and decryption is completely symmetrical, so the 'forEncryption' is
129          * irrelevant. (Like 90% of stream ciphers)
130          */
131 
132         /* Determine parameters */
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         /* Initialise engine and mark as initialised */
145         theIndex = 0;
146         setKey(newKey);
147         setIV(newIV);
148         makeKeyStream();
149 
150         /* Save reset state */
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         /* Check for errors */
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         /* Loop through the input bytes */
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      * Decode a 32-bit value from a buffer (little-endian).
203      *
204      * @param buf the input buffer
205      * @param off the input offset
206      * @return the decoded value
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      * Encode a 32-bit value into a buffer (little-endian).
217      *
218      * @param val the value to encode
219      * @param buf the output buffer
220      * @param off the output offset
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     /* Square a 32-bit unsigned integer to obtain the 64-bit result and return */
230     /* the upper 32 bits XOR the lower 32 bits */
231     static int rabbitGFunc(final int x) {
232         /* Construct high and low argument for squaring */
233         final int a = x & 0xFFFF;
234         final int b = x >>> 16;
235 
236         /* Calculate high and low result of squaring */
237         final int h = ((((a * a) >>> 17) + (a * b)) >>> 15) + b * b;
238         final int l = x * x;
239 
240         /* Return high XOR low */
241         return (h ^ l);
242     }
243 
244     /* Calculate the next internal state */
245     static void rabbitNextState(final GordianRabbitContext pContext) {
246         /* Save old counter values */
247         final int[] cOld = Arrays.copyOf(pContext.c, NUM_VARS);
248         final int[] g = new int[NUM_VARS];
249 
250         /* Calculate new counter values */
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         /* Calculate the g-values */
262         for (int i = 0; i < 8; i++) {
263             g[i] = rabbitGFunc(pContext.x[i] + pContext.c[i]);
264         }
265 
266         /* Calculate new state values */
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     /* Key setup */
282     void setKey(final byte[] key) {
283         /* Check lengths */
284         if (key == null || key.length != 16) {
285             throw new IllegalArgumentException("A key of 16 bytes is needed");
286         }
287 
288         /* Generate four subkeys */
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         /* Generate initial state variables */
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         /* Generate initial counter values */
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         /* Clear carry bit */
315         master.carry = 0;
316 
317         /* Iterate the system four times */
318         for (int i = 0; i < 4; i++) {
319             rabbitNextState(master);
320         }
321 
322         /* Modify the counters */
323         for (int i = 0; i < NUM_VARS; i++) {
324             master.c[i] ^= master.x[(i + 4) & 0x7];
325         }
326 
327         /* Copy master instance to work instance */
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     /* IV setup */
338     void setIV(final byte[] iv) {
339         /* Check lengths */
340         if (iv == null || iv.length != 8) {
341             throw new IllegalArgumentException("An IV of 8 bytes is needed");
342         }
343 
344         /* Generate four subvectors */
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         /* Modify counter values */
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         /* Copy state variables */
361         for (int i = 0; i < NUM_VARS; i++) {
362             work.x[i] = master.x[i];
363         }
364         work.carry = master.carry;
365 
366         /* Iterate the system four times */
367         for (int i = 0; i < 4; i++) {
368             rabbitNextState(work);
369         }
370     }
371 
372     /* Generate keystream */
373     void makeKeyStream() {
374         /* Generate five blocks */
375         for (int i = 0; i < STREAM_LEN; i += 16) {
376             /* Iterate the system */
377             rabbitNextState(work);
378 
379             /* Generate 16 bytes of pseudo-random data */
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 }