Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupport News AboutSign UpSign In
| Download
Project: Math 314
Views: 1617
1
r"""
2
Band on Minh Van Nguyen's implementation of Mini-AES
3
4
A simplified variant of the Advanced Encryption Standard (AES). Note that
5
Mini-AES is for educational purposes only. It is a small-scale version of
6
the AES designed to help beginners understand the basic structure of AES.
7
8
AUTHORS:
9
10
- Minh Van Nguyen (2009-05): initial version
11
- Nathan McNew (2017): Converted to SAES (modified S-box, changed matrix fill order)
12
"""
13
14
###########################################################################
15
#
16
# This program is free software; you can redistribute it and/or modify
17
# it under the terms of the GNU General Public License as published by
18
# the Free Software Foundation; either version 2 of the License, or
19
# (at your option) any later version.
20
#
21
# This program is distributed in the hope that it will be useful,
22
# but WITHOUT ANY WARRANTY; without even the implied warranty of
23
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
# GNU General Public License for more details.
25
#
26
# http://www.gnu.org/licenses/
27
###########################################################################
28
29
from sage.matrix.matrix_dense import Matrix_dense
30
from sage.matrix.matrix_space import MatrixSpace
31
from sage.monoids.string_monoid import BinaryStrings
32
from sage.monoids.string_monoid_element import StringMonoidElement
33
from sage.rings.finite_rings.finite_field_constructor import FiniteField
34
from sage.rings.integer import Integer
35
from sage.structure.sage_object import SageObject
36
37
class SAES(SageObject):
38
r"""
39
This class implements SAES.Note that SAES is for educational purposes
40
only and is not secure for practical purposes. SAES is a version of
41
the AES with all parameters significantly reduced, but at the same time
42
preserving the structure of AES. The goal of SAES is to allow a
43
beginner to understand the structure of AES, thus laying a foundation
44
for a thorough study of AES. Its goal is as a teaching tool and is
45
different from the :mod:`SR <sage.crypto.mq.sr>` small scale variants
46
of the AES. SR defines a family of parameterizable variants of the AES
47
suitable as a framework for comparing different cryptanalytic techniques
48
that can be brought to bear on the AES.
49
50
EXAMPLES:
51
52
Encrypt a plaintext::
53
54
55
sage: maes = SAES()
56
sage: K = FiniteField(16, "x")
57
sage: MS = MatrixSpace(K, 2, 2)
58
sage: P = MS([K("x^3 + x"), K("x^2 + 1"), K("x^2 + x"), K("x^3 + x^2")]); P
59
<BLANKLINE>
60
[ x^3 + x x^2 + 1]
61
[ x^2 + x x^3 + x^2]
62
sage: key = MS([K("x^3 + x^2"), K("x^3 + x"), K("x^3 + x^2 + x"), K("x^2 + x + 1")]); key
63
<BLANKLINE>
64
[ x^3 + x^2 x^3 + x]
65
[x^3 + x^2 + x x^2 + x + 1]
66
sage: C = maes.encrypt(P, key); C
67
<BLANKLINE>
68
[ x x^2 + x]
69
[x^3 + x^2 + x x^3 + x]
70
71
Decrypt the result::
72
73
sage: plaintxt = maes.decrypt(C, key)
74
sage: plaintxt; P
75
<BLANKLINE>
76
[ x^3 + x x^2 + 1]
77
[ x^2 + x x^3 + x^2]
78
<BLANKLINE>
79
[ x^3 + x x^2 + 1]
80
[ x^2 + x x^3 + x^2]
81
sage: plaintxt == P
82
True
83
84
We can also work directly with binary strings::
85
86
sage: from sage.crypto.block_cipher.miniaes import MiniAES
87
sage: maes = MiniAES()
88
sage: bin = BinaryStrings()
89
sage: key = bin.encoding("KE"); key
90
0100101101000101
91
sage: P = bin.encoding("Encrypt this secret message!"); P
92
01000101011011100110001101110010011110010111000001110100001000000111010001101000011010010111001100100000011100110110010101100011011100100110010101110100001000000110110101100101011100110111001101100001011001110110010100100001
93
sage: C = maes(P, key, algorithm="encrypt"); C
94
10001000101001101111000001111000010011001110110101000111011011010101001011101111101011001110011100100011101100101010100010100111110110011001010001000111011011010010000011000110001100000111000011100110101111000000001110001001
95
sage: plaintxt = maes(C, key, algorithm="decrypt")
96
sage: plaintxt == P
97
True
98
99
Now we work with integers `n` such that `0 \leq n \leq 15`::
100
101
sage: from sage.crypto.block_cipher.miniaes import MiniAES
102
sage: maes = MiniAES()
103
sage: P = [n for n in xrange(16)]; P
104
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
105
sage: key = [2, 3, 11, 0]; key
106
[2, 3, 11, 0]
107
sage: P = maes.integer_to_binary(P); P
108
0000000100100011010001010110011110001001101010111100110111101111
109
sage: key = maes.integer_to_binary(key); key
110
0010001110110000
111
sage: C = maes(P, key, algorithm="encrypt"); C
112
1100100000100011111001010101010101011011100111110001000011100001
113
sage: plaintxt = maes(C, key, algorithm="decrypt")
114
sage: plaintxt == P
115
True
116
117
Generate some random plaintext and a random secret key. Encrypt the
118
plaintext using that secret key and decrypt the result. Then compare the
119
decrypted plaintext with the original plaintext::
120
121
sage: from sage.crypto.block_cipher.miniaes import MiniAES
122
sage: maes = MiniAES()
123
sage: MS = MatrixSpace(FiniteField(16, "x"), 2, 2)
124
sage: P = MS.random_element()
125
sage: key = maes.random_key()
126
sage: C = maes.encrypt(P, key)
127
sage: plaintxt = maes.decrypt(C, key)
128
sage: plaintxt == P
129
True
130
131
REFERENCES:
132
133
.. [P02] \R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a
134
testbed for cryptanalysis students. Cryptologia, 26(4):283--306, 2002.
135
"""
136
137
def __init__(self):
138
r"""
139
A simplified variant of the Advanced Encryption Standard (AES).
140
141
EXAMPLES::
142
143
sage: from sage.crypto.block_cipher.miniaes import MiniAES
144
sage: maes = MiniAES(); maes
145
Mini-AES block cipher with 16-bit keys
146
sage: MS = MatrixSpace(FiniteField(16, "x"), 2, 2)
147
sage: P = MS.random_element()
148
sage: key = maes.random_key()
149
sage: C = maes.encrypt(P, key)
150
sage: plaintxt = maes.decrypt(C, key)
151
sage: plaintxt == P
152
True
153
"""
154
from sage.crypto.sbox import SBox
155
self._key_size = 16 # the number of bits in a secret key
156
B = BinaryStrings()
157
K = FiniteField(self._key_size, "x")
158
# the S-box for encryption
159
self._sboxE = SBox(9,4,10,11,13,1,8,5,6,2,0,3,12,14,15,7)
160
# (14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7)
161
# the S-box for decryption
162
self._sboxD = SBox(10,5,9,11,1,7,8,15,6,0,2,3,12,4,13,14)
163
# nibble to finite field element
164
self._bin_to_GF = { B("0000"): K("0"),
165
B("0001"): K("1"),
166
B("0010"): K("x"),
167
B("0011"): K("x + 1"),
168
B("0100"): K("x^2"),
169
B("0101"): K("x^2 + 1"),
170
B("0110"): K("x^2 + x"),
171
B("0111"): K("x^2 + x + 1"),
172
B("1000"): K("x^3"),
173
B("1001"): K("x^3 + 1"),
174
B("1010"): K("x^3 + x"),
175
B("1011"): K("x^3 + x + 1"),
176
B("1100"): K("x^3 + x^2"),
177
B("1101"): K("x^3 + x^2 + 1"),
178
B("1110"): K("x^3 + x^2 + x"),
179
B("1111"): K("x^3 + x^2 + x+ 1") }
180
# nibble to integer
181
self._bin_to_int = { B("0000"): Integer(0),
182
B("0001"): Integer(1),
183
B("0010"): Integer(2),
184
B("0011"): Integer(3),
185
B("0100"): Integer(4),
186
B("0101"): Integer(5),
187
B("0110"): Integer(6),
188
B("0111"): Integer(7),
189
B("1000"): Integer(8),
190
B("1001"): Integer(9),
191
B("1010"): Integer(10),
192
B("1011"): Integer(11),
193
B("1100"): Integer(12),
194
B("1101"): Integer(13),
195
B("1110"): Integer(14),
196
B("1111"): Integer(15) }
197
# finite field element to nibble
198
self._GF_to_bin = { K("0"): B("0000"),
199
K("1"): B("0001"),
200
K("x"): B("0010"),
201
K("x + 1"): B("0011"),
202
K("x^2"): B("0100"),
203
K("x^2 + 1"): B("0101"),
204
K("x^2 + x"): B("0110"),
205
K("x^2 + x + 1"): B("0111"),
206
K("x^3"): B("1000"),
207
K("x^3 + 1"): B("1001"),
208
K("x^3 + x"): B("1010"),
209
K("x^3 + x + 1"): B("1011"),
210
K("x^3 + x^2"): B("1100"),
211
K("x^3 + x^2 + 1"): B("1101"),
212
K("x^3 + x^2 + x"): B("1110"),
213
K("x^3 + x^2 + x+ 1"): B("1111") }
214
# finite field element to integer
215
self._GF_to_int = { K("0"): Integer(0),
216
K("1"): Integer(1),
217
K("x"): Integer(2),
218
K("x + 1"): Integer(3),
219
K("x^2"): Integer(4),
220
K("x^2 + 1"): Integer(5),
221
K("x^2 + x"): Integer(6),
222
K("x^2 + x + 1"): Integer(7),
223
K("x^3"): Integer(8),
224
K("x^3 + 1"): Integer(9),
225
K("x^3 + x"): Integer(10),
226
K("x^3 + x + 1"): Integer(11),
227
K("x^3 + x^2"): Integer(12),
228
K("x^3 + x^2 + 1"): Integer(13),
229
K("x^3 + x^2 + x"): Integer(14),
230
K("x^3 + x^2 + x+ 1"): Integer(15) }
231
# integer to nibble
232
self._int_to_bin = { Integer(0): B("0000"),
233
Integer(1): B("0001"),
234
Integer(2): B("0010"),
235
Integer(3): B("0011"),
236
Integer(4): B("0100"),
237
Integer(5): B("0101"),
238
Integer(6): B("0110"),
239
Integer(7): B("0111"),
240
Integer(8): B("1000"),
241
Integer(9): B("1001"),
242
Integer(10): B("1010"),
243
Integer(11): B("1011"),
244
Integer(12): B("1100"),
245
Integer(13): B("1101"),
246
Integer(14): B("1110"),
247
Integer(15): B("1111") }
248
# integer to finite field element
249
self._int_to_GF = { Integer(0): K("0"),
250
Integer(1): K("1"),
251
Integer(2): K("x"),
252
Integer(3): K("x + 1"),
253
Integer(4): K("x^2"),
254
Integer(5): K("x^2 + 1"),
255
Integer(6): K("x^2 + x"),
256
Integer(7): K("x^2 + x + 1"),
257
Integer(8): K("x^3"),
258
Integer(9): K("x^3 + 1"),
259
Integer(10): K("x^3 + x"),
260
Integer(11): K("x^3 + x + 1"),
261
Integer(12): K("x^3 + x^2"),
262
Integer(13): K("x^3 + x^2 + 1"),
263
Integer(14): K("x^3 + x^2 + x"),
264
Integer(15): K("x^3 + x^2 + x+ 1") }
265
266
def __call__(self, B, key, algorithm="encrypt"):
267
r"""
268
Apply Mini-AES encryption or decryption on the binary string ``B``
269
using the key ``key``. The flag ``algorithm`` controls what action is
270
to be performed on ``B``.
271
272
INPUT:
273
274
- ``B`` -- a binary string, where the number of bits is positive and
275
a multiple of 16. The number of 16 bits is evenly divided into four
276
nibbles. Hence 16 bits can be conveniently represented as a
277
`2 \times 2` matrix block for encryption or decryption.
278
279
- ``key`` -- a secret key; this must be a 16-bit binary string
280
281
- ``algorithm`` -- (default: ``"encrypt"``) a string; a flag to signify
282
whether encryption or decryption is to be applied to the binary
283
string ``B``. The encryption flag is ``"encrypt"`` and the decryption
284
flag is ``"decrypt"``.
285
286
OUTPUT:
287
288
- The ciphertext (respectively plaintext) corresponding to the
289
binary string ``B``.
290
291
EXAMPLES::
292
293
sage: from sage.crypto.block_cipher.miniaes import MiniAES
294
sage: maes = MiniAES()
295
sage: bin = BinaryStrings()
296
sage: key = bin.encoding("KE"); key
297
0100101101000101
298
sage: P = bin.encoding("Encrypt this secret message!"); P
299
01000101011011100110001101110010011110010111000001110100001000000111010001101000011010010111001100100000011100110110010101100011011100100110010101110100001000000110110101100101011100110111001101100001011001110110010100100001
300
sage: C = maes(P, key, algorithm="encrypt"); C
301
10001000101001101111000001111000010011001110110101000111011011010101001011101111101011001110011100100011101100101010100010100111110110011001010001000111011011010010000011000110001100000111000011100110101111000000001110001001
302
sage: plaintxt = maes(C, key, algorithm="decrypt")
303
sage: plaintxt == P
304
True
305
306
TESTS:
307
308
The binary string ``B`` must be non-empty and the number of bits must
309
be a multiple of 16::
310
311
sage: from sage.crypto.block_cipher.miniaes import MiniAES
312
sage: maes = MiniAES()
313
sage: maes("B", "key")
314
Traceback (most recent call last):
315
...
316
TypeError: input B must be a non-empty binary string with number of bits a multiple of 16
317
sage: bin = BinaryStrings()
318
sage: B = bin.encoding("A")
319
sage: maes(B, "key")
320
Traceback (most recent call last):
321
...
322
ValueError: the number of bits in the binary string B must be positive and a multiple of 16
323
324
The secret key ``key`` must be a 16-bit binary string::
325
326
sage: B = bin.encoding("ABCD")
327
sage: maes(B, "key")
328
Traceback (most recent call last):
329
...
330
TypeError: secret key must be a 16-bit binary string
331
sage: key = bin.encoding("K")
332
sage: maes(B, key)
333
Traceback (most recent call last):
334
...
335
ValueError: secret key must be a 16-bit binary string
336
337
The value for ``algorithm`` must be either ``"encrypt"`` or
338
``"decrypt"``::
339
340
sage: B = bin.encoding("ABCD")
341
sage: key = bin.encoding("KE")
342
sage: maes(B, key, algorithm="ABC")
343
Traceback (most recent call last):
344
...
345
ValueError: algorithm must be either 'encrypt' or 'decrypt'
346
sage: maes(B, key, algorithm="e")
347
Traceback (most recent call last):
348
...
349
ValueError: algorithm must be either 'encrypt' or 'decrypt'
350
sage: maes(B, key, algorithm="d")
351
Traceback (most recent call last):
352
...
353
ValueError: algorithm must be either 'encrypt' or 'decrypt'
354
"""
355
from sage.rings.finite_rings.integer_mod import Mod
356
if not isinstance(B, StringMonoidElement):
357
raise TypeError("input B must be a non-empty binary string with number of bits a multiple of 16")
358
if (len(B) == 0) or (Mod(len(B), self._key_size).lift() != 0):
359
raise ValueError("the number of bits in the binary string B must be positive and a multiple of 16")
360
if not isinstance(key, StringMonoidElement):
361
raise TypeError("secret key must be a 16-bit binary string")
362
if len(key) != self._key_size:
363
raise ValueError("secret key must be a 16-bit binary string")
364
365
N = len(B) // self._key_size # the number of 16-bit blocks
366
MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)
367
bin = BinaryStrings()
368
S = ""
369
if algorithm == "encrypt":
370
# encrypt each 16-bit block in succession
371
for i in xrange(N):
372
# here 16 is the number of bits per encryption block
373
block = B[i*16 : (i+1)*16]
374
matB = MS(self.binary_to_GF(block)).transpose()
375
matK = MS(self.binary_to_GF(key)).transpose()
376
e = self.encrypt(matB, matK)
377
e = self.GF_to_binary(e)
378
S = "".join([S, str(e)])
379
return bin(S)
380
elif algorithm == "decrypt":
381
# decrypt each 16-bit block in succession
382
for i in xrange(N):
383
# here 16 is the number of bits per encryption block
384
block = B[i*16 : (i+1)*16]
385
matB = MS(self.binary_to_GF(block)).transpose()
386
matK = MS(self.binary_to_GF(key)).transpose()
387
e = self.decrypt(matB, matK)
388
e = self.GF_to_binary(e)
389
S = "".join([S, str(e)])
390
return bin(S)
391
else:
392
raise ValueError("algorithm must be either 'encrypt' or 'decrypt'")
393
394
def __eq__(self, other):
395
r"""
396
Compare ``self`` with ``other``.
397
398
Mini-AES objects are the same if they have the same key size and
399
the same S-boxes.
400
401
EXAMPLES::
402
403
sage: from sage.crypto.block_cipher.miniaes import MiniAES
404
sage: m = MiniAES()
405
sage: m == loads(dumps(m))
406
True
407
"""
408
return ( (self._key_size == other._key_size) and
409
(self._sboxE == other._sboxE) and
410
(self._sboxD == other._sboxD) )
411
412
def __repr__(self):
413
r"""
414
Return the string representation of self.
415
416
EXAMPLES::
417
418
sage: from sage.crypto.block_cipher.miniaes import MiniAES
419
sage: m = MiniAES(); m
420
Mini-AES block cipher with 16-bit keys
421
"""
422
return "Mini-AES block cipher with 16-bit keys"
423
424
def add_key(self, block, rkey):
425
r"""
426
Return the matrix addition of ``block`` and ``rkey``. Both ``block``
427
and ``rkey`` are `2 \times 2` matrices over the finite field
428
`\GF{2^4}`. This method just return the matrix addition of
429
these two matrices.
430
431
INPUT:
432
433
- ``block`` -- a `2 \times 2` matrix with entries over
434
`\GF{2^4}`
435
436
- ``rkey`` -- a round key; a `2 \times 2` matrix with entries over
437
`\GF{2^4}`
438
439
OUTPUT:
440
441
- The matrix addition of ``block`` and ``rkey``.
442
443
EXAMPLES:
444
445
We can work with elements of `\GF{2^4}`::
446
447
sage: from sage.crypto.block_cipher.miniaes import MiniAES
448
sage: maes = MiniAES()
449
sage: K = FiniteField(16, "x")
450
sage: MS = MatrixSpace(K, 2, 2)
451
sage: D = MS([ [K("x^3 + x^2 + x + 1"), K("x^3 + x")], [K("0"), K("x^3 + x^2")] ]); D
452
<BLANKLINE>
453
[x^3 + x^2 + x + 1 x^3 + x]
454
[ 0 x^3 + x^2]
455
sage: k = MS([ [K("x^2 + 1"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); k
456
<BLANKLINE>
457
[ x^2 + 1 x^3 + x^2 + x + 1]
458
[ x + 1 0]
459
sage: maes.add_key(D, k)
460
<BLANKLINE>
461
[ x^3 + x x^2 + 1]
462
[ x + 1 x^3 + x^2]
463
464
Or work with binary strings::
465
466
sage: bin = BinaryStrings()
467
sage: B = bin.encoding("We"); B
468
0101011101100101
469
sage: B = MS(maes.binary_to_GF(B)); B
470
<BLANKLINE>
471
[ x^2 + 1 x^2 + x + 1]
472
[ x^2 + x x^2 + 1]
473
sage: key = bin.encoding("KY"); key
474
0100101101011001
475
sage: key = MS(maes.binary_to_GF(key)); key
476
<BLANKLINE>
477
[ x^2 x^3 + x + 1]
478
[ x^2 + 1 x^3 + 1]
479
sage: maes.add_key(B, key)
480
<BLANKLINE>
481
[ 1 x^3 + x^2]
482
[ x + 1 x^3 + x^2]
483
484
We can also work with integers `n` such that `0 \leq n \leq 15`::
485
486
sage: N = [2, 3, 5, 7]; N
487
[2, 3, 5, 7]
488
sage: key = [9, 11, 13, 15]; key
489
[9, 11, 13, 15]
490
sage: N = MS(maes.integer_to_GF(N)); N
491
<BLANKLINE>
492
[ x x + 1]
493
[ x^2 + 1 x^2 + x + 1]
494
sage: key = MS(maes.integer_to_GF(key)); key
495
<BLANKLINE>
496
[ x^3 + 1 x^3 + x + 1]
497
[ x^3 + x^2 + 1 x^3 + x^2 + x + 1]
498
sage: maes.add_key(N, key)
499
<BLANKLINE>
500
[x^3 + x + 1 x^3]
501
[ x^3 x^3]
502
503
TESTS:
504
505
The input block and key must each be a matrix::
506
507
sage: from sage.crypto.block_cipher.miniaes import MiniAES
508
sage: maes = MiniAES()
509
sage: K = FiniteField(16, "x")
510
sage: MSB = MatrixSpace(K, 2, 2)
511
sage: B = MSB([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
512
sage: maes.add_key(B, "key")
513
Traceback (most recent call last):
514
...
515
TypeError: round key must be a 2 x 2 matrix over GF(16)
516
sage: maes.add_key("block", "key")
517
Traceback (most recent call last):
518
...
519
TypeError: input block must be a 2 x 2 matrix over GF(16)
520
521
In addition, the dimensions of the input matrices must each be
522
`2 \times 2`::
523
524
sage: MSB = MatrixSpace(K, 1, 2)
525
sage: B = MSB([ [K("x^3 + 1"), K("x^2 + x")] ])
526
sage: maes.add_key(B, "key")
527
Traceback (most recent call last):
528
...
529
TypeError: input block must be a 2 x 2 matrix over GF(16)
530
sage: MSB = MatrixSpace(K, 2, 2)
531
sage: B = MSB([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
532
sage: MSK = MatrixSpace(K, 1, 2)
533
sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])
534
sage: maes.add_key(B, key)
535
Traceback (most recent call last):
536
...
537
TypeError: round key must be a 2 x 2 matrix over GF(16)
538
"""
539
if not isinstance(block, Matrix_dense) or \
540
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
541
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
542
if not (block.nrows() == block.ncols() == 2):
543
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
544
545
if not isinstance(rkey, Matrix_dense) or \
546
not (rkey.base_ring().order() == 16 and rkey.base_ring().is_field()):
547
raise TypeError("round key must be a 2 x 2 matrix over GF(16)")
548
if not (rkey.nrows() == rkey.ncols() == 2):
549
raise TypeError("round key must be a 2 x 2 matrix over GF(16)")
550
551
return block + rkey
552
553
def block_length(self):
554
r"""
555
Return the block length of Phan's Mini-AES block cipher. A key in
556
Phan's Mini-AES is a block of 16 bits. Each nibble of a key can be
557
considered as an element of the finite field `\GF{2^4}`.
558
Therefore the key consists of four elements from `\GF{2^4}`.
559
560
OUTPUT:
561
562
- The block (or key) length in number of bits.
563
564
EXAMPLES::
565
566
sage: from sage.crypto.block_cipher.miniaes import MiniAES
567
sage: maes = MiniAES()
568
sage: maes.block_length()
569
16
570
"""
571
return self._key_size
572
573
def decrypt(self, C, key):
574
r"""
575
Use Phan's Mini-AES to decrypt the ciphertext ``C`` with the secret
576
key ``key``. Both ``C`` and ``key`` must be `2 \times 2` matrices over
577
the finite field `\GF{2^4}`. Let `\gamma` denote the
578
operation of nibble-sub, `\pi` denote shift-row, `\theta` denote
579
mix-column, and `\sigma_{K_i}` denote add-key with the round key
580
`K_i`. Then decryption `D` using Phan's Mini-AES is the function
581
composition
582
583
.. MATH::
584
585
D = \sigma_{K_0} \circ \gamma^{-1} \circ \pi \circ \theta \circ \sigma_{K_1} \circ \gamma^{-1} \circ \pi \circ \sigma_{K_2}
586
587
where `\gamma^{-1}` is the nibble-sub operation that uses the S-box
588
for decryption, and the order of execution is from right to left.
589
590
INPUT:
591
592
- ``C`` -- a ciphertext block; must be a `2 \times 2` matrix over
593
the finite field `\GF{2^4}`
594
595
- ``key`` -- a secret key for this Mini-AES block cipher; must be a
596
`2 \times 2` matrix over the finite field `\GF{2^4}`
597
598
OUTPUT:
599
600
- The plaintext corresponding to ``C``.
601
602
EXAMPLES:
603
604
We encrypt a plaintext, decrypt the ciphertext, then compare the
605
decrypted plaintext with the original plaintext. Here we work with
606
elements of `\GF{2^4}`::
607
608
sage: from sage.crypto.block_cipher.miniaes import MiniAES
609
sage: maes = MiniAES()
610
sage: K = FiniteField(16, "x")
611
sage: MS = MatrixSpace(K, 2, 2)
612
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ]); P
613
<BLANKLINE>
614
[ x^3 + 1 x^2 + x]
615
[x^3 + x^2 x + 1]
616
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); key
617
<BLANKLINE>
618
[ x^3 + x^2 x^3 + x^2 + x + 1]
619
[ x + 1 0]
620
sage: C = maes.encrypt(P, key); C
621
<BLANKLINE>
622
[x^2 + x + 1 x^3 + x^2]
623
[ x x^2 + x]
624
sage: plaintxt = maes.decrypt(C, key)
625
sage: plaintxt; P
626
<BLANKLINE>
627
[ x^3 + 1 x^2 + x]
628
[x^3 + x^2 x + 1]
629
<BLANKLINE>
630
[ x^3 + 1 x^2 + x]
631
[x^3 + x^2 x + 1]
632
sage: plaintxt == P
633
True
634
635
But we can also work with binary strings::
636
637
sage: bin = BinaryStrings()
638
sage: P = bin.encoding("de"); P
639
0110010001100101
640
sage: P = MS(maes.binary_to_GF(P)); P
641
<BLANKLINE>
642
[x^2 + x x^2]
643
[x^2 + x x^2 + 1]
644
sage: key = bin.encoding("ke"); key
645
0110101101100101
646
sage: key = MS(maes.binary_to_GF(key)); key
647
<BLANKLINE>
648
[ x^2 + x x^3 + x + 1]
649
[ x^2 + x x^2 + 1]
650
sage: C = maes.encrypt(P, key)
651
sage: plaintxt = maes.decrypt(C, key)
652
sage: plaintxt == P
653
True
654
655
Here we work with integers `n` such that `0 \leq n \leq 15`::
656
657
sage: P = [3, 5, 7, 14]; P
658
[3, 5, 7, 14]
659
sage: key = [2, 6, 7, 8]; key
660
[2, 6, 7, 8]
661
sage: P = MS(maes.integer_to_GF(P)); P
662
<BLANKLINE>
663
[ x + 1 x^2 + 1]
664
[ x^2 + x + 1 x^3 + x^2 + x]
665
sage: key = MS(maes.integer_to_GF(key)); key
666
<BLANKLINE>
667
[ x x^2 + x]
668
[x^2 + x + 1 x^3]
669
sage: C = maes.encrypt(P, key)
670
sage: plaintxt = maes.decrypt(C, key)
671
sage: plaintxt == P
672
True
673
674
TESTS:
675
676
The input block must be a matrix::
677
678
sage: from sage.crypto.block_cipher.miniaes import MiniAES
679
sage: maes = MiniAES()
680
sage: K = FiniteField(16, "x")
681
sage: MS = MatrixSpace(K, 2, 2)
682
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
683
sage: maes.decrypt("C", key)
684
Traceback (most recent call last):
685
...
686
TypeError: ciphertext block must be a 2 x 2 matrix over GF(16)
687
sage: C = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
688
sage: maes.decrypt(C, "key")
689
Traceback (most recent call last):
690
...
691
TypeError: secret key must be a 2 x 2 matrix over GF(16)
692
693
In addition, the dimensions of the input matrices must be
694
`2 \times 2`::
695
696
sage: MS = MatrixSpace(K, 1, 2)
697
sage: C = MS([ [K("x^3 + 1"), K("x^2 + x")]])
698
sage: maes.decrypt(C, "key")
699
Traceback (most recent call last):
700
...
701
TypeError: ciphertext block must be a 2 x 2 matrix over GF(16)
702
sage: MSC = MatrixSpace(K, 2, 2)
703
sage: C = MSC([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
704
sage: MSK = MatrixSpace(K, 1, 2)
705
sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])
706
sage: maes.decrypt(C, key)
707
Traceback (most recent call last):
708
...
709
TypeError: secret key must be a 2 x 2 matrix over GF(16)
710
"""
711
if not isinstance(C, Matrix_dense) or \
712
not (C.base_ring().order() == 16 and C.base_ring().is_field()):
713
raise TypeError("ciphertext block must be a 2 x 2 matrix over GF(16)")
714
if not (C.nrows() == C.ncols() == 2):
715
raise TypeError("ciphertext block must be a 2 x 2 matrix over GF(16)")
716
if not isinstance(key, Matrix_dense) or \
717
not (key.base_ring().order() == 16 and key.base_ring().is_field()):
718
raise TypeError("secret key must be a 2 x 2 matrix over GF(16)")
719
if not (key.nrows() == key.ncols() == 2):
720
raise TypeError("secret key must be a 2 x 2 matrix over GF(16)")
721
722
# pre-compute the round keys
723
rkey0 = self.round_key(key, 0)
724
rkey1 = self.round_key(key, 1)
725
rkey2 = self.round_key(key, 2)
726
727
# now proceed with decrypting the ciphertext
728
729
# undo the result of round 2
730
plaintext = self.add_key(C, rkey2)
731
#print(self.GF_to_binary(rkey0))
732
#print(self.GF_to_binary(rkey1))
733
#print(self.GF_to_binary(rkey2))
734
#print(self.GF_to_binary(plaintext))
735
plaintext = self.shift_row(plaintext)
736
#print(self.GF_to_binary(plaintext))
737
plaintext = self.nibble_sub(plaintext, algorithm="decrypt")
738
739
# undo the result of round 1
740
plaintext = self.add_key(plaintext, rkey1)
741
plaintext = self.inverse_mix_column(plaintext)
742
plaintext = self.shift_row(plaintext)
743
plaintext = self.nibble_sub(plaintext, algorithm="decrypt")
744
745
# undo the result of round 0
746
plaintext = self.add_key(plaintext, rkey0)
747
748
return plaintext
749
750
def encrypt(self, P, key):
751
r"""
752
Use Phan's Mini-AES to encrypt the plaintext ``P`` with the secret
753
key ``key``. Both ``P`` and ``key`` must be `2 \times 2` matrices
754
over the finite field `\GF{2^4}`. Let `\gamma` denote the
755
operation of nibble-sub, `\pi` denote shift-row, `\theta` denote
756
mix-column, and `\sigma_{K_i}` denote add-key with the round key
757
`K_i`. Then encryption `E` using Phan's Mini-AES is the function
758
composition
759
760
.. MATH::
761
762
E = \sigma_{K_2} \circ \pi \circ \gamma \circ \sigma_{K_1} \circ \theta \circ \pi \circ \gamma \circ \sigma_{K_0}
763
764
where the order of execution is from right to left. Note that
765
`\gamma` is the nibble-sub operation that uses the S-box for
766
encryption.
767
768
INPUT:
769
770
- ``P`` -- a plaintext block; must be a `2 \times 2` matrix over
771
the finite field `\GF{2^4}`
772
773
- ``key`` -- a secret key for this Mini-AES block cipher; must be a
774
`2 \times 2` matrix over the finite field `\GF{2^4}`
775
776
OUTPUT:
777
778
- The ciphertext corresponding to ``P``.
779
780
EXAMPLES:
781
782
Here we work with elements of `\GF{2^4}`::
783
784
sage: from sage.crypto.block_cipher.miniaes import MiniAES
785
sage: maes = MiniAES()
786
sage: K = FiniteField(16, "x")
787
sage: MS = MatrixSpace(K, 2, 2)
788
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ]); P
789
<BLANKLINE>
790
[ x^3 + 1 x^2 + x]
791
[x^3 + x^2 x + 1]
792
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); key
793
<BLANKLINE>
794
[ x^3 + x^2 x^3 + x^2 + x + 1]
795
[ x + 1 0]
796
sage: maes.encrypt(P, key)
797
<BLANKLINE>
798
[x^2 + x + 1 x^3 + x^2]
799
[ x x^2 + x]
800
801
But we can also work with binary strings::
802
803
sage: bin = BinaryStrings()
804
sage: P = bin.encoding("de"); P
805
0110010001100101
806
sage: P = MS(maes.binary_to_GF(P)); P
807
<BLANKLINE>
808
[x^2 + x x^2]
809
[x^2 + x x^2 + 1]
810
sage: key = bin.encoding("ke"); key
811
0110101101100101
812
sage: key = MS(maes.binary_to_GF(key)); key
813
<BLANKLINE>
814
[ x^2 + x x^3 + x + 1]
815
[ x^2 + x x^2 + 1]
816
sage: C = maes.encrypt(P, key)
817
sage: plaintxt = maes.decrypt(C, key)
818
sage: plaintxt == P
819
True
820
821
Now we work with integers `n` such that `0 \leq n \leq 15`::
822
823
sage: P = [1, 5, 8, 12]; P
824
[1, 5, 8, 12]
825
sage: key = [5, 9, 15, 0]; key
826
[5, 9, 15, 0]
827
sage: P = MS(maes.integer_to_GF(P)); P
828
<BLANKLINE>
829
[ 1 x^2 + 1]
830
[ x^3 x^3 + x^2]
831
sage: key = MS(maes.integer_to_GF(key)); key
832
<BLANKLINE>
833
[ x^2 + 1 x^3 + 1]
834
[x^3 + x^2 + x + 1 0]
835
sage: C = maes.encrypt(P, key)
836
sage: plaintxt = maes.decrypt(C, key)
837
sage: plaintxt == P
838
True
839
840
TESTS:
841
842
The input block must be a matrix::
843
844
sage: from sage.crypto.block_cipher.miniaes import MiniAES
845
sage: maes = MiniAES()
846
sage: K = FiniteField(16, "x")
847
sage: MS = MatrixSpace(K, 2, 2)
848
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
849
sage: maes.encrypt("P", key)
850
Traceback (most recent call last):
851
...
852
TypeError: plaintext block must be a 2 x 2 matrix over GF(16)
853
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
854
sage: maes.encrypt(P, "key")
855
Traceback (most recent call last):
856
...
857
TypeError: secret key must be a 2 x 2 matrix over GF(16)
858
859
In addition, the dimensions of the input matrices must be
860
`2 \times 2`::
861
862
sage: MS = MatrixSpace(K, 1, 2)
863
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")]])
864
sage: maes.encrypt(P, "key")
865
Traceback (most recent call last):
866
...
867
TypeError: plaintext block must be a 2 x 2 matrix over GF(16)
868
sage: MSP = MatrixSpace(K, 2, 2)
869
sage: P = MSP([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
870
sage: MSK = MatrixSpace(K, 1, 2)
871
sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])
872
sage: maes.encrypt(P, key)
873
Traceback (most recent call last):
874
...
875
TypeError: secret key must be a 2 x 2 matrix over GF(16)
876
"""
877
if not isinstance(P, Matrix_dense) or \
878
not (P.base_ring().order() == 16 and P.base_ring().is_field()):
879
raise TypeError("plaintext block must be a 2 x 2 matrix over GF(16)")
880
if not (P.nrows() == P.ncols() == 2):
881
raise TypeError("plaintext block must be a 2 x 2 matrix over GF(16)")
882
if not isinstance(key, Matrix_dense) or \
883
not (key.base_ring().order() == 16 and key.base_ring().is_field()):
884
raise TypeError("secret key must be a 2 x 2 matrix over GF(16)")
885
if not (key.nrows() == key.ncols() == 2):
886
raise TypeError("secret key must be a 2 x 2 matrix over GF(16)")
887
888
# pre-compute the round keys
889
rkey0 = self.round_key(key, 0)
890
rkey1 = self.round_key(key, 1)
891
rkey2 = self.round_key(key, 2)
892
893
#print(self.GF_to_binary(rkey0))
894
#print(self.GF_to_binary(rkey1))
895
#print(self.GF_to_binary(rkey2))
896
897
# now proceed with encrypting the plaintext
898
899
# round 0
900
ciphertext = self.add_key(P, rkey0)
901
902
903
# round 1
904
ciphertext = self.nibble_sub(ciphertext, algorithm="encrypt")
905
ciphertext = self.shift_row(ciphertext)
906
ciphertext = self.mix_column(ciphertext)
907
ciphertext = self.add_key(ciphertext, rkey1)
908
909
# round 2
910
ciphertext = self.nibble_sub(ciphertext, algorithm="encrypt")
911
ciphertext = self.shift_row(ciphertext)
912
ciphertext = self.add_key(ciphertext, rkey2)
913
914
return ciphertext
915
916
def inverse_mix_column(self, block):
917
r"""
918
Return the matrix multiplication of ``block`` with a constant matrix.
919
The constant matrix is
920
921
.. MATH::
922
923
\begin{bmatrix}
924
x + 1 & x \\
925
x & x + 1
926
\end{bmatrix}
927
928
If the input block is
929
930
.. MATH::
931
932
\begin{bmatrix}
933
c_0 & c_2 \\
934
c_1 & c_3
935
\end{bmatrix}
936
937
then the output block is
938
939
.. MATH::
940
941
\begin{bmatrix}
942
d_0 & d_2 \\
943
d_1 & d_3
944
\end{bmatrix}
945
=
946
\begin{bmatrix}
947
x + 1 & x \\
948
x & x + 1
949
\end{bmatrix}
950
\begin{bmatrix}
951
c_0 & c_2 \\
952
c_1 & c_3
953
\end{bmatrix}
954
955
INPUT:
956
957
- ``block`` -- a `2 \times 2` matrix with entries over
958
`\GF{2^4}`
959
960
OUTPUT:
961
962
- A `2 \times 2` matrix resulting from multiplying the above constant
963
matrix with the input matrix ``block``.
964
965
EXAMPLES:
966
967
Here we work with elements of `\GF{2^4}`::
968
969
sage: from sage.crypto.block_cipher.miniaes import MiniAES
970
sage: maes = MiniAES()
971
sage: K = FiniteField(16, "x")
972
sage: MS = MatrixSpace(K, 2, 2)
973
sage: mat = MS([ [K("x^2 + x + 1"), K("x^3 + x^2 + 1")], [K("x^3"), K("x")] ])
974
sage: maes.mix_column(mat)
975
<BLANKLINE>
976
[ x^3 + x 0]
977
[ x^2 + 1 x^3 + x^2 + x + 1]
978
979
Multiplying by the identity matrix should leave the constant matrix
980
unchanged::
981
982
sage: eye = MS([ [K("1"), K("0")], [K("0"), K("1")] ])
983
sage: maes.mix_column(eye)
984
<BLANKLINE>
985
[x + 1 x]
986
[ x x + 1]
987
988
We can also work with binary strings::
989
990
sage: bin = BinaryStrings()
991
sage: B = bin.encoding("rT"); B
992
0111001001010100
993
sage: B = MS(maes.binary_to_GF(B)); B
994
<BLANKLINE>
995
[x^2 + x + 1 x]
996
[ x^2 + 1 x^2]
997
sage: maes.mix_column(B)
998
<BLANKLINE>
999
[ x + 1 x^3 + x^2 + x]
1000
[ 1 x^3]
1001
1002
We can also work with integers `n` such that `0 \leq n \leq 15`::
1003
1004
sage: P = [10, 5, 2, 7]; P
1005
[10, 5, 2, 7]
1006
sage: P = MS(maes.integer_to_GF(P)); P
1007
<BLANKLINE>
1008
[ x^3 + x x^2 + 1]
1009
[ x x^2 + x + 1]
1010
sage: maes.mix_column(P)
1011
<BLANKLINE>
1012
[x^3 + 1 1]
1013
[ 1 x + 1]
1014
1015
TESTS:
1016
1017
The input block must be a matrix::
1018
1019
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1020
sage: maes = MiniAES()
1021
sage: maes.mix_column("mat")
1022
Traceback (most recent call last):
1023
...
1024
TypeError: input block must be a 2 x 2 matrix over GF(16)
1025
1026
In addition, the dimensions of the input matrix must be `2 \times 2`::
1027
1028
sage: K = FiniteField(16, "x")
1029
sage: MS = MatrixSpace(K, 1, 2)
1030
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1031
sage: maes.mix_column(mat)
1032
Traceback (most recent call last):
1033
...
1034
TypeError: input block must be a 2 x 2 matrix over GF(16)
1035
"""
1036
if not isinstance(block, Matrix_dense) or \
1037
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1038
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1039
if not (block.nrows() == block.ncols() == 2):
1040
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1041
1042
K = FiniteField(self._key_size, "x")
1043
MS = MatrixSpace(K, 2, 2)
1044
M = MS( [ [K("x^3+1"), K("x")],
1045
[K("x"), K("x^3+1")] ] )
1046
return M * block
1047
1048
def mix_column(self, block):
1049
r"""
1050
Return the matrix multiplication of ``block`` with a constant matrix.
1051
The constant matrix is
1052
1053
.. MATH::
1054
1055
\begin{bmatrix}
1056
x + 1 & x \\
1057
x & x + 1
1058
\end{bmatrix}
1059
1060
If the input block is
1061
1062
.. MATH::
1063
1064
\begin{bmatrix}
1065
c_0 & c_2 \\
1066
c_1 & c_3
1067
\end{bmatrix}
1068
1069
then the output block is
1070
1071
.. MATH::
1072
1073
\begin{bmatrix}
1074
d_0 & d_2 \\
1075
d_1 & d_3
1076
\end{bmatrix}
1077
=
1078
\begin{bmatrix}
1079
x + 1 & x \\
1080
x & x + 1
1081
\end{bmatrix}
1082
\begin{bmatrix}
1083
c_0 & c_2 \\
1084
c_1 & c_3
1085
\end{bmatrix}
1086
1087
INPUT:
1088
1089
- ``block`` -- a `2 \times 2` matrix with entries over
1090
`\GF{2^4}`
1091
1092
OUTPUT:
1093
1094
- A `2 \times 2` matrix resulting from multiplying the above constant
1095
matrix with the input matrix ``block``.
1096
1097
EXAMPLES:
1098
1099
Here we work with elements of `\GF{2^4}`::
1100
1101
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1102
sage: maes = MiniAES()
1103
sage: K = FiniteField(16, "x")
1104
sage: MS = MatrixSpace(K, 2, 2)
1105
sage: mat = MS([ [K("x^2 + x + 1"), K("x^3 + x^2 + 1")], [K("x^3"), K("x")] ])
1106
sage: maes.mix_column(mat)
1107
<BLANKLINE>
1108
[ x^3 + x 0]
1109
[ x^2 + 1 x^3 + x^2 + x + 1]
1110
1111
Multiplying by the identity matrix should leave the constant matrix
1112
unchanged::
1113
1114
sage: eye = MS([ [K("1"), K("0")], [K("0"), K("1")] ])
1115
sage: maes.mix_column(eye)
1116
<BLANKLINE>
1117
[x + 1 x]
1118
[ x x + 1]
1119
1120
We can also work with binary strings::
1121
1122
sage: bin = BinaryStrings()
1123
sage: B = bin.encoding("rT"); B
1124
0111001001010100
1125
sage: B = MS(maes.binary_to_GF(B)); B
1126
<BLANKLINE>
1127
[x^2 + x + 1 x]
1128
[ x^2 + 1 x^2]
1129
sage: maes.mix_column(B)
1130
<BLANKLINE>
1131
[ x + 1 x^3 + x^2 + x]
1132
[ 1 x^3]
1133
1134
We can also work with integers `n` such that `0 \leq n \leq 15`::
1135
1136
sage: P = [10, 5, 2, 7]; P
1137
[10, 5, 2, 7]
1138
sage: P = MS(maes.integer_to_GF(P)); P
1139
<BLANKLINE>
1140
[ x^3 + x x^2 + 1]
1141
[ x x^2 + x + 1]
1142
sage: maes.mix_column(P)
1143
<BLANKLINE>
1144
[x^3 + 1 1]
1145
[ 1 x + 1]
1146
1147
TESTS:
1148
1149
The input block must be a matrix::
1150
1151
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1152
sage: maes = MiniAES()
1153
sage: maes.mix_column("mat")
1154
Traceback (most recent call last):
1155
...
1156
TypeError: input block must be a 2 x 2 matrix over GF(16)
1157
1158
In addition, the dimensions of the input matrix must be `2 \times 2`::
1159
1160
sage: K = FiniteField(16, "x")
1161
sage: MS = MatrixSpace(K, 1, 2)
1162
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1163
sage: maes.mix_column(mat)
1164
Traceback (most recent call last):
1165
...
1166
TypeError: input block must be a 2 x 2 matrix over GF(16)
1167
"""
1168
if not isinstance(block, Matrix_dense) or \
1169
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1170
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1171
if not (block.nrows() == block.ncols() == 2):
1172
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1173
1174
K = FiniteField(self._key_size, "x")
1175
MS = MatrixSpace(K, 2, 2)
1176
M = MS( [ [K("1"), K("x^2")],
1177
[K("x^2"), K("1")] ] )
1178
return M * block
1179
1180
def nibble_sub(self, block, algorithm="encrypt"):
1181
r"""
1182
Substitute a nibble (or a block of 4 bits) using the following S-box:
1183
1184
.. MATH::
1185
1186
\begin{tabular}{ll|ll} \hline
1187
Input & Output & Input & Output \\\hline
1188
0000 & 1110 & 1000 & 0011 \\
1189
0001 & 0100 & 1001 & 1010 \\
1190
0010 & 1101 & 1010 & 0110 \\
1191
0011 & 0001 & 1011 & 1100 \\
1192
0100 & 0010 & 1100 & 0101 \\
1193
0101 & 1111 & 1101 & 1001 \\
1194
0110 & 1011 & 1110 & 0000 \\
1195
0111 & 1000 & 1111 & 0111 \\\hline
1196
\end{tabular}
1197
1198
The values in the above S-box are taken from the first row of the first
1199
S-box of the Data Encryption Standard (DES). Each nibble can be
1200
thought of as an element of the finite field `\GF{2^4}` of 16
1201
elements. Thus in terms of `\GF{2^4}`, the S-box can also be
1202
specified as:
1203
1204
.. MATH::
1205
1206
\begin{tabular}{ll} \hline
1207
Input & Output \\\hline
1208
$0$ & $x^3 + x^2 + x$ \\
1209
$1$ & $x^2$ \\
1210
$x$ & $x^3 + x^2 + 1$ \\
1211
$x + 1$ & $1$ \\
1212
$x^2$ & $x$ \\
1213
$x^2 + 1$ & $x^3 + x^2 + x + 1$ \\
1214
$x^2 + x$ & $x^3 + x + 1$ \\
1215
$x^2 + x + 1$ & $x^3$ \\
1216
$x^3$ & $x + 1$ \\
1217
$x^3 + 1$ & $x^3 + x$ \\
1218
$x^3 + x$ & $x^2 + x$ \\
1219
$x^3 + x + 1$ & $x^3 + x^2$ \\
1220
$x^3 + x^2$ & $x^2 + 1$ \\
1221
$x^3 + x^2 + 1$ & $x^3 + 1$ \\
1222
$x^3 + x^2 + x$ & $0$ \\
1223
$x^3 + x^2 + x + 1$ & $x^2 + x + 1$ \\\hline
1224
\end{tabular}
1225
1226
Note that the above S-box is used for encryption. The S-box for
1227
decryption is obtained from the above S-box by reversing the role of
1228
the Input and Output columns. Thus the previous Input column for
1229
encryption now becomes the Output column for decryption, and the
1230
previous Output column for encryption is now the Input column for
1231
decryption. The S-box used for decryption can be specified as:
1232
1233
.. MATH::
1234
1235
\begin{tabular}{ll} \hline
1236
Input & Output \\\hline
1237
$0$ & $x^3 + x^2 + x$ \\
1238
$1$ & $x + 1$ \\
1239
$x$ & $x^2$ \\
1240
$x + 1$ & $x^3$ \\
1241
$x^2$ & $1$ \\
1242
$x^2 + 1$ & $x^3 + x^2$ \\
1243
$x^2 + x$ & $x^3 + x$ \\
1244
$x^2 + x + 1$ & $x^3 + x^2 + x + 1$ \\
1245
$x^3$ & $x^2 + x + 1$ \\
1246
$x^3 + 1$ & $x^3 + x^2 + 1$ \\
1247
$x^3 + x$ & $x^3 + 1$ \\
1248
$x^3 + x + 1$ & $x^2 + x$ \\
1249
$x^3 + x^2$ & $x^3 + x + 1$ \\
1250
$x^3 + x^2 + 1$ & $x$ \\
1251
$x^3 + x^2 + x$ & $0$ \\
1252
$x^3 + x^2 + x + 1$ & $x^2 + 1$ \\\hline
1253
\end{tabular}
1254
1255
INPUT:
1256
1257
- ``block`` -- a `2 \times 2` matrix with entries over
1258
`\GF{2^4}`
1259
1260
- ``algorithm`` -- (default: ``"encrypt"``) a string; a flag to signify
1261
whether this nibble-sub operation is used for encryption or
1262
decryption. The encryption flag is ``"encrypt"`` and the decryption
1263
flag is ``"decrypt"``.
1264
1265
OUTPUT:
1266
1267
- A `2 \times 2` matrix resulting from applying an S-box on
1268
entries of the `2 \times 2` matrix ``block``.
1269
1270
EXAMPLES:
1271
1272
Here we work with elements of the finite field `\GF{2^4}`::
1273
1274
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1275
sage: maes = MiniAES()
1276
sage: K = FiniteField(16, "x")
1277
sage: MS = MatrixSpace(K, 2, 2)
1278
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])
1279
sage: maes.nibble_sub(mat, algorithm="encrypt")
1280
<BLANKLINE>
1281
[ x^2 + x + 1 x^3 + x^2 + x]
1282
[ x^3 x^2 + x]
1283
1284
But we can also work with binary strings::
1285
1286
sage: bin = BinaryStrings()
1287
sage: B = bin.encoding("bi"); B
1288
0110001001101001
1289
sage: B = MS(maes.binary_to_GF(B)); B
1290
<BLANKLINE>
1291
[x^2 + x x]
1292
[x^2 + x x^3 + 1]
1293
sage: maes.nibble_sub(B, algorithm="encrypt")
1294
<BLANKLINE>
1295
[ x^3 + x + 1 x^3 + x^2 + 1]
1296
[ x^3 + x + 1 x^3 + x]
1297
sage: maes.nibble_sub(B, algorithm="decrypt")
1298
<BLANKLINE>
1299
[ x^3 + x x^2]
1300
[ x^3 + x x^3 + x^2 + 1]
1301
1302
Here we work with integers `n` such that `0 \leq n \leq 15`::
1303
1304
sage: P = [2, 6, 8, 14]; P
1305
[2, 6, 8, 14]
1306
sage: P = MS(maes.integer_to_GF(P)); P
1307
<BLANKLINE>
1308
[ x x^2 + x]
1309
[ x^3 x^3 + x^2 + x]
1310
sage: maes.nibble_sub(P, algorithm="encrypt")
1311
<BLANKLINE>
1312
[x^3 + x^2 + 1 x^3 + x + 1]
1313
[ x + 1 0]
1314
sage: maes.nibble_sub(P, algorithm="decrypt")
1315
<BLANKLINE>
1316
[ x^2 x^3 + x]
1317
[x^2 + x + 1 0]
1318
1319
TESTS:
1320
1321
The input block must be a matrix::
1322
1323
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1324
sage: maes = MiniAES()
1325
sage: maes.nibble_sub("mat")
1326
Traceback (most recent call last):
1327
...
1328
TypeError: input block must be a 2 x 2 matrix over GF(16)
1329
1330
In addition, the dimensions of the input matrix must be `2 \times 2`::
1331
1332
sage: K = FiniteField(16, "x")
1333
sage: MS = MatrixSpace(K, 1, 2)
1334
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1335
sage: maes.nibble_sub(mat)
1336
Traceback (most recent call last):
1337
...
1338
TypeError: input block must be a 2 x 2 matrix over GF(16)
1339
1340
The value for the option ``algorithm`` must be either the string
1341
``"encrypt"`` or ``"decrypt"``::
1342
1343
sage: K = FiniteField(16, "x")
1344
sage: MS = MatrixSpace(K, 2, 2)
1345
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])
1346
sage: maes.nibble_sub(mat, algorithm="abc")
1347
Traceback (most recent call last):
1348
...
1349
ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'
1350
sage: maes.nibble_sub(mat, algorithm="e")
1351
Traceback (most recent call last):
1352
...
1353
ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'
1354
sage: maes.nibble_sub(mat, algorithm="d")
1355
Traceback (most recent call last):
1356
...
1357
ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'
1358
"""
1359
if not isinstance(block, Matrix_dense) or \
1360
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1361
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1362
if not (block.nrows() == block.ncols() == 2):
1363
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1364
1365
MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)
1366
# get the integer representation of each GF(2^4) element
1367
# in the input matrix block
1368
lst = [self._GF_to_int[block[i][j]] for i in xrange(block.nrows()) for j in xrange(block.ncols())]
1369
if algorithm == "encrypt":
1370
# Now run each resulting integer through the S-box for
1371
# encryption. Then convert the result output by the S-box
1372
# to an element of GF(2^4).
1373
return MS([self._int_to_GF[self._sboxE[e]] for e in lst])
1374
elif algorithm == "decrypt":
1375
# Now run each resulting integer through the S-box for
1376
# decryption. Then convert the result output by the S-box
1377
# to an element of GF(2^4).
1378
return MS([self._int_to_GF[self._sboxD[e]] for e in lst])
1379
else:
1380
raise ValueError("the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'")
1381
1382
def random_key(self):
1383
r"""
1384
A random key within the key space of this Mini-AES block cipher. Like
1385
the AES, Phan's Mini-AES is a symmetric-key block cipher. A Mini-AES
1386
key is a block of 16 bits, or a `2 \times 2` matrix with entries over
1387
the finite field `\GF{2^4}`. Thus the number of possible keys is
1388
`2^{16} = 16^4`.
1389
1390
OUTPUT:
1391
1392
- A `2 \times 2` matrix over the finite field `\GF{2^4}`, used
1393
as a secret key for this Mini-AES block cipher.
1394
1395
EXAMPLES:
1396
1397
Each nibble of a key is an element of the finite field
1398
`\GF{2^4}`::
1399
1400
sage: K = FiniteField(16, "x")
1401
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1402
sage: maes = MiniAES()
1403
sage: key = maes.random_key()
1404
sage: [key[i][j] in K for i in xrange(key.nrows()) for j in xrange(key.ncols())]
1405
[True, True, True, True]
1406
1407
Generate a random key, then perform encryption and decryption using
1408
that key::
1409
1410
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1411
sage: maes = MiniAES()
1412
sage: K = FiniteField(16, "x")
1413
sage: MS = MatrixSpace(K, 2, 2)
1414
sage: key = maes.random_key()
1415
sage: P = MS.random_element()
1416
sage: C = maes.encrypt(P, key)
1417
sage: plaintxt = maes.decrypt(C, key)
1418
sage: plaintxt == P
1419
True
1420
"""
1421
MS = MatrixSpace(FiniteField(16, "x"), 2, 2)
1422
return MS.random_element()
1423
1424
def round_key(self, key, n):
1425
r"""
1426
Return the round key for round ``n``. Phan's Mini-AES is defined to
1427
have two rounds. The round key `K_0` is generated and used prior to
1428
the first round, with round keys `K_1` and `K_2` being used in rounds
1429
1 and 2 respectively. In total, there are three round keys, each
1430
generated from the secret key ``key``.
1431
1432
INPUT:
1433
1434
- ``key`` -- the secret key
1435
1436
- ``n`` -- non-negative integer; the round number
1437
1438
OUTPUT:
1439
1440
- The `n`-th round key.
1441
1442
EXAMPLES:
1443
1444
Obtaining the round keys from the secret key::
1445
1446
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1447
sage: maes = MiniAES()
1448
sage: K = FiniteField(16, "x")
1449
sage: MS = MatrixSpace(K, 2, 2)
1450
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
1451
sage: maes.round_key(key, 0)
1452
<BLANKLINE>
1453
[ x^3 + x^2 x^3 + x^2 + x + 1]
1454
[ x + 1 0]
1455
sage: key
1456
<BLANKLINE>
1457
[ x^3 + x^2 x^3 + x^2 + x + 1]
1458
[ x + 1 0]
1459
sage: maes.round_key(key, 1)
1460
<BLANKLINE>
1461
[ x + 1 x^3 + x^2 + x + 1]
1462
[ 0 x^3 + x^2 + x + 1]
1463
sage: maes.round_key(key, 2)
1464
<BLANKLINE>
1465
[x^2 + x x^3 + 1]
1466
[x^2 + x x^2 + x]
1467
1468
TESTS:
1469
1470
Only two rounds are defined for this AES variant::
1471
1472
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1473
sage: maes = MiniAES()
1474
sage: K = FiniteField(16, "x")
1475
sage: MS = MatrixSpace(K, 2, 2)
1476
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
1477
sage: maes.round_key(key, -1)
1478
Traceback (most recent call last):
1479
...
1480
ValueError: Mini-AES only defines two rounds
1481
sage: maes.round_key(key, 3)
1482
Traceback (most recent call last):
1483
...
1484
ValueError: Mini-AES only defines two rounds
1485
1486
The input key must be a matrix::
1487
1488
sage: maes.round_key("key", 0)
1489
Traceback (most recent call last):
1490
...
1491
TypeError: secret key must be a 2 x 2 matrix over GF(16)
1492
1493
In addition, the dimensions of the key matrix must be `2 \times 2`::
1494
1495
sage: K = FiniteField(16, "x")
1496
sage: MS = MatrixSpace(K, 1, 2)
1497
sage: key = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1498
sage: maes.round_key(key, 2)
1499
Traceback (most recent call last):
1500
...
1501
TypeError: secret key must be a 2 x 2 matrix over GF(16)
1502
"""
1503
if not isinstance(key, Matrix_dense) or \
1504
not (key.base_ring().order() == 16 and key.base_ring().is_field()):
1505
raise TypeError("secret key must be a 2 x 2 matrix over GF(16)")
1506
if not (key.nrows() == key.ncols() == 2):
1507
raise TypeError("secret key must be a 2 x 2 matrix over GF(16)")
1508
1509
K = FiniteField(self._key_size, "x")
1510
MS = MatrixSpace(K, 2, 2)
1511
# round 0
1512
if n == 0:
1513
return key
1514
# round 1
1515
if n == 1:
1516
round_constant_1 = K("x^3")
1517
#w4 = key[0][0] + self._sboxE[key[1][1]] + round_constant_1
1518
#print(key[0][0],self._sboxE[key[1][1]],round_constant_1)
1519
w4 = key[0][0] + self._sboxE[key[1][1]] + round_constant_1
1520
#print(key[1][0])
1521
w5 = key[1][0] + self._sboxE[key[0][1]]
1522
w6 = key[0][1] + w4
1523
w7 = key[1][1] + w5
1524
return MS([ [w4, w6], [w5, w7] ])
1525
# round 2
1526
if n == 2:
1527
round_constant_2 = K("x+1")
1528
key1 = self.round_key(key, 1)
1529
w8 = key1[0][0] + self._sboxE[key1[1][1]] + round_constant_2
1530
#print(key1[0][1],self._sboxE[key1[1][0]])
1531
w9 = key1[1][0] + self._sboxE[key1[0][1]]
1532
w10 = key1[0][1] + w8
1533
w11 = key1[1][1] + w9
1534
return MS([ [w8, w10], [w9, w11] ])
1535
# unsupported round number
1536
if (n < 0) or (n > 2):
1537
raise ValueError("Mini-AES only defines two rounds")
1538
1539
def sbox(self):
1540
r"""
1541
Return the S-box of Mini-AES.
1542
1543
EXAMPLES::
1544
1545
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1546
sage: maes = MiniAES()
1547
sage: maes.sbox()
1548
(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7)
1549
"""
1550
return self._sboxE
1551
1552
def shift_row(self, block):
1553
r"""
1554
Rotate each row of ``block`` to the left by different nibble
1555
amounts. The first or zero-th row is left unchanged, while the
1556
second or row one is rotated left by one nibble. This has the effect
1557
of only interchanging the nibbles in the second row. Let
1558
`b_0, b_1, b_2, b_3` be four nibbles arranged as the following
1559
`2 \times 2` matrix
1560
1561
.. MATH::
1562
1563
\begin{bmatrix}
1564
b_0 & b_2 \\
1565
b_1 & b_3
1566
\end{bmatrix}
1567
1568
Then the operation of shift-row is the mapping
1569
1570
.. MATH::
1571
1572
\begin{bmatrix}
1573
b_0 & b_2 \\
1574
b_1 & b_3
1575
\end{bmatrix}
1576
\longmapsto
1577
\begin{bmatrix}
1578
b_0 & b_2 \\
1579
b_3 & b_1
1580
\end{bmatrix}
1581
1582
INPUT:
1583
1584
- ``block`` -- a `2 \times 2` matrix with entries over
1585
`\GF{2^4}`
1586
1587
OUTPUT:
1588
1589
- A `2 \times 2` matrix resulting from applying shift-row on ``block``.
1590
1591
EXAMPLES:
1592
1593
Here we work with elements of the finite field `\GF{2^4}`::
1594
1595
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1596
sage: maes = MiniAES()
1597
sage: K = FiniteField(16, "x")
1598
sage: MS = MatrixSpace(K, 2, 2)
1599
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])
1600
sage: maes.shift_row(mat)
1601
<BLANKLINE>
1602
[x^3 + x^2 + x + 1 0]
1603
[ x^3 + x x^2 + x + 1]
1604
sage: mat
1605
<BLANKLINE>
1606
[x^3 + x^2 + x + 1 0]
1607
[ x^2 + x + 1 x^3 + x]
1608
1609
But we can also work with binary strings::
1610
1611
sage: bin = BinaryStrings()
1612
sage: B = bin.encoding("Qt"); B
1613
0101000101110100
1614
sage: B = MS(maes.binary_to_GF(B)); B
1615
<BLANKLINE>
1616
[ x^2 + 1 1]
1617
[x^2 + x + 1 x^2]
1618
sage: maes.shift_row(B)
1619
<BLANKLINE>
1620
[ x^2 + 1 1]
1621
[ x^2 x^2 + x + 1]
1622
1623
Here we work with integers `n` such that `0 \leq n \leq 15`::
1624
1625
sage: P = [3, 6, 9, 12]; P
1626
[3, 6, 9, 12]
1627
sage: P = MS(maes.integer_to_GF(P)); P
1628
<BLANKLINE>
1629
[ x + 1 x^2 + x]
1630
[ x^3 + 1 x^3 + x^2]
1631
sage: maes.shift_row(P)
1632
<BLANKLINE>
1633
[ x + 1 x^2 + x]
1634
[x^3 + x^2 x^3 + 1]
1635
1636
TESTS:
1637
1638
The input block must be a matrix::
1639
1640
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1641
sage: maes = MiniAES()
1642
sage: maes.shift_row("block")
1643
Traceback (most recent call last):
1644
...
1645
TypeError: input block must be a 2 x 2 matrix over GF(16)
1646
1647
In addition, the dimensions of the input matrix must be `2 \times 2`::
1648
1649
sage: K = FiniteField(16, "x")
1650
sage: MS = MatrixSpace(K, 1, 2)
1651
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1652
sage: maes.shift_row(mat)
1653
Traceback (most recent call last):
1654
...
1655
TypeError: input block must be a 2 x 2 matrix over GF(16)
1656
"""
1657
if not isinstance(block, Matrix_dense) or \
1658
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1659
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1660
if not (block.nrows() == block.ncols() == 2):
1661
raise TypeError("input block must be a 2 x 2 matrix over GF(16)")
1662
1663
MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)
1664
mat = MS([ [block[0][0], block[0][1]],
1665
[block[1][1], block[1][0]] ] )
1666
return mat
1667
1668
1669
### conversion functions to convert between different data formats
1670
1671
def GF_to_binary(self, G):
1672
r"""
1673
Return the binary representation of ``G``.
1674
If ``G`` is an element of the finite field `\GF{2^4}`, then
1675
obtain the binary representation of ``G``. If ``G`` is a list of
1676
elements belonging to `\GF{2^4}`, obtain the 4-bit
1677
representation of each element of the list, then concatenate the
1678
resulting 4-bit strings into a binary string. If ``G`` is a matrix
1679
with entries over `\GF{2^4}`, convert each matrix entry to its
1680
4-bit representation, then concatenate the 4-bit strings. The
1681
concatenation is performed starting from the top-left corner of the
1682
matrix, working across left to right, top to bottom. Each element of
1683
`\GF{2^4}` can be associated with a unique 4-bit string
1684
according to the following table:
1685
1686
.. MATH::
1687
1688
\begin{tabular}{ll|ll} \hline
1689
4-bit string & $\GF{2^4}$ & 4-bit string & $\GF{2^4}$ \\\hline
1690
0000 & $0$ & 1000 & $x^3$ \\
1691
0001 & $1$ & 1001 & $x^3 + 1$ \\
1692
0010 & $x$ & 1010 & $x^3 + x$ \\
1693
0011 & $x + 1$ & 1011 & $x^3 + x + 1$ \\
1694
0100 & $x^2$ & 1100 & $x^3 + x^2$ \\
1695
0101 & $x^2 + 1$ & 1101 & $x^3 + x^2 + 1$ \\
1696
0110 & $x^2 + x$ & 1110 & $x^3 + x^2 + x$ \\
1697
0111 & $x^2 + x + 1$ & 1111 & $x^3 + x^2 + x+ 1$ \\\hline
1698
\end{tabular}
1699
1700
INPUT:
1701
1702
- ``G`` -- an element of `\GF{2^4}`, a list of elements of
1703
`\GF{2^4}`, or a matrix over `\GF{2^4}`
1704
1705
OUTPUT:
1706
1707
- A binary string representation of ``G``.
1708
1709
EXAMPLES:
1710
1711
Obtain the binary representation of all elements of `\GF{2^4}`::
1712
1713
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1714
sage: maes = MiniAES()
1715
sage: K = FiniteField(16, "x")
1716
sage: S = Set(K); len(S) # GF(2^4) has this many elements
1717
16
1718
sage: [maes.GF_to_binary(S[i]) for i in xrange(len(S))]
1719
<BLANKLINE>
1720
[0000,
1721
0001,
1722
0010,
1723
0011,
1724
0100,
1725
0101,
1726
0110,
1727
0111,
1728
1000,
1729
1001,
1730
1010,
1731
1011,
1732
1100,
1733
1101,
1734
1110,
1735
1111]
1736
1737
The binary representation of a list of elements belonging to
1738
`\GF{2^4}`::
1739
1740
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1741
sage: maes = MiniAES()
1742
sage: K = FiniteField(16, "x")
1743
sage: G = [K("x^2 + x + 1"), K("x^3 + x^2"), K("x"), K("x^3 + x + 1"), K("x^3 + x^2 + x + 1"), K("x^2 + x"), K("1"), K("x^2 + x + 1")]
1744
sage: maes.GF_to_binary(G)
1745
01111100001010111111011000010111
1746
1747
The binary representation of a matrix over `\GF{2^4}`::
1748
1749
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1750
sage: maes = MiniAES()
1751
sage: K = FiniteField(16, "x")
1752
sage: MS = MatrixSpace(K, 2, 2)
1753
sage: G = MS([K("x^3 + x^2"), K("x + 1"), K("x^2 + x + 1"), K("x^3 + x^2 + x")]); G
1754
<BLANKLINE>
1755
[ x^3 + x^2 x + 1]
1756
[ x^2 + x + 1 x^3 + x^2 + x]
1757
sage: maes.GF_to_binary(G)
1758
1100001101111110
1759
sage: MS = MatrixSpace(K, 2, 4)
1760
sage: G = MS([K("x^2 + x + 1"), K("x^3 + x^2"), K("x"), K("x^3 + x + 1"), K("x^3 + x^2 + x + 1"), K("x^2 + x"), K("1"), K("x^2 + x + 1")]); G
1761
<BLANKLINE>
1762
[ x^2 + x + 1 x^3 + x^2 x x^3 + x + 1]
1763
[x^3 + x^2 + x + 1 x^2 + x 1 x^2 + x + 1]
1764
sage: maes.GF_to_binary(G)
1765
01111100001010111111011000010111
1766
1767
TESTS:
1768
1769
Input must be an element of `\GF{2^4}`::
1770
1771
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1772
sage: maes = MiniAES()
1773
sage: K = FiniteField(8, "x")
1774
sage: G = K.random_element()
1775
sage: maes.GF_to_binary(G)
1776
Traceback (most recent call last):
1777
...
1778
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1779
1780
A list of elements belonging to `\GF{2^4}`::
1781
1782
sage: maes.GF_to_binary([])
1783
Traceback (most recent call last):
1784
...
1785
ValueError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1786
sage: G = [K.random_element() for i in xrange(5)]
1787
sage: maes.GF_to_binary(G)
1788
Traceback (most recent call last):
1789
...
1790
KeyError:...
1791
1792
A matrix over `\GF{2^4}`::
1793
1794
sage: MS = MatrixSpace(FiniteField(7, "x"), 4, 5)
1795
sage: maes.GF_to_binary(MS.random_element())
1796
Traceback (most recent call last):
1797
...
1798
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1799
"""
1800
B = BinaryStrings()
1801
K = FiniteField(self._key_size, "x")
1802
# G is an element over GF(16)
1803
if G in K:
1804
return self._GF_to_bin[G]
1805
# G is a list of elements over GF(16)
1806
elif isinstance(G, list):
1807
if len(G) == 0:
1808
raise ValueError("input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)")
1809
S = "".join([str(self._GF_to_bin[g]) for g in G])
1810
return B(S)
1811
# G is a matrix over GF(16)
1812
elif isinstance(G, Matrix_dense):
1813
if not (G.base_ring() is K):
1814
raise TypeError("input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)")
1815
S = "".join([str(self._GF_to_bin[G[i][j]]) for j in xrange(G.ncols()) for i in xrange(G.nrows())])
1816
return B(S)
1817
# the type of G doesn't match the supported types
1818
else:
1819
raise TypeError("input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)")
1820
1821
def GF_to_integer(self, G):
1822
r"""
1823
Return the integer representation of the finite field element ``G``.
1824
If ``G`` is an element of the finite field `\GF{2^4}`, then
1825
obtain the integer representation of ``G``. If ``G`` is a list of
1826
elements belonging to `\GF{2^4}`, obtain the integer
1827
representation of each element of the list, and return the result
1828
as a list of integers. If ``G`` is a matrix with entries over
1829
`\GF{2^4}`, convert each matrix entry to its integer representation,
1830
and return the result as a list of integers. The resulting list is
1831
obtained by starting from the top-left corner of the matrix, working
1832
across left to right, top to bottom. Each element of `\GF{2^4}` can
1833
be associated with a unique integer according to the following table:
1834
1835
.. MATH::
1836
1837
\begin{tabular}{ll|ll} \hline
1838
integer & $\GF{2^4}$ & integer & $\GF{2^4}$ \\\hline
1839
0 & $0$ & 8 & $x^3$ \\
1840
1 & $1$ & 9 & $x^3 + 1$ \\
1841
2 & $x$ & 10 & $x^3 + x$ \\
1842
3 & $x + 1$ & 11 & $x^3 + x + 1$ \\
1843
4 & $x^2$ & 12 & $x^3 + x^2$ \\
1844
5 & $x^2 + 1$ & 13 & $x^3 + x^2 + 1$ \\
1845
6 & $x^2 + x$ & 14 & $x^3 + x^2 + x$ \\
1846
7 & $x^2 + x + 1$ & 15 & $x^3 + x^2 + x+ 1$ \\\hline
1847
\end{tabular}
1848
1849
INPUT:
1850
1851
- ``G`` -- an element of `\GF{2^4}`, a list of elements belonging to
1852
`\GF{2^4}`, or a matrix over `\GF{2^4}`
1853
1854
OUTPUT:
1855
1856
- The integer representation of ``G``.
1857
1858
EXAMPLES:
1859
1860
Obtain the integer representation of all elements of `\GF{2^4}`::
1861
1862
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1863
sage: maes = MiniAES()
1864
sage: K = FiniteField(16, "x")
1865
sage: S = Set(K); len(S) # GF(2^4) has this many elements
1866
16
1867
sage: [maes.GF_to_integer(S[i]) for i in xrange(len(S))]
1868
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1869
1870
The integer representation of a list of elements belonging to
1871
`\GF{2^4}`::
1872
1873
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1874
sage: maes = MiniAES()
1875
sage: K = FiniteField(16, "x")
1876
sage: G = [K("x^2 + x + 1"), K("x^3 + x^2"), K("x"), K("x^3 + x + 1"), K("x^3 + x^2 + x + 1"), K("x^2 + x"), K("1"), K("x^2 + x + 1")]
1877
sage: maes.GF_to_integer(G)
1878
[7, 12, 2, 11, 15, 6, 1, 7]
1879
1880
The integer representation of a matrix over `\GF{2^4}`::
1881
1882
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1883
sage: maes = MiniAES()
1884
sage: K = FiniteField(16, "x")
1885
sage: MS = MatrixSpace(K, 2, 2)
1886
sage: G = MS([K("x^3 + x^2"), K("x + 1"), K("x^2 + x + 1"), K("x^3 + x^2 + x")]); G
1887
<BLANKLINE>
1888
[ x^3 + x^2 x + 1]
1889
[ x^2 + x + 1 x^3 + x^2 + x]
1890
sage: maes.GF_to_integer(G)
1891
[12, 3, 7, 14]
1892
sage: MS = MatrixSpace(K, 2, 4)
1893
sage: G = MS([K("x^2 + x + 1"), K("x^3 + x^2"), K("x"), K("x^3 + x + 1"), K("x^3 + x^2 + x + 1"), K("x^2 + x"), K("1"), K("x^2 + x + 1")]); G
1894
<BLANKLINE>
1895
[ x^2 + x + 1 x^3 + x^2 x x^3 + x + 1]
1896
[x^3 + x^2 + x + 1 x^2 + x 1 x^2 + x + 1]
1897
sage: maes.GF_to_integer(G)
1898
[7, 12, 2, 11, 15, 6, 1, 7]
1899
1900
TESTS:
1901
1902
Input must be an element of `\GF{2^4}`::
1903
1904
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1905
sage: maes = MiniAES()
1906
sage: K = FiniteField(7, "x")
1907
sage: G = K.random_element()
1908
sage: maes.GF_to_integer(G)
1909
Traceback (most recent call last):
1910
...
1911
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1912
1913
A list of elements belonging to `\GF{2^4}`::
1914
1915
sage: maes.GF_to_integer([])
1916
Traceback (most recent call last):
1917
...
1918
ValueError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1919
sage: G = [K.random_element() for i in xrange(5)]
1920
sage: maes.GF_to_integer(G)
1921
Traceback (most recent call last):
1922
...
1923
KeyError:...
1924
1925
A matrix over `\GF{2^4}`::
1926
1927
sage: MS = MatrixSpace(FiniteField(7, "x"), 4, 5)
1928
sage: maes.GF_to_integer(MS.random_element())
1929
Traceback (most recent call last):
1930
...
1931
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1932
"""
1933
K = FiniteField(self._key_size, "x")
1934
# G is an element over GF(16)
1935
if G in K:
1936
return self._GF_to_int[G]
1937
# G is a list of elements over GF(16)
1938
elif isinstance(G, list):
1939
if len(G) == 0:
1940
raise ValueError("input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)")
1941
return [self._GF_to_int[g] for g in G]
1942
# G is a matrix over GF(16)
1943
elif isinstance(G, Matrix_dense):
1944
if not (G.base_ring() is K):
1945
raise TypeError("input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)")
1946
return [self._GF_to_int[G[i][j]] for i in xrange(G.nrows()) for j in xrange(G.ncols())]
1947
# the type of G doesn't match the supported types
1948
else:
1949
raise TypeError("input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)")
1950
1951
def binary_to_GF(self, B):
1952
r"""
1953
Return a list of elements of `\GF{2^4}` that represents the
1954
binary string ``B``. The number of bits in ``B`` must be greater
1955
than zero and a multiple of 4. Each nibble (or 4-bit string) is
1956
uniquely associated with an element of `\GF{2^4}` as
1957
specified by the following table:
1958
1959
.. MATH::
1960
1961
\begin{tabular}{ll|ll} \hline
1962
4-bit string & $\GF{2^4}$ & 4-bit string & $\GF{2^4}$ \\\hline
1963
0000 & $0$ & 1000 & $x^3$ \\
1964
0001 & $1$ & 1001 & $x^3 + 1$ \\
1965
0010 & $x$ & 1010 & $x^3 + x$ \\
1966
0011 & $x + 1$ & 1011 & $x^3 + x + 1$ \\
1967
0100 & $x^2$ & 1100 & $x^3 + x^2$ \\
1968
0101 & $x^2 + 1$ & 1101 & $x^3 + x^2 + 1$ \\
1969
0110 & $x^2 + x$ & 1110 & $x^3 + x^2 + x$ \\
1970
0111 & $x^2 + x + 1$ & 1111 & $x^3 + x^2 + x+ 1$ \\\hline
1971
\end{tabular}
1972
1973
INPUT:
1974
1975
- ``B`` -- a binary string, where the number of bits is positive and
1976
a multiple of 4
1977
1978
OUTPUT:
1979
1980
- A list of elements of the finite field `\GF{2^4}` that
1981
represent the binary string ``B``.
1982
1983
EXAMPLES:
1984
1985
Obtain all the elements of the finite field `\GF{2^4}`::
1986
1987
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1988
sage: maes = MiniAES()
1989
sage: bin = BinaryStrings()
1990
sage: B = bin("0000000100100011010001010110011110001001101010111100110111101111")
1991
sage: maes.binary_to_GF(B)
1992
<BLANKLINE>
1993
[0,
1994
1,
1995
x,
1996
x + 1,
1997
x^2,
1998
x^2 + 1,
1999
x^2 + x,
2000
x^2 + x + 1,
2001
x^3,
2002
x^3 + 1,
2003
x^3 + x,
2004
x^3 + x + 1,
2005
x^3 + x^2,
2006
x^3 + x^2 + 1,
2007
x^3 + x^2 + x,
2008
x^3 + x^2 + x + 1]
2009
2010
TESTS:
2011
2012
The input ``B`` must be a non-empty binary string, where the number
2013
of bits is a multiple of 4::
2014
2015
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2016
sage: maes = MiniAES()
2017
sage: maes.binary_to_GF("")
2018
Traceback (most recent call last):
2019
...
2020
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
2021
sage: maes.binary_to_GF("101")
2022
Traceback (most recent call last):
2023
...
2024
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
2025
"""
2026
from sage.rings.finite_rings.integer_mod import Mod
2027
bin = BinaryStrings()
2028
b = bin(B)
2029
# an empty string
2030
if len(b) == 0:
2031
raise ValueError("the number of bits in the binary string B must be positive and a multiple of 4")
2032
# a string with number of bits that is a multiple of 4
2033
if Mod(len(b), 4).lift() == 0:
2034
M = len(b) // 4 # the number of nibbles
2035
return [self._bin_to_GF[b[i*4 : (i+1)*4]] for i in xrange(M)]
2036
else:
2037
raise ValueError("the number of bits in the binary string B must be positive and a multiple of 4")
2038
2039
def binary_to_integer(self, B):
2040
r"""
2041
Return a list of integers representing the binary string ``B``. The
2042
number of bits in ``B`` must be greater than zero and a multiple of
2043
4. Each nibble (or 4-bit string) is uniquely associated with an
2044
integer as specified by the following table:
2045
2046
.. MATH::
2047
2048
\begin{tabular}{ll|ll} \hline
2049
4-bit string & integer & 4-bit string & integer \\\hline
2050
0000 & 0 & 1000 & 8 \\
2051
0001 & 1 & 1001 & 9 \\
2052
0010 & 2 & 1010 & 10 \\
2053
0011 & 3 & 1011 & 11 \\
2054
0100 & 4 & 1100 & 12 \\
2055
0101 & 5 & 1101 & 13 \\
2056
0110 & 6 & 1110 & 14 \\
2057
0111 & 7 & 1111 & 15 \\\hline
2058
\end{tabular}
2059
2060
INPUT:
2061
2062
- ``B`` -- a binary string, where the number of bits is positive and
2063
a multiple of 4
2064
2065
OUTPUT:
2066
2067
- A list of integers that represent the binary string ``B``.
2068
2069
EXAMPLES:
2070
2071
Obtain the integer representation of every 4-bit string::
2072
2073
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2074
sage: maes = MiniAES()
2075
sage: bin = BinaryStrings()
2076
sage: B = bin("0000000100100011010001010110011110001001101010111100110111101111")
2077
sage: maes.binary_to_integer(B)
2078
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2079
2080
TESTS:
2081
2082
The input ``B`` must be a non-empty binary string, where the number
2083
of bits is a multiple of 4::
2084
2085
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2086
sage: maes = MiniAES()
2087
sage: maes.binary_to_integer("")
2088
Traceback (most recent call last):
2089
...
2090
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
2091
sage: maes.binary_to_integer("101")
2092
Traceback (most recent call last):
2093
...
2094
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
2095
"""
2096
from sage.rings.finite_rings.integer_mod import Mod
2097
bin = BinaryStrings()
2098
b = bin(B)
2099
# an empty string
2100
if len(b) == 0:
2101
raise ValueError("the number of bits in the binary string B must be positive and a multiple of 4")
2102
# a string with number of bits that is a multiple of 4
2103
if Mod(len(b), 4).lift() == 0:
2104
M = len(b) // 4 # the number of nibbles
2105
return [self._bin_to_int[b[i*4 : (i+1)*4]] for i in xrange(M)]
2106
else:
2107
raise ValueError("the number of bits in the binary string B must be positive and a multiple of 4")
2108
2109
def integer_to_binary(self, N):
2110
r"""
2111
Return the binary representation of ``N``. If `N` is an integer such
2112
that `0 \leq N \leq 15`, return the binary representation of ``N``.
2113
If ``N`` is a list of integers each of which is `\geq 0` and
2114
`\leq 15`, then obtain the binary representation of each integer,
2115
and concatenate the individual binary representations into a single
2116
binary string. Each integer between 0 and 15, inclusive, can be
2117
associated with a unique 4-bit string according to the following
2118
table:
2119
2120
.. MATH::
2121
2122
\begin{tabular}{ll|ll} \hline
2123
4-bit string & integer & 4-bit string & integer \\\hline
2124
0000 & 0 & 1000 & 8 \\
2125
0001 & 1 & 1001 & 9 \\
2126
0010 & 2 & 1010 & 10 \\
2127
0011 & 3 & 1011 & 11 \\
2128
0100 & 4 & 1100 & 12 \\
2129
0101 & 5 & 1101 & 13 \\
2130
0110 & 6 & 1110 & 14 \\
2131
0111 & 7 & 1111 & 15 \\\hline
2132
\end{tabular}
2133
2134
INPUT:
2135
2136
- ``N`` -- a non-negative integer less than or equal to 15, or a list
2137
of such integers
2138
2139
OUTPUT:
2140
2141
- A binary string representing ``N``.
2142
2143
EXAMPLES:
2144
2145
The binary representations of all integers between 0 and
2146
15, inclusive::
2147
2148
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2149
sage: maes = MiniAES()
2150
sage: lst = [n for n in xrange(16)]; lst
2151
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2152
sage: maes.integer_to_binary(lst)
2153
0000000100100011010001010110011110001001101010111100110111101111
2154
2155
The binary representation of an integer between 0 and 15,
2156
inclusive::
2157
2158
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2159
sage: maes = MiniAES()
2160
sage: maes.integer_to_binary(3)
2161
0011
2162
sage: maes.integer_to_binary(5)
2163
0101
2164
sage: maes.integer_to_binary(7)
2165
0111
2166
2167
TESTS:
2168
2169
The input ``N`` can be an integer, but must be bounded such that
2170
`0 \leq N \leq 15`::
2171
2172
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2173
sage: maes = MiniAES()
2174
sage: maes.integer_to_binary(-1)
2175
Traceback (most recent call last):
2176
...
2177
KeyError:...
2178
sage: maes.integer_to_binary("1")
2179
Traceback (most recent call last):
2180
...
2181
TypeError: N must be an integer 0 <= N <= 15 or a list of such integers
2182
sage: maes.integer_to_binary("")
2183
Traceback (most recent call last):
2184
...
2185
TypeError: N must be an integer 0 <= N <= 15 or a list of such integers
2186
2187
The input ``N`` can be a list of integers, but each integer `n` of
2188
the list must be `0 \leq n \leq 15`::
2189
2190
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2191
sage: maes = MiniAES()
2192
sage: maes.integer_to_binary([])
2193
Traceback (most recent call last):
2194
...
2195
ValueError: N must be an integer 0 <= N <= 15 or a list of such integers
2196
sage: maes.integer_to_binary([""])
2197
Traceback (most recent call last):
2198
...
2199
KeyError:...
2200
sage: maes.integer_to_binary([0, 1, 2, 16])
2201
Traceback (most recent call last):
2202
...
2203
KeyError:...
2204
"""
2205
if isinstance(N, list):
2206
if len(N) == 0:
2207
raise ValueError("N must be an integer 0 <= N <= 15 or a list of such integers")
2208
bin = BinaryStrings()
2209
# Here, we assume that each element of the list is an integer n
2210
# such that 0 <= n <= 15. An error will be raised if otherwise.
2211
b = "".join([str(self._int_to_bin[n]) for n in N])
2212
return bin(b)
2213
elif isinstance(N, Integer):
2214
# Here, we assume that N is an integer such that 0 <= n <= 15.
2215
# An error will be raised if otherwise.
2216
return self._int_to_bin[N]
2217
else:
2218
raise TypeError("N must be an integer 0 <= N <= 15 or a list of such integers")
2219
2220
def integer_to_GF(self, N):
2221
r"""
2222
Return the finite field representation of ``N``. If `N` is an
2223
integer such that `0 \leq N \leq 15`, return the element of
2224
`\GF{2^4}` that represents ``N``. If ``N`` is a list of integers
2225
each of which is `\geq 0` and `\leq 15`, then obtain the element
2226
of `\GF{2^4}` that represents each such integer, and return a list
2227
of such finite field representations. Each integer between 0 and 15,
2228
inclusive, can be associated with a unique element of `\GF{2^4}`
2229
according to the following table:
2230
2231
.. MATH::
2232
2233
\begin{tabular}{ll|ll} \hline
2234
integer & $\GF{2^4}$ & integer & $\GF{2^4}$ \\\hline
2235
0 & $0$ & 8 & $x^3$ \\
2236
1 & $1$ & 9 & $x^3 + 1$ \\
2237
2 & $x$ & 10 & $x^3 + x$ \\
2238
3 & $x + 1$ & 11 & $x^3 + x + 1$ \\
2239
4 & $x^2$ & 12 & $x^3 + x^2$ \\
2240
5 & $x^2 + 1$ & 13 & $x^3 + x^2 + 1$ \\
2241
6 & $x^2 + x$ & 14 & $x^3 + x^2 + x$ \\
2242
7 & $x^2 + x + 1$ & 15 & $x^3 + x^2 + x+ 1$ \\\hline
2243
\end{tabular}
2244
2245
INPUT:
2246
2247
- ``N`` -- a non-negative integer less than or equal to 15, or a list
2248
of such integers
2249
2250
OUTPUT:
2251
2252
- Elements of the finite field `\GF{2^4}`.
2253
2254
EXAMPLES:
2255
2256
Obtain the element of `\GF{2^4}` representing an integer `n`, where
2257
`0 \leq n \leq 15`::
2258
2259
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2260
sage: maes = MiniAES()
2261
sage: maes.integer_to_GF(0)
2262
0
2263
sage: maes.integer_to_GF(2)
2264
x
2265
sage: maes.integer_to_GF(7)
2266
x^2 + x + 1
2267
2268
Obtain the finite field elements corresponding to all non-negative
2269
integers less than or equal to 15::
2270
2271
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2272
sage: maes = MiniAES()
2273
sage: lst = [n for n in xrange(16)]; lst
2274
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2275
sage: maes.integer_to_GF(lst)
2276
<BLANKLINE>
2277
[0,
2278
1,
2279
x,
2280
x + 1,
2281
x^2,
2282
x^2 + 1,
2283
x^2 + x,
2284
x^2 + x + 1,
2285
x^3,
2286
x^3 + 1,
2287
x^3 + x,
2288
x^3 + x + 1,
2289
x^3 + x^2,
2290
x^3 + x^2 + 1,
2291
x^3 + x^2 + x,
2292
x^3 + x^2 + x + 1]
2293
2294
TESTS:
2295
2296
The input ``N`` can be an integer, but it must be such that
2297
`0 \leq N \leq 15`::
2298
2299
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2300
sage: maes = MiniAES()
2301
sage: maes.integer_to_GF(-1)
2302
Traceback (most recent call last):
2303
...
2304
KeyError:...
2305
sage: maes.integer_to_GF(16)
2306
Traceback (most recent call last):
2307
...
2308
KeyError:...
2309
sage: maes.integer_to_GF("2")
2310
Traceback (most recent call last):
2311
...
2312
TypeError: N must be an integer 0 <= N <= 15 or a list of such integers
2313
2314
The input ``N`` can be a list of integers, but each integer `n` in
2315
the list must be bounded such that `0 \leq n \leq 15`::
2316
2317
sage: maes.integer_to_GF([])
2318
Traceback (most recent call last):
2319
...
2320
ValueError: N must be an integer 0 <= N <= 15 or a list of such integers
2321
sage: maes.integer_to_GF([""])
2322
Traceback (most recent call last):
2323
...
2324
KeyError:...
2325
sage: maes.integer_to_GF([0, 2, 3, "4"])
2326
Traceback (most recent call last):
2327
...
2328
KeyError:...
2329
sage: maes.integer_to_GF([0, 2, 3, 16])
2330
Traceback (most recent call last):
2331
...
2332
KeyError:...
2333
"""
2334
if isinstance(N, list):
2335
if len(N) == 0:
2336
raise ValueError("N must be an integer 0 <= N <= 15 or a list of such integers")
2337
# Here, we assume that each element of the list is an integer n
2338
# such that 0 <= n <= 15. An error will be raised if otherwise.
2339
return [self._int_to_GF[n] for n in N]
2340
elif isinstance(N, Integer):
2341
# Here, we assume that N is an integer such that 0 <= n <= 15.
2342
# An error will be raised if otherwise.
2343
return self._int_to_GF[N]
2344
else:
2345
raise TypeError("N must be an integer 0 <= N <= 15 or a list of such integers")
2346
2347