1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.impl.ext.engines;
18
19 import org.bouncycastle.crypto.BlockCipher;
20 import org.bouncycastle.crypto.CipherParameters;
21 import org.bouncycastle.crypto.params.KeyParameter;
22 import org.bouncycastle.util.Pack;
23
24
25
26
27
28
29
30 public class GordianSimonEngine
31 implements BlockCipher {
32
33
34
35 private static final byte[] ROUNDS = {68, 69, 72};
36
37
38
39
40 private static final byte[][] Z = {
41 {1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0,
42 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1,
43 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0},
44 {1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0,
45 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1,
46 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0},
47 {1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0,
48 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0,
49 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1},
50 {1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0,
51 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0,
52 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1},
53 {1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0,
54 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0,
55 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1}
56 };
57
58
59
60
61 private static final int NUMWORDS = 2;
62
63
64
65
66 private static final int BLOCKSIZE = NUMWORDS * Long.BYTES;
67
68
69
70
71 private static final int NUMWORDS128 = 2;
72
73
74
75
76 private static final int NUMWORDS192 = 3;
77
78
79
80
81 private static final int NUMWORDS256 = 4;
82
83
84
85
86 private static final int ROT1 = 1;
87
88
89
90
91 private static final int ROT2 = 2;
92
93
94
95
96 private static final int ROT3 = 3;
97
98
99
100
101 private static final int ROT8 = 8;
102
103
104
105
106 private int theRounds;
107
108
109
110
111 private long[] theRoundKeys;
112
113
114
115
116 private boolean forEncryption;
117
118 @Override
119 public void init(final boolean pEncrypt,
120 final CipherParameters pParams) {
121
122 if (!(pParams instanceof KeyParameter)) {
123 throw new IllegalArgumentException("Invalid parameter passed to Speck init - "
124 + pParams.getClass().getName());
125 }
126
127
128 final byte[] myKey = ((KeyParameter) pParams).getKey();
129 final int myKeyLen = myKey.length;
130 if ((((myKeyLen << 1) % BLOCKSIZE) != 0)
131 || myKeyLen < BLOCKSIZE
132 || myKeyLen > (BLOCKSIZE << 1)) {
133 throw new IllegalArgumentException("KeyBitSize must be 128, 192 or 256");
134 }
135
136
137 forEncryption = pEncrypt;
138 generateRoundKeys(myKey);
139 }
140
141 @Override
142 public void reset() {
143
144 }
145
146 @Override
147 public String getAlgorithmName() {
148 return "Simon";
149 }
150
151 @Override
152 public int getBlockSize() {
153 return BLOCKSIZE;
154 }
155
156 @Override
157 public int processBlock(final byte[] pInput,
158 final int pInOff,
159 final byte[] pOutput,
160 final int pOutOff) {
161
162 if (pInput == null || pInput.length - pInOff < BLOCKSIZE) {
163 throw new IllegalArgumentException("Invalid input buffer");
164 }
165 if (pOutput == null || pOutput.length - pOutOff < BLOCKSIZE) {
166 throw new IllegalArgumentException("Invalid output buffer");
167 }
168
169
170 return forEncryption
171 ? encryptBlock(pInput, pInOff, pOutput, pOutOff)
172 : decryptBlock(pInput, pInOff, pOutput, pOutOff);
173 }
174
175
176
177
178
179
180
181
182
183
184 private int encryptBlock(final byte[] pInput,
185 final int pInOff,
186 final byte[] pOutput,
187 final int pOutOff) {
188
189 long myX = Pack.bigEndianToLong(pInput, pInOff);
190 long myY = Pack.bigEndianToLong(pInput, pInOff + Long.BYTES);
191
192
193 for (int i = 0; i < theRounds; i++) {
194
195 final long myTmp = myX;
196 myX = myY ^ (rol64(myX, ROT1) & rol64(myX, ROT8)) ^ rol64(myX, ROT2) ^ theRoundKeys[i];
197 myY = myTmp;
198 }
199
200
201 Pack.longToBigEndian(myX, pOutput, pOutOff);
202 Pack.longToBigEndian(myY, pOutput, pOutOff + Long.BYTES);
203
204
205 return BLOCKSIZE;
206 }
207
208
209
210
211
212
213
214
215
216
217 private int decryptBlock(final byte[] pInput,
218 final int pInOff,
219 final byte[] pOutput,
220 final int pOutOff) {
221
222 long myX = Pack.bigEndianToLong(pInput, pInOff);
223 long myY = Pack.bigEndianToLong(pInput, pInOff + Long.BYTES);
224
225
226 for (int i = theRounds - 1; i >= 0; i--) {
227
228 final long myTmp = myY;
229 myY = myX ^ (rol64(myY, ROT1) & rol64(myY, ROT8)) ^ rol64(myY, ROT2) ^ theRoundKeys[i];
230 myX = myTmp;
231 }
232
233
234 Pack.longToBigEndian(myX, pOutput, pOutOff);
235 Pack.longToBigEndian(myY, pOutput, pOutOff + Long.BYTES);
236
237
238 return BLOCKSIZE;
239 }
240
241
242
243
244
245
246 private void generateRoundKeys(final byte[] pKey) {
247
248 final int numWords = pKey.length / Long.BYTES;
249 final byte[] myConstants = Z[numWords];
250
251
252 theRounds = ROUNDS[numWords - NUMWORDS128];
253 theRoundKeys = new long[theRounds];
254
255
256 for (int i = 0; i < numWords; i++) {
257 theRoundKeys[i] = Pack.bigEndianToLong(pKey, (numWords - i - 1) * Long.BYTES);
258 }
259
260
261 for (int i = numWords; i < theRounds; i++) {
262 long tmp = ror64(theRoundKeys[i - 1], ROT3);
263 if (numWords == NUMWORDS256) {
264 tmp ^= theRoundKeys[i - NUMWORDS192];
265 }
266 tmp = tmp ^ ror64(tmp, ROT1);
267 theRoundKeys[i] = tmp ^ theRoundKeys[i - numWords]
268 ^ myConstants[(i - numWords) % myConstants.length] ^ ~NUMWORDS192;
269 }
270 }
271
272
273
274
275
276
277
278
279 private static long rol64(final long pValue,
280 final long pBits) {
281 return (pValue << pBits) | (pValue >>> (Long.SIZE - pBits));
282 }
283
284
285
286
287
288
289
290
291 private static long ror64(final long pValue,
292 final long pBits) {
293 return (pValue >>> pBits) | (pValue << (Long.SIZE - pBits));
294 }
295 }