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)) {
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         /* Initialise engine and mark as initialised */
147         theIndex = 0;
148         setKey(newKey);
149         setIV(newIV);
150         makeKeyStream();
151 
152         /* Save reset state */
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         /* Check for errors */
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         /* Loop through the input bytes */
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      * Decode a 32-bit value from a buffer (little-endian).
205      *
206      * @param buf the input buffer
207      * @param off the input offset
208      * @return the decoded value
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      * Encode a 32-bit value into a buffer (little-endian).
219      *
220      * @param val the value to encode
221      * @param buf the output buffer
222      * @param off the output offset
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     /* Square a 32-bit unsigned integer to obtain the 64-bit result and return */
232     /* the upper 32 bits XOR the lower 32 bits */
233     static int rabbitGFunc(final int x) {
234         /* Construct high and low argument for squaring */
235         final int a = x & 0xFFFF;
236         final int b = x >>> 16;
237 
238         /* Calculate high and low result of squaring */
239         final int h = ((((a * a) >>> 17) + (a * b)) >>> 15) + b * b;
240         final int l = x * x;
241 
242         /* Return high XOR low */
243         return (h ^ l);
244     }
245 
246     /* Calculate the next internal state */
247     static void rabbitNextState(final GordianRabbitContext pContext) {
248         /* Save old counter values */
249         final int[] cOld = Arrays.copyOf(pContext.c, NUM_VARS);
250         final int[] g = new int[NUM_VARS];
251 
252         /* Calculate new counter values */
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         /* Calculate the g-values */
264         for (int i = 0; i < 8; i++) {
265             g[i] = rabbitGFunc(pContext.x[i] + pContext.c[i]);
266         }
267 
268         /* Calculate new state values */
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     /* Key setup */
284     void setKey(final byte[] key) {
285         /* Check lengths */
286         if (key == null || key.length != 16) {
287             throw new IllegalArgumentException("A key of 16 bytes is needed");
288         }
289 
290         /* Generate four subkeys */
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         /* Generate initial state variables */
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         /* Generate initial counter values */
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         /* Clear carry bit */
317         master.carry = 0;
318 
319         /* Iterate the system four times */
320         for (int i = 0; i < 4; i++) {
321             rabbitNextState(master);
322         }
323 
324         /* Modify the counters */
325         for (int i = 0; i < NUM_VARS; i++) {
326             master.c[i] ^= master.x[(i + 4) & 0x7];
327         }
328 
329         /* Copy master instance to work instance */
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     /* IV setup */
340     void setIV(final byte[] iv) {
341         /* Check lengths */
342         if (iv == null || iv.length != 8) {
343             throw new IllegalArgumentException("An IV of 8 bytes is needed");
344         }
345 
346         /* Generate four subvectors */
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         /* Modify counter values */
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         /* Copy state variables */
363         for (int i = 0; i < NUM_VARS; i++) {
364             work.x[i] = master.x[i];
365         }
366         work.carry = master.carry;
367 
368         /* Iterate the system four times */
369         for (int i = 0; i < 4; i++) {
370             rabbitNextState(work);
371         }
372     }
373 
374     /* Generate keystream */
375     void makeKeyStream() {
376         /* Generate five blocks */
377         for (int i = 0; i < STREAM_LEN; i += 16) {
378             /* Iterate the system */
379             rabbitNextState(work);
380 
381             /* Generate 16 bytes of pseudo-random data */
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 }