001: package org.bouncycastle.crypto.engines;
002:
003: import java.util.Hashtable;
004:
005: import org.bouncycastle.crypto.BlockCipher;
006: import org.bouncycastle.crypto.CipherParameters;
007: import org.bouncycastle.crypto.DataLengthException;
008: import org.bouncycastle.crypto.params.KeyParameter;
009: import org.bouncycastle.crypto.params.ParametersWithSBox;
010: import org.bouncycastle.util.Strings;
011:
012: /**
013: * implementation of GOST 28147-89
014: */
015: public class GOST28147Engine implements BlockCipher {
016: protected static final int BLOCK_SIZE = 8;
017: private int[] workingKey = null;
018: private boolean forEncryption;
019:
020: // these are the S-boxes given in Applied Cryptography 2nd Ed., p. 333
021: // This is default S-box!
022: private byte S[] = { 0x4, 0xA, 0x9, 0x2, 0xD, 0x8, 0x0, 0xE, 0x6,
023: 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3, 0xE, 0xB, 0x4, 0xC, 0x6,
024: 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7, 0x5, 0x9, 0x5,
025: 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF, 0xC, 0x7, 0x6,
026: 0x0, 0x9, 0xB, 0x7, 0xD, 0xA, 0x1, 0x0, 0x8, 0x9, 0xF, 0xE,
027: 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3, 0x6, 0xC, 0x7, 0x1, 0x5,
028: 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3, 0xB, 0x2, 0x4,
029: 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6, 0x8, 0x5, 0x9,
030: 0xC, 0xF, 0xE, 0xD, 0xB, 0x4, 0x1, 0x3, 0xF, 0x5, 0x9, 0x0,
031: 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC, 0x1, 0xF, 0xD, 0x0, 0x5,
032: 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB, 0x8, 0xC };
033:
034: /*
035: * class content S-box parameters for encrypting
036: * getting from, see: http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
037: * http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-02.txt
038: */
039: private static byte[] ESbox_Test = { 0x4, 0x2, 0xF, 0x5, 0x9, 0x1,
040: 0x0, 0x8, 0xE, 0x3, 0xB, 0xC, 0xD, 0x7, 0xA, 0x6, 0xC, 0x9,
041: 0xF, 0xE, 0x8, 0x1, 0x3, 0xA, 0x2, 0x7, 0x4, 0xD, 0x6, 0x0,
042: 0xB, 0x5, 0xD, 0x8, 0xE, 0xC, 0x7, 0x3, 0x9, 0xA, 0x1, 0x5,
043: 0x2, 0x4, 0x6, 0xF, 0x0, 0xB, 0xE, 0x9, 0xB, 0x2, 0x5, 0xF,
044: 0x7, 0x1, 0x0, 0xD, 0xC, 0x6, 0xA, 0x4, 0x3, 0x8, 0x3, 0xE,
045: 0x5, 0x9, 0x6, 0x8, 0x0, 0xD, 0xA, 0xB, 0x7, 0xC, 0x2, 0x1,
046: 0xF, 0x4, 0x8, 0xF, 0x6, 0xB, 0x1, 0x9, 0xC, 0x5, 0xD, 0x3,
047: 0x7, 0xA, 0x0, 0xE, 0x2, 0x4, 0x9, 0xB, 0xC, 0x0, 0x3, 0x6,
048: 0x7, 0x5, 0x4, 0x8, 0xE, 0xF, 0x1, 0xA, 0x2, 0xD, 0xC, 0x6,
049: 0x5, 0x2, 0xB, 0x0, 0x9, 0xD, 0x3, 0xE, 0x7, 0xA, 0xF, 0x4,
050: 0x1, 0x8 };
051:
052: private static byte[] ESbox_A = { 0x9, 0x6, 0x3, 0x2, 0x8, 0xB,
053: 0x1, 0x7, 0xA, 0x4, 0xE, 0xF, 0xC, 0x0, 0xD, 0x5, 0x3, 0x7,
054: 0xE, 0x9, 0x8, 0xA, 0xF, 0x0, 0x5, 0x2, 0x6, 0xC, 0xB, 0x4,
055: 0xD, 0x1, 0xE, 0x4, 0x6, 0x2, 0xB, 0x3, 0xD, 0x8, 0xC, 0xF,
056: 0x5, 0xA, 0x0, 0x7, 0x1, 0x9, 0xE, 0x7, 0xA, 0xC, 0xD, 0x1,
057: 0x3, 0x9, 0x0, 0x2, 0xB, 0x4, 0xF, 0x8, 0x5, 0x6, 0xB, 0x5,
058: 0x1, 0x9, 0x8, 0xD, 0xF, 0x0, 0xE, 0x4, 0x2, 0x3, 0xC, 0x7,
059: 0xA, 0x6, 0x3, 0xA, 0xD, 0xC, 0x1, 0x2, 0x0, 0xB, 0x7, 0x5,
060: 0x9, 0x4, 0x8, 0xF, 0xE, 0x6, 0x1, 0xD, 0x2, 0x9, 0x7, 0xA,
061: 0x6, 0x0, 0x8, 0xC, 0x4, 0x5, 0xF, 0x3, 0xB, 0xE, 0xB, 0xA,
062: 0xF, 0x5, 0x0, 0xC, 0xE, 0x8, 0x6, 0x2, 0x3, 0x9, 0x1, 0x7,
063: 0xD, 0x4 };
064:
065: private static byte[] ESbox_B = { 0x8, 0x4, 0xB, 0x1, 0x3, 0x5,
066: 0x0, 0x9, 0x2, 0xE, 0xA, 0xC, 0xD, 0x6, 0x7, 0xF, 0x0, 0x1,
067: 0x2, 0xA, 0x4, 0xD, 0x5, 0xC, 0x9, 0x7, 0x3, 0xF, 0xB, 0x8,
068: 0x6, 0xE, 0xE, 0xC, 0x0, 0xA, 0x9, 0x2, 0xD, 0xB, 0x7, 0x5,
069: 0x8, 0xF, 0x3, 0x6, 0x1, 0x4, 0x7, 0x5, 0x0, 0xD, 0xB, 0x6,
070: 0x1, 0x2, 0x3, 0xA, 0xC, 0xF, 0x4, 0xE, 0x9, 0x8, 0x2, 0x7,
071: 0xC, 0xF, 0x9, 0x5, 0xA, 0xB, 0x1, 0x4, 0x0, 0xD, 0x6, 0x8,
072: 0xE, 0x3, 0x8, 0x3, 0x2, 0x6, 0x4, 0xD, 0xE, 0xB, 0xC, 0x1,
073: 0x7, 0xF, 0xA, 0x0, 0x9, 0x5, 0x5, 0x2, 0xA, 0xB, 0x9, 0x1,
074: 0xC, 0x3, 0x7, 0x4, 0xD, 0x0, 0x6, 0xF, 0x8, 0xE, 0x0, 0x4,
075: 0xB, 0xE, 0x8, 0x3, 0x7, 0x1, 0xA, 0x2, 0x9, 0x6, 0xF, 0xD,
076: 0x5, 0xC };
077:
078: private static byte[] ESbox_C = { 0x1, 0xB, 0xC, 0x2, 0x9, 0xD,
079: 0x0, 0xF, 0x4, 0x5, 0x8, 0xE, 0xA, 0x7, 0x6, 0x3, 0x0, 0x1,
080: 0x7, 0xD, 0xB, 0x4, 0x5, 0x2, 0x8, 0xE, 0xF, 0xC, 0x9, 0xA,
081: 0x6, 0x3, 0x8, 0x2, 0x5, 0x0, 0x4, 0x9, 0xF, 0xA, 0x3, 0x7,
082: 0xC, 0xD, 0x6, 0xE, 0x1, 0xB, 0x3, 0x6, 0x0, 0x1, 0x5, 0xD,
083: 0xA, 0x8, 0xB, 0x2, 0x9, 0x7, 0xE, 0xF, 0xC, 0x4, 0x8, 0xD,
084: 0xB, 0x0, 0x4, 0x5, 0x1, 0x2, 0x9, 0x3, 0xC, 0xE, 0x6, 0xF,
085: 0xA, 0x7, 0xC, 0x9, 0xB, 0x1, 0x8, 0xE, 0x2, 0x4, 0x7, 0x3,
086: 0x6, 0x5, 0xA, 0x0, 0xF, 0xD, 0xA, 0x9, 0x6, 0x8, 0xD, 0xE,
087: 0x2, 0x0, 0xF, 0x3, 0x5, 0xB, 0x4, 0x1, 0xC, 0x7, 0x7, 0x4,
088: 0x0, 0x5, 0xA, 0x2, 0xF, 0xE, 0xC, 0x6, 0x1, 0xB, 0xD, 0x9,
089: 0x3, 0x8 };
090:
091: private static byte[] ESbox_D = { 0xF, 0xC, 0x2, 0xA, 0x6, 0x4,
092: 0x5, 0x0, 0x7, 0x9, 0xE, 0xD, 0x1, 0xB, 0x8, 0x3, 0xB, 0x6,
093: 0x3, 0x4, 0xC, 0xF, 0xE, 0x2, 0x7, 0xD, 0x8, 0x0, 0x5, 0xA,
094: 0x9, 0x1, 0x1, 0xC, 0xB, 0x0, 0xF, 0xE, 0x6, 0x5, 0xA, 0xD,
095: 0x4, 0x8, 0x9, 0x3, 0x7, 0x2, 0x1, 0x5, 0xE, 0xC, 0xA, 0x7,
096: 0x0, 0xD, 0x6, 0x2, 0xB, 0x4, 0x9, 0x3, 0xF, 0x8, 0x0, 0xC,
097: 0x8, 0x9, 0xD, 0x2, 0xA, 0xB, 0x7, 0x3, 0x6, 0x5, 0x4, 0xE,
098: 0xF, 0x1, 0x8, 0x0, 0xF, 0x3, 0x2, 0x5, 0xE, 0xB, 0x1, 0xA,
099: 0x4, 0x7, 0xC, 0x9, 0xD, 0x6, 0x3, 0x0, 0x6, 0xF, 0x1, 0xE,
100: 0x9, 0x2, 0xD, 0x8, 0xC, 0x4, 0xB, 0xA, 0x5, 0x7, 0x1, 0xA,
101: 0x6, 0x8, 0xF, 0xB, 0x0, 0x4, 0xC, 0x3, 0x5, 0x9, 0x7, 0xD,
102: 0x2, 0xE };
103:
104: //S-box for digest
105: private static byte DSbox_Test[] = { 0x4, 0xA, 0x9, 0x2, 0xD, 0x8,
106: 0x0, 0xE, 0x6, 0xB, 0x1, 0xC, 0x7, 0xF, 0x5, 0x3, 0xE, 0xB,
107: 0x4, 0xC, 0x6, 0xD, 0xF, 0xA, 0x2, 0x3, 0x8, 0x1, 0x0, 0x7,
108: 0x5, 0x9, 0x5, 0x8, 0x1, 0xD, 0xA, 0x3, 0x4, 0x2, 0xE, 0xF,
109: 0xC, 0x7, 0x6, 0x0, 0x9, 0xB, 0x7, 0xD, 0xA, 0x1, 0x0, 0x8,
110: 0x9, 0xF, 0xE, 0x4, 0x6, 0xC, 0xB, 0x2, 0x5, 0x3, 0x6, 0xC,
111: 0x7, 0x1, 0x5, 0xF, 0xD, 0x8, 0x4, 0xA, 0x9, 0xE, 0x0, 0x3,
112: 0xB, 0x2, 0x4, 0xB, 0xA, 0x0, 0x7, 0x2, 0x1, 0xD, 0x3, 0x6,
113: 0x8, 0x5, 0x9, 0xC, 0xF, 0xE, 0xD, 0xB, 0x4, 0x1, 0x3, 0xF,
114: 0x5, 0x9, 0x0, 0xA, 0xE, 0x7, 0x6, 0x8, 0x2, 0xC, 0x1, 0xF,
115: 0xD, 0x0, 0x5, 0x7, 0xA, 0x4, 0x9, 0x2, 0x3, 0xE, 0x6, 0xB,
116: 0x8, 0xC };
117:
118: private static byte DSbox_A[] = { 0xA, 0x4, 0x5, 0x6, 0x8, 0x1,
119: 0x3, 0x7, 0xD, 0xC, 0xE, 0x0, 0x9, 0x2, 0xB, 0xF, 0x5, 0xF,
120: 0x4, 0x0, 0x2, 0xD, 0xB, 0x9, 0x1, 0x7, 0x6, 0x3, 0xC, 0xE,
121: 0xA, 0x8, 0x7, 0xF, 0xC, 0xE, 0x9, 0x4, 0x1, 0x0, 0x3, 0xB,
122: 0x5, 0x2, 0x6, 0xA, 0x8, 0xD, 0x4, 0xA, 0x7, 0xC, 0x0, 0xF,
123: 0x2, 0x8, 0xE, 0x1, 0x6, 0x5, 0xD, 0xB, 0x9, 0x3, 0x7, 0x6,
124: 0x4, 0xB, 0x9, 0xC, 0x2, 0xA, 0x1, 0x8, 0x0, 0xE, 0xF, 0xD,
125: 0x3, 0x5, 0x7, 0x6, 0x2, 0x4, 0xD, 0x9, 0xF, 0x0, 0xA, 0x1,
126: 0x5, 0xB, 0x8, 0xE, 0xC, 0x3, 0xD, 0xE, 0x4, 0x1, 0x7, 0x0,
127: 0x5, 0xA, 0x3, 0xC, 0x8, 0xF, 0x6, 0x2, 0x9, 0xB, 0x1, 0x3,
128: 0xA, 0x9, 0x5, 0xB, 0x4, 0xF, 0x8, 0x6, 0x7, 0xE, 0xD, 0x0,
129: 0x2, 0xC };
130:
131: //
132: // pre-defined sbox table
133: //
134: private static Hashtable sBoxes = new Hashtable();
135:
136: static {
137: sBoxes.put("E-TEST", ESbox_Test);
138: sBoxes.put("E-A", ESbox_A);
139: sBoxes.put("E-B", ESbox_B);
140: sBoxes.put("E-C", ESbox_C);
141: sBoxes.put("E-D", ESbox_D);
142: sBoxes.put("D-TEST", DSbox_Test);
143: sBoxes.put("D-A", DSbox_A);
144: }
145:
146: /**
147: * standard constructor.
148: */
149: public GOST28147Engine() {
150: }
151:
152: /**
153: * initialise an GOST28147 cipher.
154: *
155: * @param forEncryption whether or not we are for encryption.
156: * @param params the parameters required to set up the cipher.
157: * @exception IllegalArgumentException if the params argument is
158: * inappropriate.
159: */
160: public void init(boolean forEncryption, CipherParameters params) {
161: if (params instanceof ParametersWithSBox) {
162: ParametersWithSBox param = (ParametersWithSBox) params;
163:
164: //
165: // Set the S-Box
166: //
167: System.arraycopy(param.getSBox(), 0, this .S, 0, param
168: .getSBox().length);
169:
170: //
171: // set key if there is one
172: //
173: if (param.getParameters() != null) {
174: workingKey = generateWorkingKey(forEncryption,
175: ((KeyParameter) param.getParameters()).getKey());
176: }
177: } else if (params instanceof KeyParameter) {
178: workingKey = generateWorkingKey(forEncryption,
179: ((KeyParameter) params).getKey());
180: } else {
181: throw new IllegalArgumentException(
182: "invalid parameter passed to GOST28147 init - "
183: + params.getClass().getName());
184: }
185: }
186:
187: public String getAlgorithmName() {
188: return "GOST28147";
189: }
190:
191: public int getBlockSize() {
192: return BLOCK_SIZE;
193: }
194:
195: public int processBlock(byte[] in, int inOff, byte[] out, int outOff) {
196: if (workingKey == null) {
197: throw new IllegalStateException(
198: "GOST28147 engine not initialised");
199: }
200:
201: if ((inOff + BLOCK_SIZE) > in.length) {
202: throw new DataLengthException("input buffer too short");
203: }
204:
205: if ((outOff + BLOCK_SIZE) > out.length) {
206: throw new DataLengthException("output buffer too short");
207: }
208:
209: GOST28147Func(workingKey, in, inOff, out, outOff);
210:
211: return BLOCK_SIZE;
212: }
213:
214: public void reset() {
215: }
216:
217: private int[] generateWorkingKey(boolean forEncryption,
218: byte[] userKey) {
219: this .forEncryption = forEncryption;
220:
221: if (userKey.length != 32) {
222: throw new IllegalArgumentException(
223: "Key length invalid. Key needs to be 32 byte - 256 bit!!!");
224: }
225:
226: int key[] = new int[8];
227: for (int i = 0; i != 8; i++) {
228: key[i] = bytesToint(userKey, i * 4);
229: }
230:
231: return key;
232: }
233:
234: private int GOST28147_mainStep(int n1, int key) {
235: int cm = (key + n1); // CM1
236:
237: // S-box replacing
238:
239: int om = S[0 + ((cm >> (0 * 4)) & 0xF)] << (0 * 4);
240: om += S[16 + ((cm >> (1 * 4)) & 0xF)] << (1 * 4);
241: om += S[32 + ((cm >> (2 * 4)) & 0xF)] << (2 * 4);
242: om += S[48 + ((cm >> (3 * 4)) & 0xF)] << (3 * 4);
243: om += S[64 + ((cm >> (4 * 4)) & 0xF)] << (4 * 4);
244: om += S[80 + ((cm >> (5 * 4)) & 0xF)] << (5 * 4);
245: om += S[96 + ((cm >> (6 * 4)) & 0xF)] << (6 * 4);
246: om += S[112 + ((cm >> (7 * 4)) & 0xF)] << (7 * 4);
247:
248: return om << 11 | om >>> (32 - 11); // 11-leftshift
249: }
250:
251: private void GOST28147Func(int[] workingKey, byte[] in, int inOff,
252: byte[] out, int outOff) {
253: int N1, N2, tmp; //tmp -> for saving N1
254: N1 = bytesToint(in, inOff);
255: N2 = bytesToint(in, inOff + 4);
256:
257: if (this .forEncryption) {
258: for (int k = 0; k < 3; k++) // 1-24 steps
259: {
260: for (int j = 0; j < 8; j++) {
261: tmp = N1;
262: N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
263: N2 = tmp;
264: }
265: }
266: for (int j = 7; j > 0; j--) // 25-31 steps
267: {
268: tmp = N1;
269: N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
270: N2 = tmp;
271: }
272: } else //decrypt
273: {
274: for (int j = 0; j < 8; j++) // 1-8 steps
275: {
276: tmp = N1;
277: N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
278: N2 = tmp;
279: }
280: for (int k = 0; k < 3; k++) //9-31 steps
281: {
282: for (int j = 7; j >= 0; j--) {
283: if ((k == 2) && (j == 0)) {
284: break; // break 32 step
285: }
286: tmp = N1;
287: N1 = N2 ^ GOST28147_mainStep(N1, workingKey[j]); // CM2
288: N2 = tmp;
289: }
290: }
291: }
292:
293: N2 = N2 ^ GOST28147_mainStep(N1, workingKey[0]); // 32 step (N1=N1)
294:
295: intTobytes(N1, out, outOff);
296: intTobytes(N2, out, outOff + 4);
297: }
298:
299: //array of bytes to type int
300: private int bytesToint(byte[] in, int inOff) {
301: return ((in[inOff + 3] << 24) & 0xff000000)
302: + ((in[inOff + 2] << 16) & 0xff0000)
303: + ((in[inOff + 1] << 8) & 0xff00) + (in[inOff] & 0xff);
304: }
305:
306: //int to array of bytes
307: private void intTobytes(int num, byte[] out, int outOff) {
308: out[outOff + 3] = (byte) (num >>> 24);
309: out[outOff + 2] = (byte) (num >>> 16);
310: out[outOff + 1] = (byte) (num >>> 8);
311: out[outOff] = (byte) num;
312: }
313:
314: /**
315: * Return the S-Box associated with SBoxName
316: * @param sBoxName name of the S-Box
317: * @return byte array representing the S-Box
318: */
319: public static byte[] getSBox(String sBoxName) {
320: byte[] namedSBox = (byte[]) sBoxes.get(Strings
321: .toUpperCase(sBoxName));
322:
323: if (namedSBox != null) {
324: byte[] sBox = new byte[namedSBox.length];
325:
326: System.arraycopy(namedSBox, 0, sBox, 0, sBox.length);
327:
328: return sBox;
329: } else {
330: throw new IllegalArgumentException(
331: "Unknown S-Box - possible types: "
332: + "\"E-Test\", \"E-A\", \"E-B\", \"E-C\", \"E-D\", \"D-Test\", \"D-A\".");
333: }
334: }
335: }
|