001: package org.bouncycastle.crypto.engines;
002:
003: import org.bouncycastle.crypto.CipherParameters;
004: import org.bouncycastle.crypto.DataLengthException;
005: import org.bouncycastle.crypto.StreamCipher;
006: import org.bouncycastle.crypto.params.KeyParameter;
007: import org.bouncycastle.crypto.params.ParametersWithIV;
008:
009: /**
010: * HC-128 is a software-efficient stream cipher created by Hongjun Wu. It
011: * generates keystream from a 128-bit secret key and a 128-bit initialization
012: * vector.
013: * <p>
014: * http://www.ecrypt.eu.org/stream/p3ciphers/hc/hc256_p3.pdf
015: * </p><p>
016: * It is a third phase candidate in the eStream contest, and is patent-free.
017: * No attacks are known as of today (April 2007). See
018: *
019: * http://www.ecrypt.eu.org/stream/hcp3.html
020: * </p>
021: */
022: public class HC128Engine implements StreamCipher {
023: private int[] p = new int[512];
024: private int[] q = new int[512];
025: private int cnt = 0;
026:
027: private static int f1(int x) {
028: return rotateRight(x, 7) ^ rotateRight(x, 18) ^ (x >>> 3);
029: }
030:
031: private static int f2(int x) {
032: return rotateRight(x, 17) ^ rotateRight(x, 19) ^ (x >>> 10);
033: }
034:
035: private int g1(int x, int y, int z) {
036: return (rotateRight(x, 10) ^ rotateRight(z, 23))
037: + rotateRight(y, 8);
038: }
039:
040: private int g2(int x, int y, int z) {
041: return (rotateLeft(x, 10) ^ rotateLeft(z, 23))
042: + rotateLeft(y, 8);
043: }
044:
045: private static int rotateLeft(int x, int bits) {
046: return (x << bits) | (x >>> -bits);
047: }
048:
049: private static int rotateRight(int x, int bits) {
050: return (x >>> bits) | (x << -bits);
051: }
052:
053: private int h1(int x) {
054: return q[x & 0xFF] + q[((x >> 16) & 0xFF) + 256];
055: }
056:
057: private int h2(int x) {
058: return p[x & 0xFF] + p[((x >> 16) & 0xFF) + 256];
059: }
060:
061: private static int mod1024(int x) {
062: return x & 0x3FF;
063: }
064:
065: private static int mod512(int x) {
066: return x & 0x1FF;
067: }
068:
069: private static int dim(int x, int y) {
070: return mod512(x - y);
071: }
072:
073: private int step() {
074: int j = mod512(cnt);
075: int ret;
076: if (cnt < 512) {
077: p[j] += g1(p[dim(j, 3)], p[dim(j, 10)], p[dim(j, 511)]);
078: ret = h1(p[dim(j, 12)]) ^ p[j];
079: } else {
080: q[j] += g2(q[dim(j, 3)], q[dim(j, 10)], q[dim(j, 511)]);
081: ret = h2(q[dim(j, 12)]) ^ q[j];
082: }
083: cnt = mod1024(cnt + 1);
084: return ret;
085: }
086:
087: private byte[] key, iv;
088: private boolean initialised;
089:
090: private void init() {
091: if (key.length != 16) {
092: throw new java.lang.IllegalArgumentException(
093: "The key must be 128 bit long");
094: }
095:
096: cnt = 0;
097:
098: int[] w = new int[1280];
099:
100: for (int i = 0; i < 16; i++) {
101: w[i >> 3] |= key[i] << (i & 0x7);
102: }
103: System.arraycopy(w, 0, w, 4, 4);
104:
105: for (int i = 0; i < iv.length && i < 16; i++) {
106: w[(i >> 3) + 8] |= iv[i] << (i & 0x7);
107: }
108: System.arraycopy(w, 8, w, 12, 4);
109:
110: for (int i = 16; i < 1280; i++) {
111: w[i] = f2(w[i - 2]) + w[i - 7] + f1(w[i - 15]) + w[i - 16]
112: + i;
113: }
114:
115: System.arraycopy(w, 256, p, 0, 512);
116: System.arraycopy(w, 768, q, 0, 512);
117:
118: for (int i = 0; i < 512; i++) {
119: p[i] = step();
120: }
121: for (int i = 0; i < 512; i++) {
122: q[i] = step();
123: }
124:
125: cnt = 0;
126: }
127:
128: public String getAlgorithmName() {
129: return "HC-128";
130: }
131:
132: /**
133: * Initialise a HC-128 cipher.
134: *
135: * @param forEncryption whether or not we are for encryption. Irrelevant, as
136: * encryption and decryption are the same.
137: * @param params the parameters required to set up the cipher.
138: * @throws IllegalArgumentException if the params argument is
139: * inappropriate (ie. the key is not 128 bit long).
140: */
141: public void init(boolean forEncryption, CipherParameters params)
142: throws IllegalArgumentException {
143: CipherParameters keyParam = params;
144:
145: if (params instanceof ParametersWithIV) {
146: iv = ((ParametersWithIV) params).getIV();
147: keyParam = ((ParametersWithIV) params).getParameters();
148: } else {
149: iv = new byte[0];
150: }
151:
152: if (keyParam instanceof KeyParameter) {
153: key = ((KeyParameter) keyParam).getKey();
154: init();
155: } else {
156: throw new IllegalArgumentException(
157: "Invalid parameter passed to HC128 init - "
158: + params.getClass().getName());
159: }
160:
161: initialised = true;
162: }
163:
164: private byte[] buf = new byte[4];
165: private int idx = 0;
166:
167: private byte getByte() {
168: if (idx == 0) {
169: int step = step();
170: buf[3] = (byte) (step & 0xFF);
171: step >>= 8;
172: buf[2] = (byte) (step & 0xFF);
173: step >>= 8;
174: buf[1] = (byte) (step & 0xFF);
175: step >>= 8;
176: buf[0] = (byte) (step & 0xFF);
177: }
178: byte ret = buf[idx];
179: idx = idx + 1 & 0x3;
180: return ret;
181: }
182:
183: public void processBytes(byte[] in, int inOff, int len, byte[] out,
184: int outOff) throws DataLengthException {
185: if (!initialised) {
186: throw new IllegalStateException(getAlgorithmName()
187: + " not initialised");
188: }
189:
190: if ((inOff + len) > in.length) {
191: throw new DataLengthException("input buffer too short");
192: }
193:
194: if ((outOff + len) > out.length) {
195: throw new DataLengthException("output buffer too short");
196: }
197:
198: for (int i = 0; i < len; i++) {
199: out[outOff + i] = (byte) (in[inOff + i] ^ getByte());
200: }
201: }
202:
203: public void reset() {
204: idx = 0;
205: init();
206: }
207:
208: public byte returnByte(byte in) {
209: return (byte) (in ^ getByte());
210: }
211: }
|