3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2 of the License, or
6 (at your option) any later version.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 **** AES en/decryption 2 (byte version) ****************************************
21 Copyright Volker van Nek, 2012 - 2015
24 This is an alternative to share/contrib/gf/aes.mac. It works at byte level
25 and shows how to use precomputed lookup tables. It also presents a concise
26 description of the mix_columns operation by using matrix-dot-multiplication
27 controlled by Maxima's global variables matrix_element_add and .._mult.
29 In this file states are implemented as 4x4 arrays with destructively working
32 See below for examples.
35 June, 2015: New user interface.
36 The functions make_state and coerce_state provide flexible support for
37 different data types. States can be build from octet-strings, integers and
38 lists of octets and can be coerced back to these three types.
39 The new interface is based on new functions in share/stringproc. So you
40 should have installed Maxima version 5.37 or higher.
44 /* GF arithmetic by lookup tables ******************************************* */
46 gf_set_data(2, x^8+x^4+x^3+x+1)$
50 mult_by_table(a, b) :=
51 if a = 0 or b = 0 then 0
52 else gf_powers[ ?mod(gf_logs[a] + gf_logs[b], 255.) ]$
56 else gf_powers[255. - gf_logs[a]]$
59 /* sub_bytes **************************************************************** */
61 %_byte_sub : make_array(fixnum, 256.)$
64 ?logior(?logand(?ash(b, n), 255.), ?ash(b, n-8))$
66 for byte : 0 thru 255. do
69 x : inv_by_table(byte),
72 s : cons(byte_lrot(x, n), s),
76 printf(true, "~{~16@{~2,'0x ~}~%~}", listarray(%_byte_sub))$
81 state[i, j] : %_byte_sub[state[i, j]] $
84 /* inv_sub_bytes ************************************************************ */
86 %_inv_byte_sub : make_array(fixnum, 256.)$
88 for byte : 0 thru 255. do
89 %_inv_byte_sub[byte] :
91 x : ?logxor(byte, 99.),
92 s : [byte_lrot(x, 1), byte_lrot(x, 3), byte_lrot(x, 6)],
93 inv_by_table( apply('?logxor, s) ) )$
95 print_inv_byte_sub() :=
96 printf(true, "~{~16@{~2,'0x ~}~%~}", listarray(%_inv_byte_sub))$
98 inv_sub_bytes(state) :=
101 state[r, c] : %_inv_byte_sub[state[r, c]] $
104 /* shift_rows *************************************************************** */
106 rotate1(state, r) := block([sr0 : state[r, 0]],
108 state[r, c] : state[r, c+1],
111 rotate2(state) := block([s],
112 s : state[2, 0], state[2, 0] : state[2, 2], state[2, 2] : s,
113 s : state[2, 1], state[2, 1] : state[2, 3], state[2, 3] : s )$
115 inv_rotate1(state, r) := block([sr3 : state[r, 3]],
116 for c:3 step -1 thru 1 do
117 state[r, c] : state[r, c-1],
120 shift_rows(state) := (
121 rotate1(state, 1), rotate2(state), inv_rotate1(state, 3) )$
124 /* inv_shift_rows *********************************************************** */
126 inv_shift_rows(state) := (
127 rotate1(state, 3), rotate2(state), inv_rotate1(state, 1) )$
130 /* mix_columns ************************************************************** */
132 matrix_element_add : ?logxor $
134 matrix_element_mult : mult_by_table $
136 %_MIX_COLUMNS : matrix(
142 mix_columns(state) := block([mat],
143 mat : %_MIX_COLUMNS . genmatrix(state, 3,3,0,0),
144 fillarray(state, flatten(args(mat))) )$
147 /* inv_mix_columns ********************************************************** */
150 matrixmap('gf_p2n, gf_invert_by_lu( matrixmap('gf_n2p, %_MIX_COLUMNS) ) )$
152 inv_mix_columns(state) := block([mat],
153 mat : %_INV_MIX_COLUMNS . genmatrix(state, 3,3,0,0),
154 fillarray(state, flatten(args(mat))) )$
157 /* add_round_key ************************************************************ */
159 add_round_key(state, key) :=
162 state[r, c] : ?logxor(state[r, c], key[r, c])$
165 /* key_expansion ************************************************************ */
167 %_rcon : make_array(fixnum, 4, 10.)$
170 %_rcon[0, i] : gf_p2n(gf_exp(x, i))$
172 rot_word(old, new) := (
173 new[3, 0] : old[0, 3],
175 new[r, 0] : old[r+1, 3] )$
177 key_expansion1(old, new, n) := (
180 new[r, 0] : %_byte_sub[new[r, 0]],
182 new[r, 0] : ?logxor(old[r, 0], new[r, 0], %_rcon[r, n-1]) )$
184 key_expansion2(old, new) :=
187 new[r, c] : ?logxor(new[r, c-1], old[r, c])$
189 %_round_key : make_array(any, 11.)$
191 make_round_key(n) := (
192 %_round_key[n] : make_array(fixnum, 4, 4),
193 key_expansion1(%_round_key[n-1], %_round_key[n], n),
194 key_expansion2(%_round_key[n-1], %_round_key[n]) )$
196 key_expansion(key) := (
197 %_round_key[0] : key,
198 for n:1 thru 10. do make_round_key(n) )$
201 /* cipher ******************************************************************* */
204 cipher and inv_cipher both change the state destructively.
207 add_round_key(state, %_round_key[0]),
213 add_round_key(state, %_round_key[n]) ),
217 add_round_key(state, %_round_key[10.]) )$
220 /* inv_cipher *************************************************************** */
222 inv_cipher(state) := (
223 add_round_key(state, %_round_key[10.]),
225 for n:9 step -1 thru 1 do (
226 inv_shift_rows(state),
227 inv_sub_bytes(state),
228 add_round_key(state, %_round_key[n]),
229 inv_mix_columns(state) ),
231 inv_sub_bytes(state),
232 inv_shift_rows(state),
233 add_round_key(state, %_round_key[0]) )$
236 /* user interface *********************************************************** */
238 matrix_to_state(matrix) := block([state],
239 state : make_array(fixnum, 4, 4),
240 fillarray(state, flatten(args(matrix))) )$
242 state_to_matrix(state) := genmatrix(state, 3, 3, 0, 0)$
244 print_block(block) :=
245 printf(true, "~{~4@{~2,'0x ~}~%~}", listarray(block))$
248 Setting ibase causes SBCL not to compile make_state.
250 arg can be an octet-string, an integer or a list of octets.
251 The return value is a Lisp array.
253 make_state(arg) := block([state, c:0, r:0, ibase:16.],
254 if stringp(arg) then arg : parse_string(sconcat(0, arg)),
255 if integerp(arg) then arg : number_to_octets(arg),
256 while length(arg) < 16. do arg : cons(0, arg),
257 state : make_array(fixnum, 4, 4),
258 for octet in arg do (
260 if (r : r+1) = 4 then (r : 0, c : c+1) ),
264 The first argument must be the state, a 4x4 Lisp array.
265 The second optional argument can be 'list (the default), 'number or 'string.
266 Accordingly the return value is a list of octets, an integer or an octet-string.
268 coerce_state([args]) := block([state:args[1], type, res:[]],
269 for c:3 thru 0 step -1 do
270 for r:3 thru 0 step -1 do
271 res : cons(state[r, c], res),
272 type : if length(args) = 2 then args[2],
273 if type = 'number then res : octets_to_number(res),
274 if type = 'string then res : printf(false, "~{~2,'0x~}", res),
278 /* compilation ************************************************************** */
281 If speed matters it is recommended to compile the functions rather than to
282 compile the file. When compiling use input base 10.
284 SBCL doesn't want to compile make_state.
289 mult_by_table, inv_by_table,
290 byte_lrot, sub_bytes, inv_sub_bytes, print_byte_sub, print_inv_byte_sub,
291 rotate1, rotate2, inv_rotate1, shift_rows, inv_shift_rows,
292 mix_columns, inv_mix_columns,
294 rot_word, key_expansion1, key_expansion2, make_round_key, key_expansion,
296 matrix_to_state, state_to_matrix, print_block, coerce_state )$
300 /* examples ***************************************************************** */
303 http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
304 (Nov 26, 2001, AES specification)
309 (%i2) ibase : obase : 16.$
311 (%i3) state : make_state("00112233445566778899aabbccddeeff")$
313 (%i4) key : make_state("000102030405060708090a0b0c0d0e0f")$
315 (%i5) key_expansion(key)$
319 (%i7) coerce_state(state);
320 (%o7) [69,0C4,0E0,0D8,6A,7B,4,30,0D8,0CD,0B7,80,70,0B4,0C5,5A]
321 (%i8) inv_cipher(state);
323 (%i9) coerce_state(state, 'string);
324 (%o9) 00112233445566778899AABBCCDDEEFF
326 fips-197.pdf, page 33/34:
328 (%i0A) state : make_state("3243f6a8885a308d313198a2e0370734");
329 (%o0A) Lisp array [4,4]
330 (%i0B) key : make_state("2b7e151628aed2a6abf7158809cf4f3c")$
332 (%i0C) key_expansion(key)$
334 (%i0D) print_block(key)$
340 (%i0E) print_block(state)$
345 (%i0F) cipher(state);
347 (%i10) print_block(state)$
352 (%i11) inv_cipher(state);
354 (%i12) print_block(state)$