|
1 local bit = require("bit"); |
|
2 |
|
3 local gf = require("aeslua.gf"); |
|
4 local util = require("aeslua.util"); |
|
5 |
|
6 -- |
|
7 -- Implementation of AES with nearly pure lua (only bitlib is needed) |
|
8 -- |
|
9 -- AES with lua is slow, really slow :-) |
|
10 -- |
|
11 |
|
12 local public = {}; |
|
13 local private = {}; |
|
14 |
|
15 local aeslua = require("aeslua"); |
|
16 aeslua.aes = public; |
|
17 |
|
18 -- some constants |
|
19 public.ROUNDS = "rounds"; |
|
20 public.KEY_TYPE = "type"; |
|
21 public.ENCRYPTION_KEY=1; |
|
22 public.DECRYPTION_KEY=2; |
|
23 |
|
24 -- aes SBOX |
|
25 private.SBox = {}; |
|
26 private.iSBox = {}; |
|
27 |
|
28 -- aes tables |
|
29 private.table0 = {}; |
|
30 private.table1 = {}; |
|
31 private.table2 = {}; |
|
32 private.table3 = {}; |
|
33 |
|
34 private.tableInv0 = {}; |
|
35 private.tableInv1 = {}; |
|
36 private.tableInv2 = {}; |
|
37 private.tableInv3 = {}; |
|
38 |
|
39 -- round constants |
|
40 private.rCon = {0x01000000, |
|
41 0x02000000, |
|
42 0x04000000, |
|
43 0x08000000, |
|
44 0x10000000, |
|
45 0x20000000, |
|
46 0x40000000, |
|
47 0x80000000, |
|
48 0x1b000000, |
|
49 0x36000000, |
|
50 0x6c000000, |
|
51 0xd8000000, |
|
52 0xab000000, |
|
53 0x4d000000, |
|
54 0x9a000000, |
|
55 0x2f000000}; |
|
56 |
|
57 -- |
|
58 -- affine transformation for calculating the S-Box of AES |
|
59 -- |
|
60 function private.affinMap(byte) |
|
61 local mask = 0xf8; |
|
62 local result = 0; |
|
63 for i = 1,8 do |
|
64 result = bit.lshift(result,1); |
|
65 |
|
66 local parity = util.byteParity(bit.band(byte,mask)); |
|
67 result = result + parity; |
|
68 |
|
69 -- simulate roll |
|
70 local lastbit = bit.band(mask, 1); |
|
71 mask = bit.band(bit.rshift(mask, 1),0xff); |
|
72 if (lastbit ~= 0) then |
|
73 mask = bit.bor(mask, 0x80); |
|
74 else |
|
75 mask = bit.band(mask, 0x7f); |
|
76 end |
|
77 end |
|
78 |
|
79 return bit.bxor(result, 0x63); |
|
80 end |
|
81 |
|
82 -- |
|
83 -- calculate S-Box and inverse S-Box of AES |
|
84 -- apply affine transformation to inverse in finite field 2^8 |
|
85 -- |
|
86 function private.calcSBox() |
|
87 local inverse; |
|
88 for i = 0, 255 do |
|
89 if (i ~= 0) then |
|
90 inverse = gf.invert(i); |
|
91 else |
|
92 inverse = i; |
|
93 end |
|
94 local mapped = private.affinMap(inverse); |
|
95 private.SBox[i] = mapped; |
|
96 private.iSBox[mapped] = i; |
|
97 end |
|
98 end |
|
99 |
|
100 -- |
|
101 -- Calculate round tables |
|
102 -- round tables are used to calculate shiftRow, MixColumn and SubBytes |
|
103 -- with 4 table lookups and 4 xor operations. |
|
104 -- |
|
105 function private.calcRoundTables() |
|
106 for x = 0,255 do |
|
107 local byte = private.SBox[x]; |
|
108 private.table0[x] = util.putByte(gf.mul(0x03, byte), 0) |
|
109 + util.putByte( byte , 1) |
|
110 + util.putByte( byte , 2) |
|
111 + util.putByte(gf.mul(0x02, byte), 3); |
|
112 private.table1[x] = util.putByte( byte , 0) |
|
113 + util.putByte( byte , 1) |
|
114 + util.putByte(gf.mul(0x02, byte), 2) |
|
115 + util.putByte(gf.mul(0x03, byte), 3); |
|
116 private.table2[x] = util.putByte( byte , 0) |
|
117 + util.putByte(gf.mul(0x02, byte), 1) |
|
118 + util.putByte(gf.mul(0x03, byte), 2) |
|
119 + util.putByte( byte , 3); |
|
120 private.table3[x] = util.putByte(gf.mul(0x02, byte), 0) |
|
121 + util.putByte(gf.mul(0x03, byte), 1) |
|
122 + util.putByte( byte , 2) |
|
123 + util.putByte( byte , 3); |
|
124 end |
|
125 end |
|
126 |
|
127 -- |
|
128 -- Calculate inverse round tables |
|
129 -- does the inverse of the normal roundtables for the equivalent |
|
130 -- decryption algorithm. |
|
131 -- |
|
132 function private.calcInvRoundTables() |
|
133 for x = 0,255 do |
|
134 local byte = private.iSBox[x]; |
|
135 private.tableInv0[x] = util.putByte(gf.mul(0x0b, byte), 0) |
|
136 + util.putByte(gf.mul(0x0d, byte), 1) |
|
137 + util.putByte(gf.mul(0x09, byte), 2) |
|
138 + util.putByte(gf.mul(0x0e, byte), 3); |
|
139 private.tableInv1[x] = util.putByte(gf.mul(0x0d, byte), 0) |
|
140 + util.putByte(gf.mul(0x09, byte), 1) |
|
141 + util.putByte(gf.mul(0x0e, byte), 2) |
|
142 + util.putByte(gf.mul(0x0b, byte), 3); |
|
143 private.tableInv2[x] = util.putByte(gf.mul(0x09, byte), 0) |
|
144 + util.putByte(gf.mul(0x0e, byte), 1) |
|
145 + util.putByte(gf.mul(0x0b, byte), 2) |
|
146 + util.putByte(gf.mul(0x0d, byte), 3); |
|
147 private.tableInv3[x] = util.putByte(gf.mul(0x0e, byte), 0) |
|
148 + util.putByte(gf.mul(0x0b, byte), 1) |
|
149 + util.putByte(gf.mul(0x0d, byte), 2) |
|
150 + util.putByte(gf.mul(0x09, byte), 3); |
|
151 end |
|
152 end |
|
153 |
|
154 |
|
155 -- |
|
156 -- rotate word: 0xaabbccdd gets 0xbbccddaa |
|
157 -- used for key schedule |
|
158 -- |
|
159 function private.rotWord(word) |
|
160 local tmp = bit.band(word,0xff000000); |
|
161 return (bit.lshift(word,8) + bit.rshift(tmp,24)) ; |
|
162 end |
|
163 |
|
164 -- |
|
165 -- replace all bytes in a word with the SBox. |
|
166 -- used for key schedule |
|
167 -- |
|
168 function private.subWord(word) |
|
169 return util.putByte(private.SBox[util.getByte(word,0)],0) |
|
170 + util.putByte(private.SBox[util.getByte(word,1)],1) |
|
171 + util.putByte(private.SBox[util.getByte(word,2)],2) |
|
172 + util.putByte(private.SBox[util.getByte(word,3)],3); |
|
173 end |
|
174 |
|
175 -- |
|
176 -- generate key schedule for aes encryption |
|
177 -- |
|
178 -- returns table with all round keys and |
|
179 -- the necessary number of rounds saved in [public.ROUNDS] |
|
180 -- |
|
181 function public.expandEncryptionKey(key) |
|
182 local keySchedule = {}; |
|
183 local keyWords = math.floor(#key / 4); |
|
184 |
|
185 |
|
186 if ((keyWords ~= 4 and keyWords ~= 6 and keyWords ~= 8) or (keyWords * 4 ~= #key)) then |
|
187 error("Invalid key size: "..keyWords); |
|
188 return nil; |
|
189 end |
|
190 |
|
191 keySchedule[public.ROUNDS] = keyWords + 6; |
|
192 keySchedule[public.KEY_TYPE] = public.ENCRYPTION_KEY; |
|
193 |
|
194 for i = 0,keyWords - 1 do |
|
195 keySchedule[i] = util.putByte(key[i*4+1], 3) |
|
196 + util.putByte(key[i*4+2], 2) |
|
197 + util.putByte(key[i*4+3], 1) |
|
198 + util.putByte(key[i*4+4], 0); |
|
199 end |
|
200 |
|
201 for i = keyWords, (keySchedule[public.ROUNDS] + 1)*4 - 1 do |
|
202 local tmp = keySchedule[i-1]; |
|
203 |
|
204 if ( i % keyWords == 0) then |
|
205 tmp = private.rotWord(tmp); |
|
206 tmp = private.subWord(tmp); |
|
207 |
|
208 local index = math.floor(i/keyWords); |
|
209 tmp = bit.bxor(tmp,private.rCon[index]); |
|
210 elseif (keyWords > 6 and i % keyWords == 4) then |
|
211 tmp = private.subWord(tmp); |
|
212 end |
|
213 |
|
214 keySchedule[i] = bit.bxor(keySchedule[(i-keyWords)],tmp); |
|
215 end |
|
216 |
|
217 return keySchedule; |
|
218 end |
|
219 |
|
220 -- |
|
221 -- Inverse mix column |
|
222 -- used for key schedule of decryption key |
|
223 -- |
|
224 function private.invMixColumnOld(word) |
|
225 local b0 = util.getByte(word,3); |
|
226 local b1 = util.getByte(word,2); |
|
227 local b2 = util.getByte(word,1); |
|
228 local b3 = util.getByte(word,0); |
|
229 |
|
230 return util.putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b1), |
|
231 gf.mul(0x0d, b2)), |
|
232 gf.mul(0x09, b3)), |
|
233 gf.mul(0x0e, b0)),3) |
|
234 + util.putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b2), |
|
235 gf.mul(0x0d, b3)), |
|
236 gf.mul(0x09, b0)), |
|
237 gf.mul(0x0e, b1)),2) |
|
238 + util.putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b3), |
|
239 gf.mul(0x0d, b0)), |
|
240 gf.mul(0x09, b1)), |
|
241 gf.mul(0x0e, b2)),1) |
|
242 + util.putByte(gf.add(gf.add(gf.add(gf.mul(0x0b, b0), |
|
243 gf.mul(0x0d, b1)), |
|
244 gf.mul(0x09, b2)), |
|
245 gf.mul(0x0e, b3)),0); |
|
246 end |
|
247 |
|
248 -- |
|
249 -- Optimized inverse mix column |
|
250 -- look at http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf |
|
251 -- TODO: make it work |
|
252 -- |
|
253 function private.invMixColumn(word) |
|
254 local b0 = util.getByte(word,3); |
|
255 local b1 = util.getByte(word,2); |
|
256 local b2 = util.getByte(word,1); |
|
257 local b3 = util.getByte(word,0); |
|
258 |
|
259 local t = bit.bxor(b3,b2); |
|
260 local u = bit.bxor(b1,b0); |
|
261 local v = bit.bxor(t,u); |
|
262 v = bit.bxor(v,gf.mul(0x08,v)); |
|
263 v = bit.bxor(v,gf.mul(0x04, bit.bxor(b3,b1))); |
|
264 local w = bit.bxor(v,gf.mul(0x04, bit.bxor(b2,b0))); |
|
265 |
|
266 return util.putByte( bit.bxor(bit.bxor(b3,v), gf.mul(0x02, bit.bxor(b0,b3))), 0) |
|
267 + util.putByte( bit.bxor(bit.bxor(b2,w), gf.mul(0x02, t )), 1) |
|
268 + util.putByte( bit.bxor(bit.bxor(b1,v), gf.mul(0x02, bit.bxor(b0,b3))), 2) |
|
269 + util.putByte( bit.bxor(bit.bxor(b0,w), gf.mul(0x02, u )), 3); |
|
270 end |
|
271 |
|
272 -- |
|
273 -- generate key schedule for aes decryption |
|
274 -- |
|
275 -- uses key schedule for aes encryption and transforms each |
|
276 -- key by inverse mix column. |
|
277 -- |
|
278 function public.expandDecryptionKey(key) |
|
279 local keySchedule = public.expandEncryptionKey(key); |
|
280 if (keySchedule == nil) then |
|
281 return nil; |
|
282 end |
|
283 |
|
284 keySchedule[public.KEY_TYPE] = public.DECRYPTION_KEY; |
|
285 |
|
286 for i = 4, (keySchedule[public.ROUNDS] + 1)*4 - 5 do |
|
287 keySchedule[i] = private.invMixColumnOld(keySchedule[i]); |
|
288 end |
|
289 |
|
290 return keySchedule; |
|
291 end |
|
292 |
|
293 -- |
|
294 -- xor round key to state |
|
295 -- |
|
296 function private.addRoundKey(state, key, round) |
|
297 for i = 0, 3 do |
|
298 state[i] = bit.bxor(state[i], key[round*4+i]); |
|
299 end |
|
300 end |
|
301 |
|
302 -- |
|
303 -- do encryption round (ShiftRow, SubBytes, MixColumn together) |
|
304 -- |
|
305 function private.doRound(origState, dstState) |
|
306 dstState[0] = bit.bxor(bit.bxor(bit.bxor( |
|
307 private.table0[util.getByte(origState[0],3)], |
|
308 private.table1[util.getByte(origState[1],2)]), |
|
309 private.table2[util.getByte(origState[2],1)]), |
|
310 private.table3[util.getByte(origState[3],0)]); |
|
311 |
|
312 dstState[1] = bit.bxor(bit.bxor(bit.bxor( |
|
313 private.table0[util.getByte(origState[1],3)], |
|
314 private.table1[util.getByte(origState[2],2)]), |
|
315 private.table2[util.getByte(origState[3],1)]), |
|
316 private.table3[util.getByte(origState[0],0)]); |
|
317 |
|
318 dstState[2] = bit.bxor(bit.bxor(bit.bxor( |
|
319 private.table0[util.getByte(origState[2],3)], |
|
320 private.table1[util.getByte(origState[3],2)]), |
|
321 private.table2[util.getByte(origState[0],1)]), |
|
322 private.table3[util.getByte(origState[1],0)]); |
|
323 |
|
324 dstState[3] = bit.bxor(bit.bxor(bit.bxor( |
|
325 private.table0[util.getByte(origState[3],3)], |
|
326 private.table1[util.getByte(origState[0],2)]), |
|
327 private.table2[util.getByte(origState[1],1)]), |
|
328 private.table3[util.getByte(origState[2],0)]); |
|
329 end |
|
330 |
|
331 -- |
|
332 -- do last encryption round (ShiftRow and SubBytes) |
|
333 -- |
|
334 function private.doLastRound(origState, dstState) |
|
335 dstState[0] = util.putByte(private.SBox[util.getByte(origState[0],3)], 3) |
|
336 + util.putByte(private.SBox[util.getByte(origState[1],2)], 2) |
|
337 + util.putByte(private.SBox[util.getByte(origState[2],1)], 1) |
|
338 + util.putByte(private.SBox[util.getByte(origState[3],0)], 0); |
|
339 |
|
340 dstState[1] = util.putByte(private.SBox[util.getByte(origState[1],3)], 3) |
|
341 + util.putByte(private.SBox[util.getByte(origState[2],2)], 2) |
|
342 + util.putByte(private.SBox[util.getByte(origState[3],1)], 1) |
|
343 + util.putByte(private.SBox[util.getByte(origState[0],0)], 0); |
|
344 |
|
345 dstState[2] = util.putByte(private.SBox[util.getByte(origState[2],3)], 3) |
|
346 + util.putByte(private.SBox[util.getByte(origState[3],2)], 2) |
|
347 + util.putByte(private.SBox[util.getByte(origState[0],1)], 1) |
|
348 + util.putByte(private.SBox[util.getByte(origState[1],0)], 0); |
|
349 |
|
350 dstState[3] = util.putByte(private.SBox[util.getByte(origState[3],3)], 3) |
|
351 + util.putByte(private.SBox[util.getByte(origState[0],2)], 2) |
|
352 + util.putByte(private.SBox[util.getByte(origState[1],1)], 1) |
|
353 + util.putByte(private.SBox[util.getByte(origState[2],0)], 0); |
|
354 end |
|
355 |
|
356 -- |
|
357 -- do decryption round |
|
358 -- |
|
359 function private.doInvRound(origState, dstState) |
|
360 dstState[0] = bit.bxor(bit.bxor(bit.bxor( |
|
361 private.tableInv0[util.getByte(origState[0],3)], |
|
362 private.tableInv1[util.getByte(origState[3],2)]), |
|
363 private.tableInv2[util.getByte(origState[2],1)]), |
|
364 private.tableInv3[util.getByte(origState[1],0)]); |
|
365 |
|
366 dstState[1] = bit.bxor(bit.bxor(bit.bxor( |
|
367 private.tableInv0[util.getByte(origState[1],3)], |
|
368 private.tableInv1[util.getByte(origState[0],2)]), |
|
369 private.tableInv2[util.getByte(origState[3],1)]), |
|
370 private.tableInv3[util.getByte(origState[2],0)]); |
|
371 |
|
372 dstState[2] = bit.bxor(bit.bxor(bit.bxor( |
|
373 private.tableInv0[util.getByte(origState[2],3)], |
|
374 private.tableInv1[util.getByte(origState[1],2)]), |
|
375 private.tableInv2[util.getByte(origState[0],1)]), |
|
376 private.tableInv3[util.getByte(origState[3],0)]); |
|
377 |
|
378 dstState[3] = bit.bxor(bit.bxor(bit.bxor( |
|
379 private.tableInv0[util.getByte(origState[3],3)], |
|
380 private.tableInv1[util.getByte(origState[2],2)]), |
|
381 private.tableInv2[util.getByte(origState[1],1)]), |
|
382 private.tableInv3[util.getByte(origState[0],0)]); |
|
383 end |
|
384 |
|
385 -- |
|
386 -- do last decryption round |
|
387 -- |
|
388 function private.doInvLastRound(origState, dstState) |
|
389 dstState[0] = util.putByte(private.iSBox[util.getByte(origState[0],3)], 3) |
|
390 + util.putByte(private.iSBox[util.getByte(origState[3],2)], 2) |
|
391 + util.putByte(private.iSBox[util.getByte(origState[2],1)], 1) |
|
392 + util.putByte(private.iSBox[util.getByte(origState[1],0)], 0); |
|
393 |
|
394 dstState[1] = util.putByte(private.iSBox[util.getByte(origState[1],3)], 3) |
|
395 + util.putByte(private.iSBox[util.getByte(origState[0],2)], 2) |
|
396 + util.putByte(private.iSBox[util.getByte(origState[3],1)], 1) |
|
397 + util.putByte(private.iSBox[util.getByte(origState[2],0)], 0); |
|
398 |
|
399 dstState[2] = util.putByte(private.iSBox[util.getByte(origState[2],3)], 3) |
|
400 + util.putByte(private.iSBox[util.getByte(origState[1],2)], 2) |
|
401 + util.putByte(private.iSBox[util.getByte(origState[0],1)], 1) |
|
402 + util.putByte(private.iSBox[util.getByte(origState[3],0)], 0); |
|
403 |
|
404 dstState[3] = util.putByte(private.iSBox[util.getByte(origState[3],3)], 3) |
|
405 + util.putByte(private.iSBox[util.getByte(origState[2],2)], 2) |
|
406 + util.putByte(private.iSBox[util.getByte(origState[1],1)], 1) |
|
407 + util.putByte(private.iSBox[util.getByte(origState[0],0)], 0); |
|
408 end |
|
409 |
|
410 -- |
|
411 -- encrypts 16 Bytes |
|
412 -- key encryption key schedule |
|
413 -- input array with input data |
|
414 -- inputOffset start index for input |
|
415 -- output array for encrypted data |
|
416 -- outputOffset start index for output |
|
417 -- |
|
418 function public.encrypt(key, input, inputOffset, output, outputOffset) |
|
419 --default parameters |
|
420 inputOffset = inputOffset or 1; |
|
421 output = output or {}; |
|
422 outputOffset = outputOffset or 1; |
|
423 |
|
424 local state = {}; |
|
425 local tmpState = {}; |
|
426 |
|
427 if (key[public.KEY_TYPE] ~= public.ENCRYPTION_KEY) then |
|
428 error("No encryption key: "..key[public.KEY_TYPE]); |
|
429 return; |
|
430 end |
|
431 |
|
432 state = util.bytesToInts(input, inputOffset, 4); |
|
433 private.addRoundKey(state, key, 0); |
|
434 |
|
435 local round = 1; |
|
436 while (round < key[public.ROUNDS] - 1) do |
|
437 -- do a double round to save temporary assignments |
|
438 private.doRound(state, tmpState); |
|
439 private.addRoundKey(tmpState, key, round); |
|
440 round = round + 1; |
|
441 |
|
442 private.doRound(tmpState, state); |
|
443 private.addRoundKey(state, key, round); |
|
444 round = round + 1; |
|
445 end |
|
446 |
|
447 private.doRound(state, tmpState); |
|
448 private.addRoundKey(tmpState, key, round); |
|
449 round = round +1; |
|
450 |
|
451 private.doLastRound(tmpState, state); |
|
452 private.addRoundKey(state, key, round); |
|
453 |
|
454 return util.intsToBytes(state, output, outputOffset); |
|
455 end |
|
456 |
|
457 -- |
|
458 -- decrypt 16 bytes |
|
459 -- key decryption key schedule |
|
460 -- input array with input data |
|
461 -- inputOffset start index for input |
|
462 -- output array for decrypted data |
|
463 -- outputOffset start index for output |
|
464 --- |
|
465 function public.decrypt(key, input, inputOffset, output, outputOffset) |
|
466 -- default arguments |
|
467 inputOffset = inputOffset or 1; |
|
468 output = output or {}; |
|
469 outputOffset = outputOffset or 1; |
|
470 |
|
471 local state = {}; |
|
472 local tmpState = {}; |
|
473 |
|
474 if (key[public.KEY_TYPE] ~= public.DECRYPTION_KEY) then |
|
475 error("No decryption key: "..key[public.KEY_TYPE]); |
|
476 return; |
|
477 end |
|
478 |
|
479 state = util.bytesToInts(input, inputOffset, 4); |
|
480 private.addRoundKey(state, key, key[public.ROUNDS]); |
|
481 |
|
482 local round = key[public.ROUNDS] - 1; |
|
483 while (round > 2) do |
|
484 -- do a double round to save temporary assignments |
|
485 private.doInvRound(state, tmpState); |
|
486 private.addRoundKey(tmpState, key, round); |
|
487 round = round - 1; |
|
488 |
|
489 private.doInvRound(tmpState, state); |
|
490 private.addRoundKey(state, key, round); |
|
491 round = round - 1; |
|
492 end |
|
493 |
|
494 private.doInvRound(state, tmpState); |
|
495 private.addRoundKey(tmpState, key, round); |
|
496 round = round - 1; |
|
497 |
|
498 private.doInvLastRound(tmpState, state); |
|
499 private.addRoundKey(state, key, round); |
|
500 |
|
501 return util.intsToBytes(state, output, outputOffset); |
|
502 end |
|
503 |
|
504 -- calculate all tables when loading this file |
|
505 private.calcSBox(); |
|
506 private.calcRoundTables(); |
|
507 private.calcInvRoundTables(); |
|
508 |
|
509 return public; |