Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/crypto/mq/sr.py
8818 views
1
r"""
2
Small Scale Variants of the AES (SR) Polynomial System Generator
3
4
Sage supports polynomial system generation for small scale (and full
5
scale) AES variants over `\GF{2}` and `\GF{2^e}`. Also, Sage supports
6
both the specification of SR as given in the papers [CMR05]_ and
7
[CMR06]_ and a variant of SR* which is equivalent to AES.
8
9
SR is a family of parameterizable variants of the AES suitable as a
10
framework for comparing different cryptanalytic techniques that can be
11
brought to bear on the AES. It is different from
12
:class:`Mini-AES <sage.crypto.block_cipher.miniaes.MiniAES>`, whose
13
purpose is as a teaching tool to help beginners understand the basic
14
structure and working of the full AES.
15
16
AUTHORS:
17
18
- Martin Albrecht (2008,2009-01): usability improvements
19
20
- Martin Albrecht (2007-09): initial version
21
22
- Niles Johnson (2010-08): Trac #3893: ``random_element()`` should pass on ``*args`` and ``**kwds``.
23
24
EXAMPLES:
25
26
We construct SR(1,1,1,4) and study its properties.
27
::
28
29
sage: sr = mq.SR(1, 1, 1, 4)
30
31
``n`` is the number of rounds, ``r`` the number of rows in the
32
state array, ``c`` the number of columns in the state array, and ``e`` the
33
degree of the underlying field.
34
35
::
36
37
sage: sr.n, sr.r, sr.c, sr.e
38
(1, 1, 1, 4)
39
40
By default variables are ordered reverse to as they appear, e.g.::
41
42
sage: print sr.R.repr_long()
43
Polynomial Ring
44
Base Ring : Finite Field in a of size 2^4
45
Size : 20 Variables
46
Block 0 : Ordering : deglex
47
Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003, k000, k001, k002, k003
48
49
However, this can be prevented by passing in ``reverse_variables=False`` to the constructor.
50
51
For SR(1, 1, 1, 4) the ``ShiftRows`` matrix isn't that interesting.::
52
53
sage: sr.ShiftRows
54
[1 0 0 0]
55
[0 1 0 0]
56
[0 0 1 0]
57
[0 0 0 1]
58
59
Also, the ``MixColumns`` matrix is the identity matrix.::
60
61
sage: sr.MixColumns
62
[1 0 0 0]
63
[0 1 0 0]
64
[0 0 1 0]
65
[0 0 0 1]
66
67
``Lin``, however, is not the identity matrix.::
68
69
sage: sr.Lin
70
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
71
[ a a 1 a^3 + a^2 + a + 1]
72
[ a^3 + a a^2 a^2 1]
73
[ 1 a^3 a + 1 a + 1]
74
75
``M`` and ``Mstar`` are identical for SR(1, 1, 1, 4)::
76
77
sage: sr.M
78
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
79
[ a a 1 a^3 + a^2 + a + 1]
80
[ a^3 + a a^2 a^2 1]
81
[ 1 a^3 a + 1 a + 1]
82
83
::
84
85
sage: sr.Mstar
86
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
87
[ a a 1 a^3 + a^2 + a + 1]
88
[ a^3 + a a^2 a^2 1]
89
[ 1 a^3 a + 1 a + 1]
90
91
92
However, for larger instances of SR Mstar is not equal to M::
93
94
sage: sr = mq.SR(10,4,4,8)
95
sage: sr.Mstar == ~sr.MixColumns * sr.M
96
True
97
98
We can compute a Groebner basis for the ideals spanned by SR
99
instances to recover all solutions to the system.::
100
101
sage: sr = mq.SR(1,1,1,4, gf2=True, polybori=True)
102
sage: K = sr.base_ring()
103
sage: a = K.gen()
104
sage: K = [a]
105
sage: P = [1]
106
sage: F,s = sr.polynomial_system(P=P, K=K)
107
sage: F.groebner_basis()
108
[k100, k101 + 1, k102, k103 + k003,
109
x100 + 1, x101 + k003 + 1, x102 + k003 + 1,
110
x103 + k003, w100, w101, w102 + 1, w103 + k003 + 1,
111
s000 + 1, s001 + k003, s002 + k003, s003 + k003 + 1,
112
k000, k001, k002 + 1]
113
114
Note that the order of ``k000``, ``k001``, ``k002`` and ``k003`` is
115
little endian. Thus the result ``k002 + 1, k001, k000`` indicates that
116
the key is either `a` or `a+1`. We can verify that both keys encrypt P
117
to the same ciphertext::
118
119
sage: sr(P,[a])
120
[0]
121
sage: sr(P,[a+1])
122
[0]
123
124
All solutions can easily be recovered using the variety function for ideals.::
125
126
sage: I = F.ideal()
127
sage: for V in I.variety():
128
... for k,v in sorted(V.iteritems()):
129
... print k,v
130
... print
131
k003 0
132
k002 1
133
k001 0
134
k000 0
135
s003 1
136
s002 0
137
s001 0
138
s000 1
139
w103 1
140
w102 1
141
w101 0
142
w100 0
143
x103 0
144
x102 1
145
x101 1
146
x100 1
147
k103 0
148
k102 0
149
k101 1
150
k100 0
151
<BLANKLINE>
152
k003 1
153
k002 1
154
k001 0
155
k000 0
156
s003 0
157
s002 1
158
s001 1
159
s000 1
160
w103 0
161
w102 1
162
w101 0
163
w100 0
164
x103 1
165
x102 0
166
x101 0
167
x100 1
168
k103 1
169
k102 0
170
k101 1
171
k100 0
172
173
We can also verify the correctness of the variety by evaluating all
174
ideal generators on all points.::
175
176
sage: for V in I.variety():
177
... for f in I.gens():
178
... if f.subs(V) != 0:
179
... print "epic fail"
180
181
182
Note that the S-Box object for SR can be constructed with a call to ``sr.sbox()``::
183
184
sage: sr = mq.SR(1,1,1,4, gf2=True, polybori=True)
185
sage: S = sr.sbox()
186
187
For example, we can now study the difference distribution matrix of ``S``::
188
189
sage: S.difference_distribution_matrix()
190
[16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
191
[ 0 2 2 2 2 0 0 0 2 0 0 0 2 4 0 0]
192
[ 0 2 0 4 2 2 2 0 0 2 0 0 0 0 0 2]
193
[ 0 2 4 0 0 2 0 0 2 2 0 2 0 0 2 0]
194
[ 0 0 2 0 4 2 0 0 0 0 2 0 2 0 2 2]
195
[ 0 0 0 2 0 0 0 2 4 2 0 0 2 0 2 2]
196
[ 0 4 0 0 0 2 0 2 0 2 2 0 2 2 0 0]
197
[ 0 2 0 0 0 0 2 0 0 0 0 2 4 2 2 2]
198
[ 0 2 2 0 0 0 2 2 2 0 2 0 0 0 0 4]
199
[ 0 0 2 2 0 0 0 0 0 2 2 4 0 2 0 2]
200
[ 0 0 2 0 2 0 2 2 0 4 0 2 2 0 0 0]
201
[ 0 0 0 0 2 0 2 0 2 2 4 0 0 2 2 0]
202
[ 0 0 0 2 0 4 2 0 2 0 2 2 2 0 0 0]
203
[ 0 0 0 0 2 2 0 4 2 0 0 2 0 2 0 2]
204
[ 0 0 2 2 0 2 4 2 0 0 0 0 0 2 2 0]
205
[ 0 2 0 2 2 0 0 2 0 0 2 2 0 0 4 0]
206
207
or use ``S`` to find alternative polynomial representations for the S-Box.::
208
209
sage: S.polynomials(degree=3)
210
[x0*x1 + x1*x2 + x0*x3 + x0*y2 + x1 + y0 + y1 + 1,
211
x0*x1 + x0*x2 + x0*y0 + x0*y1 + x0*y2 + x1 + x2 + y0 + y1 + y2,
212
x0*x1 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x1*y0 + x0*y1 + x0*y3,
213
x0*x1 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x1*y1 + x0*y3 + x1 + y0 + y1 + 1,
214
x0*x1 + x0*x2 + x0*y2 + x1*y2 + x0*y3 + x0 + x1,
215
x0*x3 + x1*x3 + x0*y1 + x0*y2 + x1*y3 + x0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
216
x0*x1 + x1*x3 + x2*x3 + x0*y0 + x0*y2 + x0*y3 + x2 + y0 + y3,
217
x0*x1 + x0*x2 + x0*x3 + x1*x3 + x2*y0 + x0*y2 + x0 + x2 + x3 + y3,
218
x0*x3 + x1*x3 + x0*y0 + x2*y1 + x0*y2 + x3 + y3,
219
x0*x1 + x0*x2 + x0*y0 + x0*y1 + x2*y2 + x0*y3 + x1 + y0 + y1 + 1,
220
x0*x3 + x1*x3 + x0*y0 + x0*y1 + x0*y3 + x2*y3 + y0 + y3,
221
x0*x1 + x0*x2 + x3*y0 + x0*y1 + x0*y3 + y0,
222
x0*y0 + x0*y1 + x3*y1 + x0 + x2 + y0 + y3,
223
x0*y0 + x3*y2 + y0,
224
x0*x1 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x0*y2 + x3*y3 + y0,
225
x0*x2 + x0*x3 + x0*y1 + y0*y1 + x0*y3 + x2 + x3 + y3,
226
x0*x2 + x0*y0 + y0*y2 + x0*y3 + x0 + y0,
227
x0*x1 + x0*x2 + x1*x3 + x0*y2 + y0*y3 + y0,
228
x0*x1 + x0*y0 + y1*y2 + x0*y3 + x1 + x2 + y0 + 1,
229
x0*x2 + x1*x3 + x0*y1 + x0*y2 + x0*y3 + y1*y3 + x0 + y0 + y3,
230
x0*x1 + x0*x2 + x0*x3 + x0*y1 + x0*y2 + x0*y3 + y2*y3 + x0 + x1 + x2 + x3 + y1 + y3 + 1,
231
x0*x1*x2 + x0*x3 + x0*y0 + x0*y1 + x0*y2 + x0,
232
x0*x1*x3 + x0*x2 + x0*x3 + x0*y1 + x0*y3 + x0,
233
x0*x1*y0 + x0*x1 + x0*y0 + x0,
234
x0*x1*y1,
235
x0*x1*y2 + x0*x2 + x0*y2 + x0*y3 + x0,
236
x0*x1*y3 + x0*x1 + x0*x3 + x0*y0 + x0*y1 + x0*y2 + x0,
237
x0*x2*x3 + x0*x1 + x0*x3 + x0*y1 + x0*y2 + x0*y3 + x0,
238
x0*x2*y0 + x0*x1 + x0*x2 + x0*x3 + x0*y1 + x0*y2,
239
x0*x2*y1 + x0*x2 + x0*x3 + x0*y0 + x0*y1 + x0*y2 + x0,
240
x0*x2*y2 + x0*x2 + x0*y3 + x0,
241
x0*x2*y3 + x0*x2 + x0*y3 + x0,
242
x0*x3*y0 + x0*x1 + x0*x2 + x0*y0 + x0*y1 + x0*y3,
243
x0*x3*y1 + x0*x2 + x0*y1 + x0*y3 + x0,
244
x0*x3*y2,
245
x0*x3*y3 + x0*x1 + x0*y1 + x0*y2 + x0*y3 + x0,
246
x0*y0*y1 + x0*y1,
247
x0*y0*y2 + x0*x2 + x0*y3 + x0,
248
x0*y0*y3 + x0*x1 + x0*x3 + x0*y0 + x0*y1 + x0*y2 + x0*y3 + x0,
249
x0*y1*y2 + x0*x2 + x0*y3 + x0,
250
x0*y1*y3 + x0*x3 + x0*y0 + x0*y2 + x0*y3,
251
x0*y2*y3 + x0*y2,
252
x1*x2*x3 + x0*x1 + x1*x3 + x0*y0 + x0*y1 + x2 + x3 + y3,
253
x1*x2*y0 + x0*x1 + x1*x3 + x0*y0 + x0*y1 + x2 + x3 + y3,
254
x1*x2*y1 + x0*x1 + x1*x3 + x0*y0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
255
x1*x2*y2 + x0*x1 + x0*y0 + x0*y1 + x0 + x1 + y0 + y1 + 1,
256
x1*x2*y3 + x0*x1 + x1*x3 + x0*y0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
257
x1*x3*y0 + x0*x1 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x0*y1 + x0*y3,
258
x1*x3*y1 + x0*x2 + x0*x3 + x0*y3 + x2 + x3 + y3,
259
x1*x3*y2 + x0*x2 + x0*x3 + x1*x3 + x0*y1 + x0*y3 + x0,
260
x1*x3*y3 + x0*x1 + x0*x2 + x0*x3 + x0*y0 + x0*y1 + x0*y3,
261
x1*y0*y1 + x0*x2 + x0*x3 + x0*y3 + x2 + x3 + y3,
262
x1*y0*y2 + x0*x2 + x0*x3 + x1*x3 + x0*y1 + x0*y3 + x0,
263
x1*y0*y3,
264
x1*y1*y2 + x0*x1 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x0*y3 + x1 + y0 + y1 + 1,
265
x1*y1*y3 + x0*x1 + x1*x3 + x0*y0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
266
x1*y2*y3 + x0*x1 + x0*x2 + x1*x3 + x0*y0 + x0*y2 + x0*y3 + x0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
267
x2*x3*y0 + x0*x1 + x0*x3 + x1*x3 + x0*y2 + x0*y3 + x2 + x3 + y3,
268
x2*x3*y1 + x0*y1 + x0*y2 + x0*y3 + x3 + y0,
269
x2*x3*y2 + x1*x3 + x0*y1 + x0 + x2 + x3 + y3,
270
x2*x3*y3,
271
x2*y0*y1 + x0*x2 + x0*x3 + x0*y0 + x0*y1 + x0*y2 + x0,
272
x2*y0*y2 + x0*x2 + x1*x3 + x0*y1 + x0*y3 + x2 + x3 + y3,
273
x2*y0*y3 + x0*x2 + x0*y3 + x0,
274
x2*y1*y2 + x0*x1 + x0*x2 + x1*x3 + x0*y0 + x0*y3 + x0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
275
x2*y1*y3 + x0*x3 + x1*x3 + x0*y0 + x0*y1 + x0*y3 + y0 + y3,
276
x2*y2*y3 + x0*x1 + x0*x2 + x1*x3 + x0*y0 + x0*y3 + x0 + x1 + x2 + x3 + y0 + y1 + y3 + 1,
277
x3*y0*y1 + x0*x3 + x0*y1 + x0 + x2 + x3 + y3,
278
x3*y0*y2 + x0*y0 + y0,
279
x3*y0*y3 + x1*x3 + x0*y1 + x0*y2 + x0*y3 + y0,
280
x3*y1*y2 + x0*x2 + x0*x3 + x0*y3 + x2 + x3 + y3,
281
x3*y1*y3 + x0*x2 + x0*x3 + x0*y0 + x0*y2 + x0,
282
x3*y2*y3 + x0*x2 + x0*x3 + x1*x3 + x0*y0 + x0*y1 + x0*y3 + x0 + y0,
283
y0*y1*y2 + x0*x3 + x0 + x2 + x3 + y3,
284
y0*y1*y3 + x0*x3 + x0*y0 + x0*y2 + x0*y3,
285
y0*y2*y3 + x0*x3 + x1*x3 + x0*y0 + x0*y1 + y0,
286
y1*y2*y3 + x0*x1 + x0*x2 + x1*x3 + x0*y0 + x0*y3 + x0 + x1 + x2 + x3 + y0 + y1 + y3 + 1]
287
288
sage: S.interpolation_polynomial()
289
(a^2 + 1)*x^14 + x^13 + (a^3 + a^2)*x^11 + (a^2 + 1)*x^7 + a^2 + a
290
291
The :class:`SR_gf2_2` gives an example how use alternative polynomial
292
representations of the S-Box for construction of polynomial systems.
293
294
TESTS::
295
296
sage: sr == loads(dumps(sr))
297
True
298
299
REFERENCES:
300
301
.. [CMR05] C\. Cid, S\. Murphy, M\. Robshaw *Small Scale Variants of
302
the AES*\; in Proceedings of Fast Software Encryption 2005\; LNCS
303
3557\; Springer Verlag 2005\; available at
304
http://www.isg.rhul.ac.uk/~sean/smallAES-fse05.pdf
305
306
.. [CMR06] C\. Cid, S\. Murphy, and M\. Robshaw *Algebraic Aspects of
307
the Advanced Encryption Standard*\; Springer Verlag 2006
308
309
.. [MR02] S\. Murphy, M\. Robshaw *Essential Algebraic Structure
310
Within the AES*\; in Advances in Cryptology \- CRYPTO 2002\; LNCS
311
2442\; Springer Verlag 2002
312
"""
313
from sage.rings.finite_rings.constructor import FiniteField as GF
314
from sage.rings.integer_ring import ZZ
315
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing, BooleanPolynomialRing_constructor as BooleanPolynomialRing
316
317
from sage.matrix.matrix import is_Matrix
318
from sage.matrix.constructor import Matrix, random_matrix
319
from sage.matrix.matrix_space import MatrixSpace
320
321
from sage.misc.misc import get_verbose
322
from sage.misc.flatten import flatten
323
324
from sage.modules.vector_modn_dense import Vector_modn_dense
325
326
from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence
327
from mpolynomialsystemgenerator import MPolynomialSystemGenerator
328
329
from sage.rings.polynomial.term_order import TermOrder
330
331
def SR(n=1, r=1, c=1, e=4, star=False, **kwargs):
332
r"""
333
Return a small scale variant of the AES polynomial system
334
constructor subject to the following conditions:
335
336
INPUT:
337
338
- ``n`` - the number of rounds (default: 1)
339
- ``r`` - the number of rows in the state array (default: 1)
340
- ``c`` - the number of columns in the state array (default: 1)
341
- ``e`` - the exponent of the finite extension field (default: 4)
342
- ``star`` - determines if SR\* or SR should be constructed (default: ``False``)
343
- ``aes_mode`` - as the SR key schedule specification differs
344
slightly from the AES key schedule, this parameter controls
345
which schedule to use (default: ``True``)
346
- ``gf2`` - generate polynomial systems over `\GF{2}` rather than
347
over `\GF{2^e}` (default: ``False``)
348
- ``polybori`` - use the ``BooleanPolynomialRing`` as polynomial
349
representation (default: ``True``, `\GF{2}` only)
350
- ``order`` - a string to specify the term ordering of the
351
variables (default: ``deglex``)
352
- ``postfix`` - a string which is appended after the variable name
353
(default: '')
354
- ``allow_zero_inversions`` - a boolean to control whether zero
355
inversions raise an exception (default: ``False``)
356
- ``correct_only`` - only include correct inversion polynomials
357
(default: ``False``, `\GF{2}` only)
358
- ``biaffine_only`` - only include bilinear and biaffine inversion
359
polynomials (default: ``True``, `\GF{2}` only)
360
361
362
EXAMPLES::
363
364
sage: sr = mq.SR(1, 1, 1, 4)
365
sage: ShiftRows = sr.shift_rows_matrix()
366
sage: MixColumns = sr.mix_columns_matrix()
367
sage: Lin = sr.lin_matrix()
368
sage: M = MixColumns * ShiftRows * Lin
369
sage: print sr.hex_str_matrix(M)
370
5 1 C 5
371
2 2 1 F
372
A 4 4 1
373
1 8 3 3
374
375
::
376
377
sage: sr = mq.SR(1, 2, 1, 4)
378
sage: ShiftRows = sr.shift_rows_matrix()
379
sage: MixColumns = sr.mix_columns_matrix()
380
sage: Lin = sr.lin_matrix()
381
sage: M = MixColumns * ShiftRows * Lin
382
sage: print sr.hex_str_matrix(M)
383
F 3 7 F A 2 B A
384
A A 5 6 8 8 4 9
385
7 8 8 2 D C C 3
386
4 6 C C 5 E F F
387
A 2 B A F 3 7 F
388
8 8 4 9 A A 5 6
389
D C C 3 7 8 8 2
390
5 E F F 4 6 C C
391
392
::
393
394
sage: sr = mq.SR(1, 2, 2, 4)
395
sage: ShiftRows = sr.shift_rows_matrix()
396
sage: MixColumns = sr.mix_columns_matrix()
397
sage: Lin = sr.lin_matrix()
398
sage: M = MixColumns * ShiftRows * Lin
399
sage: print sr.hex_str_matrix(M)
400
F 3 7 F 0 0 0 0 0 0 0 0 A 2 B A
401
A A 5 6 0 0 0 0 0 0 0 0 8 8 4 9
402
7 8 8 2 0 0 0 0 0 0 0 0 D C C 3
403
4 6 C C 0 0 0 0 0 0 0 0 5 E F F
404
A 2 B A 0 0 0 0 0 0 0 0 F 3 7 F
405
8 8 4 9 0 0 0 0 0 0 0 0 A A 5 6
406
D C C 3 0 0 0 0 0 0 0 0 7 8 8 2
407
5 E F F 0 0 0 0 0 0 0 0 4 6 C C
408
0 0 0 0 A 2 B A F 3 7 F 0 0 0 0
409
0 0 0 0 8 8 4 9 A A 5 6 0 0 0 0
410
0 0 0 0 D C C 3 7 8 8 2 0 0 0 0
411
0 0 0 0 5 E F F 4 6 C C 0 0 0 0
412
0 0 0 0 F 3 7 F A 2 B A 0 0 0 0
413
0 0 0 0 A A 5 6 8 8 4 9 0 0 0 0
414
0 0 0 0 7 8 8 2 D C C 3 0 0 0 0
415
0 0 0 0 4 6 C C 5 E F F 0 0 0 0
416
"""
417
if not kwargs.get("gf2", False):
418
return SR_gf2n(n, r, c, e, star, **kwargs)
419
else:
420
return SR_gf2(n, r, c, e, star, **kwargs)
421
422
class SR_generic(MPolynomialSystemGenerator):
423
def __init__(self, n=1, r=1, c=1, e=4, star=False, **kwargs):
424
"""
425
Small Scale Variants of the AES.
426
427
EXAMPLES::
428
429
sage: sr = mq.SR(1, 1, 1, 4)
430
sage: ShiftRows = sr.shift_rows_matrix()
431
sage: MixColumns = sr.mix_columns_matrix()
432
sage: Lin = sr.lin_matrix()
433
sage: M = MixColumns * ShiftRows * Lin
434
sage: print sr.hex_str_matrix(M)
435
5 1 C 5
436
2 2 1 F
437
A 4 4 1
438
1 8 3 3
439
"""
440
if n-1 not in range(10):
441
raise TypeError, "n must be between 1 and 10 (inclusive)"
442
self._n = n
443
444
if r not in (1, 2, 4):
445
raise TypeError, "r must be in (1, 2, 4)"
446
self._r = r
447
448
if c not in (1, 2, 4):
449
raise TypeError, "c must be in (1, 2, 4)"
450
self._c = c
451
452
if e not in (4, 8):
453
raise TypeError, "e must be either 4 or 8"
454
self._e = e
455
456
self._star = bool(star)
457
458
self._base = self.base_ring()
459
460
self._postfix = kwargs.get("postfix", "")
461
self._order = kwargs.get("order", "deglex")
462
self._aes_mode = kwargs.get("aes_mode", True)
463
self._gf2 = kwargs.get("gf2", False)
464
self._allow_zero_inversions = bool(kwargs.get("allow_zero_inversions", False))
465
self._reverse_variables = bool(kwargs.get("reverse_variables", True))
466
467
with AllowZeroInversionsContext(self):
468
sub_byte_lookup = dict([(e, self.sub_byte(e)) for e in self._base])
469
self._sub_byte_lookup = sub_byte_lookup
470
471
if self._gf2:
472
self._polybori = kwargs.get("polybori", True)
473
474
def new_generator(self, **kwds):
475
r"""
476
Return a new ``SR`` instance equal to this instance
477
except for the parameters passed explicitly to this function.
478
479
INPUT:
480
481
- ``**kwds`` - see the ``SR`` constructor for accepted
482
parameters
483
484
EXAMPLE::
485
486
sage: sr = mq.SR(2,1,1,4); sr
487
SR(2,1,1,4)
488
sage: sr.ring().base_ring()
489
Finite Field in a of size 2^4
490
491
::
492
493
sage: sr2 = sr.new_generator(gf2=True); sr2
494
SR(2,1,1,4)
495
sage: sr2.ring().base_ring()
496
Finite Field of size 2
497
sage: sr3 = sr2.new_generator(correct_only=True)
498
sage: len(sr2.inversion_polynomials_single_sbox())
499
20
500
sage: len(sr3.inversion_polynomials_single_sbox())
501
19
502
"""
503
kwds.setdefault("n", self._n)
504
kwds.setdefault("r", self._r)
505
kwds.setdefault("c", self._c)
506
kwds.setdefault("e", self._e)
507
kwds.setdefault("star", self._star)
508
kwds.setdefault("postfix", self._postfix)
509
kwds.setdefault("order", self._order)
510
kwds.setdefault("allow_zero_inversions", self._allow_zero_inversions)
511
kwds.setdefault("aes_mode", self._aes_mode)
512
kwds.setdefault("gf2", self._gf2)
513
kwds.setdefault("reverse_variables", self._reverse_variables)
514
515
try:
516
polybori = self._polybori
517
except AttributeError:
518
polybori = False
519
kwds.setdefault("polybori", polybori)
520
521
try:
522
correct_only = self._correct_only
523
except AttributeError:
524
correct_only = False
525
kwds.setdefault("correct_only", correct_only)
526
527
try:
528
biaffine_only = self._biaffine_only
529
except AttributeError:
530
biaffine_only = False
531
kwds.setdefault("biaffine_only", biaffine_only)
532
533
if self._gf2 == kwds.get('gf2'):
534
return self.__class__(**kwds)
535
else:
536
return SR(**kwds)
537
538
def __getattr__(self, attr):
539
"""
540
EXAMPLE::
541
542
sage: sr = mq.SR(1, 2, 1, 4, gf2=True)
543
sage: sr.Mstar
544
[1 0 1 1 0 0 0 0]
545
[1 1 0 1 0 0 0 0]
546
[1 1 1 0 0 0 0 0]
547
[0 1 1 1 0 0 0 0]
548
[0 0 0 0 1 0 1 1]
549
[0 0 0 0 1 1 0 1]
550
[0 0 0 0 1 1 1 0]
551
[0 0 0 0 0 1 1 1]
552
"""
553
if attr == "e":
554
return self._e
555
elif attr == "c":
556
return self._c
557
elif attr == "n":
558
return self._n
559
elif attr == "r":
560
return self._r
561
562
elif attr == "R":
563
self.R = self.ring()
564
return self.R
565
elif attr == "k":
566
self.k = self.base_ring()
567
return self.k
568
569
elif attr == "Lin":
570
self.Lin = self.lin_matrix()
571
return self.Lin
572
573
elif attr == "ShiftRows":
574
self.ShiftRows = self.shift_rows_matrix()
575
return self.ShiftRows
576
577
elif attr == "MixColumns":
578
self.MixColumns = self.mix_columns_matrix()
579
return self.MixColumns
580
581
elif attr == "M":
582
self.M = self.MixColumns * self.ShiftRows * self.Lin
583
return self.M
584
585
elif attr == "Mstar":
586
self.Mstar = self.ShiftRows * self.Lin
587
return self.Mstar
588
589
raise AttributeError, "%s has no attribute %s"%(type(self), attr)
590
591
def _repr_(self):
592
"""
593
EXAMPLES::
594
595
sage: sr = mq.SR(1, 2, 2, 4); sr #indirect doctest
596
SR(1,2,2,4)
597
sage: sr = mq.SR(1, 2, 2, 4, star=True); sr
598
SR*(1,2,2,4)
599
"""
600
if self._star:
601
return "SR*(%d,%d,%d,%d)"%(self._n, self._r, self._c, self._e)
602
else:
603
return "SR(%d,%d,%d,%d)"%(self._n, self._r, self._c, self._e)
604
605
def base_ring(self):
606
r"""
607
Return the base field of self as determined by
608
``self.e``.
609
610
EXAMPLE::
611
612
sage: sr = mq.SR(10, 2, 2, 4)
613
sage: sr.base_ring().polynomial()
614
a^4 + a + 1
615
616
The Rijndael polynomial::
617
618
sage: sr = mq.SR(10, 4, 4, 8)
619
sage: sr.base_ring().polynomial()
620
a^8 + a^4 + a^3 + a + 1
621
"""
622
try:
623
return self._base
624
except AttributeError:
625
if self._e == 4:
626
self._base = GF(2**4, 'a', modulus=(1, 1, 0, 0, 1))
627
elif self._e == 8:
628
self._base = GF(2**8, 'a', modulus=(1, 1, 0, 1, 1, 0, 0, 0, 1))
629
630
return self._base
631
632
def __cmp__(self, other):
633
"""
634
Two generators are considered equal if they agree on all parameters
635
passed to them during construction.
636
637
EXAMPLE::
638
639
sage: sr1 = mq.SR(2, 2, 2, 4)
640
sage: sr2 = mq.SR(2, 2, 2, 4)
641
sage: sr1 == sr2
642
True
643
644
::
645
646
sage: sr1 = mq.SR(2, 2, 2, 4)
647
sage: sr2 = mq.SR(2, 2, 2, 4, gf2=True)
648
sage: sr1 == sr2
649
False
650
"""
651
return cmp( (self.n, self.r, self.c, self.e, self._postfix, self._order, self._allow_zero_inversions, self._aes_mode, self._gf2, self._star ),
652
(other.n, other.r, other.c, other.e, other._postfix, other._order, other._allow_zero_inversions, other._aes_mode, other._gf2, other._star ) )
653
654
def sub_bytes(self, d):
655
r"""
656
Perform the non-linear transform on ``d``.
657
658
INPUT:
659
660
- ``d`` - state array or something coercible to a state array
661
662
EXAMPLE::
663
664
sage: sr = mq.SR(2, 1, 2, 8, gf2=True)
665
sage: k = sr.base_ring()
666
sage: A = Matrix(k, 1, 2 , [k(1), k.gen()])
667
sage: sr.sub_bytes(A)
668
[ a^6 + a^5 + a^4 + a^3 + a^2 a^6 + a^5 + a^4 + a^2 + a + 1]
669
"""
670
d = self.state_array(d)
671
return Matrix(self.base_ring(), d.nrows(), d.ncols(), [self.sub_byte(b) for b in d.list()])
672
673
def sub_byte(self, b):
674
r"""
675
Perform ``SubByte`` on a single byte/halfbyte ``b``.
676
677
A ``ZeroDivision`` exception is raised if an attempt is made
678
to perform an inversion on the zero element. This can be
679
disabled by passing ``allow_zero_inversion=True`` to the
680
constructor. A zero inversion can result in an inconsistent
681
equation system.
682
683
INPUT:
684
685
- ``b`` - an element in ``self.base_ring()``
686
687
688
EXAMPLE:
689
690
The S-Box table for `\GF{2^4}`::
691
692
sage: sr = mq.SR(1, 1, 1, 4, allow_zero_inversions=True)
693
sage: for e in sr.base_ring():
694
... print '% 20s % 20s'%(e, sr.sub_byte(e))
695
0 a^2 + a
696
a a^2 + 1
697
a^2 a
698
a^3 a^3 + 1
699
a + 1 a^2
700
a^2 + a a^2 + a + 1
701
a^3 + a^2 a + 1
702
a^3 + a + 1 a^3 + a^2
703
a^2 + 1 a^3 + a^2 + a
704
a^3 + a a^3 + a^2 + a + 1
705
a^2 + a + 1 a^3 + a
706
a^3 + a^2 + a 0
707
a^3 + a^2 + a + 1 a^3
708
a^3 + a^2 + 1 1
709
a^3 + 1 a^3 + a^2 + 1
710
1 a^3 + a + 1
711
"""
712
if not b:
713
if not self._allow_zero_inversions:
714
raise ZeroDivisionError, "A zero inversion occurred during an encryption or key schedule."
715
else:
716
return self.sbox_constant()
717
try:
718
return self._sub_byte_lookup[b]
719
except AttributeError:
720
e = self.e
721
k = self.k
722
723
# inversion
724
b = b ** ( 2**e - 2 )
725
726
# GF(2) linear map
727
if e == 4:
728
if not hasattr(self, "_L"):
729
self._L = Matrix(GF(2), 4, 4, [[1, 1, 1, 0],
730
[0, 1, 1, 1],
731
[1, 0, 1, 1],
732
[1, 1, 0, 1]])
733
734
elif e==8:
735
if not hasattr(self, "_L"):
736
self._L = Matrix(GF(2), 8, 8, [[1, 0, 0, 0, 1, 1, 1, 1],
737
[1, 1, 0, 0, 0, 1, 1, 1],
738
[1, 1, 1, 0, 0, 0, 1, 1],
739
[1, 1, 1, 1, 0, 0, 0, 1],
740
[1, 1, 1, 1, 1, 0, 0, 0],
741
[0, 1, 1, 1, 1, 1, 0, 0],
742
[0, 0, 1, 1, 1, 1, 1, 0],
743
[0, 0, 0, 1, 1, 1, 1, 1]])
744
745
b = k(self._L * b._vector_())
746
747
# constant addition
748
if e == 4:
749
b = b + k.fetch_int(6)
750
elif e == 8:
751
b = b + k.fetch_int(99)
752
753
return b
754
755
def sbox_constant(self):
756
"""
757
Return the S-Box constant which is added after `L(x^{-1})` was
758
performed. That is ``0x63`` if ``e == 8`` or ``0x6`` if ``e ==
759
4``.
760
761
EXAMPLE::
762
763
sage: sr = mq.SR(10, 1, 1, 8)
764
sage: sr.sbox_constant()
765
a^6 + a^5 + a + 1
766
"""
767
k = self.k
768
if self.e == 4:
769
return k.fetch_int(6)
770
elif self.e == 8:
771
return k.fetch_int(99)
772
else:
773
raise TypeError, "sbox constant only defined for e in (4, 8)"
774
775
def sbox(self, inversion_only=False):
776
r"""
777
Return an S-Box object for this SR instance.
778
779
INPUT:
780
781
- ``inversion_only`` - do not include the `\GF{2}` affine map when
782
computing the S-Box (default: ``False``)
783
784
EXAMPLE::
785
786
sage: sr = mq.SR(1,2,2,4, allow_zero_inversions=True)
787
sage: S = sr.sbox(); S
788
(6, 11, 5, 4, 2, 14, 7, 10, 9, 13, 15, 12, 3, 1, 0, 8)
789
790
sage: sr.sub_byte(0)
791
a^2 + a
792
sage: sage_eval(str(sr.sub_byte(0)), {'a':2})
793
6
794
sage: S(0)
795
6
796
797
sage: sr.sub_byte(1)
798
a^3 + a + 1
799
sage: sage_eval(str(sr.sub_byte(1)), {'a':2})
800
11
801
sage: S(1)
802
11
803
804
sage: sr = mq.SR(1,2,2,8, allow_zero_inversions=True)
805
sage: S = sr.sbox(); S
806
(99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43,
807
254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240,
808
173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38,
809
54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4,
810
199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39,
811
178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214,
812
179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91,
813
106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251,
814
67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81,
815
163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16,
816
255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196,
817
167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42,
818
144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58,
819
10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121,
820
231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234,
821
101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198,
822
232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102,
823
72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225,
824
248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206,
825
85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65,
826
153, 45, 15, 176, 84, 187, 22)
827
828
sage: sr.sub_byte(0)
829
a^6 + a^5 + a + 1
830
831
sage: sage_eval(str(sr.sub_byte(0)), {'a':2})
832
99
833
sage: S(0)
834
99
835
836
sage: sr.sub_byte(1)
837
a^6 + a^5 + a^4 + a^3 + a^2
838
839
sage: sage_eval(str(sr.sub_byte(1)), {'a':2})
840
124
841
842
sage: S(1)
843
124
844
845
sage: sr = mq.SR(1,2,2,4, allow_zero_inversions=True)
846
sage: S = sr.sbox(inversion_only=True); S
847
(0, 1, 9, 14, 13, 11, 7, 6, 15, 2, 12, 5, 10, 4, 3, 8)
848
849
sage: S(0)
850
0
851
sage: S(1)
852
1
853
854
sage: S(sr.k.gen())
855
a^3 + 1
856
"""
857
from sage.crypto.mq.sbox import SBox
858
859
k = self.base_ring()
860
if not inversion_only:
861
with AllowZeroInversionsContext(self):
862
S = [self.sub_byte(elem) for elem in sorted(k)]
863
return SBox(S)
864
else:
865
e = self.e
866
S = [elem ** (2**e - 2) for elem in sorted(k)]
867
return SBox(S)
868
869
def shift_rows(self, d):
870
r"""
871
Perform the ``ShiftRows`` operation on ``d``.
872
873
INPUT:
874
875
- ``d`` - state array or something coercible to a state array
876
877
EXAMPLES::
878
879
sage: sr = mq.SR(10, 4, 4, 4)
880
sage: E = sr.state_array() + 1; E
881
[1 0 0 0]
882
[0 1 0 0]
883
[0 0 1 0]
884
[0 0 0 1]
885
886
::
887
888
sage: sr.shift_rows(E)
889
[1 0 0 0]
890
[1 0 0 0]
891
[1 0 0 0]
892
[1 0 0 0]
893
"""
894
d = self.state_array(d)
895
ret = []
896
for i in range(d.nrows()):
897
ret += list(d.row(i)[i%d.ncols():]) + list(d.row(i)[:i%d.ncols()])
898
return Matrix(self.base_ring(), self._r, self._c, ret)
899
900
def mix_columns(self, d):
901
r"""
902
Perform the ``MixColumns`` operation on
903
``d``.
904
905
INPUT:
906
907
908
- ``d`` - state array or something coercible to a
909
state array
910
911
912
EXAMPLES::
913
914
sage: sr = mq.SR(10, 4, 4, 4)
915
sage: E = sr.state_array() + 1; E
916
[1 0 0 0]
917
[0 1 0 0]
918
[0 0 1 0]
919
[0 0 0 1]
920
921
::
922
923
sage: sr.mix_columns(E)
924
[ a a + 1 1 1]
925
[ 1 a a + 1 1]
926
[ 1 1 a a + 1]
927
[a + 1 1 1 a]
928
"""
929
d = self.state_array(d)
930
k = self.base_ring()
931
a = k.gen()
932
r = self._r
933
if r == 1:
934
M = Matrix(self.base_ring(), 1, 1, [[1]])
935
elif r == 2:
936
M = Matrix(self.base_ring(), 2, 2, [[a + 1, a],
937
[a, a + 1]])
938
939
elif r == 4:
940
M = Matrix(self.base_ring(), 4, 4, [[a, a+1, 1, 1],
941
[1, a, a+1, 1],
942
[1, 1, a, a+1],
943
[a+1, 1, 1, a]])
944
ret =[]
945
for column in d.columns():
946
ret.append(M * column)
947
# AES uses the column major ordering
948
return Matrix(k, d.ncols(), d.nrows(), ret).transpose()
949
950
951
def add_round_key(self, d, key):
952
r"""
953
Perform the ``AddRoundKey`` operation on
954
``d`` using ``key``.
955
956
INPUT:
957
958
959
- ``d`` - state array or something coercible to a
960
state array
961
962
- ``key`` - state array or something coercible to a
963
state array
964
965
966
EXAMPLE::
967
968
sage: sr = mq.SR(10, 4, 4, 4)
969
sage: D = sr.random_state_array()
970
sage: K = sr.random_state_array()
971
sage: sr.add_round_key(D, K) == K + D
972
True
973
"""
974
d = self.state_array(d)
975
key = self.state_array(key)
976
977
return d+key
978
979
def state_array(self, d=None):
980
"""
981
Convert the parameter to a state array.
982
983
INPUT:
984
985
986
- ``d`` - a matrix, a list, or a tuple (default: ``None``)
987
988
989
EXAMPLES::
990
991
sage: sr = mq.SR(2, 2, 2, 4)
992
sage: k = sr.base_ring()
993
sage: e1 = [k.fetch_int(e) for e in range(2*2)]; e1
994
[0, 1, a, a + 1]
995
sage: e2 = sr.phi( Matrix(k, 2*2, 1, e1) )
996
sage: sr.state_array(e1) # note the column major ordering
997
[ 0 a]
998
[ 1 a + 1]
999
sage: sr.state_array(e2)
1000
[ 0 a]
1001
[ 1 a + 1]
1002
1003
::
1004
1005
sage: sr.state_array()
1006
[0 0]
1007
[0 0]
1008
"""
1009
r = self.r
1010
c = self.c
1011
e = self.e
1012
k = self.base_ring()
1013
1014
if d is None:
1015
return Matrix(k, r, c)
1016
1017
if is_Matrix(d):
1018
if d.nrows() == r*c*e:
1019
return Matrix(k, c, r, self.antiphi(d).list()).transpose()
1020
elif d.ncols() == c and d.nrows() == r and d.base_ring() == k:
1021
return d
1022
1023
if isinstance(d, tuple([list, tuple])):
1024
return Matrix(k, c, r, d).transpose()
1025
1026
def is_state_array(self, d):
1027
"""
1028
Return ``True`` if ``d`` is a state array, i.e. has the correct
1029
dimensions and base field.
1030
1031
EXAMPLE::
1032
1033
sage: sr = mq.SR(2, 2, 4, 8)
1034
sage: k = sr.base_ring()
1035
sage: sr.is_state_array( matrix(k, 2, 4) )
1036
True
1037
1038
::
1039
1040
sage: sr = mq.SR(2, 2, 4, 8)
1041
sage: k = sr.base_ring()
1042
sage: sr.is_state_array( matrix(k, 4, 4) )
1043
False
1044
"""
1045
return is_Matrix(d) and \
1046
d.nrows() == self.r and \
1047
d.ncols() == self.c and \
1048
d.base_ring() == self.base_ring()
1049
1050
def random_state_array(self, *args, **kwds):
1051
r"""
1052
Return a random element in ``MatrixSpace(self.base_ring(),
1053
self.r, self.c)``.
1054
1055
EXAMPLE::
1056
1057
sage: sr = mq.SR(2, 2, 2, 4)
1058
sage: sr.random_state_array()
1059
[ a^2 a^3 + a + 1]
1060
[a^3 + a^2 + a + 1 a + 1]
1061
"""
1062
return random_matrix(self.base_ring(), self._r, self._c, *args, **kwds)
1063
1064
def random_vector(self, *args, **kwds):
1065
"""
1066
Return a random vector as it might appear in the algebraic
1067
expression of self.
1068
1069
EXAMPLE::
1070
1071
sage: sr = mq.SR(2, 2, 2, 4)
1072
sage: sr.random_vector()
1073
[ a^2]
1074
[ a + 1]
1075
[ a^2 + 1]
1076
[ a]
1077
[a^3 + a^2 + a + 1]
1078
[ a^3 + a]
1079
[ a^3]
1080
[ a^3 + a^2]
1081
[ a^3 + a + 1]
1082
[ a^3 + 1]
1083
[ a^3 + a^2 + 1]
1084
[ a^3 + a^2 + a]
1085
[ a + 1]
1086
[ a^2 + 1]
1087
[ a]
1088
[ a^2]
1089
1090
.. note::
1091
1092
`\phi` was already applied to the result.
1093
"""
1094
return self.vector(self.random_state_array(*args, **kwds))
1095
1096
def random_element(self, elem_type = "vector", *args, **kwds):
1097
"""
1098
Return a random element for self. Other arguments and keywords are
1099
passed to random_* methods.
1100
1101
INPUT:
1102
1103
1104
- ``elem_type`` - either 'vector' or 'state array'
1105
(default: ``'vector'``)
1106
1107
1108
EXAMPLE::
1109
1110
sage: sr = mq.SR()
1111
sage: sr.random_element()
1112
[ a^2]
1113
[ a + 1]
1114
[a^2 + 1]
1115
[ a]
1116
sage: sr.random_element('state_array')
1117
[a^3 + a + 1]
1118
1119
Passes extra positional or keyword arguments through::
1120
1121
sage: sr.random_element(density=0)
1122
[0]
1123
[0]
1124
[0]
1125
[0]
1126
"""
1127
if elem_type == "vector":
1128
return self.random_vector(*args, **kwds)
1129
elif elem_type == "state_array":
1130
return self.random_state_array(*args, **kwds)
1131
else:
1132
raise TypeError, "parameter type not understood"
1133
1134
def key_schedule(self, kj, i):
1135
"""
1136
Return `k_i` for a given `i` and `k_j`
1137
with `j = i-1`.
1138
1139
EXAMPLES::
1140
1141
sage: sr = mq.SR(10, 4, 4, 8, star=True, allow_zero_inversions=True)
1142
sage: ki = sr.state_array()
1143
sage: for i in range(10):
1144
... ki = sr.key_schedule(ki, i+1)
1145
sage: print sr.hex_str_matrix(ki)
1146
B4 3E 23 6F
1147
EF 92 E9 8F
1148
5B E2 51 18
1149
CB 11 CF 8E
1150
"""
1151
if i < 0:
1152
raise TypeError, "i must be >= i"
1153
1154
if i == 0:
1155
return kj
1156
1157
r = self.r
1158
c = self.c
1159
F = self.base_ring()
1160
a = F.gen()
1161
SubByte = self.sub_byte
1162
1163
rc = Matrix(F, r, c, ([a**(i-1)] * c) + [F(0)]*((r-1)*c) )
1164
ki = Matrix(F, r, c)
1165
1166
if r == 1:
1167
s0 = SubByte(kj[0, c-1])
1168
1169
if c > 1:
1170
for q in range(c):
1171
ki[0, q] = s0 + sum([kj[0, t] for t in range(q+1) ])
1172
else:
1173
ki[0, 0] = s0
1174
1175
elif r == 2:
1176
s0 = SubByte(kj[1, c-1])
1177
s1 = SubByte(kj[0, c-1])
1178
1179
if c > 1:
1180
for q in range(c):
1181
ki[0, q] = s0 + sum([ kj[0, t] for t in range(q+1) ])
1182
ki[1, q] = s1 + sum([ kj[1, t] for t in range(q+1) ])
1183
else:
1184
ki[0, 0] = s0
1185
ki[1, 0] = s1
1186
1187
elif r == 4:
1188
1189
if self._aes_mode:
1190
s0 = SubByte(kj[1, c-1])
1191
s1 = SubByte(kj[2, c-1])
1192
s2 = SubByte(kj[3, c-1])
1193
s3 = SubByte(kj[0, c-1])
1194
else:
1195
s0 = SubByte(kj[3, c-1])
1196
s1 = SubByte(kj[2, c-1])
1197
s2 = SubByte(kj[1, c-1])
1198
s3 = SubByte(kj[0, c-1])
1199
1200
if c > 1:
1201
for q in range(c):
1202
ki[0, q] = s0 + sum([ kj[0, t] for t in range(q+1) ])
1203
ki[1, q] = s1 + sum([ kj[1, t] for t in range(q+1) ])
1204
ki[2, q] = s2 + sum([ kj[2, t] for t in range(q+1) ])
1205
ki[3, q] = s3 + sum([ kj[3, t] for t in range(q+1) ])
1206
1207
else:
1208
ki[0, 0] = s0
1209
ki[1, 0] = s1
1210
ki[2, 0] = s2
1211
ki[3, 0] = s3
1212
ki += rc
1213
1214
return ki
1215
1216
def __call__(self, P, K):
1217
r"""
1218
Encrypts the plaintext `P` using the key `K`.
1219
1220
Both must be given as state arrays or coercible to state arrays.
1221
1222
INPUTS:
1223
1224
- ``P`` - plaintext as state array or something coercible to a
1225
qstate array
1226
1227
- ``K`` - key as state array or something coercible to a state
1228
array
1229
1230
TESTS: The official AES test vectors::
1231
1232
sage: sr = mq.SR(10, 4, 4, 8, star=True, allow_zero_inversions=True)
1233
sage: k = sr.base_ring()
1234
sage: plaintext = sr.state_array([k.fetch_int(e) for e in range(16)])
1235
sage: key = sr.state_array([k.fetch_int(e) for e in range(16)])
1236
sage: print sr.hex_str_matrix( sr(plaintext, key) )
1237
0A 41 F1 C6
1238
94 6E C3 53
1239
0B F0 94 EA
1240
B5 45 58 5A
1241
1242
Brian Gladman's development vectors (dev_vec.txt)::
1243
1244
sage: sr = mq.SR(10, 4, 4, 8, star=True, allow_zero_inversions=True, aes_mode=True)
1245
sage: k = sr.base_ring()
1246
sage: plain = '3243f6a8885a308d313198a2e0370734'
1247
sage: key = '2b7e151628aed2a6abf7158809cf4f3c'
1248
sage: set_verbose(2)
1249
sage: cipher = sr(plain, key)
1250
R[01].start 193DE3BEA0F4E22B9AC68D2AE9F84808
1251
R[01].s_box D42711AEE0BF98F1B8B45DE51E415230
1252
R[01].s_row D4BF5D30E0B452AEB84111F11E2798E5
1253
R[01].m_col 046681E5E0CB199A48F8D37A2806264C
1254
R[01].k_sch A0FAFE1788542CB123A339392A6C7605
1255
R[02].start A49C7FF2689F352B6B5BEA43026A5049
1256
R[02].s_box 49DED28945DB96F17F39871A7702533B
1257
R[02].s_row 49DB873B453953897F02D2F177DE961A
1258
R[02].m_col 584DCAF11B4B5AACDBE7CAA81B6BB0E5
1259
R[02].k_sch F2C295F27A96B9435935807A7359F67F
1260
R[03].start AA8F5F0361DDE3EF82D24AD26832469A
1261
R[03].s_box AC73CF7BEFC111DF13B5D6B545235AB8
1262
R[03].s_row ACC1D6B8EFB55A7B1323CFDF457311B5
1263
R[03].m_col 75EC0993200B633353C0CF7CBB25D0DC
1264
R[03].k_sch 3D80477D4716FE3E1E237E446D7A883B
1265
R[04].start 486C4EEE671D9D0D4DE3B138D65F58E7
1266
R[04].s_box 52502F2885A45ED7E311C807F6CF6A94
1267
R[04].s_row 52A4C89485116A28E3CF2FD7F6505E07
1268
R[04].m_col 0FD6DAA9603138BF6FC0106B5EB31301
1269
R[04].k_sch EF44A541A8525B7FB671253BDB0BAD00
1270
R[05].start E0927FE8C86363C0D9B1355085B8BE01
1271
R[05].s_box E14FD29BE8FBFBBA35C89653976CAE7C
1272
R[05].s_row E1FB967CE8C8AE9B356CD2BA974FFB53
1273
R[05].m_col 25D1A9ADBD11D168B63A338E4C4CC0B0
1274
R[05].k_sch D4D1C6F87C839D87CAF2B8BC11F915BC
1275
R[06].start F1006F55C1924CEF7CC88B325DB5D50C
1276
R[06].s_box A163A8FC784F29DF10E83D234CD503FE
1277
R[06].s_row A14F3DFE78E803FC10D5A8DF4C632923
1278
R[06].m_col 4B868D6D2C4A8980339DF4E837D218D8
1279
R[06].k_sch 6D88A37A110B3EFDDBF98641CA0093FD
1280
R[07].start 260E2E173D41B77DE86472A9FDD28B25
1281
R[07].s_box F7AB31F02783A9FF9B4340D354B53D3F
1282
R[07].s_row F783403F27433DF09BB531FF54ABA9D3
1283
R[07].m_col 1415B5BF461615EC274656D7342AD843
1284
R[07].k_sch 4E54F70E5F5FC9F384A64FB24EA6DC4F
1285
R[08].start 5A4142B11949DC1FA3E019657A8C040C
1286
R[08].s_box BE832CC8D43B86C00AE1D44DDA64F2FE
1287
R[08].s_row BE3BD4FED4E1F2C80A642CC0DA83864D
1288
R[08].m_col 00512FD1B1C889FF54766DCDFA1B99EA
1289
R[08].k_sch EAD27321B58DBAD2312BF5607F8D292F
1290
R[09].start EA835CF00445332D655D98AD8596B0C5
1291
R[09].s_box 87EC4A8CF26EC3D84D4C46959790E7A6
1292
R[09].s_row 876E46A6F24CE78C4D904AD897ECC395
1293
R[09].m_col 473794ED40D4E4A5A3703AA64C9F42BC
1294
R[09].k_sch AC7766F319FADC2128D12941575C006E
1295
R[10].s_box E9098972CB31075F3D327D94AF2E2CB5
1296
R[10].s_row E9317DB5CB322C723D2E895FAF090794
1297
R[10].k_sch D014F9A8C9EE2589E13F0CC8B6630CA6
1298
R[10].output 3925841D02DC09FBDC118597196A0B32
1299
sage: set_verbose(0)
1300
"""
1301
r,c,e = self.r,self.c,self.e
1302
F = self.base_ring()
1303
1304
_type = self.state_array
1305
1306
if isinstance(P, str):
1307
P = self.state_array([F.fetch_int(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)])
1308
if isinstance(K, str):
1309
K = self.state_array([F.fetch_int(ZZ(K[i:i+2], 16)) for i in range(0, len(K), 2)])
1310
1311
if self.is_state_array(P) and self.is_state_array(K):
1312
_type = self.state_array
1313
elif self.is_vector(P) and self.is_vector(K):
1314
_type = self.vector
1315
elif isinstance(P, (list,tuple)) and isinstance(K, (list,tuple)):
1316
if len(P) == len(K) == r*c:
1317
_type = self.state_array
1318
elif len(P) == len(K) == r*c*e:
1319
_type = self.vector
1320
else:
1321
raise TypeError, "length %d or %d doesn't match either %d or %d"%(len(P),len(K),r*c,r*c*e)
1322
else:
1323
raise TypeError, "plaintext or key parameter not understood"
1324
1325
P = self.state_array(P)
1326
K = self.state_array(K)
1327
1328
AddRoundKey = self.add_round_key
1329
SubBytes = self.sub_bytes
1330
MixColumns = self.mix_columns
1331
ShiftRows = self.shift_rows
1332
KeyExpansion = self.key_schedule
1333
1334
P = AddRoundKey(P, K)
1335
1336
for r in range(self._n-1):
1337
if get_verbose() >= 2:
1338
print "R[%02d].start %s"%(r+1, self.hex_str_vector(P))
1339
1340
P = SubBytes(P)
1341
if get_verbose() >= 2:
1342
print "R[%02d].s_box %s"%(r+1, self.hex_str_vector(P))
1343
1344
P = ShiftRows(P)
1345
if get_verbose() >= 2:
1346
print "R[%02d].s_row %s"%(r+1, self.hex_str_vector(P))
1347
1348
P = MixColumns(P)
1349
if get_verbose() >= 2:
1350
print "R[%02d].m_col %s"%(r+1, self.hex_str_vector(P))
1351
1352
K = KeyExpansion(K, r+1)
1353
if get_verbose() >= 2:
1354
print "R[%02d].k_sch %s"%(r+1, self.hex_str_vector(K))
1355
1356
P = AddRoundKey(P, K)
1357
1358
P = SubBytes(P)
1359
if get_verbose() >= 2:
1360
print "R[%02d].s_box %s"%(self.n, self.hex_str_vector(P))
1361
1362
P = ShiftRows(P)
1363
if get_verbose() >= 2:
1364
print "R[%02d].s_row %s"%(self.n, self.hex_str_vector(P))
1365
1366
if not self._star:
1367
P = MixColumns(P)
1368
if get_verbose() >= 2:
1369
print "R[%02d].m_col %s"%(self.n, self.hex_str_vector(P))
1370
1371
K = KeyExpansion(K, self._n)
1372
if get_verbose() >= 2:
1373
print "R[%02d].k_sch %s"%(self.n, self.hex_str_vector(K))
1374
1375
P = AddRoundKey(P, K)
1376
if get_verbose() >= 2:
1377
print "R[%02d].output %s"%(self.n, self.hex_str_vector(P))
1378
1379
return _type(P)
1380
1381
def hex_str(self, M, typ="matrix"):
1382
r"""
1383
Return a hex string for the provided AES state array/matrix.
1384
1385
INPUT:
1386
1387
1388
- ``M`` - state array
1389
1390
- ``typ`` - controls what to return, either 'matrix'
1391
or 'vector' (default: ``'matrix'``)
1392
1393
1394
EXAMPLE::
1395
1396
sage: sr = mq.SR(2, 2, 2, 4)
1397
sage: k = sr.base_ring()
1398
sage: A = matrix(k, 2, 2, [1, k.gen(), 0, k.gen()^2])
1399
sage: sr.hex_str(A)
1400
' 1 2 \n 0 4 \n'
1401
1402
::
1403
1404
sage: sr.hex_str(A, typ='vector')
1405
'1024'
1406
"""
1407
if typ == "matrix":
1408
return self.hex_str_matrix(M)
1409
elif typ == "vector":
1410
return self.hex_str_vector(M)
1411
else:
1412
raise TypeError, "parameter type must either be 'matrix' or 'vector'"
1413
1414
def hex_str_matrix(self, M):
1415
r"""
1416
Return a two-dimensional AES-like representation of the matrix M.
1417
1418
That is, show the finite field elements as hex strings.
1419
1420
INPUT:
1421
1422
1423
- ``M`` - an AES state array
1424
1425
1426
EXAMPLE::
1427
1428
sage: sr = mq.SR(2, 2, 2, 4)
1429
sage: k = sr.base_ring()
1430
sage: A = matrix(k, 2, 2, [1, k.gen(), 0, k.gen()^2])
1431
sage: sr.hex_str_matrix(A)
1432
' 1 2 \n 0 4 \n'
1433
"""
1434
e = M.base_ring().degree()
1435
st = [""]
1436
for x in range(M.nrows()):
1437
for y in range(M.ncols()):
1438
if e == 8:
1439
st.append("%02X"%(int(str(M[x, y].int_repr()))))
1440
else:
1441
st.append("%X"%(int(str(M[x, y].int_repr()))))
1442
st.append("\n")
1443
return " ".join(st)
1444
1445
def hex_str_vector(self, M):
1446
"""
1447
Return a one-dimensional AES-like representation of the matrix M.
1448
1449
That is, show the finite field elements as hex strings.
1450
1451
INPUT:
1452
1453
1454
- ``M`` - an AES state array
1455
1456
1457
EXAMPLE::
1458
1459
sage: sr = mq.SR(2, 2, 2, 4)
1460
sage: k = sr.base_ring()
1461
sage: A = matrix(k, 2, 2, [1, k.gen(), 0, k.gen()^2])
1462
sage: sr.hex_str_vector(A)
1463
'1024'
1464
"""
1465
e = M.base_ring().degree()
1466
st = [""]
1467
for y in range(M.ncols()):
1468
for x in range(M.nrows()):
1469
if e == 8:
1470
st.append("%02X"%(int(str(M[x, y].int_repr()))))
1471
else:
1472
st.append("%X"%(int(str(M[x, y].int_repr()))))
1473
#st.append("\n")
1474
return "".join(st)
1475
1476
def _insert_matrix_into_matrix(self, dst, src, row, col):
1477
"""
1478
Insert matrix src into matrix dst starting at row and col.
1479
1480
INPUT:
1481
1482
1483
- ``dst`` - a matrix
1484
1485
- ``src`` - a matrix
1486
1487
- ``row`` - offset row
1488
1489
- ``col`` - offset columns
1490
1491
1492
EXAMPLE::
1493
1494
sage: sr = mq.SR(10, 4, 4, 4)
1495
sage: a = sr.k.gen()
1496
sage: A = sr.state_array() + 1; A
1497
[1 0 0 0]
1498
[0 1 0 0]
1499
[0 0 1 0]
1500
[0 0 0 1]
1501
sage: B = Matrix(sr.base_ring(), 2, 2, [0, a, a+1, a^2]); B
1502
[ 0 a]
1503
[a + 1 a^2]
1504
sage: sr._insert_matrix_into_matrix(A, B, 1, 1)
1505
[ 1 0 0 0]
1506
[ 0 0 a 0]
1507
[ 0 a + 1 a^2 0]
1508
[ 0 0 0 1]
1509
"""
1510
for i in range(src.nrows()):
1511
for j in range(src.ncols()):
1512
dst[row+i, col+j] = src[i, j]
1513
return dst
1514
1515
1516
def varformatstr(self, name, n=None, rc=None, e=None):
1517
"""
1518
Return a format string which is understood by print et al.
1519
1520
If a numerical value is omitted, the default value of ``self``
1521
is used. The numerical values (``n``, ``rc``, ``e``) are used
1522
to determine the width of the respective fields in the format
1523
string.
1524
1525
INPUT:
1526
1527
- ``name`` - name of the variable
1528
- ``n`` - number of rounds (default: ``None``)
1529
- ``rc`` - number of rows \* number of cols (default: ``None``)
1530
- ``e`` - exponent of base field (default: ``None``)
1531
1532
1533
EXAMPLE::
1534
1535
sage: sr = mq.SR(1, 2, 2, 4)
1536
sage: sr.varformatstr('x')
1537
'x%01d%01d%01d'
1538
sage: sr.varformatstr('x', n=1000)
1539
'x%03d%03d%03d'
1540
"""
1541
if n is None:
1542
n = self.n
1543
if rc is None:
1544
rc = self.r * self.c
1545
if e is None:
1546
e = self.e
1547
1548
l = str(max([ len(str(rc-1)), len(str(n-1)), len(str(e-1)) ] ))
1549
if name not in ("k", "s"):
1550
pf = self._postfix
1551
else:
1552
pf = ""
1553
format_string = name + pf + "%0" + l + "d" + "%0" + l + "d" + "%0" + l + "d"
1554
return format_string
1555
1556
def varstr(self, name, nr, rc, e):
1557
"""
1558
Return a string representing a variable for the small scale
1559
AES subject to the given constraints.
1560
1561
INPUT:
1562
1563
- ``name`` - variable name
1564
- ``nr`` - number of round to create variable strings for
1565
- ``rc`` - row*column index in state array
1566
- ``e`` - exponent of base field
1567
1568
EXAMPLE::
1569
1570
sage: sr = mq.SR(10, 1, 2, 4)
1571
sage: sr.varstr('x', 2, 1, 1)
1572
'x211'
1573
"""
1574
format_string = self.varformatstr(name, self.n, self.r*self.c, self.e)
1575
return format_string % (nr, rc, e)
1576
1577
def varstrs(self, name, nr, rc = None, e = None):
1578
"""
1579
Return a list of strings representing variables in ``self``.
1580
1581
INPUT:
1582
1583
- ``name`` - variable name
1584
- ``nr`` - number of round to create variable strings for
1585
- ``rc`` - number of rows * number of columns in the state array (default: ``None``)
1586
- ``e`` - exponent of base field (default: ``None``)
1587
1588
EXAMPLE::
1589
1590
sage: sr = mq.SR(10, 1, 2, 4)
1591
sage: sr.varstrs('x', 2)
1592
('x200', 'x201', 'x202', 'x203', 'x210', 'x211', 'x212', 'x213')
1593
1594
"""
1595
if rc is None:
1596
rc = self.r * self.c
1597
1598
if e is None:
1599
e = self.e
1600
1601
n = self._n
1602
1603
format_string = self.varformatstr(name, n, rc, e)
1604
1605
return tuple([format_string % (nr, rci, ei) for rci in range(rc) for ei in range(e)])
1606
1607
def vars(self, name, nr, rc=None, e=None):
1608
"""
1609
Return a list of variables in ``self``.
1610
1611
INPUT:
1612
1613
- ``name`` - variable name
1614
- ``nr`` - number of round to create variable strings for
1615
- ``rc`` - number of rounds * number of columns in the state array (default: ``None``)
1616
- ``e`` - exponent of base field (default: ``None``)
1617
1618
EXAMPLE::
1619
1620
sage: sr = mq.SR(10, 1, 2, 4)
1621
sage: sr.vars('x', 2)
1622
(x200, x201, x202, x203, x210, x211, x212, x213)
1623
1624
"""
1625
gd = self.variable_dict()
1626
return tuple([gd[e] for e in self.varstrs(name, nr, rc, e)])
1627
1628
def variable_dict(self):
1629
"""
1630
Return a dictionary to access variables in ``self.R`` by their
1631
names.
1632
1633
EXAMPLE::
1634
1635
sage: sr = mq.SR(1,1,1,4)
1636
sage: sr.variable_dict()
1637
{'x101': x101, 'x100': x100, 'x103': x103, 'x102': x102,
1638
's002': s002, 'w100': w100, 'w101': w101, 'w102': w102,
1639
'w103': w103, 'k100': k100, 'k101': k101, 'k102': k102,
1640
'k103': k103, 's003': s003, 's001': s001, 'k002': k002,
1641
'k001': k001, 'k000': k000, 'k003': k003, 's000': s000}
1642
1643
sage: sr = mq.SR(1,1,1,4,gf2=True)
1644
sage: sr.variable_dict()
1645
{'x101': x101, 'x100': x100, 'x103': x103, 'x102': x102,
1646
's002': s002, 'w100': w100, 'w101': w101, 'w102': w102,
1647
'w103': w103, 'k100': k100, 'k101': k101, 'k102': k102,
1648
'k103': k103, 's003': s003, 's001': s001, 'k002': k002,
1649
'k001': k001, 'k000': k000, 'k003': k003, 's000': s000}
1650
1651
"""
1652
try:
1653
R,gd = self._variable_dict
1654
if R is self.R:
1655
return gd
1656
else:
1657
pass
1658
except AttributeError:
1659
pass
1660
1661
gd = self.R.gens_dict()
1662
self._variable_dict = self.R,gd
1663
return gd
1664
1665
def block_order(self):
1666
"""
1667
Return a block order for self where each round is a block.
1668
1669
EXAMPLE::
1670
1671
sage: sr = mq.SR(2, 1, 1, 4)
1672
sage: sr.block_order()
1673
Block term order with blocks:
1674
(Degree lexicographic term order of length 16,
1675
Degree lexicographic term order of length 16,
1676
Degree lexicographic term order of length 4)
1677
1678
::
1679
1680
sage: P = sr.ring(order='block')
1681
sage: print P.repr_long()
1682
Polynomial Ring
1683
Base Ring : Finite Field in a of size 2^4
1684
Size : 36 Variables
1685
Block 0 : Ordering : deglex
1686
Names : k200, k201, k202, k203, x200, x201, x202, x203, w200, w201, w202, w203, s100, s101, s102, s103
1687
Block 1 : Ordering : deglex
1688
Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003
1689
Block 2 : Ordering : deglex
1690
Names : k000, k001, k002, k003
1691
"""
1692
r = self.r
1693
c = self.c
1694
e = self.e
1695
n = self.n
1696
1697
T = None
1698
for _n in range(n):
1699
T = TermOrder('deglex', r*e + 3*r*c*e ) + T
1700
1701
T += TermOrder('deglex', r*c*e)
1702
1703
return T
1704
1705
def ring(self, order=None, reverse_variables=None):
1706
r"""
1707
Construct a ring as a base ring for the polynomial system.
1708
1709
By default, variables are ordered in the reverse of their natural
1710
ordering, i.e. the reverse of as they appear.
1711
1712
INPUT:
1713
1714
- ``order`` - a monomial ordering (default: ``None``)
1715
- ``reverse_variables`` - reverse rounds of variables (default: ``True``)
1716
1717
The variable assignment is as follows:
1718
1719
- `k_{i,j,l}` - subkey round `i` word `j` conjugate/bit `l`
1720
- `s_{i,j,l}` - subkey inverse round `i` word `j` conjugate/bit `l`
1721
- `w_{i,j,l}` - inversion input round `i` word `j` conjugate/bit `l`
1722
- `x_{i,j,l}` - inversion output round `i` word `j` conjugate/bit `l`
1723
1724
1725
Note that the variables are ordered in column major ordering
1726
in the state array and that the bits are ordered in little
1727
endian ordering.
1728
1729
For example, if `x_{0,1,0}` is a variable over `\GF{2}` for
1730
`r=2` and `c=2` then refers to the *most* significant bit of
1731
the entry in the position (1,0) in the state array matrix.
1732
1733
EXAMPLE::
1734
1735
sage: sr = mq.SR(2, 1, 1, 4)
1736
sage: P = sr.ring(order='block')
1737
sage: print P.repr_long()
1738
Polynomial Ring
1739
Base Ring : Finite Field in a of size 2^4
1740
Size : 36 Variables
1741
Block 0 : Ordering : deglex
1742
Names : k200, k201, k202, k203, x200, x201, x202, x203, w200, w201, w202, w203, s100, s101, s102, s103
1743
Block 1 : Ordering : deglex
1744
Names : k100, k101, k102, k103, x100, x101, x102, x103, w100, w101, w102, w103, s000, s001, s002, s003
1745
Block 2 : Ordering : deglex
1746
Names : k000, k001, k002, k003
1747
"""
1748
r = self.r
1749
c = self.c
1750
e = self.e
1751
n = self.n
1752
if not self._gf2:
1753
k = self.base_ring()
1754
else:
1755
k = GF(2)
1756
1757
if order is not None:
1758
self._order = order
1759
if self._order == 'block':
1760
self._order = self.block_order()
1761
1762
if reverse_variables is None:
1763
reverse_variables = self._reverse_variables
1764
1765
if reverse_variables:
1766
process = lambda x: reversed(x)
1767
else:
1768
process = lambda x: x
1769
1770
if reverse_variables:
1771
names = []
1772
else:
1773
names = self.varstrs("k", 0, r*c, e)
1774
1775
1776
for _n in process(xrange(n)):
1777
names += self.varstrs("k", _n+1, r*c, e)
1778
names += self.varstrs("x", _n+1, r*c, e)
1779
names += self.varstrs("w", _n+1, r*c, e)
1780
names += self.varstrs("s", _n, r, e)
1781
1782
if reverse_variables:
1783
names += self.varstrs("k", 0, r*c, e)
1784
1785
#from sage.rings.polynomial.pbori import BooleanPolynomialRing
1786
1787
if self._gf2 and self._polybori:
1788
return BooleanPolynomialRing(2*n*r*c*e + (n+1)*r*c*e + n*r*e, names, order=self._order)
1789
else:
1790
return PolynomialRing(k, 2*n*r*c*e + (n+1)*r*c*e + n*r*e, names, order=self._order)
1791
1792
def round_polynomials(self, i, plaintext=None, ciphertext=None):
1793
r"""
1794
Return list of polynomials for a given round `i`.
1795
1796
If ``i == 0`` a plaintext must be provided, if ``i == n`` a
1797
ciphertext must be provided.
1798
1799
INPUT:
1800
1801
1802
- ``i`` - round number
1803
1804
- ``plaintext`` - optional plaintext (mandatory in
1805
first round)
1806
1807
- ``ciphertext`` - optional ciphertext (mandatory in
1808
last round)
1809
1810
1811
OUTPUT: tuple
1812
1813
EXAMPLE::
1814
1815
sage: sr = mq.SR(1, 1, 1, 4)
1816
sage: k = sr.base_ring()
1817
sage: p = [k.random_element() for _ in range(sr.r*sr.c)]
1818
sage: sr.round_polynomials(0, plaintext=p)
1819
(w100 + k000 + (a^2 + 1), w101 + k001 + (a), w102 + k002 + (a^2), w103 + k003 + (a + 1))
1820
"""
1821
r = self._r
1822
c = self._c
1823
e = self._e
1824
n = self._n
1825
R = self.R
1826
1827
M = self.M
1828
1829
_vars = self.vars
1830
1831
if i == 0:
1832
w1 = Matrix(R, r*c*e, 1, _vars("w", 1, r*c, e))
1833
k0 = Matrix(R, r*c*e, 1, _vars("k", 0, r*c, e))
1834
if isinstance(plaintext, (tuple, list)) and len(plaintext) == r*c:
1835
plaintext = Matrix(R, r*c*e, 1, self.phi(plaintext))
1836
return tuple((w1 + k0 + plaintext).list())
1837
1838
elif i>0 and i<=n:
1839
1840
if self._star and i == n:
1841
M = self.Mstar
1842
1843
xj = Matrix(R, r*c*e, 1, _vars("x", i, r*c, e))
1844
ki = Matrix(R, r*c*e, 1, _vars("k", i, r*c, e))
1845
rcon = Matrix(R, r*c*e, 1, self.phi([self.sbox_constant()]*r*c))
1846
1847
if i < n:
1848
wj = Matrix(R, r*c*e, 1, _vars("w", i+1, r*c, e))
1849
if i == n:
1850
if isinstance(ciphertext, (tuple, list)) and len(ciphertext) == r*c:
1851
ciphertext = Matrix(R, r*c*e, 1, self.phi(ciphertext))
1852
wj = ciphertext
1853
1854
lin = (wj + ki + M * xj + rcon).list()
1855
1856
1857
wi = Matrix(R, r*c*e, 1, _vars("w", i, r*c, e))
1858
xi = Matrix(R, r*c*e, 1, _vars("x", i, r*c, e))
1859
sbox = []
1860
sbox += self.inversion_polynomials(xi, wi, r*c*e)
1861
sbox += self.field_polynomials("x", i)
1862
sbox += self.field_polynomials("w", i)
1863
return tuple(lin + sbox)
1864
1865
def key_schedule_polynomials(self, i):
1866
"""
1867
Return polynomials for the `i`-th round of the key
1868
schedule.
1869
1870
INPUT:
1871
1872
- ``i`` - round (`0 \leq i \leq n`)
1873
1874
EXAMPLE::
1875
1876
sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=False)
1877
1878
The 0-th subkey is the user provided key, so only conjugacy
1879
relations or field polynomials are added.::
1880
1881
sage: sr.key_schedule_polynomials(0)
1882
(k000^2 + k000, k001^2 + k001, k002^2 + k002, k003^2 + k003)
1883
1884
The 1-th subkey is derived from the user provided key according to
1885
the key schedule which is non-linear.::
1886
1887
sage: sr.key_schedule_polynomials(1)
1888
(k100 + s000 + s002 + s003,
1889
k101 + s000 + s001 + s003 + 1,
1890
k102 + s000 + s001 + s002 + 1,
1891
k103 + s001 + s002 + s003 + 1,
1892
k100^2 + k100, k101^2 + k101, k102^2 + k102, k103^2 + k103,
1893
s000^2 + s000, s001^2 + s001, s002^2 + s002, s003^2 + s003,
1894
s000*k000 + s000*k003 + s001*k002 + s002*k001 + s003*k000,
1895
s000*k000 + s000*k001 + s001*k000 + s001*k003 + s002*k002 + s003*k001,
1896
s000*k001 + s000*k002 + s001*k000 + s001*k001 + s002*k000 + s002*k003 + s003*k002,
1897
s000*k000 + s000*k001 + s000*k003 + s001*k001 + s002*k000 + s002*k002 + s003*k000 + k000,
1898
s000*k002 + s001*k000 + s001*k001 + s001*k003 + s002*k001 + s003*k000 + s003*k002 + k001,
1899
s000*k000 + s000*k001 + s000*k002 + s001*k002 + s002*k000 + s002*k001 + s002*k003 + s003*k001 + k002,
1900
s000*k001 + s001*k000 + s001*k002 + s002*k000 + s003*k001 + s003*k003 + k003,
1901
s000*k000 + s000*k002 + s000*k003 + s001*k000 + s001*k001 + s002*k002 + s003*k000 + s000,
1902
s000*k001 + s000*k003 + s001*k001 + s001*k002 + s002*k000 + s002*k003 + s003*k001 + s001,
1903
s000*k000 + s000*k002 + s001*k000 + s001*k002 + s001*k003 + s002*k000 + s002*k001 + s003*k002 + s002,
1904
s000*k001 + s000*k002 + s001*k000 + s001*k003 + s002*k001 + s003*k003 + s003,
1905
s000*k002 + s001*k001 + s002*k000 + s003*k003 + 1)
1906
"""
1907
R = self.R
1908
r = self.r
1909
e = self.e
1910
c = self.c
1911
k = self.k
1912
a = k.gen()
1913
1914
if i < 0:
1915
raise TypeError, "i must by >= 0"
1916
1917
if i == 0:
1918
return tuple(self.field_polynomials("k", i, r*c))
1919
else:
1920
L = self.lin_matrix(r)
1921
ki = Matrix(R, r*c*e, 1, self.vars("k", i , r*c, e))
1922
kj = Matrix(R, r*c*e, 1, self.vars("k", i-1, r*c, e))
1923
si = Matrix(R, r*e, 1, self.vars("s", i-1, r, e))
1924
1925
rc = Matrix(R, r*e, 1, self.phi([a**(i-1)] + [k(0)]*(r-1)) )
1926
d = Matrix(R, r*e, 1, self.phi([self.sbox_constant()]*r) )
1927
1928
sbox = []
1929
1930
sbox += self.field_polynomials("k", i)
1931
sbox += self.field_polynomials("s", i-1, r)
1932
1933
if r == 1:
1934
sbox += self.inversion_polynomials(kj[(c - 1)*e:(c - 1)*e + e], si[0:e], e)
1935
if r == 2:
1936
sbox += self.inversion_polynomials( kj[(2*c -1)*e : (2*c -1)*e + e] , si[0:1*e], e )
1937
sbox += self.inversion_polynomials( kj[(2*c -2)*e : (2*c -2)*e + e] , si[e:2*e], e )
1938
if r == 4:
1939
if self._aes_mode:
1940
sbox += self.inversion_polynomials( kj[(4*c-3)*e : (4*c-3)*e + e] , si[0*e : 1*e] , e )
1941
sbox += self.inversion_polynomials( kj[(4*c-2)*e : (4*c-2)*e + e] , si[1*e : 2*e] , e )
1942
sbox += self.inversion_polynomials( kj[(4*c-1)*e : (4*c-1)*e + e] , si[2*e : 3*e] , e )
1943
sbox += self.inversion_polynomials( kj[(4*c-4)*e : (4*c-4)*e + e] , si[3*e : 4*e] , e )
1944
else:
1945
sbox += self.inversion_polynomials( kj[(4*c-1)*e : (4*c-1)*e + e] , si[0*e : 1*e] , e )
1946
sbox += self.inversion_polynomials( kj[(4*c-2)*e : (4*c-2)*e + e] , si[1*e : 2*e] , e )
1947
sbox += self.inversion_polynomials( kj[(4*c-3)*e : (4*c-3)*e + e] , si[2*e : 3*e] , e )
1948
sbox += self.inversion_polynomials( kj[(4*c-4)*e : (4*c-4)*e + e] , si[3*e : 4*e] , e )
1949
1950
si = L * si + d + rc
1951
Sum = Matrix(R, r*e, 1)
1952
lin = []
1953
if c > 1:
1954
for q in range(c):
1955
t = range(r*e*(q) , r*e*(q+1) )
1956
Sum += kj.matrix_from_rows(t)
1957
lin += (ki.matrix_from_rows(t) + si + Sum).list()
1958
1959
else:
1960
lin += (ki + si).list()
1961
return tuple(lin + sbox)
1962
1963
def polynomial_system(self, P=None, K=None, C=None):
1964
"""
1965
Return a polynomial system for this small scale AES variant for a
1966
given plaintext-key pair.
1967
1968
If neither ``P``, ``K`` nor ``C`` are provided, a random pair
1969
(``P``, ``K``) will be generated. If ``P`` and ``C`` are
1970
provided no ``K`` needs to be provided.
1971
1972
INPUT:
1973
1974
- ``P`` - vector, list, or tuple (default: ``None``)
1975
- ``K`` - vector, list, or tuple (default: ``None``)
1976
- ``C`` - vector, list, or tuple (default: ``None``)
1977
1978
EXAMPLE::
1979
1980
sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=True)
1981
sage: P = sr.vector([0, 0, 1, 0])
1982
sage: K = sr.vector([1, 0, 0, 1])
1983
sage: F, s = sr.polynomial_system(P, K)
1984
1985
This returns a polynomial system::
1986
1987
sage: F
1988
Polynomial Sequence with 36 Polynomials in 20 Variables
1989
1990
and a solution::
1991
1992
sage: s # random -- maybe we need a better doctest here?
1993
{k000: 1, k001: 0, k003: 1, k002: 0}
1994
1995
This solution is not the only solution that we can learn from the
1996
Groebner basis of the system.
1997
1998
::
1999
2000
sage: F.groebner_basis()[-3:]
2001
[k000 + 1, k001, k003 + 1]
2002
2003
In particular we have two solutions::
2004
2005
sage: len(F.ideal().variety())
2006
2
2007
2008
In the following example we provide ``C`` explicitly::
2009
2010
sage: C = sr(P,K)
2011
sage: F,s = sr.polynomial_system(P=P, C=C)
2012
sage: F
2013
Polynomial Sequence with 36 Polynomials in 20 Variables
2014
2015
Alternatively, we can use symbols for the ``P`` and
2016
``C``. First, we have to create a polynomial ring::
2017
2018
sage: sr = mq.SR(1, 1, 1, 4, gf2=True, polybori=True)
2019
sage: R = sr.R
2020
sage: vn = sr.varstrs("P",0,1,4) + R.variable_names() + sr.varstrs("C",0,1,4)
2021
sage: R = BooleanPolynomialRing(len(vn),vn)
2022
sage: sr.R = R
2023
2024
2025
Now, we can construct the purely symbolic equation system::
2026
2027
sage: C = sr.vars("C",0); C
2028
(C000, C001, C002, C003)
2029
sage: P = sr.vars("P",0)
2030
sage: F,s = sr.polynomial_system(P=P,C=C)
2031
sage: [(k,v) for k,v in sorted(s.iteritems())] # this can be ignored
2032
[(k003, 1), (k002, 1), (k001, 0), (k000, 1)]
2033
sage: F
2034
Polynomial Sequence with 36 Polynomials in 28 Variables
2035
sage: F.part(0)
2036
(P000 + w100 + k000, P001 + w101 + k001, P002 + w102 + k002, P003 + w103 + k003)
2037
sage: F.part(-2)
2038
(k100 + x100 + x102 + x103 + C000, k101 + x100 + x101 + x103 + C001 + 1, ...)
2039
2040
We show that the (returned) key is a solution to the returned system::
2041
2042
sage: sr = mq.SR(3,4,4,8, star=True, gf2=True, polybori=True)
2043
sage: F,s = sr.polynomial_system()
2044
sage: F.subs(s).groebner_basis() # long time
2045
Polynomial Sequence with 1248 Polynomials in 1248 Variables
2046
"""
2047
plaintext = P
2048
key = K
2049
ciphertext = C
2050
2051
system = []
2052
n = self._n
2053
2054
data = []
2055
2056
R = self.R
2057
r,c,e = self.r,self.c,self.e
2058
2059
for d in (plaintext, key, ciphertext):
2060
if d is None:
2061
data.append( None )
2062
elif isinstance(d, (tuple, list)):
2063
if isinstance(d[0], (int,long)):
2064
d = map(GF(2),d)
2065
if len(d) == r*c*e and (d[0].parent() is R or d[0].parent() == R):
2066
data.append( Matrix(R,r*c*e,1,d) )
2067
continue
2068
try:
2069
data.append( self.phi(self.state_array(d)) )
2070
except ValueError: # GF2 vectors maybe?
2071
data.append( self.vector(d) )
2072
elif self.is_state_array(d):
2073
data.append( self.phi(d) )
2074
elif self.is_vector(d):
2075
data.append( d )
2076
else:
2077
data.append( False )
2078
2079
plaintext, key, ciphertext = data
2080
2081
if plaintext is False:
2082
raise TypeError, "type %s of P not understood"%(type(plaintext))
2083
elif plaintext is None:
2084
plaintext = self.random_element("vector")
2085
2086
if key is None:
2087
key = self.random_element("vector")
2088
elif key is False and ciphertext is False:
2089
raise TypeError, "type %s of K not understood"%(type(key))
2090
2091
if ciphertext is None:
2092
ciphertext = self(plaintext, key)
2093
elif ciphertext is False:
2094
raise TypeError, "type %s of C not understood"%(type(ciphertext))
2095
2096
for i in range(n+1):
2097
system.append( self.round_polynomials(i, plaintext, ciphertext) )
2098
system.append( self.key_schedule_polynomials(i) )
2099
2100
if key is not None:
2101
K = dict(zip(self.vars("k", 0), key.list()))
2102
else:
2103
K = None
2104
return PolynomialSequence(self.R, system), K
2105
2106
2107
class SR_gf2n(SR_generic):
2108
r"""
2109
Small Scale Variants of the AES polynomial system constructor over
2110
`\GF{2^n}`.
2111
"""
2112
def vector(self, d=None):
2113
"""
2114
Constructs a vector suitable for the algebraic representation of
2115
SR, i.e. BES.
2116
2117
INPUT:
2118
2119
- ``d`` - values for vector, must be understood by ``self.phi`` (default:``None``)
2120
2121
EXAMPLE::
2122
2123
sage: sr = mq.SR()
2124
sage: sr
2125
SR(1,1,1,4)
2126
sage: k = sr.base_ring()
2127
sage: A = Matrix(k, 1, 1, [k.gen()])
2128
sage: sr.vector(A)
2129
[ a]
2130
[ a^2]
2131
[ a + 1]
2132
[a^2 + 1]
2133
"""
2134
r = self.r
2135
c = self.c
2136
e = self.e
2137
k = self.base_ring()
2138
2139
if d is None:
2140
return Matrix(k, r*c*e, 1)
2141
elif d.ncols() == c and d.nrows() == r and d.base_ring() == k:
2142
return Matrix(k, r*c*e, 1, self.phi(d).transpose().list())
2143
2144
def is_vector(self, d):
2145
"""
2146
Return ``True`` if ``d`` can be used as a vector for ``self``.
2147
2148
EXAMPLE::
2149
2150
sage: sr = mq.SR()
2151
sage: sr
2152
SR(1,1,1,4)
2153
sage: k = sr.base_ring()
2154
sage: A = Matrix(k, 1, 1, [k.gen()])
2155
sage: B = sr.vector(A)
2156
sage: sr.is_vector(A)
2157
False
2158
sage: sr.is_vector(B)
2159
True
2160
"""
2161
return is_Matrix(d) and \
2162
d.nrows() == self.r*self.c*self.e and \
2163
d.ncols() == 1 and \
2164
d.base_ring() == self.base_ring()
2165
2166
def phi(self, l):
2167
r"""
2168
The operation `\phi` from [MR02]_
2169
2170
Projects state arrays to their algebraic representation.
2171
2172
INPUT:
2173
2174
- ``l`` - element to perform `\phi` on.
2175
2176
EXAMPLE::
2177
2178
sage: sr = mq.SR(2, 1, 2, 4)
2179
sage: k = sr.base_ring()
2180
sage: A = matrix(k, 1, 2, [k.gen(), 0] )
2181
sage: sr.phi(A)
2182
[ a 0]
2183
[ a^2 0]
2184
[ a + 1 0]
2185
[a^2 + 1 0]
2186
"""
2187
ret = []
2188
if is_Matrix(l):
2189
for e in l.transpose().list():
2190
ret += [e**(2**i) for i in range(self.e)]
2191
else:
2192
for e in l:
2193
ret += [e**(2**i) for i in range(self.e)]
2194
if isinstance(l, list):
2195
return ret
2196
elif isinstance(l, tuple):
2197
return tuple(ret)
2198
elif is_Matrix(l):
2199
return Matrix(l.base_ring(), l.ncols(), l.nrows()*self.e, ret).transpose()
2200
else:
2201
raise TypeError
2202
2203
def antiphi(self, l):
2204
"""
2205
The operation `\phi^{-1}` from [MR02]_ or the inverse of ``self.phi``.
2206
2207
INPUT:
2208
2209
2210
- ``l`` - a vector in the sense of
2211
``self.is_vector``
2212
2213
2214
EXAMPLE::
2215
2216
sage: sr = mq.SR()
2217
sage: A = sr.random_state_array()
2218
sage: A
2219
[a^2]
2220
sage: sr.antiphi(sr.phi(A)) == A
2221
True
2222
"""
2223
if is_Matrix(l):
2224
ret = [e for e in l.transpose().list()[0:-1:self.e]]
2225
else:
2226
ret = [e for e in l[0:-1:self.e]]
2227
2228
if isinstance(l, list):
2229
return ret
2230
elif isinstance(l, tuple):
2231
return tuple(ret)
2232
elif is_Matrix(l):
2233
return Matrix(self.base_ring(), l.ncols(), l.nrows()/self.e, ret).transpose()
2234
else:
2235
raise TypeError
2236
2237
def shift_rows_matrix(self):
2238
"""
2239
Return the ``ShiftRows`` matrix.
2240
2241
EXAMPLE::
2242
2243
sage: sr = mq.SR(1, 2, 2, 4)
2244
sage: s = sr.random_state_array()
2245
sage: r1 = sr.shift_rows(s)
2246
sage: r2 = sr.state_array( sr.shift_rows_matrix() * sr.vector(s) )
2247
sage: r1 == r2
2248
True
2249
"""
2250
e = self.e
2251
r = self.r
2252
c = self.c
2253
k = self.base_ring()
2254
bs = r*c*e
2255
shift_rows = Matrix(k, bs, bs)
2256
I = MatrixSpace(k, e, e)(1)
2257
for x in range(0, c):
2258
for y in range(0, r):
2259
_r = ((x*r)+y) * e
2260
_c = (((x*r)+((r+1)*y)) * e) % bs
2261
self._insert_matrix_into_matrix(shift_rows, I, _r, _c)
2262
2263
return shift_rows
2264
2265
def lin_matrix(self, length = None):
2266
"""
2267
Return the ``Lin`` matrix.
2268
2269
If no ``length`` is provided, the standard state space size is
2270
used. The key schedule calls this method with an explicit
2271
length argument because only ``self.r`` S-Box applications are
2272
performed in the key schedule.
2273
2274
INPUT:
2275
2276
- ``length`` - length of state space (default: ``None``)
2277
2278
2279
EXAMPLE::
2280
2281
sage: sr = mq.SR(1, 1, 1, 4)
2282
sage: sr.lin_matrix()
2283
[ a^2 + 1 1 a^3 + a^2 a^2 + 1]
2284
[ a a 1 a^3 + a^2 + a + 1]
2285
[ a^3 + a a^2 a^2 1]
2286
[ 1 a^3 a + 1 a + 1]
2287
"""
2288
r = self.r
2289
c = self.c
2290
e = self.e
2291
k = self.k
2292
2293
if length is None:
2294
length = r*c
2295
2296
lin = Matrix(self.base_ring(), length*e, length*e)
2297
if e == 4:
2298
l = [ k.fetch_int(x) for x in (5, 1, 12, 5) ]
2299
for k in range( 0, length ):
2300
for i in range(0, 4):
2301
for j in range(0, 4):
2302
lin[k*4+j, k*4+i] = l[(i-j)%4] ** (2**j)
2303
elif e == 8:
2304
l = [ k.fetch_int(x) for x in (5, 9, 249, 37, 244, 1, 181, 143) ]
2305
for k in range( 0, length ):
2306
for i in range(0, 8):
2307
for j in range(0, 8):
2308
lin[k*8+j, k*8+i] = l[(i-j)%8] ** (2**j)
2309
2310
return lin
2311
2312
def mix_columns_matrix(self):
2313
"""
2314
Return the ``MixColumns`` matrix.
2315
2316
EXAMPLE::
2317
2318
sage: sr = mq.SR(1, 2, 2, 4)
2319
sage: s = sr.random_state_array()
2320
sage: r1 = sr.mix_columns(s)
2321
sage: r2 = sr.state_array(sr.mix_columns_matrix() * sr.vector(s))
2322
sage: r1 == r2
2323
True
2324
"""
2325
2326
def D(b):
2327
"""
2328
Return the `e x e` matrix `D` with `b^i` along the
2329
diagonal.
2330
2331
EXAMPLE::
2332
2333
sage: sr = mq.SR(1, 2, 1, 4)
2334
sage: sr.mix_columns_matrix() # indirect doctest
2335
[ a + 1 0 0 0 a 0 0 0]
2336
[ 0 a^2 + 1 0 0 0 a^2 0 0]
2337
[ 0 0 a 0 0 0 a + 1 0]
2338
[ 0 0 0 a^2 0 0 0 a^2 + 1]
2339
[ a 0 0 0 a + 1 0 0 0]
2340
[ 0 a^2 0 0 0 a^2 + 1 0 0]
2341
[ 0 0 a + 1 0 0 0 a 0]
2342
[ 0 0 0 a^2 + 1 0 0 0 a^2]
2343
"""
2344
D = Matrix(self.base_ring(), self._e, self._e)
2345
for i in range(self._e):
2346
D[i, i] = b**(2**i)
2347
return D
2348
2349
r = self.r
2350
c = self.c
2351
e = self.e
2352
k = self.k
2353
a = k.gen()
2354
2355
M = Matrix(k, r*e, r*e)
2356
2357
if r == 1:
2358
self._insert_matrix_into_matrix(M, D(1), 0, 0)
2359
2360
elif r == 2:
2361
self._insert_matrix_into_matrix(M, D(a+1), 0, 0)
2362
self._insert_matrix_into_matrix(M, D(a+1), e, e)
2363
self._insert_matrix_into_matrix(M, D(a), e, 0)
2364
self._insert_matrix_into_matrix(M, D(a), 0, e)
2365
2366
elif r == 4:
2367
self._insert_matrix_into_matrix(M, D(a), 0, 0)
2368
self._insert_matrix_into_matrix(M, D(a), e, e)
2369
self._insert_matrix_into_matrix(M, D(a), 2*e, 2*e)
2370
self._insert_matrix_into_matrix(M, D(a), 3*e, 3*e)
2371
2372
self._insert_matrix_into_matrix(M, D(a+1), 0, e)
2373
self._insert_matrix_into_matrix(M, D(a+1), e, 2*e)
2374
self._insert_matrix_into_matrix(M, D(a+1), 2*e, 3*e)
2375
self._insert_matrix_into_matrix(M, D(a+1), 3*e, 0)
2376
2377
self._insert_matrix_into_matrix(M, D(1), 0, 2*e)
2378
self._insert_matrix_into_matrix(M, D(1), e, 3*e)
2379
self._insert_matrix_into_matrix(M, D(1), 2*e, 0)
2380
self._insert_matrix_into_matrix(M, D(1), 3*e, 1*e)
2381
2382
self._insert_matrix_into_matrix(M, D(1), 0, 3*e)
2383
self._insert_matrix_into_matrix(M, D(1), e, 0)
2384
self._insert_matrix_into_matrix(M, D(1), 2*e, 1*e)
2385
self._insert_matrix_into_matrix(M, D(1), 3*e, 2*e)
2386
2387
mix_columns = Matrix(k, r*c*e, r*c*e)
2388
2389
for i in range(c):
2390
self._insert_matrix_into_matrix(mix_columns, M, r*e*i, r*e*i)
2391
2392
return mix_columns
2393
2394
def inversion_polynomials(self, xi, wi, length):
2395
"""
2396
Return polynomials to represent the inversion in the AES S-Box.
2397
2398
INPUT:
2399
2400
2401
- ``xi`` - output variables
2402
2403
- ``wi`` - input variables
2404
2405
- ``length`` - length of both lists
2406
2407
2408
EXAMPLE::
2409
2410
sage: sr = mq.SR(1, 1, 1, 8)
2411
sage: R = sr.ring()
2412
sage: xi = Matrix(R, 8, 1, sr.vars('x', 1))
2413
sage: wi = Matrix(R, 8, 1, sr.vars('w', 1))
2414
sage: sr.inversion_polynomials(xi, wi, 8)
2415
[x100*w100 + 1,
2416
x101*w101 + 1,
2417
x102*w102 + 1,
2418
x103*w103 + 1,
2419
x104*w104 + 1,
2420
x105*w105 + 1,
2421
x106*w106 + 1,
2422
x107*w107 + 1]
2423
"""
2424
return [xi[j, 0]*wi[j, 0] + 1 for j in range(length)]
2425
2426
def field_polynomials(self, name, i, l=None):
2427
"""
2428
Return list of conjugacy polynomials for a given round ``i``
2429
and name ``name``.
2430
2431
INPUT:
2432
2433
- ``name`` - variable name
2434
- ``i`` - round number
2435
- ``l`` - r\*c (default: ``None``)
2436
2437
EXAMPLE::
2438
2439
sage: sr = mq.SR(3, 1, 1, 8)
2440
sage: sr.field_polynomials('x', 2)
2441
[x200^2 + x201,
2442
x201^2 + x202,
2443
x202^2 + x203,
2444
x203^2 + x204,
2445
x204^2 + x205,
2446
x205^2 + x206,
2447
x206^2 + x207,
2448
x207^2 + x200]
2449
"""
2450
r = self._r
2451
c = self._c
2452
e = self._e
2453
n = self._n
2454
2455
if l is None:
2456
l = r*c
2457
2458
_vars = self.vars(name, i, l, e)
2459
return [_vars[e*j+i]**2 - _vars[e*j+(i+1)%e] for j in range(l) for i in range(e)]
2460
2461
class SR_gf2(SR_generic):
2462
def __init__(self, n=1, r=1, c=1, e=4, star=False, **kwargs):
2463
r"""
2464
Small Scale Variants of the AES polynomial system constructor over
2465
`\GF{2}`. See help for SR.
2466
2467
EXAMPLE::
2468
2469
sage: sr = mq.SR(gf2=True)
2470
sage: sr
2471
SR(1,1,1,4)
2472
"""
2473
SR_generic.__init__(self, n, r, c, e, star, **kwargs)
2474
self._correct_only = kwargs.get("correct_only", False)
2475
self._biaffine_only = kwargs.get("biaffine_only", True)
2476
2477
def vector(self, d=None):
2478
"""
2479
Constructs a vector suitable for the algebraic representation of
2480
SR.
2481
2482
INPUT:
2483
2484
- ``d`` - values for vector (default: ``None``)
2485
2486
2487
EXAMPLE::
2488
2489
sage: sr = mq.SR(gf2=True)
2490
sage: sr
2491
SR(1,1,1,4)
2492
sage: k = sr.base_ring()
2493
sage: A = Matrix(k, 1, 1, [k.gen()])
2494
sage: sr.vector(A)
2495
[0]
2496
[0]
2497
[1]
2498
[0]
2499
"""
2500
r = self.r
2501
c = self.c
2502
e = self.e
2503
k = GF(2)
2504
2505
if d is None:
2506
return Matrix(k, r*c*e, 1)
2507
elif is_Matrix(d) and d.ncols() == c and d.nrows() == r and d.base_ring() == self.k:
2508
l = flatten([self.phi(x) for x in d.transpose().list()], (Vector_modn_dense,list,tuple))
2509
return Matrix(k, r*c*e, 1, l)
2510
elif isinstance(d, (list, tuple)):
2511
if len(d) == self.r*self.c:
2512
l = flatten([self.phi(x) for x in d], (Vector_modn_dense,list,tuple))
2513
return Matrix(k, r*c*e, 1, l)
2514
elif len(d) == self.r*self.c*self.e:
2515
return Matrix(k, r*c*e, 1, d)
2516
else:
2517
raise TypeError
2518
else:
2519
raise TypeError
2520
2521
def is_vector(self, d):
2522
"""
2523
Return ``True`` if the given matrix satisfies the conditions
2524
for a vector as it appears in the algebraic expression of
2525
``self``.
2526
2527
INPUT:
2528
2529
2530
- ``d`` - matrix
2531
2532
2533
EXAMPLE::
2534
2535
sage: sr = mq.SR(gf2=True)
2536
sage: sr
2537
SR(1,1,1,4)
2538
sage: k = sr.base_ring()
2539
sage: A = Matrix(k, 1, 1, [k.gen()])
2540
sage: B = sr.vector(A)
2541
sage: sr.is_vector(A)
2542
False
2543
sage: sr.is_vector(B)
2544
True
2545
"""
2546
return is_Matrix(d) and \
2547
d.nrows() == self.r*self.c*self.e and \
2548
d.ncols() == 1 and \
2549
d.base_ring() == GF(2)
2550
2551
def phi(self, l, diffusion_matrix=False):
2552
r"""
2553
The operation `\phi` from [MR02]_
2554
2555
Given a list/matrix of elements in `\GF{2^e}`, return a
2556
matching list/matrix of elements in `\GF{2}`.
2557
2558
INPUT:
2559
2560
- ``l`` - element to perform `\phi` on.
2561
- ``diffusion_matrix`` - if ``True``, the given matrix ``l`` is
2562
transformed to a matrix which performs the same operation
2563
over `\GF{2}` as ``l`` over `\GF{2^n}` (default: ``False``).
2564
2565
EXAMPLE::
2566
2567
sage: sr = mq.SR(2, 1, 2, 4, gf2=True)
2568
sage: k = sr.base_ring()
2569
sage: A = matrix(k, 1, 2, [k.gen(), 0] )
2570
sage: sr.phi(A)
2571
[0 0]
2572
[0 0]
2573
[1 0]
2574
[0 0]
2575
"""
2576
ret = []
2577
r, c, e = self.r, self.c, self.e
2578
2579
# handle diffusion layer matrices first
2580
if is_Matrix(l) and diffusion_matrix and \
2581
l.nrows() == r*c and l.ncols() == r*c and \
2582
l.base_ring() == self.k:
2583
B = Matrix(GF(2), r*c*e, r*c*e)
2584
for x in range(r*c):
2585
for y in range(r*c):
2586
T = self._mul_matrix(l[x, y])
2587
self._insert_matrix_into_matrix(B, T, x*e, y*e)
2588
return B
2589
2590
# ground field elements
2591
if l in self.k:
2592
return list(reversed(l._vector_()))
2593
2594
# remaining matrices
2595
if is_Matrix(l):
2596
for x in l.transpose().list():
2597
ret += list(reversed(x._vector_()))
2598
# or lists
2599
else:
2600
for x in l:
2601
ret += list(reversed(x._vector_()))
2602
2603
if isinstance(l, list):
2604
return ret
2605
elif isinstance(l, tuple):
2606
return tuple(ret)
2607
elif is_Matrix(l):
2608
return Matrix(GF(2), l.ncols(), l.nrows()*self.e, ret).transpose()
2609
else: raise TypeError
2610
2611
def antiphi(self, l):
2612
"""
2613
The operation `\phi^{-1}` from [MR02]_ or the inverse of ``self.phi``.
2614
2615
INPUT:
2616
2617
- ``l`` - a vector in the sense of ``self.is_vector``
2618
2619
EXAMPLE::
2620
2621
sage: sr = mq.SR(gf2=True)
2622
sage: A = sr.random_state_array()
2623
sage: A
2624
[a^2]
2625
sage: sr.antiphi(sr.phi(A)) == A
2626
True
2627
"""
2628
e = self.e
2629
V = self.k.vector_space()
2630
2631
if is_Matrix(l):
2632
l2 = l.transpose().list()
2633
else:
2634
l2 = l
2635
2636
ret = []
2637
for i in range(0, len(l2), e):
2638
ret.append( self.k(V(list(reversed(l2[i:i+e])))) )
2639
2640
if isinstance(l, list):
2641
return ret
2642
elif isinstance(l, tuple):
2643
return tuple(ret)
2644
elif is_Matrix(l):
2645
return Matrix(self.base_ring(), self.r *self.c, 1, ret)
2646
else:
2647
raise TypeError
2648
2649
def shift_rows_matrix(self):
2650
"""
2651
Return the ``ShiftRows`` matrix.
2652
2653
EXAMPLE::
2654
2655
sage: sr = mq.SR(1, 2, 2, 4, gf2=True)
2656
sage: s = sr.random_state_array()
2657
sage: r1 = sr.shift_rows(s)
2658
sage: r2 = sr.state_array( sr.shift_rows_matrix() * sr.vector(s) )
2659
sage: r1 == r2
2660
True
2661
"""
2662
r = self.r
2663
c = self.c
2664
k = self.k
2665
bs = r*c
2666
shift_rows = Matrix(k, r*c, r*c)
2667
for x in range(0, c):
2668
for y in range(0, r):
2669
_r = ((x*r)+y)
2670
_c = ((x*r)+((r+1)*y)) % bs
2671
shift_rows[_r, _c] = 1
2672
return self.phi(shift_rows, diffusion_matrix=True)
2673
2674
def mix_columns_matrix(self):
2675
"""
2676
Return the ``MixColumns`` matrix.
2677
2678
EXAMPLE::
2679
2680
sage: sr = mq.SR(1, 2, 2, 4, gf2=True)
2681
sage: s = sr.random_state_array()
2682
sage: r1 = sr.mix_columns(s)
2683
sage: r2 = sr.state_array(sr.mix_columns_matrix() * sr.vector(s))
2684
sage: r1 == r2
2685
True
2686
"""
2687
r = self.r
2688
c = self.c
2689
k = self.k
2690
a = k.gen()
2691
2692
2693
2694
if r == 1:
2695
M = Matrix(k, r, r, 1)
2696
2697
elif r == 2:
2698
M = Matrix(k, r, r, [a+1, a, a, a+1])
2699
2700
elif r == 4:
2701
M = Matrix(k, r, [a, a+1, 1, 1, \
2702
1, a, a+1, 1, \
2703
1, 1, a, a+1, \
2704
a+1, 1, 1, a])
2705
2706
mix_columns = Matrix(k, r*c, r*c)
2707
2708
for i in range(c):
2709
self._insert_matrix_into_matrix(mix_columns, M, r*i, r*i)
2710
2711
return self.phi(mix_columns, diffusion_matrix=True)
2712
2713
def lin_matrix(self, length=None):
2714
"""
2715
Return the ``Lin`` matrix.
2716
2717
If no ``length`` is provided, the standard state space size is
2718
used. The key schedule calls this method with an explicit
2719
length argument because only ``self.r`` S-Box applications are
2720
performed in the key schedule.
2721
2722
INPUT:
2723
2724
- ``length`` - length of state space (default: ``None``)
2725
2726
2727
EXAMPLE::
2728
2729
sage: sr = mq.SR(1, 1, 1, 4, gf2=True)
2730
sage: sr.lin_matrix()
2731
[1 0 1 1]
2732
[1 1 0 1]
2733
[1 1 1 0]
2734
[0 1 1 1]
2735
"""
2736
r, c, e = self.r, self.c, self.e
2737
2738
if length is None:
2739
length = r*c
2740
2741
if e == 8:
2742
Z = Matrix(GF(2), 8, 8, [1, 0, 0, 0, 1, 1, 1, 1, \
2743
1, 1, 0, 0, 0, 1, 1, 1, \
2744
1, 1, 1, 0, 0, 0, 1, 1, \
2745
1, 1, 1, 1, 0, 0, 0, 1, \
2746
1, 1, 1, 1, 1, 0, 0, 0, \
2747
0, 1, 1, 1, 1, 1, 0, 0, \
2748
0, 0, 1, 1, 1, 1, 1, 0, \
2749
0, 0, 0, 1, 1, 1, 1, 1])
2750
else:
2751
Z = Matrix(GF(2), 4, 4, [1, 1, 1, 0, \
2752
0, 1, 1, 1, \
2753
1, 0, 1, 1, \
2754
1, 1, 0, 1])
2755
2756
2757
Z = Z.transpose() # account for endianess mismatch
2758
2759
lin = Matrix(GF(2), length*e, length*e)
2760
2761
for i in range(length):
2762
self._insert_matrix_into_matrix(lin, Z, i*e, i*e)
2763
return lin
2764
2765
def _mul_matrix(self, x):
2766
r"""
2767
Given an element `x` in self.base_ring(), return a matrix
2768
which performs the same operation on a when interpreted over
2769
`\GF{2^e}` as `x` over `\GF{2^e}`.
2770
2771
INPUT:
2772
2773
2774
- ``x`` - an element in self.base_ring()
2775
2776
2777
EXAMPLE::
2778
2779
sage: sr = mq.SR(gf2=True)
2780
sage: a = sr.k.gen()
2781
sage: A = sr._mul_matrix(a^2+1)
2782
sage: sr.antiphi( A * sr.vector([a+1]) )
2783
[a^3 + a^2 + a + 1]
2784
2785
::
2786
2787
sage: (a^2 + 1)*(a+1)
2788
a^3 + a^2 + a + 1
2789
"""
2790
a = self.k.gen()
2791
k = self.k
2792
e = self.e
2793
a = k.gen()
2794
2795
columns = []
2796
for i in reversed(range(e)):
2797
columns.append( list(reversed((x * a**i)._vector_())) )
2798
return Matrix(GF(2), e, e, columns).transpose()
2799
2800
def _square_matrix(self):
2801
"""
2802
Return a matrix of dimension self.e x self.e which performs the
2803
squaring operation over `GF(2^n)` on vectors of length e.
2804
2805
EXAMPLE::
2806
2807
sage: sr = mq.SR(gf2=True)
2808
sage: a = sr.k.gen()
2809
sage: S = sr._square_matrix()
2810
sage: sr.antiphi( S * sr.vector([a^3+1]) )
2811
[a^3 + a^2 + 1]
2812
2813
::
2814
2815
sage: (a^3 + 1)^2
2816
a^3 + a^2 + 1
2817
"""
2818
a = self.k.gen()
2819
e = self.e
2820
2821
columns = []
2822
for i in reversed(range(e)):
2823
columns.append( list(reversed(((a**i)**2)._vector_())) )
2824
return Matrix(GF(2), e , e, columns).transpose()
2825
2826
def inversion_polynomials_single_sbox(self, x=None, w=None, biaffine_only=None, correct_only=None):
2827
"""
2828
Return inversion polynomials of a single S-Box.
2829
2830
INPUT:
2831
2832
- ``xi`` - output variables
2833
- ``wi`` - input variables
2834
- ``length`` - length of both lists
2835
2836
EXAMPLES::
2837
2838
sage: sr = mq.SR(1, 1, 1, 8, gf2=True)
2839
sage: len(sr.inversion_polynomials_single_sbox())
2840
24
2841
sage: len(sr.inversion_polynomials_single_sbox(correct_only=True))
2842
23
2843
sage: len(sr.inversion_polynomials_single_sbox(biaffine_only=False))
2844
40
2845
sage: len(sr.inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True))
2846
39
2847
2848
2849
sage: sr = mq.SR(1, 1, 1, 8, gf2=True)
2850
sage: l0 = sr.inversion_polynomials_single_sbox(); len(l0)
2851
24
2852
sage: l1 = sr.inversion_polynomials_single_sbox(correct_only=True); len(l1)
2853
23
2854
sage: l2 = sr.inversion_polynomials_single_sbox(biaffine_only=False); len(l2)
2855
40
2856
sage: l3 = sr.inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True); len(l3)
2857
39
2858
2859
sage: set(l0) == set(sr._inversion_polynomials_single_sbox())
2860
True
2861
sage: set(l1) == set(sr._inversion_polynomials_single_sbox(correct_only=True))
2862
True
2863
sage: set(l2) == set(sr._inversion_polynomials_single_sbox(biaffine_only=False))
2864
True
2865
sage: set(l3) == set(sr._inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True))
2866
True
2867
2868
sage: sr = mq.SR(1, 1, 1, 4, gf2=True)
2869
sage: l0 = sr.inversion_polynomials_single_sbox(); len(l0)
2870
12
2871
sage: l1 = sr.inversion_polynomials_single_sbox(correct_only=True); len(l1)
2872
11
2873
sage: l2 = sr.inversion_polynomials_single_sbox(biaffine_only=False); len(l2)
2874
20
2875
sage: l3 = sr.inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True); len(l3)
2876
19
2877
2878
sage: set(l0) == set(sr._inversion_polynomials_single_sbox())
2879
True
2880
sage: set(l1) == set(sr._inversion_polynomials_single_sbox(correct_only=True))
2881
True
2882
sage: set(l2) == set(sr._inversion_polynomials_single_sbox(biaffine_only=False))
2883
True
2884
sage: set(l3) == set(sr._inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True))
2885
True
2886
"""
2887
e = self.e
2888
2889
if biaffine_only is None:
2890
biaffine_only = self._biaffine_only
2891
if correct_only is None:
2892
correct_only = self._correct_only
2893
2894
if x is None and w is None:
2895
# make sure it prints like in the book.
2896
names = ["w%d" % i for i in reversed(range(e))] + ["x%d"%i for i in reversed(range(e))]
2897
P = PolynomialRing(GF(2), e*2, names, order='lex')
2898
x = P.gens()[e:]
2899
w = P.gens()[:e]
2900
else:
2901
if isinstance(x, (tuple, list)):
2902
P = x[0].parent()
2903
elif is_Matrix(x):
2904
P = x.base_ring()
2905
else:
2906
raise TypeError, "x not understood"
2907
2908
if is_Matrix(x):
2909
x = x.column(0).list()
2910
if is_Matrix(w):
2911
w = w.column(0).list()
2912
2913
if e == 4:
2914
w3,w2,w1,w0 = w
2915
x3,x2,x1,x0 = x
2916
2917
l = [w3*x3 + w3*x0 + w2*x1 + w1*x2 + w0*x3,
2918
w3*x3 + w3*x2 + w2*x3 + w2*x0 + w1*x1 + w0*x2,
2919
w3*x2 + w3*x1 + w2*x3 + w2*x2 + w1*x3 + w1*x0 + w0*x1,
2920
w3*x3 + w3*x2 + w3*x0 + w2*x2 + w1*x3 + w1*x1 + w0*x3 + x3,
2921
w3*x1 + w2*x3 + w2*x2 + w2*x0 + w1*x2 + w0*x3 + w0*x1 + x2,
2922
w3*x3 + w3*x2 + w3*x1 + w2*x1 + w1*x3 + w1*x2 + w1*x0 + w0*x2 + x1,
2923
w3*x2 + w2*x3 + w2*x1 + w1*x3 + w0*x2 + w0*x0 + x0,
2924
w3*x3 + w3*x1 + w3*x0 + w3 + w2*x3 + w2*x2 + w1*x1 + w0*x3,
2925
w3*x2 + w3*x0 + w2*x2 + w2*x1 + w2 + w1*x3 + w1*x0 + w0*x2,
2926
w3*x3 + w3*x1 + w2*x3 + w2*x1 + w2*x0 + w1*x3 + w1*x2 + w1 + w0*x1,
2927
w3*x2 + w3*x1 + w2*x3 + w2*x0 + w1*x2 + w0*x0 + w0]
2928
2929
if not correct_only:
2930
l.append(w3*x1 + w2*x2 + w1*x3 + w0*x0 + 1)
2931
2932
if not biaffine_only:
2933
l.extend([w3*x2 + w3*x1 + w3*x0 + w2*x3 + w2*x1 + w1*x3 + w1*x2 + w0*x3 + x3**2 + x3*x2 + x3*x1 + x2**2 + x1**2,
2934
w3*x2 + w2*x2 + w2*x1 + w2*x0 + w1*x3 + w1*x1 + w0*x3 + w0*x2 + x3*x2 + x3*x1 + x3*x0 + x2**2 + x2*x1 + x2*x0 + x1*x0,
2935
w3*x2 + w3*x1 + w2*x2 + w1*x2 + w1*x1 + w1*x0 + w0*x3 + w0*x1 + x3**2 + x3*x2 + x2*x0 + x1*x0,
2936
w3*x3 + w3*x1 + w2*x3 + w2*x2 + w1*x3 + w0*x3 + w0*x2 + w0*x1 + w0*x0 + x3*x1 + x2*x1 + x2*x0 + x0**2,
2937
w3**2 + w3*w2 + w3*w1 + w3*x2 + w3*x1 + w3*x0 + w2**2 + w2*x3 + w2*x1 + w1**2 + w1*x3 + w1*x2 + w0*x3,
2938
w3*w2 + w3*w1 + w3*w0 + w3*x1 + w3*x0 + w2**2 + w2*w1 + w2*w0 + w2*x3 + w2*x2 + w2*x0 + w1*w0 + w1*x2 + w1*x1 + w0*x2,
2939
w3**2 + w3*w2 + w3*x0 + w2*w0 + w2*x3 + w2*x2 + w2*x1 + w1*w0 + w1*x3 + w1*x1 + w1*x0 + w0*x1,
2940
w3*w1 + w3*x3 + w3*x2 + w3*x1 + w3*x0 + w2*w1 + w2*w0 + w2*x2 + w2*x0 + w1*x3 + w1*x0 + w0**2 + w0*x0])
2941
return l
2942
2943
else:
2944
w7,w6,w5,w4,w3,w2,w1,w0 = w
2945
x7,x6,x5,x4,x3,x2,x1,x0 = x
2946
2947
l = [w7*x7 + w7*x5 + w7*x4 + w7*x0 + w6*x6 + w6*x5 + w6*x1 + w5*x7 + w5*x6 + w5*x2 + w4*x7 + w4*x3 + w3*x4 + w2*x5 + w1*x6 + w0*x7,
2948
w7*x6 + w7*x4 + w7*x3 + w6*x7 + w6*x5 + w6*x4 + w6*x0 + w5*x6 + w5*x5 + w5*x1 + w4*x7 + w4*x6 + w4*x2 + w3*x7 + w3*x3 + w2*x4 + w1*x5 + w0*x6,
2949
w7*x5 + w7*x3 + w7*x2 + w6*x6 + w6*x4 + w6*x3 + w5*x7 + w5*x5 + w5*x4 + w5*x0 + w4*x6 + w4*x5 + w4*x1 + w3*x7 + w3*x6 + w3*x2 + w2*x7 + w2*x3 + w1*x4 + w0*x5,
2950
w7*x7 + w7*x4 + w7*x2 + w7*x1 + w6*x5 + w6*x3 + w6*x2 + w5*x6 + w5*x4 + w5*x3 + w4*x7 + w4*x5 + w4*x4 + w4*x0 + w3*x6 + w3*x5 + w3*x1 + w2*x7 + w2*x6 + w2*x2 + w1*x7 + w1*x3 + w0*x4,
2951
w7*x7 + w7*x6 + w7*x5 + w7*x4 + w7*x3 + w7*x1 + w6*x7 + w6*x6 + w6*x5 + w6*x4 + w6*x2 + w5*x7 + w5*x6 + w5*x5 + w5*x3 + w4*x7 + w4*x6 + w4*x4 + w3*x7 + w3*x5 + w3*x0 + w2*x6 + w2*x1 \
2952
+ w1*x7 + w1*x2 + w0*x3,
2953
w7*x6 + w7*x3 + w7*x2 + w6*x7 + w6*x4 + w6*x3 + w5*x5 + w5*x4 + w4*x6 + w4*x5 + w3*x7 + w3*x6 + w2*x7 + w2*x0 + w1*x1 + w0*x2,
2954
w7*x7 + w7*x5 + w7*x2 + w7*x1 + w6*x6 + w6*x3 + w6*x2 + w5*x7 + w5*x4 + w5*x3 + w4*x5 + w4*x4 + w3*x6 + w3*x5 + w2*x7 + w2*x6 + w1*x7 + w1*x0 + w0*x1,
2955
w7*x6 + w7*x5 + w7*x2 + w7*x0 + w6*x7 + w6*x4 + w6*x3 + w5*x7 + w5*x6 + w5*x3 + w5*x1 + w4*x5 + w4*x4 + w3*x7 + w3*x4 + w3*x2 + w2*x6 + w2*x5 + w1*x5 + w1*x3 + w0*x7 + w0*x6 + x7,
2956
w7*x6 + w7*x3 + w7*x2 + w6*x6 + w6*x5 + w6*x2 + w6*x0 + w5*x7 + w5*x4 + w5*x3 + w4*x7 + w4*x6 + w4*x3 + w4*x1 + w3*x5 + w3*x4 + w2*x7 + w2*x4 + w2*x2 + w1*x6 + w1*x5 + w0*x5 + w0*x3 \
2957
+ x6,
2958
w7*x7 + w7*x5 + w7*x4 + w7*x1 + w6*x6 + w6*x3 + w6*x2 + w5*x6 + w5*x5 + w5*x2 + w5*x0 + w4*x7 + w4*x4 + w4*x3 + w3*x7 + w3*x6 + w3*x3 + w3*x1 + w2*x5 + w2*x4 + w1*x7 + w1*x4 + w1*x2 \
2959
+ w0*x6 + w0*x5 + x5,
2960
w7*x7 + w7*x5 + w7*x2 + w7*x1 + w6*x7 + w6*x5 + w6*x4 + w6*x1 + w5*x6 + w5*x3 + w5*x2 + w4*x6 + w4*x5 + w4*x2 + w4*x0 + w3*x7 + w3*x4 + w3*x3 + w2*x7 + w2*x6 + w2*x3 + w2*x1 + w1*x5 \
2961
+ w1*x4 + w0*x7 + w0*x4 + w0*x2 + x4,
2962
w7*x5 + w7*x4 + w7*x3 + w7*x2 + w6*x5 + w6*x4 + w6*x3 + w6*x2 + w6*x1 + w5*x6 + w5*x5 + w5*x4 + w5*x3 + w4*x6 + w4*x5 + w4*x4 + w4*x3 + w4*x2 + w3*x7 + w3*x6 + w3*x5 + w3*x4 + w3*x0 \
2963
+ w2*x7 + w2*x6 + w2*x5 + w2*x4 + w2*x3 + w1*x7 + w1*x6 + w1*x5 + w1*x1 + w0*x7 + w0*x6 + w0*x5 + w0*x4 + x3,
2964
w7*x7 + w7*x6 + w7*x5 + w7*x4 + w7*x3 + w7*x1 + w6*x7 + w6*x5 + w6*x2 + w5*x7 + w5*x6 + w5*x5 + w5*x4 + w5*x2 + w4*x6 + w4*x3 + w3*x7 + w3*x6 + w3*x5 + w3*x3 + w2*x7 + w2*x4 + w2*x0 \
2965
+ w1*x7 + w1*x6 + w1*x4 + w0*x5 + w0*x1 + x2,
2966
w7*x6 + w7*x4 + w7*x1 + w6*x7 + w6*x6 + w6*x5 + w6*x4 + w6*x3 + w6*x1 + w5*x7 + w5*x5 + w5*x2 + w4*x7 + w4*x6 + w4*x5 + w4*x4 + w4*x2 + w3*x6 + w3*x3 + w2*x7 + w2*x6 + w2*x5 + w2*x3 \
2967
+ w1*x7 + w1*x4 + w1*x0 + w0*x7 + w0*x6 + w0*x4 + x1,
2968
w7*x7 + w7*x4 + w7*x3 + w6*x7 + w6*x6 + w6*x3 + w6*x1 + w5*x5 + w5*x4 + w4*x7 + w4*x4 + w4*x2 + w3*x6 + w3*x5 + w2*x5 + w2*x3 + w1*x7 + w1*x6 + w0*x6 + w0*x4 + w0*x0 + x0,
2969
w7*x6 + w7*x5 + w7*x3 + w7*x0 + w7 + w6*x7 + w6*x5 + w6*x2 + w6*x0 + w5*x7 + w5*x4 + w5*x2 + w5*x1 + w4*x6 + w4*x4 + w4*x3 + w3*x6 + w3*x5 + w3*x1 + w2*x7 + w2*x3 + w1*x5 + w0*x7,
2970
w7*x5 + w7*x4 + w7*x2 + w6*x7 + w6*x6 + w6*x4 + w6*x1 + w6 + w5*x6 + w5*x3 + w5*x1 + w5*x0 + w4*x5 + w4*x3 + w4*x2 + w3*x7 + w3*x5 + w3*x4 + w3*x0 + w2*x7 + w2*x6 + w2*x2 + w1*x4 \
2971
+ w0*x6,
2972
w7*x7 + w7*x4 + w7*x3 + w7*x1 + w6*x6 + w6*x5 + w6*x3 + w6*x0 + w5*x7 + w5*x5 + w5*x2 + w5*x0 + w5 + w4*x7 + w4*x4 + w4*x2 + w4*x1 + w3*x6 + w3*x4 + w3*x3 + w2*x6 + w2*x5 + w2*x1 \
2973
+ w1*x7 + w1*x3 + w0*x5,
2974
w7*x7 + w7*x6 + w7*x3 + w7*x2 + w7*x0 + w6*x5 + w6*x4 + w6*x2 + w5*x7 + w5*x6 + w5*x4 + w5*x1 + w4*x6 + w4*x3 + w4*x1 + w4*x0 + w4 + w3*x5 + w3*x3 + w3*x2 + w2*x7 + w2*x5 + w2*x4 \
2975
+ w2*x0 + w1*x7 + w1*x6 + w1*x2 + w0*x4,
2976
w7*x3 + w7*x2 + w7*x1 + w7*x0 + w6*x5 + w6*x4 + w6*x3 + w6*x2 + w6*x1 + w6*x0 + w5*x7 + w5*x6 + w5*x5 + w5*x4 + w5*x3 + w5*x2 + w5*x1 + w5*x0 + w4*x7 + w4*x6 + w4*x5 + w4*x4 \
2977
+ w4*x3 + w4*x2 + w4*x0 + w3*x7 + w3*x6 + w3*x5 + w3*x4 + w3*x2 + w3 + w2*x7 + w2*x6 + w2*x4 + w1*x6 + w1*x1 + w0*x3,
2978
w7*x7 + w7*x6 + w7*x5 + w7*x3 + w7*x2 + w7*x1 + w6*x7 + w6*x5 + w6*x4 + w6*x3 + w6*x1 + w5*x7 + w5*x6 + w5*x5 + w5*x3 + w5*x0 + w4*x7 + w4*x5 + w4*x2 + w4*x1 + w3*x7 + w3*x4 \
2979
+ w3*x3 + w2*x6 + w2*x5 + w2 + w1*x7 + w1*x0 + w0*x2,
2980
w7*x6 + w7*x5 + w7*x4 + w7*x2 + w7*x1 + w7*x0 + w6*x7 + w6*x6 + w6*x4 + w6*x3 + w6*x2 + w6*x0 + w5*x6 + w5*x5 + w5*x4 + w5*x2 + w4*x7 + w4*x6 + w4*x4 + w4*x1 + w4*x0 + w3*x6 \
2981
+ w3*x3 + w3*x2 + w2*x5 + w2*x4 + w1*x7 + w1*x6 + w1 + w0*x1,
2982
w7*x7 + w7*x6 + w7*x4 + w7*x1 + w6*x6 + w6*x3 + w6*x1 + w6*x0 + w5*x5 + w5*x3 + w5*x2 + w4*x7 + w4*x5 + w4*x4 + w4*x0 + w3*x7 + w3*x6 + w3*x2 + w2*x4 + w1*x6 + w0*x0 + w0]
2983
2984
if not correct_only:
2985
l.append(w7*x6 + w7*x5 + w7*x1 + w6*x7 + w6*x6 + w6*x2 + w5*x7 + w5*x3 + w4*x4 + w3*x5 + w2*x6 + w1*x7 + w0*x0 + 1)
2986
2987
if not biaffine_only:
2988
l.extend([w7**2 + w7*w6 + w7*w3 + w7*w1 + w7*x7 + w7*x6 + w7*x5 + w7*x2 + w7*x1 + w7*x0 + w6**2 + w6*w0 + w6*x6 + w6*x5 + w6*x4 + w6*x3 + w6*x1 + w6*x0 + w5**2 + w5*w4 + w5*w3 \
2989
+ w5*w2 + w5*x7 + w5*x5 + w5*x4 + w5*x1 + w5*x0 + w4**2 + w4*w2 + w4*w0 + w4*x5 + w4*x4 + w4*x2 + w3*w2 + w3*x6 + w3*x3 + w3*x1 + w3*x0 + w2*x7 + w2*x5 + w2*x4 \
2990
+ w2*x0 + w1*x4 + w0**2 + w0*x0,
2991
w7*x6 + w7*x4 + w7*x1 + w6*x7 + w6*x6 + w6*x5 + w6*x2 + w5*x7 + w5*x6 + w5*x5 + w5*x4 + w5*x3 + w5*x1 + w4*x5 + w4*x4 + w4*x3 + w4*x1 + w4*x0 + w3*x7 + w3*x5 + w3*x2 \
2992
+ w2*x7 + w2*x6 + w2*x3 + w1*x7 + w1*x6 + w1*x5 + w1*x4 + w1*x2 + w0*x6 + w0*x5 + w0*x4 + w0*x2 + w0*x1 + x7**2 + x7*x6 + x7*x5 + x7*x3 + x7*x1 + x7*x0 + x6*x2 \
2993
+ x6*x1 + x5*x4 + x5*x3 + x5*x2 + x5*x1 + x4*x3 + x4*x2 + x4*x1 + x3**2 + x3*x2 + x2*x1 + x2*x0,
2994
w7*x5 + w7*x4 + w7*x3 + w7*x1 + w7*x0 + w6*x7 + w6*x5 + w6*x2 + w5*x7 + w5*x6 + w5*x3 + w4*x7 + w4*x6 + w4*x5 + w4*x4 + w4*x2 + w3*x6 + w3*x5 + w3*x4 + w3*x2 + w3*x1 \
2995
+ w2*x6 + w2*x3 + w1*x7 + w1*x4 + w0*x7 + w0*x6 + w0*x5 + w0*x3 + x7*x3 + x7*x2 + x6*x5 + x6*x4 + x6*x3 + x6*x2 + x6*x0 + x5*x4 + x5*x3 + x5*x2 + x4**2 + x4*x3 \
2996
+ x3*x2 + x3*x1,
2997
w7*w3 + w7*w2 + w7*x6 + w7*x5 + w7*x4 + w7*x1 + w7*x0 + w6*w5 + w6*w4 + w6*w3 + w6*w2 + w6*w0 + w6*x5 + w6*x4 + w6*x3 + w6*x2 + w6*x0 + w5*w4 + w5*w3 + w5*w2 + w5*x7 \
2998
+ w5*x6 + w5*x4 + w5*x3 + w5*x0 + w4**2 + w4*w3 + w4*x7 + w4*x4 + w4*x3 + w4*x1 + w3*w2 + w3*w1 + w3*x7 + w3*x5 + w3*x2 + w3*x0 + w2*x6 + w2*x4 + w2*x3 + w1*x7 \
2999
+ w1*x3 + w0*x7,
3000
w7*x5 + w7*x2 + w7*x1 + w6*x7 + w6*x6 + w6*x5 + w6*x4 + w6*x2 + w6*x1 + w5*x5 + w5*x3 + w5*x2 + w4*x3 + w4*x2 + w4*x1 + w3*x6 + w3*x3 + w3*x2 + w3*x0 + w2*x7 + w2*x6 \
3001
+ w2*x5 + w2*x3 + w2*x2 + w1*x6 + w1*x4 + w1*x3 + w0*x4 + w0*x3 + w0*x2 + x7*x5 + x7*x4 + x7*x1 + x7*x0 + x6*x0 + x5**2 + x5*x2 + x5*x1 + x5*x0 + x4**2 + x4*x0 \
3002
+ x3*x2 + x3*x0 + x1**2,
3003
w7*w6 + w7*w5 + w7*w4 + w7*w3 + w7*x7 + w7*x5 + w7*x4 + w7*x3 + w7*x0 + w6**2 + w6*w5 + w6*w4 + w6*w2 + w6*w1 + w6*w0 + w6*x7 + w6*x4 + w6*x3 + w6*x2 + w6*x1 + w5*w4 \
3004
+ w5*w1 + w5*w0 + w5*x7 + w5*x6 + w5*x5 + w5*x3 + w5*x2 + w4*w2 + w4*w1 + w4*x7 + w4*x6 + w4*x3 + w4*x2 + w4*x0 + w3*w0 + w3*x7 + w3*x6 + w3*x4 + w3*x1 + w2**2 \
3005
+ w2*x5 + w2*x3 + w2*x2 + w1*x7 + w1*x6 + w1*x2 + w0*x6,
3006
w7*w5 + w7*w4 + w7*w1 + w7*w0 + w7*x6 + w7*x2 + w6*w0 + w6*x6 + w6*x3 + w6*x2 + w6*x1 + w5**2 + w5*w2 + w5*w1 + w5*w0 + w5*x7 + w5*x6 + w5*x5 + w5*x2 + w4**2 + w4*w0 \
3007
+ w4*x6 + w4*x1 + w4*x0 + w3*w2 + w3*w0 + w3*x5 + w3*x4 + w3*x3 + w3*x2 + w3*x1 + w3*x0 + w2*x7 + w2*x6 + w2*x5 + w2*x4 + w2*x3 + w2*x2 + w2*x0 + w1**2 + w1*x7 \
3008
+ w1*x6 + w1*x4 + w0*x3,
3009
w7*x7 + w7*x6 + w7*x5 + w7*x2 + w6*x7 + w6*x6 + w6*x5 + w6*x4 + w6*x3 + w6*x1 + w5*x5 + w5*x4 + w5*x3 + w5*x1 + w5*x0 + w4*x7 + w4*x5 + w4*x2 + w3*x7 + w3*x6 + w3*x3 \
3010
+ w2*x7 + w2*x6 + w2*x5 + w2*x4 + w2*x2 + w1*x6 + w1*x5 + w1*x4 + w1*x2 + w1*x1 + w0*x6 + w0*x3 + x7**2 + x7*x5 + x7*x3 + x6**2 + x6*x5 + x6*x2 + x6*x0 + x5**2 \
3011
+ x4**2 + x4*x3 + x4*x2 + x4*x1 + x3**2 + x3*x1 + x2*x1,
3012
w7**2 + w7*w6 + w7*w5 + w7*w3 + w7*w1 + w7*w0 + w7*x6 + w7*x5 + w7*x3 + w7*x2 + w7*x1 + w6*w2 + w6*w1 + w6*x7 + w6*x6 + w6*x5 + w6*x2 + w6*x1 + w6*x0 + w5*w4 + w5*w3 \
3013
+ w5*w2 + w5*w1 + w5*x6 + w5*x5 + w5*x4 + w5*x3 + w5*x1 + w5*x0 + w4*w3 + w4*w2 + w4*w1 + w4*x7 + w4*x5 + w4*x4 + w4*x1 + w4*x0 + w3**2 + w3*w2 + w3*x5 + w3*x4 \
3014
+ w3*x2 + w2*w1 + w2*w0 + w2*x6 + w2*x3 + w2*x1 + w2*x0 + w1*x7 + w1*x5 + w1*x4 + w1*x0 + w0*x4,
3015
w7*x7 + w7*x5 + w7*x2 + w6*x7 + w6*x6 + w6*x3 + w5*x7 + w5*x6 + w5*x5 + w5*x4 + w5*x2 + w4*x6 + w4*x5 + w4*x4 + w4*x2 + w4*x1 + w3*x6 + w3*x3 + w2*x7 + w2*x4 + w1*x7 \
3016
+ w1*x6 + w1*x5 + w1*x3 + w0*x7 + w0*x6 + w0*x5 + w0*x3 + w0*x2 + w0*x0 + x7**2 + x7*x6 + x7*x3 + x7*x1 + x6**2 + x6*x0 + x5**2 + x5*x4 + x5*x3 + x5*x2 + x4**2 \
3017
+ x4*x2 + x4*x0 + x3*x2 + x0**2,
3018
w7*x7 + w7*x6 + w7*x5 + w7*x4 + w7*x3 + w7*x1 + w6*x5 + w6*x4 + w6*x3 + w6*x1 + w6*x0 + w5*x7 + w5*x5 + w5*x2 + w4*x7 + w4*x6 + w4*x3 + w3*x7 + w3*x6 + w3*x5 + w3*x4 \
3019
+ w3*x2 + w2*x6 + w2*x5 + w2*x4 + w2*x2 + w2*x1 + w1*x6 + w1*x3 + w0*x7 + w0*x4 + x7*x6 + x7*x5 + x7*x4 + x7*x3 + x6**2 + x6*x5 + x6*x4 + x6*x2 + x6*x1 + x6*x0 \
3020
+ x5*x4 + x5*x1 + x5*x0 + x4*x2 + x4*x1 + x3*x0 + x2**2,
3021
w7*x5 + w7*x4 + w7*x3 + w7*x2 + w6*x7 + w6*x1 + w5*x5 + w5*x4 + w5*x3 + w5*x2 + w5*x1 + w4*x7 + w4*x6 + w4*x4 + w4*x3 + w3*x6 + w3*x5 + w3*x4 + w3*x3 + w2*x2 + w2*x0 \
3022
+ w1*x6 + w1*x5 + w1*x4 + w1*x3 + w1*x2 + w0*x7 + w0*x5 + w0*x4 + x7**2 + x7*x4 + x7*x2 + x6*x4 + x6*x3 + x6*x2 + x6*x1 + x5**2 + x5*x4 + x5*x3 + x5*x2 + x5*x0 \
3023
+ x4*x3 + x4*x2 + x4*x1 + x3**2 + x2*x0 + x1*x0,
3024
w7*x6 + w7*x5 + w7*x3 + w7*x2 + w6*x5 + w6*x4 + w6*x3 + w6*x2 + w5*x7 + w5*x1 + w4*x5 + w4*x4 + w4*x3 + w4*x2 + w4*x1 + w3*x7 + w3*x6 + w3*x4 + w3*x3 + w2*x6 + w2*x5 \
3025
+ w2*x4 + w2*x3 + w1*x2 + w1*x0 + w0*x6 + w0*x5 + w0*x4 + w0*x3 + w0*x2 + x7*x5 + x7*x2 + x7*x0 + x6**2 + x6*x5 + x6*x2 + x6*x1 + x6*x0 + x5**2 + x5*x4 + x4**2 \
3026
+ x4*x2 + x4*x1 + x4*x0 + x3**2 + x3*x2 + x1*x0,
3027
w7**2 + w7*w5 + w7*w3 + w7*x7 + w7*x6 + w7*x4 + w7*x3 + w7*x2 + w6**2 + w6*w5 + w6*w2 + w6*w0 + w6*x7 + w6*x6 + w6*x3 + w6*x2 + w6*x1 + w6*x0 + w5**2 + w5*x7 + w5*x6 \
3028
+ w5*x5 + w5*x4 + w5*x2 + w5*x1 + w4**2 + w4*w3 + w4*w2 + w4*w1 + w4*x6 + w4*x5 + w4*x2 + w4*x1 + w3**2 + w3*w1 + w3*x6 + w3*x5 + w3*x3 + w3*x0 + w2*w1 + w2*x7 \
3029
+ w2*x4 + w2*x2 + w2*x1 + w1*x6 + w1*x5 + w1*x1 + w0*x5,
3030
w7*w5 + w7*w2 + w7*w0 + w7*x5 + w7*x3 + w6**2 + w6*w5 + w6*w2 + w6*w1 + w6*w0 + w6*x7 + w6*x3 + w6*x2 + w6*x0 + w5**2 + w5*w4 + w5*x7 + w5*x6 + w5*x4 + w5*x2 + w5*x0 \
3031
+ w4**2 + w4*w2 + w4*w1 + w4*w0 + w4*x6 + w4*x4 + w4*x3 + w4*x2 + w4*x0 + w3**2 + w3*w2 + w3*x7 + w3*x6 + w3*x4 + w3*x3 + w3*x2 + w3*x0 + w2*x7 + w2*x6 + w2*x4 \
3032
+ w2*x1 + w2*x0 + w1*w0 + w1*x5 + w1*x4 + w0*x1,
3033
w7**2 + w7*w4 + w7*w2 + w7*x6 + w7*x4 + w7*x0 + w6*w4 + w6*w3 + w6*w2 + w6*w1 + w6*x4 + w6*x3 + w6*x1 + w5**2 + w5*w4 + w5*w3 + w5*w2 + w5*w0 + w5*x7 + w5*x5 + w5*x3 \
3034
+ w5*x1 + w5*x0 + w4*w3 + w4*w2 + w4*w1 + w4*x7 + w4*x5 + w4*x4 + w4*x3 + w4*x1 + w4*x0 + w3**2 + w3*x7 + w3*x5 + w3*x4 + w3*x3 + w3*x1 + w2*w0 + w2*x7 + w2*x5 \
3035
+ w2*x2 + w2*x1 + w1*w0 + w1*x6 + w1*x5 + w0*x2])
3036
3037
return l
3038
3039
def _inversion_polynomials_single_sbox(self, x= None, w=None, biaffine_only=None, correct_only=None):
3040
"""
3041
Generate inversion polynomials of a single S-box.
3042
3043
INPUT:
3044
3045
- ``x`` - output variables (default: ``None``)
3046
- ``w`` - input variables (default: ``None``)
3047
- ``biaffine_only`` - only include biaffine polynomials (default: object default)
3048
- ``correct_only`` - only include correct polynomials (default: object default)
3049
3050
EXAMPLES::
3051
3052
sage: sr = mq.SR(1, 1, 1, 8, gf2=True)
3053
sage: len(sr._inversion_polynomials_single_sbox())
3054
24
3055
sage: len(sr._inversion_polynomials_single_sbox(correct_only=True))
3056
23
3057
sage: len(sr._inversion_polynomials_single_sbox(biaffine_only=False))
3058
40
3059
sage: len(sr._inversion_polynomials_single_sbox(biaffine_only=False, correct_only=True))
3060
39
3061
"""
3062
e = self.e
3063
3064
if biaffine_only is None:
3065
biaffine_only = self._biaffine_only
3066
if correct_only is None:
3067
correct_only = self._correct_only
3068
3069
if x is None and w is None:
3070
# make sure it prints like in the book.
3071
names = ["w%d" % i for i in reversed(range(e))] + ["x%d"%i for i in reversed(range(e))]
3072
P = PolynomialRing(GF(2), e*2, names, order='lex')
3073
x = Matrix(P, e, 1, P.gens()[e:])
3074
w = Matrix(P, e, 1, P.gens()[:e])
3075
else:
3076
if isinstance(x, (tuple, list)):
3077
P = x[0].parent()
3078
elif is_Matrix(x):
3079
P = x.base_ring()
3080
else:
3081
raise TypeError, "x not understood"
3082
3083
if isinstance(x, (tuple, list)):
3084
x = Matrix(P, e, 1, x)
3085
if isinstance(w, (tuple, list)):
3086
w = Matrix(P, e, 1, w)
3087
3088
T = self._mul_matrix(self.k.gen())
3089
o = Matrix(P, e, 1, [0]*(e-1) + [1])
3090
3091
columns = []
3092
for i in reversed(range(e)):
3093
columns.append((T**i * w).list())
3094
Cw = Matrix(P, e, e, columns).transpose()
3095
3096
columns = []
3097
for i in reversed(range(e)):
3098
columns.append((T**i * x).list())
3099
Cx = Matrix(P, e, e, columns).transpose()
3100
3101
S = self._square_matrix()
3102
3103
l = []
3104
if correct_only:
3105
l.append( (Cw * x + o).list()[:-1] )
3106
else:
3107
l.append( (Cw * x + o).list() )
3108
l.append( (Cw * S *x + x).list() )
3109
l.append( (Cx * S *w + w).list() )
3110
if not biaffine_only:
3111
l.append( ((Cw * S**2 + Cx*S)*x).list() )
3112
l.append( ((Cx * S**2 + Cw*S)*w).list() )
3113
3114
return sum(l, [])
3115
3116
def inversion_polynomials(self, xi, wi, length):
3117
"""
3118
Return polynomials to represent the inversion in the AES S-Box.
3119
3120
INPUT:
3121
3122
3123
- ``xi`` - output variables
3124
3125
- ``wi`` - input variables
3126
3127
- ``length`` - length of both lists
3128
3129
3130
EXAMPLE::
3131
3132
sage: sr = mq.SR(1, 1, 1, 8, gf2=True)
3133
sage: xi = sr.vars('x', 1)
3134
sage: wi = sr.vars('w', 1)
3135
sage: sr.inversion_polynomials(xi, wi, len(xi))[:3]
3136
[x100*w100 + x100*w102 + x100*w103 + x100*w107 + x101*w101 + x101*w102 + x101*w106 + x102*w100 + x102*w101 + x102*w105 + x103*w100 + x103*w104 + x104*w103 + x105*w102 + x106*w101 + x107*w100,
3137
x100*w101 + x100*w103 + x100*w104 + x101*w100 + x101*w102 + x101*w103 + x101*w107 + x102*w101 + x102*w102 + x102*w106 + x103*w100 + x103*w101 + x103*w105 + x104*w100 + x104*w104 + x105*w103 + x106*w102 + x107*w101,
3138
x100*w102 + x100*w104 + x100*w105 + x101*w101 + x101*w103 + x101*w104 + x102*w100 + x102*w102 + x102*w103 + x102*w107 + x103*w101 + x103*w102 + x103*w106 + x104*w100 + x104*w101 + x104*w105 + x105*w100 + x105*w104 + x106*w103 + x107*w102]
3139
"""
3140
if is_Matrix(xi):
3141
xi = xi.list()
3142
if is_Matrix(wi):
3143
wi = wi.list()
3144
3145
e = self.e
3146
l = []
3147
for j in range(0, length, e):
3148
l += self.inversion_polynomials_single_sbox(xi[j:j+e], wi[j:j+e])
3149
return l
3150
3151
def field_polynomials(self, name, i, l=None):
3152
"""
3153
Return list of field polynomials for a given round ``i`` and
3154
name ``name``.
3155
3156
INPUT:
3157
3158
- ``name`` - variable name
3159
- ``i`` - round number
3160
- ``l`` - length of variable list (default: ``None`` = r\*c)
3161
3162
EXAMPLE::
3163
3164
sage: sr = mq.SR(3, 1, 1, 8, gf2=True, polybori=False)
3165
sage: sr.field_polynomials('x', 2)
3166
[x200^2 + x200, x201^2 + x201,
3167
x202^2 + x202, x203^2 + x203,
3168
x204^2 + x204, x205^2 + x205,
3169
x206^2 + x206, x207^2 + x207]
3170
3171
::
3172
3173
sage: sr = mq.SR(3, 1, 1, 8, gf2=True, polybori=True)
3174
sage: sr.field_polynomials('x', 2)
3175
[]
3176
"""
3177
r = self._r
3178
c = self._c
3179
e = self._e
3180
n = self._n
3181
3182
if l is None:
3183
l = r*c
3184
3185
if self._polybori:
3186
return []
3187
_vars = self.vars(name, i, l, e)
3188
return [_vars[e*j+i]**2 - _vars[e*j+i] for j in range(l) for i in range(e)]
3189
3190
class SR_gf2_2(SR_gf2):
3191
"""
3192
This is an example how to customize the SR constructor.
3193
3194
In this example, we replace the S-Box inversion polynomials by the
3195
polynomials generated by the S-Box class.
3196
"""
3197
def inversion_polynomials_single_sbox(self, x=None, w=None, biaffine_only=None, correct_only=None, groebner=False):
3198
"""
3199
Return inversion polynomials of a single S-Box.
3200
3201
INPUT:
3202
3203
- ``x`` - output variables (default: ``None``)
3204
- ``w`` - input variables (default: ``None``)
3205
- ``biaffine_only`` - ignored (always ``False``)
3206
- ``correct_only`` - ignored (always ``True``)
3207
- ``groebner`` - precompute the Groebner basis for this S-Box (default: ``False``).
3208
3209
EXAMPLES::
3210
3211
sage: from sage.crypto.mq.sr import SR_gf2_2
3212
sage: e = 4
3213
sage: sr = SR_gf2_2(1, 1, 1, e)
3214
sage: P = PolynomialRing(GF(2),['x%d'%i for i in range(e)] + ['w%d'%i for i in range(e)],order='lex')
3215
sage: X,W = P.gens()[:e],P.gens()[e:]
3216
sage: sr.inversion_polynomials_single_sbox(X, W, groebner=True)
3217
[x0 + w0*w1*w2 + w0*w1 + w0*w2 + w0*w3 + w0 + w1 + w2,
3218
x1 + w0*w1*w3 + w0*w3 + w0 + w1*w3 + w1 + w2*w3,
3219
x2 + w0*w2*w3 + w0*w2 + w0 + w1*w2 + w1*w3 + w2*w3,
3220
x3 + w0*w1*w2 + w0 + w1*w2*w3 + w1*w2 + w1*w3 + w1 + w2 + w3]
3221
3222
sage: from sage.crypto.mq.sr import SR_gf2_2
3223
sage: e = 4
3224
sage: sr = SR_gf2_2(1, 1, 1, e)
3225
sage: sr.inversion_polynomials_single_sbox()
3226
[w3*w1 + w3*w0 + w3*x2 + w3*x1 + w3 + w2*w1 + w1 + x3 + x2 + x1,
3227
w3*w2 + w3*w1 + w3*x3 + w2 + w1 + x3,
3228
w3*w2 + w3*w1 + w3*x2 + w3 + w2*x3 + x2 + x1,
3229
w3*w2 + w3*w1 + w3*x3 + w3*x2 + w3*x1 + w3 + w2*x2 + w0 + x3 + x2 + x1 + x0,
3230
w3*w2 + w3*w1 + w3*x1 + w3*x0 + w2*x1 + w0 + x3 + x0,
3231
w3*w2 + w3*w1 + w3*w0 + w3*x2 + w3*x1 + w2*w0 + w2*x0 + w0 + x3 + x2 + x1 + x0,
3232
w3*w2 + w3*x1 + w3 + w2*w0 + w1*w0 + w1 + x3 + x2,
3233
w3*w2 + w3*w1 + w3*x1 + w1*x3 + x3 + x2 + x1,
3234
w3*x3 + w3*x2 + w3*x0 + w3 + w1*x2 + w1 + w0 + x2 + x0,
3235
w3*w2 + w3*w1 + w3*x2 + w3*x1 + w1*x1 + w1 + w0 + x2 + x0,
3236
w3*w2 + w3*w1 + w3*w0 + w3*x3 + w3*x1 + w2*w0 + w1*x0 + x3 + x2,
3237
w3*w2 + w3*w1 + w3*x2 + w3*x1 + w3*x0 + w3 + w1 + w0*x3 + x3 + x2,
3238
w3*w2 + w3*w1 + w3*w0 + w3*x3 + w3 + w2*w0 + w1 + w0*x2 + x3 + x2,
3239
w3*w0 + w3*x2 + w2*w0 + w0*x1 + w0 + x3 + x1 + x0,
3240
w3*w0 + w3*x3 + w3*x0 + w2*w0 + w1 + w0*x0 + w0 + x3 + x2,
3241
w3*w2 + w3 + w1 + x3*x2 + x3 + x1,
3242
w3*w2 + w3*x3 + w1 + x3*x1 + x3 + x2,
3243
w3*w2 + w3*w0 + w3*x3 + w3*x2 + w3*x1 + w0 + x3*x0 + x1 + x0,
3244
w3*w2 + w3*w1 + w3*w0 + w3*x3 + w1 + w0 + x2*x1 + x2 + x0,
3245
w3*w2 + w2*w0 + w1 + x3 + x2*x0,
3246
w3*x3 + w3*x1 + w2*w0 + w1 + x3 + x2 + x1*x0 + x1]
3247
3248
TESTS:
3249
3250
Note that ``biaffine_only`` and ``correct_only`` are always
3251
ignored. The former is always false while the second is always
3252
true. They are only accepted for compatibility with the base
3253
class.
3254
3255
sage: from sage.crypto.mq.sr import SR_gf2_2
3256
sage: e = 4
3257
sage: sr = SR_gf2_2(1, 1, 1, e)
3258
sage: l = sr.inversion_polynomials_single_sbox()
3259
sage: l == sr.inversion_polynomials_single_sbox(biaffine_only=True, correct_only=False)
3260
True
3261
3262
"""
3263
e = self.e
3264
if x is None and w is None:
3265
# make sure it prints like in the book.
3266
names = ["w%d" % i for i in reversed(range(e))] + ["x%d"%i for i in reversed(range(e))]
3267
P = PolynomialRing(GF(2), e*2, names, order='lex')
3268
x = P.gens()[e:]
3269
w = P.gens()[:e]
3270
3271
S = self.sbox(inversion_only=True)
3272
F = S.polynomials(w, x, degree=e-2, groebner=groebner)
3273
return F
3274
3275
class AllowZeroInversionsContext:
3276
"""
3277
Temporarily allow zero inversion.
3278
"""
3279
def __init__(self, sr):
3280
"""
3281
EXAMPLE::
3282
3283
sage: from sage.crypto.mq.sr import AllowZeroInversionsContext
3284
sage: sr = mq.SR(1,2,2,4)
3285
sage: with AllowZeroInversionsContext(sr):
3286
... sr.sub_byte(0)
3287
a^2 + a
3288
"""
3289
self.sr = sr
3290
def __enter__(self):
3291
"""
3292
EXAMPLE::
3293
3294
sage: from sage.crypto.mq.sr import AllowZeroInversionsContext
3295
sage: sr = mq.SR(1,2,2,4)
3296
sage: sr.sub_byte(0)
3297
Traceback (most recent call last):
3298
...
3299
ZeroDivisionError: A zero inversion occurred during an encryption or key schedule.
3300
3301
sage: with AllowZeroInversionsContext(sr):
3302
... sr.sub_byte(0)
3303
a^2 + a
3304
"""
3305
self.allow_zero_inversions = self.sr._allow_zero_inversions
3306
self.sr._allow_zero_inversions = True
3307
def __exit__(self, typ, value, tb):
3308
"""
3309
EXAMPLE::
3310
3311
sage: from sage.crypto.mq.sr import AllowZeroInversionsContext
3312
sage: sr = mq.SR(1,2,2,4)
3313
sage: with AllowZeroInversionsContext(sr):
3314
... sr.sub_byte(0)
3315
a^2 + a
3316
sage: sr._allow_zero_inversions
3317
False
3318
"""
3319
self.sr._allow_zero_inversions = self.allow_zero_inversions
3320
3321
def test_consistency(max_n=2, **kwargs):
3322
r"""
3323
Test all combinations of ``r``, ``c``, ``e`` and ``n`` in ``(1,
3324
2)`` for consistency of random encryptions and their polynomial
3325
systems. `\GF{2}` and `\GF{2^e}` systems are tested. This test takes
3326
a while.
3327
3328
INPUT:
3329
3330
- ``max_n`` -- maximal number of rounds to consider (default: 2)
3331
- ``kwargs`` -- are passed to the SR constructor
3332
3333
TESTS:
3334
3335
The following test called with ``max_n`` = 2 requires a LOT of RAM
3336
(much more than 2GB). Since this might cause the doctest to fail
3337
on machines with "only" 2GB of RAM, we test ``max_n`` = 1, which
3338
has a more reasonable memory usage. ::
3339
3340
sage: from sage.crypto.mq.sr import test_consistency
3341
sage: test_consistency(1) # long time (65s on sage.math, 2012)
3342
True
3343
"""
3344
consistent = True
3345
for r in (1, 2, 4):
3346
for c in (1, 2, 4):
3347
for e in (4, 8):
3348
for n in range(1, max_n+1):
3349
for gf2 in (True, False):
3350
zero_division = True
3351
while zero_division:
3352
sr = SR(n, r, c, e, gf2=gf2, **kwargs)
3353
try:
3354
F, s = sr.polynomial_system()
3355
F = F.subs(s)
3356
consistent &= (F.groebner_basis()[0] != 1)
3357
if not consistent:
3358
print sr, " is not consistent"
3359
zero_division = False
3360
3361
except ZeroDivisionError:
3362
pass
3363
return consistent
3364
3365