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 GordianSpeckEngine
31 implements BlockCipher {
32
33
34
35 private static final int BASEROUNDS = 32;
36
37
38
39
40 private static final int NUMWORDS = 2;
41
42
43
44
45 private static final int BLOCKSIZE = NUMWORDS * Long.BYTES;
46
47
48
49
50 private static final int ROT3 = 3;
51
52
53
54
55 private static final int ROT8 = 8;
56
57
58
59
60 private int theRounds;
61
62
63
64
65 private long[] theRoundKeys;
66
67
68
69
70 private boolean forEncryption;
71
72 @Override
73 public void init(final boolean pEncrypt,
74 final CipherParameters pParams) {
75
76 if (!(pParams instanceof KeyParameter)) {
77 throw new IllegalArgumentException("Invalid parameter passed to Speck init - "
78 + pParams.getClass().getName());
79 }
80
81
82 final byte[] myKey = ((KeyParameter) pParams).getKey();
83 final int myKeyLen = myKey.length;
84 if ((((myKeyLen << 1) % BLOCKSIZE) != 0)
85 || myKeyLen < BLOCKSIZE
86 || myKeyLen > (BLOCKSIZE << 1)) {
87 throw new IllegalArgumentException("KeyBitSize must be 128, 192 or 256");
88 }
89
90
91 forEncryption = pEncrypt;
92 generateRoundKeys(myKey);
93 }
94
95 @Override
96 public void reset() {
97
98 }
99
100 @Override
101 public String getAlgorithmName() {
102 return "Speck";
103 }
104
105 @Override
106 public int getBlockSize() {
107 return BLOCKSIZE;
108 }
109
110 @Override
111 public int processBlock(final byte[] pInput,
112 final int pInOff,
113 final byte[] pOutput,
114 final int pOutOff) {
115
116 if (pInput == null || pInput.length - pInOff < BLOCKSIZE) {
117 throw new IllegalArgumentException("Invalid input buffer");
118 }
119 if (pOutput == null || pOutput.length - pOutOff < BLOCKSIZE) {
120 throw new IllegalArgumentException("Invalid output buffer");
121 }
122
123
124 return forEncryption
125 ? encryptBlock(pInput, pInOff, pOutput, pOutOff)
126 : decryptBlock(pInput, pInOff, pOutput, pOutOff);
127 }
128
129
130
131
132
133
134
135
136
137
138 private int encryptBlock(final byte[] pInput,
139 final int pInOff,
140 final byte[] pOutput,
141 final int pOutOff) {
142
143 long myX = Pack.bigEndianToLong(pInput, pInOff);
144 long myY = Pack.bigEndianToLong(pInput, pInOff + Long.BYTES);
145
146
147 for (int i = 0; i < theRounds; i++) {
148
149 myX = (ror64(myX, ROT8) + myY) ^ theRoundKeys[i];
150 myY = rol64(myY, ROT3) ^ myX;
151 }
152
153
154 Pack.longToBigEndian(myX, pOutput, pOutOff);
155 Pack.longToBigEndian(myY, pOutput, pOutOff + Long.BYTES);
156
157
158 return BLOCKSIZE;
159 }
160
161
162
163
164
165
166
167
168
169
170 private int decryptBlock(final byte[] pInput,
171 final int pInOff,
172 final byte[] pOutput,
173 final int pOutOff) {
174
175 long myX = Pack.bigEndianToLong(pInput, pInOff);
176 long myY = Pack.bigEndianToLong(pInput, pInOff + Long.BYTES);
177
178
179 for (int i = theRounds - 1; i >= 0; i--) {
180
181 myY = ror64(myX ^ myY, ROT3);
182 myX = rol64((myX ^ theRoundKeys[i]) - myY, ROT8);
183 }
184
185
186 Pack.longToBigEndian(myX, pOutput, pOutOff);
187 Pack.longToBigEndian(myY, pOutput, pOutOff + Long.BYTES);
188
189
190 return BLOCKSIZE;
191 }
192
193
194
195
196
197
198 private void generateRoundKeys(final byte[] pKey) {
199
200 final int numWords = pKey.length / Long.BYTES;
201
202
203 theRounds = BASEROUNDS + (numWords - 2);
204 theRoundKeys = new long[theRounds];
205
206
207 theRoundKeys[0] = Pack.bigEndianToLong(pKey, (numWords - 1) * Long.BYTES);
208
209
210 final long[] myL = new long[numWords];
211 for (int i = 0; i < numWords - 1; i++) {
212 myL[i] = Pack.bigEndianToLong(pKey, (numWords - i - 2) * Long.BYTES);
213 }
214
215
216 for (int i = 0; i < theRounds - 1; i++) {
217 final int lw = (i + numWords - 1) % numWords;
218 myL[lw] = (ror64(myL[i % numWords], ROT8) + theRoundKeys[i]) ^ i;
219 theRoundKeys[i + 1] = rol64(theRoundKeys[i], ROT3) ^ myL[lw];
220 }
221 }
222
223
224
225
226
227
228
229
230 private static long rol64(final long pValue,
231 final long pBits) {
232 return (pValue << pBits) | (pValue >>> (Long.SIZE - pBits));
233 }
234
235
236
237
238
239
240
241
242 private static long ror64(final long pValue,
243 final long pBits) {
244 return (pValue >>> pBits) | (pValue << (Long.SIZE - pBits));
245 }
246 }