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