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