1 package org.bouncycastle.crypto.engines;
2 
3 import org.bouncycastle.crypto.CipherParameters;
4 import org.bouncycastle.crypto.DataLengthException;
5 import org.bouncycastle.crypto.OutputLengthException;
6 import org.bouncycastle.crypto.StreamCipher;
7 import org.bouncycastle.crypto.params.KeyParameter;
8 import org.bouncycastle.crypto.params.ParametersWithIV;
9 
10 /**
11  * HC-256 is a software-efficient stream cipher created by Hongjun Wu. It
12  * generates keystream from a 256-bit secret key and a 256-bit initialization
13  * vector.
14  * <p>
15  * https://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
16  * </p><p>
17  * Its brother, HC-128, is a third phase candidate in the eStream contest.
18  * The algorithm is patent-free. No attacks are known as of today (April 2007).
19  * See
20  *
21  * https://www.ecrypt.eu.org/stream/hcp3.html
22  * </p>
23  */
24 public class HC256Engine
25     implements StreamCipher
26 {
27     private int[] p = new int[1024];
28     private int[] q = new int[1024];
29     private int cnt = 0;
30 
step()31     private int step()
32     {
33         int j = cnt & 0x3FF;
34         int ret;
35         if (cnt < 1024)
36         {
37             int x = p[(j - 3 & 0x3FF)];
38             int y = p[(j - 1023 & 0x3FF)];
39             p[j] += p[(j - 10 & 0x3FF)]
40                 + (rotateRight(x, 10) ^ rotateRight(y, 23))
41                 + q[((x ^ y) & 0x3FF)];
42 
43             x = p[(j - 12 & 0x3FF)];
44             ret = (q[x & 0xFF] + q[((x >> 8) & 0xFF) + 256]
45                 + q[((x >> 16) & 0xFF) + 512] + q[((x >> 24) & 0xFF) + 768])
46                 ^ p[j];
47         }
48         else
49         {
50             int x = q[(j - 3 & 0x3FF)];
51             int y = q[(j - 1023 & 0x3FF)];
52             q[j] += q[(j - 10 & 0x3FF)]
53                 + (rotateRight(x, 10) ^ rotateRight(y, 23))
54                 + p[((x ^ y) & 0x3FF)];
55 
56             x = q[(j - 12 & 0x3FF)];
57             ret = (p[x & 0xFF] + p[((x >> 8) & 0xFF) + 256]
58                 + p[((x >> 16) & 0xFF) + 512] + p[((x >> 24) & 0xFF) + 768])
59                 ^ q[j];
60         }
61         cnt = cnt + 1 & 0x7FF;
62         return ret;
63     }
64 
65     private byte[] key, iv;
66     private boolean initialised;
67 
init()68     private void init()
69     {
70         if (key.length != 32 && key.length != 16)
71         {
72             throw new IllegalArgumentException(
73                 "The key must be 128/256 bits long");
74         }
75 
76         if (iv.length < 16)
77         {
78             throw new IllegalArgumentException(
79                 "The IV must be at least 128 bits long");
80         }
81 
82         if (key.length != 32)
83         {
84             byte[] k = new byte[32];
85 
86             System.arraycopy(key, 0, k, 0, key.length);
87             System.arraycopy(key, 0, k, 16, key.length);
88 
89             key = k;
90         }
91 
92         if (iv.length < 32)
93         {
94             byte[] newIV = new byte[32];
95 
96             System.arraycopy(iv, 0, newIV, 0, iv.length);
97             System.arraycopy(iv, 0, newIV, iv.length, newIV.length - iv.length);
98 
99             iv = newIV;
100         }
101 
102         idx = 0;
103         cnt = 0;
104 
105         int[] w = new int[2560];
106 
107         for (int i = 0; i < 32; i++)
108         {
109             w[i >> 2] |= (key[i] & 0xff) << (8 * (i & 0x3));
110         }
111 
112         for (int i = 0; i < 32; i++)
113         {
114             w[(i >> 2) + 8] |= (iv[i] & 0xff) << (8 * (i & 0x3));
115         }
116 
117         for (int i = 16; i < 2560; i++)
118         {
119             int x = w[i - 2];
120             int y = w[i - 15];
121             w[i] = (rotateRight(x, 17) ^ rotateRight(x, 19) ^ (x >>> 10))
122                 + w[i - 7]
123                 + (rotateRight(y, 7) ^ rotateRight(y, 18) ^ (y >>> 3))
124                 + w[i - 16] + i;
125         }
126 
127         System.arraycopy(w, 512, p, 0, 1024);
128         System.arraycopy(w, 1536, q, 0, 1024);
129 
130         for (int i = 0; i < 4096; i++)
131         {
132             step();
133         }
134 
135         cnt = 0;
136     }
137 
getAlgorithmName()138     public String getAlgorithmName()
139     {
140         return "HC-256";
141     }
142 
143     /**
144      * Initialise a HC-256 cipher.
145      *
146      * @param forEncryption whether or not we are for encryption. Irrelevant, as
147      *                      encryption and decryption are the same.
148      * @param params        the parameters required to set up the cipher.
149      * @throws IllegalArgumentException if the params argument is
150      *                                  inappropriate (ie. the key is not 256 bit long).
151      */
init(boolean forEncryption, CipherParameters params)152     public void init(boolean forEncryption, CipherParameters params)
153         throws IllegalArgumentException
154     {
155         CipherParameters keyParam = params;
156 
157         if (params instanceof ParametersWithIV)
158         {
159             iv = ((ParametersWithIV)params).getIV();
160             keyParam = ((ParametersWithIV)params).getParameters();
161         }
162         else
163         {
164             iv = new byte[0];
165         }
166 
167         if (keyParam instanceof KeyParameter)
168         {
169             key = ((KeyParameter)keyParam).getKey();
170             init();
171         }
172         else
173         {
174             throw new IllegalArgumentException(
175                 "Invalid parameter passed to HC256 init - "
176                     + params.getClass().getName());
177         }
178 
179         initialised = true;
180     }
181 
182     private byte[] buf = new byte[4];
183     private int idx = 0;
184 
getByte()185     private byte getByte()
186     {
187         if (idx == 0)
188         {
189             int step = step();
190             buf[0] = (byte)(step & 0xFF);
191             step >>= 8;
192             buf[1] = (byte)(step & 0xFF);
193             step >>= 8;
194             buf[2] = (byte)(step & 0xFF);
195             step >>= 8;
196             buf[3] = (byte)(step & 0xFF);
197         }
198         byte ret = buf[idx];
199         idx = idx + 1 & 0x3;
200         return ret;
201     }
202 
processBytes(byte[] in, int inOff, int len, byte[] out, int outOff)203     public int processBytes(byte[] in, int inOff, int len, byte[] out,
204                              int outOff) throws DataLengthException
205     {
206         if (!initialised)
207         {
208             throw new IllegalStateException(getAlgorithmName()
209                 + " not initialised");
210         }
211 
212         if ((inOff + len) > in.length)
213         {
214             throw new DataLengthException("input buffer too short");
215         }
216 
217         if ((outOff + len) > out.length)
218         {
219             throw new OutputLengthException("output buffer too short");
220         }
221 
222         for (int i = 0; i < len; i++)
223         {
224             out[outOff + i] = (byte)(in[inOff + i] ^ getByte());
225         }
226 
227         return len;
228     }
229 
reset()230     public void reset()
231     {
232         init();
233     }
234 
returnByte(byte in)235     public byte returnByte(byte in)
236     {
237         return (byte)(in ^ getByte());
238     }
239 
rotateRight( int x, int bits)240     private static int rotateRight(
241         int     x,
242         int     bits)
243     {
244         return (x >>> bits) | (x << -bits);
245     }
246 }
247