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