Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/crypto/block_cipher/miniaes.py
4069 views
1
r"""
2
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
"""
12
13
###########################################################################
14
# Copyright (c) 2009 Minh Van Nguyen <[email protected]>
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.constructor import FiniteField
34
from sage.rings.integer import Integer
35
from sage.structure.sage_object import SageObject
36
37
class MiniAES(SageObject):
38
r"""
39
This class implements the Mini Advanced Encryption Standard (Mini-AES)
40
described in [P02]_. Note that Phan's Mini-AES is for educational purposes
41
only and is not secure for practical purposes. Mini-AES is a version of
42
the AES with all parameters significantly reduced, but at the same time
43
preserving the structure of AES. The goal of Mini-AES is to allow a
44
beginner to understand the structure of AES, thus laying a foundation
45
for a thorough study of AES. Its goal is as a teaching tool and is
46
different from the :mod:`SR <sage.crypto.mq.sr>` small scale variants
47
of the AES. SR defines a family of parameterizable variants of the AES
48
suitable as a framework for comparing different cryptanalytic techniques
49
that can be brought to bear on the AES.
50
51
EXAMPLES:
52
53
Encrypt a plaintext::
54
55
sage: from sage.crypto.block_cipher.miniaes import MiniAES
56
sage: maes = MiniAES()
57
sage: K = FiniteField(16, "x")
58
sage: MS = MatrixSpace(K, 2, 2)
59
sage: P = MS([K("x^3 + x"), K("x^2 + 1"), K("x^2 + x"), K("x^3 + x^2")]); P
60
<BLANKLINE>
61
[ x^3 + x x^2 + 1]
62
[ x^2 + x x^3 + x^2]
63
sage: key = MS([K("x^3 + x^2"), K("x^3 + x"), K("x^3 + x^2 + x"), K("x^2 + x + 1")]); key
64
<BLANKLINE>
65
[ x^3 + x^2 x^3 + x]
66
[x^3 + x^2 + x x^2 + x + 1]
67
sage: C = maes.encrypt(P, key); C
68
<BLANKLINE>
69
[ x x^2 + x]
70
[x^3 + x^2 + x x^3 + x]
71
72
Decrypt the result::
73
74
sage: plaintxt = maes.decrypt(C, key)
75
sage: plaintxt; P
76
<BLANKLINE>
77
[ x^3 + x x^2 + 1]
78
[ x^2 + x x^3 + x^2]
79
<BLANKLINE>
80
[ x^3 + x x^2 + 1]
81
[ x^2 + x x^3 + x^2]
82
sage: plaintxt == P
83
True
84
85
We can also work directly with binary strings::
86
87
sage: from sage.crypto.block_cipher.miniaes import MiniAES
88
sage: maes = MiniAES()
89
sage: bin = BinaryStrings()
90
sage: key = bin.encoding("KE"); key
91
0100101101000101
92
sage: P = bin.encoding("Encrypt this secret message!"); P
93
01000101011011100110001101110010011110010111000001110100001000000111010001101000011010010111001100100000011100110110010101100011011100100110010101110100001000000110110101100101011100110111001101100001011001110110010100100001
94
sage: C = maes(P, key, algorithm="encrypt"); C
95
10001000101001101111000001111000010011001110110101000111011011010101001011101111101011001110011100100011101100101010100010100111110110011001010001000111011011010010000011000110001100000111000011100110101111000000001110001001
96
sage: plaintxt = maes(C, key, algorithm="decrypt")
97
sage: plaintxt == P
98
True
99
100
Now we work with integers `n` such that `0 \leq n \leq 15`::
101
102
sage: from sage.crypto.block_cipher.miniaes import MiniAES
103
sage: maes = MiniAES()
104
sage: P = [n for n in xrange(16)]; P
105
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
106
sage: key = [2, 3, 11, 0]; key
107
[2, 3, 11, 0]
108
sage: P = maes.integer_to_binary(P); P
109
0000000100100011010001010110011110001001101010111100110111101111
110
sage: key = maes.integer_to_binary(key); key
111
0010001110110000
112
sage: C = maes(P, key, algorithm="encrypt"); C
113
1100100000100011111001010101010101011011100111110001000011100001
114
sage: plaintxt = maes(C, key, algorithm="decrypt")
115
sage: plaintxt == P
116
True
117
118
Generate some random plaintext and a random secret key. Encrypt the
119
plaintext using that secret key and decrypt the result. Then compare the
120
decrypted plaintext with the original plaintext::
121
122
sage: from sage.crypto.block_cipher.miniaes import MiniAES
123
sage: maes = MiniAES()
124
sage: MS = MatrixSpace(FiniteField(16, "x"), 2, 2)
125
sage: P = MS.random_element()
126
sage: key = maes.random_key()
127
sage: C = maes.encrypt(P, key)
128
sage: plaintxt = maes.decrypt(C, key)
129
sage: plaintxt == P
130
True
131
132
REFERENCES:
133
134
.. [P02] R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a
135
testbed for cryptanalysis students. Cryptologia, 26(4):283--306, 2002.
136
"""
137
138
def __init__(self):
139
r"""
140
A simplified variant of the Advanced Encryption Standard (AES).
141
142
EXAMPLES::
143
144
sage: from sage.crypto.block_cipher.miniaes import MiniAES
145
sage: maes = MiniAES(); maes
146
Mini-AES block cipher with 16-bit keys
147
sage: MS = MatrixSpace(FiniteField(16, "x"), 2, 2)
148
sage: P = MS.random_element()
149
sage: key = maes.random_key()
150
sage: C = maes.encrypt(P, key)
151
sage: plaintxt = maes.decrypt(C, key)
152
sage: plaintxt == P
153
True
154
"""
155
from sage.crypto.mq import SBox
156
self._key_size = 16 # the number of bits in a secret key
157
B = BinaryStrings()
158
K = FiniteField(self._key_size, "x")
159
# the S-box for encryption
160
self._sboxE = SBox(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(14, 3, 4, 8, 1, 12, 10, 15, 7, 13, 9, 6, 11, 2, 0, 5)
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))
375
matK = MS(self.binary_to_GF(key))
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))
386
matK = MS(self.binary_to_GF(key))
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
plaintext = self.shift_row(plaintext)
732
plaintext = self.nibble_sub(plaintext, algorithm="decrypt")
733
734
# undo the result of round 1
735
plaintext = self.add_key(plaintext, rkey1)
736
plaintext = self.mix_column(plaintext)
737
plaintext = self.shift_row(plaintext)
738
plaintext = self.nibble_sub(plaintext, algorithm="decrypt")
739
740
# undo the result of round 0
741
plaintext = self.add_key(plaintext, rkey0)
742
743
return plaintext
744
745
def encrypt(self, P, key):
746
r"""
747
Use Phan's Mini-AES to encrypt the plaintext ``P`` with the secret
748
key ``key``. Both ``P`` and ``key`` must be `2 \times 2` matrices
749
over the finite field `\GF{2^4}`. Let `\gamma` denote the
750
operation of nibble-sub, `\pi` denote shift-row, `\theta` denote
751
mix-column, and `\sigma_{K_i}` denote add-key with the round key
752
`K_i`. Then encryption `E` using Phan's Mini-AES is the function
753
composition
754
755
.. MATH::
756
757
E = \sigma_{K_2} \circ \pi \circ \gamma \circ \sigma_{K_1} \circ \theta \circ \pi \circ \gamma \circ \sigma_{K_0}
758
759
where the order of execution is from right to left. Note that
760
`\gamma` is the nibble-sub operation that uses the S-box for
761
encryption.
762
763
INPUT:
764
765
- ``P`` -- a plaintext block; must be a `2 \times 2` matrix over
766
the finite field `\GF{2^4}`
767
768
- ``key`` -- a secret key for this Mini-AES block cipher; must be a
769
`2 \times 2` matrix over the finite field `\GF{2^4}`
770
771
OUTPUT:
772
773
- The ciphertext corresponding to ``P``.
774
775
EXAMPLES:
776
777
Here we work with elements of `\GF{2^4}`::
778
779
sage: from sage.crypto.block_cipher.miniaes import MiniAES
780
sage: maes = MiniAES()
781
sage: K = FiniteField(16, "x")
782
sage: MS = MatrixSpace(K, 2, 2)
783
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ]); P
784
<BLANKLINE>
785
[ x^3 + 1 x^2 + x]
786
[x^3 + x^2 x + 1]
787
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); key
788
<BLANKLINE>
789
[ x^3 + x^2 x^3 + x^2 + x + 1]
790
[ x + 1 0]
791
sage: maes.encrypt(P, key)
792
<BLANKLINE>
793
[x^2 + x + 1 x^3 + x^2]
794
[ x x^2 + x]
795
796
But we can also work with binary strings::
797
798
sage: bin = BinaryStrings()
799
sage: P = bin.encoding("de"); P
800
0110010001100101
801
sage: P = MS(maes.binary_to_GF(P)); P
802
<BLANKLINE>
803
[x^2 + x x^2]
804
[x^2 + x x^2 + 1]
805
sage: key = bin.encoding("ke"); key
806
0110101101100101
807
sage: key = MS(maes.binary_to_GF(key)); key
808
<BLANKLINE>
809
[ x^2 + x x^3 + x + 1]
810
[ x^2 + x x^2 + 1]
811
sage: C = maes.encrypt(P, key)
812
sage: plaintxt = maes.decrypt(C, key)
813
sage: plaintxt == P
814
True
815
816
Now we work with integers `n` such that `0 \leq n \leq 15`::
817
818
sage: P = [1, 5, 8, 12]; P
819
[1, 5, 8, 12]
820
sage: key = [5, 9, 15, 0]; key
821
[5, 9, 15, 0]
822
sage: P = MS(maes.integer_to_GF(P)); P
823
<BLANKLINE>
824
[ 1 x^2 + 1]
825
[ x^3 x^3 + x^2]
826
sage: key = MS(maes.integer_to_GF(key)); key
827
<BLANKLINE>
828
[ x^2 + 1 x^3 + 1]
829
[x^3 + x^2 + x + 1 0]
830
sage: C = maes.encrypt(P, key)
831
sage: plaintxt = maes.decrypt(C, key)
832
sage: plaintxt == P
833
True
834
835
TESTS:
836
837
The input block must be a matrix::
838
839
sage: from sage.crypto.block_cipher.miniaes import MiniAES
840
sage: maes = MiniAES()
841
sage: K = FiniteField(16, "x")
842
sage: MS = MatrixSpace(K, 2, 2)
843
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
844
sage: maes.encrypt("P", key)
845
Traceback (most recent call last):
846
...
847
TypeError: plaintext block must be a 2 x 2 matrix over GF(16)
848
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
849
sage: maes.encrypt(P, "key")
850
Traceback (most recent call last):
851
...
852
TypeError: secret key must be a 2 x 2 matrix over GF(16)
853
854
In addition, the dimensions of the input matrices must be
855
`2 \times 2`::
856
857
sage: MS = MatrixSpace(K, 1, 2)
858
sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")]])
859
sage: maes.encrypt(P, "key")
860
Traceback (most recent call last):
861
...
862
TypeError: plaintext block must be a 2 x 2 matrix over GF(16)
863
sage: MSP = MatrixSpace(K, 2, 2)
864
sage: P = MSP([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])
865
sage: MSK = MatrixSpace(K, 1, 2)
866
sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])
867
sage: maes.encrypt(P, key)
868
Traceback (most recent call last):
869
...
870
TypeError: secret key must be a 2 x 2 matrix over GF(16)
871
"""
872
if not isinstance(P, Matrix_dense) or \
873
not (P.base_ring().order() == 16 and P.base_ring().is_field()):
874
raise TypeError, "plaintext block must be a 2 x 2 matrix over GF(16)"
875
if not (P.nrows() == P.ncols() == 2):
876
raise TypeError, "plaintext block must be a 2 x 2 matrix over GF(16)"
877
if not isinstance(key, Matrix_dense) or \
878
not (key.base_ring().order() == 16 and key.base_ring().is_field()):
879
raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"
880
if not (key.nrows() == key.ncols() == 2):
881
raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"
882
883
# pre-compute the round keys
884
rkey0 = self.round_key(key, 0)
885
rkey1 = self.round_key(key, 1)
886
rkey2 = self.round_key(key, 2)
887
888
# now proceed with encrypting the plaintext
889
890
# round 0
891
ciphertext = self.add_key(P, rkey0)
892
893
# round 1
894
ciphertext = self.nibble_sub(ciphertext, algorithm="encrypt")
895
ciphertext = self.shift_row(ciphertext)
896
ciphertext = self.mix_column(ciphertext)
897
ciphertext = self.add_key(ciphertext, rkey1)
898
899
# round 2
900
ciphertext = self.nibble_sub(ciphertext, algorithm="encrypt")
901
ciphertext = self.shift_row(ciphertext)
902
ciphertext = self.add_key(ciphertext, rkey2)
903
904
return ciphertext
905
906
def mix_column(self, block):
907
r"""
908
Return the matrix multiplication of ``block`` with a constant matrix.
909
The constant matrix is
910
911
.. MATH::
912
913
\begin{bmatrix}
914
x + 1 & x \\
915
x & x + 1
916
\end{bmatrix}
917
918
If the input block is
919
920
.. MATH::
921
922
\begin{bmatrix}
923
c_0 & c_2 \\
924
c_1 & c_3
925
\end{bmatrix}
926
927
then the output block is
928
929
.. MATH::
930
931
\begin{bmatrix}
932
d_0 & d_2 \\
933
d_1 & d_3
934
\end{bmatrix}
935
=
936
\begin{bmatrix}
937
x + 1 & x \\
938
x & x + 1
939
\end{bmatrix}
940
\begin{bmatrix}
941
c_0 & c_2 \\
942
c_1 & c_3
943
\end{bmatrix}
944
945
INPUT:
946
947
- ``block`` -- a `2 \times 2` matrix with entries over
948
`\GF{2^4}`
949
950
OUTPUT:
951
952
- A `2 \times 2` matrix resulting from multiplying the above constant
953
matrix with the input matrix ``block``.
954
955
EXAMPLES:
956
957
Here we work with elements of `\GF{2^4}`::
958
959
sage: from sage.crypto.block_cipher.miniaes import MiniAES
960
sage: maes = MiniAES()
961
sage: K = FiniteField(16, "x")
962
sage: MS = MatrixSpace(K, 2, 2)
963
sage: mat = MS([ [K("x^2 + x + 1"), K("x^3 + x^2 + 1")], [K("x^3"), K("x")] ])
964
sage: maes.mix_column(mat)
965
<BLANKLINE>
966
[ x^3 + x 0]
967
[ x^2 + 1 x^3 + x^2 + x + 1]
968
969
Multiplying by the identity matrix should leave the constant matrix
970
unchanged::
971
972
sage: eye = MS([ [K("1"), K("0")], [K("0"), K("1")] ])
973
sage: maes.mix_column(eye)
974
<BLANKLINE>
975
[x + 1 x]
976
[ x x + 1]
977
978
We can also work with binary strings::
979
980
sage: bin = BinaryStrings()
981
sage: B = bin.encoding("rT"); B
982
0111001001010100
983
sage: B = MS(maes.binary_to_GF(B)); B
984
<BLANKLINE>
985
[x^2 + x + 1 x]
986
[ x^2 + 1 x^2]
987
sage: maes.mix_column(B)
988
<BLANKLINE>
989
[ x + 1 x^3 + x^2 + x]
990
[ 1 x^3]
991
992
We can also work with integers `n` such that `0 \leq n \leq 15`::
993
994
sage: P = [10, 5, 2, 7]; P
995
[10, 5, 2, 7]
996
sage: P = MS(maes.integer_to_GF(P)); P
997
<BLANKLINE>
998
[ x^3 + x x^2 + 1]
999
[ x x^2 + x + 1]
1000
sage: maes.mix_column(P)
1001
<BLANKLINE>
1002
[x^3 + 1 1]
1003
[ 1 x + 1]
1004
1005
TESTS:
1006
1007
The input block must be a matrix::
1008
1009
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1010
sage: maes = MiniAES()
1011
sage: maes.mix_column("mat")
1012
Traceback (most recent call last):
1013
...
1014
TypeError: input block must be a 2 x 2 matrix over GF(16)
1015
1016
In addition, the dimensions of the input matrix must be `2 \times 2`::
1017
1018
sage: K = FiniteField(16, "x")
1019
sage: MS = MatrixSpace(K, 1, 2)
1020
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
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
if not isinstance(block, Matrix_dense) or \
1027
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1028
raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"
1029
if not (block.nrows() == block.ncols() == 2):
1030
raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"
1031
1032
K = FiniteField(self._key_size, "x")
1033
MS = MatrixSpace(K, 2, 2)
1034
M = MS( [ [K("x + 1"), K("x")],
1035
[K("x"), K("x + 1")] ] )
1036
return M * block
1037
1038
def nibble_sub(self, block, algorithm="encrypt"):
1039
r"""
1040
Substitute a nibble (or a block of 4 bits) using the following S-box:
1041
1042
.. MATH::
1043
1044
\begin{tabular}{ll|ll} \hline
1045
Input & Output & Input & Output \\\hline
1046
0000 & 1110 & 1000 & 0011 \\
1047
0001 & 0100 & 1001 & 1010 \\
1048
0010 & 1101 & 1010 & 0110 \\
1049
0011 & 0001 & 1011 & 1100 \\
1050
0100 & 0010 & 1100 & 0101 \\
1051
0101 & 1111 & 1101 & 1001 \\
1052
0110 & 1011 & 1110 & 0000 \\
1053
0111 & 1000 & 1111 & 0111 \\\hline
1054
\end{tabular}
1055
1056
The values in the above S-box are taken from the first row of the first
1057
S-box of the Data Encryption Standard (DES). Each nibble can be
1058
thought of as an element of the finite field `\GF{2^4}` of 16
1059
elements. Thus in terms of `\GF{2^4}`, the S-box can also be
1060
specified as:
1061
1062
.. MATH::
1063
1064
\begin{tabular}{ll} \hline
1065
Input & Output \\\hline
1066
$0$ & $x^3 + x^2 + x$ \\
1067
$1$ & $x^2$ \\
1068
$x$ & $x^3 + x^2 + 1$ \\
1069
$x + 1$ & $1$ \\
1070
$x^2$ & $x$ \\
1071
$x^2 + 1$ & $x^3 + x^2 + x + 1$ \\
1072
$x^2 + x$ & $x^3 + x + 1$ \\
1073
$x^2 + x + 1$ & $x^3$ \\
1074
$x^3$ & $x + 1$ \\
1075
$x^3 + 1$ & $x^3 + x$ \\
1076
$x^3 + x$ & $x^2 + x$ \\
1077
$x^3 + x + 1$ & $x^3 + x^2$ \\
1078
$x^3 + x^2$ & $x^2 + 1$ \\
1079
$x^3 + x^2 + 1$ & $x^3 + 1$ \\
1080
$x^3 + x^2 + x$ & $0$ \\
1081
$x^3 + x^2 + x + 1$ & $x^2 + x + 1$ \\\hline
1082
\end{tabular}
1083
1084
Note that the above S-box is used for encryption. The S-box for
1085
decryption is obtained from the above S-box by reversing the role of
1086
the Input and Output columns. Thus the previous Input column for
1087
encryption now becomes the Output column for decryption, and the
1088
previous Output column for encryption is now the Input column for
1089
decryption. The S-box used for decryption can be specified as:
1090
1091
.. MATH::
1092
1093
\begin{tabular}{ll} \hline
1094
Input & Output \\\hline
1095
$0$ & $x^3 + x^2 + x$ \\
1096
$1$ & $x + 1$ \\
1097
$x$ & $x^2$ \\
1098
$x + 1$ & $x^3$ \\
1099
$x^2$ & $1$ \\
1100
$x^2 + 1$ & $x^3 + x^2$ \\
1101
$x^2 + x$ & $x^3 + x$ \\
1102
$x^2 + x + 1$ & $x^3 + x^2 + x + 1$ \\
1103
$x^3$ & $x^2 + x + 1$ \\
1104
$x^3 + 1$ & $x^3 + x^2 + 1$ \\
1105
$x^3 + x$ & $x^3 + 1$ \\
1106
$x^3 + x + 1$ & $x^2 + x$ \\
1107
$x^3 + x^2$ & $x^3 + x + 1$ \\
1108
$x^3 + x^2 + 1$ & $x$ \\
1109
$x^3 + x^2 + x$ & $0$ \\
1110
$x^3 + x^2 + x + 1$ & $x^2 + 1$ \\\hline
1111
\end{tabular}
1112
1113
INPUT:
1114
1115
- ``block`` -- a `2 \times 2` matrix with entries over
1116
`\GF{2^4}`
1117
1118
- ``algorithm`` -- (default: ``"encrypt"``) a string; a flag to signify
1119
whether this nibble-sub operation is used for encryption or
1120
decryption. The encryption flag is ``"encrypt"`` and the decryption
1121
flag is ``"decrypt"``.
1122
1123
OUTPUT:
1124
1125
- A `2 \times 2` matrix resulting from applying an S-box on
1126
entries of the `2 \times 2` matrix ``block``.
1127
1128
EXAMPLES:
1129
1130
Here we work with elements of the finite field `\GF{2^4}`::
1131
1132
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1133
sage: maes = MiniAES()
1134
sage: K = FiniteField(16, "x")
1135
sage: MS = MatrixSpace(K, 2, 2)
1136
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])
1137
sage: maes.nibble_sub(mat, algorithm="encrypt")
1138
<BLANKLINE>
1139
[ x^2 + x + 1 x^3 + x^2 + x]
1140
[ x^3 x^2 + x]
1141
1142
But we can also work with binary strings::
1143
1144
sage: bin = BinaryStrings()
1145
sage: B = bin.encoding("bi"); B
1146
0110001001101001
1147
sage: B = MS(maes.binary_to_GF(B)); B
1148
<BLANKLINE>
1149
[x^2 + x x]
1150
[x^2 + x x^3 + 1]
1151
sage: maes.nibble_sub(B, algorithm="encrypt")
1152
<BLANKLINE>
1153
[ x^3 + x + 1 x^3 + x^2 + 1]
1154
[ x^3 + x + 1 x^3 + x]
1155
sage: maes.nibble_sub(B, algorithm="decrypt")
1156
<BLANKLINE>
1157
[ x^3 + x x^2]
1158
[ x^3 + x x^3 + x^2 + 1]
1159
1160
Here we work with integers `n` such that `0 \leq n \leq 15`::
1161
1162
sage: P = [2, 6, 8, 14]; P
1163
[2, 6, 8, 14]
1164
sage: P = MS(maes.integer_to_GF(P)); P
1165
<BLANKLINE>
1166
[ x x^2 + x]
1167
[ x^3 x^3 + x^2 + x]
1168
sage: maes.nibble_sub(P, algorithm="encrypt")
1169
<BLANKLINE>
1170
[x^3 + x^2 + 1 x^3 + x + 1]
1171
[ x + 1 0]
1172
sage: maes.nibble_sub(P, algorithm="decrypt")
1173
<BLANKLINE>
1174
[ x^2 x^3 + x]
1175
[x^2 + x + 1 0]
1176
1177
TESTS:
1178
1179
The input block must be a matrix::
1180
1181
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1182
sage: maes = MiniAES()
1183
sage: maes.nibble_sub("mat")
1184
Traceback (most recent call last):
1185
...
1186
TypeError: input block must be a 2 x 2 matrix over GF(16)
1187
1188
In addition, the dimensions of the input matrix must be `2 \times 2`::
1189
1190
sage: K = FiniteField(16, "x")
1191
sage: MS = MatrixSpace(K, 1, 2)
1192
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1193
sage: maes.nibble_sub(mat)
1194
Traceback (most recent call last):
1195
...
1196
TypeError: input block must be a 2 x 2 matrix over GF(16)
1197
1198
The value for the option ``algorithm`` must be either the string
1199
``"encrypt"`` or ``"decrypt"``::
1200
1201
sage: K = FiniteField(16, "x")
1202
sage: MS = MatrixSpace(K, 2, 2)
1203
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])
1204
sage: maes.nibble_sub(mat, algorithm="abc")
1205
Traceback (most recent call last):
1206
...
1207
ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'
1208
sage: maes.nibble_sub(mat, algorithm="e")
1209
Traceback (most recent call last):
1210
...
1211
ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'
1212
sage: maes.nibble_sub(mat, algorithm="d")
1213
Traceback (most recent call last):
1214
...
1215
ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'
1216
"""
1217
if not isinstance(block, Matrix_dense) or \
1218
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1219
raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"
1220
if not (block.nrows() == block.ncols() == 2):
1221
raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"
1222
1223
MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)
1224
# get the integer representation of each GF(2^4) element
1225
# in the input matrix block
1226
lst = [self._GF_to_int[block[i][j]] for i in xrange(block.nrows()) for j in xrange(block.ncols())]
1227
if algorithm == "encrypt":
1228
# Now run each resulting integer through the S-box for
1229
# encryption. Then convert the result output by the S-box
1230
# to an element of GF(2^4).
1231
return MS([self._int_to_GF[self._sboxE[e]] for e in lst])
1232
elif algorithm == "decrypt":
1233
# Now run each resulting integer through the S-box for
1234
# decryption. Then convert the result output by the S-box
1235
# to an element of GF(2^4).
1236
return MS([self._int_to_GF[self._sboxD[e]] for e in lst])
1237
else:
1238
raise ValueError, "the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'"
1239
1240
def random_key(self):
1241
r"""
1242
A random key within the key space of this Mini-AES block cipher. Like
1243
the AES, Phan's Mini-AES is a symmetric-key block cipher. A Mini-AES
1244
key is a block of 16 bits, or a `2 \times 2` matrix with entries over
1245
the finite field `\GF{2^4}`. Thus the number of possible keys is
1246
`2^{16} = 16^4`.
1247
1248
OUTPUT:
1249
1250
- A `2 \times 2` matrix over the finite field `\GF{2^4}`, used
1251
as a secret key for this Mini-AES block cipher.
1252
1253
EXAMPLES:
1254
1255
Each nibble of a key is an element of the finite field
1256
`\GF{2^4}`::
1257
1258
sage: K = FiniteField(16, "x")
1259
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1260
sage: maes = MiniAES()
1261
sage: key = maes.random_key()
1262
sage: [key[i][j] in K for i in xrange(key.nrows()) for j in xrange(key.ncols())]
1263
[True, True, True, True]
1264
1265
Generate a random key, then perform encryption and decryption using
1266
that key::
1267
1268
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1269
sage: maes = MiniAES()
1270
sage: K = FiniteField(16, "x")
1271
sage: MS = MatrixSpace(K, 2, 2)
1272
sage: key = maes.random_key()
1273
sage: P = MS.random_element()
1274
sage: C = maes.encrypt(P, key)
1275
sage: plaintxt = maes.decrypt(C, key)
1276
sage: plaintxt == P
1277
True
1278
"""
1279
MS = MatrixSpace(FiniteField(16, "x"), 2, 2)
1280
return MS.random_element()
1281
1282
def round_key(self, key, n):
1283
r"""
1284
Return the round key for round ``n``. Phan's Mini-AES is defined to
1285
have two rounds. The round key `K_0` is generated and used prior to
1286
the first round, with round keys `K_1` and `K_2` being used in rounds
1287
1 and 2 respectively. In total, there are three round keys, each
1288
generated from the secret key ``key``.
1289
1290
INPUT:
1291
1292
- ``key`` -- the secret key
1293
1294
- ``n`` -- non-negative integer; the round number
1295
1296
OUTPUT:
1297
1298
- The `n`-th round key.
1299
1300
EXAMPLES:
1301
1302
Obtaining the round keys from the secret key::
1303
1304
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1305
sage: maes = MiniAES()
1306
sage: K = FiniteField(16, "x")
1307
sage: MS = MatrixSpace(K, 2, 2)
1308
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
1309
sage: maes.round_key(key, 0)
1310
<BLANKLINE>
1311
[ x^3 + x^2 x^3 + x^2 + x + 1]
1312
[ x + 1 0]
1313
sage: key
1314
<BLANKLINE>
1315
[ x^3 + x^2 x^3 + x^2 + x + 1]
1316
[ x + 1 0]
1317
sage: maes.round_key(key, 1)
1318
<BLANKLINE>
1319
[ x + 1 x^3 + x^2 + x + 1]
1320
[ 0 x^3 + x^2 + x + 1]
1321
sage: maes.round_key(key, 2)
1322
<BLANKLINE>
1323
[x^2 + x x^3 + 1]
1324
[x^2 + x x^2 + x]
1325
1326
TESTS:
1327
1328
Only two rounds are defined for this AES variant::
1329
1330
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1331
sage: maes = MiniAES()
1332
sage: K = FiniteField(16, "x")
1333
sage: MS = MatrixSpace(K, 2, 2)
1334
sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])
1335
sage: maes.round_key(key, -1)
1336
Traceback (most recent call last):
1337
...
1338
ValueError: Mini-AES only defines two rounds
1339
sage: maes.round_key(key, 3)
1340
Traceback (most recent call last):
1341
...
1342
ValueError: Mini-AES only defines two rounds
1343
1344
The input key must be a matrix::
1345
1346
sage: maes.round_key("key", 0)
1347
Traceback (most recent call last):
1348
...
1349
TypeError: secret key must be a 2 x 2 matrix over GF(16)
1350
1351
In addition, the dimensions of the key matrix must be `2 \times 2`::
1352
1353
sage: K = FiniteField(16, "x")
1354
sage: MS = MatrixSpace(K, 1, 2)
1355
sage: key = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1356
sage: maes.round_key(key, 2)
1357
Traceback (most recent call last):
1358
...
1359
TypeError: secret key must be a 2 x 2 matrix over GF(16)
1360
"""
1361
if not isinstance(key, Matrix_dense) or \
1362
not (key.base_ring().order() == 16 and key.base_ring().is_field()):
1363
raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"
1364
if not (key.nrows() == key.ncols() == 2):
1365
raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"
1366
1367
K = FiniteField(self._key_size, "x")
1368
MS = MatrixSpace(K, 2, 2)
1369
# round 0
1370
if n == 0:
1371
return key
1372
# round 1
1373
if n == 1:
1374
round_constant_1 = K("1")
1375
w4 = key[0][0] + self._sboxE[key[1][1]] + round_constant_1
1376
w5 = key[1][0] + w4
1377
w6 = key[0][1] + w5
1378
w7 = key[1][1] + w6
1379
return MS([ [w4, w6], [w5, w7] ])
1380
# round 2
1381
if n == 2:
1382
round_constant_2 = K("x")
1383
key1 = self.round_key(key, 1)
1384
w8 = key1[0][0] + self._sboxE[key1[1][1]] + round_constant_2
1385
w9 = key1[1][0] + w8
1386
w10 = key1[0][1] + w9
1387
w11 = key1[1][1] + w10
1388
return MS([ [w8, w10], [w9, w11] ])
1389
# unsupported round number
1390
if (n < 0) or (n > 2):
1391
raise ValueError, "Mini-AES only defines two rounds"
1392
1393
def sbox(self):
1394
r"""
1395
Return the S-box of Mini-AES.
1396
1397
EXAMPLES::
1398
1399
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1400
sage: maes = MiniAES()
1401
sage: maes.sbox()
1402
(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7)
1403
"""
1404
return self._sboxE
1405
1406
def shift_row(self, block):
1407
r"""
1408
Rotate each row of ``block`` to the left by different nibble
1409
amounts. The first or zero-th row is left unchanged, while the
1410
second or row one is rotated left by one nibble. This has the effect
1411
of only interchanging the nibbles in the second row. Let
1412
`b_0, b_1, b_2, b_3` be four nibbles arranged as the following
1413
`2 \times 2` matrix
1414
1415
.. MATH::
1416
1417
\begin{bmatrix}
1418
b_0 & b_2 \\
1419
b_1 & b_3
1420
\end{bmatrix}
1421
1422
Then the operation of shift-row is the mapping
1423
1424
.. MATH::
1425
1426
\begin{bmatrix}
1427
b_0 & b_2 \\
1428
b_1 & b_3
1429
\end{bmatrix}
1430
\longmapsto
1431
\begin{bmatrix}
1432
b_0 & b_2 \\
1433
b_3 & b_1
1434
\end{bmatrix}
1435
1436
INPUT:
1437
1438
- ``block`` -- a `2 \times 2` matrix with entries over
1439
`\GF{2^4}`
1440
1441
OUTPUT:
1442
1443
- A `2 \times 2` matrix resulting from applying shift-row on ``block``.
1444
1445
EXAMPLES:
1446
1447
Here we work with elements of the finite field `\GF{2^4}`::
1448
1449
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1450
sage: maes = MiniAES()
1451
sage: K = FiniteField(16, "x")
1452
sage: MS = MatrixSpace(K, 2, 2)
1453
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])
1454
sage: maes.shift_row(mat)
1455
<BLANKLINE>
1456
[x^3 + x^2 + x + 1 0]
1457
[ x^3 + x x^2 + x + 1]
1458
sage: mat
1459
<BLANKLINE>
1460
[x^3 + x^2 + x + 1 0]
1461
[ x^2 + x + 1 x^3 + x]
1462
1463
But we can also work with binary strings::
1464
1465
sage: bin = BinaryStrings()
1466
sage: B = bin.encoding("Qt"); B
1467
0101000101110100
1468
sage: B = MS(maes.binary_to_GF(B)); B
1469
<BLANKLINE>
1470
[ x^2 + 1 1]
1471
[x^2 + x + 1 x^2]
1472
sage: maes.shift_row(B)
1473
<BLANKLINE>
1474
[ x^2 + 1 1]
1475
[ x^2 x^2 + x + 1]
1476
1477
Here we work with integers `n` such that `0 \leq n \leq 15`::
1478
1479
sage: P = [3, 6, 9, 12]; P
1480
[3, 6, 9, 12]
1481
sage: P = MS(maes.integer_to_GF(P)); P
1482
<BLANKLINE>
1483
[ x + 1 x^2 + x]
1484
[ x^3 + 1 x^3 + x^2]
1485
sage: maes.shift_row(P)
1486
<BLANKLINE>
1487
[ x + 1 x^2 + x]
1488
[x^3 + x^2 x^3 + 1]
1489
1490
TESTS:
1491
1492
The input block must be a matrix::
1493
1494
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1495
sage: maes = MiniAES()
1496
sage: maes.shift_row("block")
1497
Traceback (most recent call last):
1498
...
1499
TypeError: input block must be a 2 x 2 matrix over GF(16)
1500
1501
In addition, the dimensions of the input matrix must be `2 \times 2`::
1502
1503
sage: K = FiniteField(16, "x")
1504
sage: MS = MatrixSpace(K, 1, 2)
1505
sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])
1506
sage: maes.shift_row(mat)
1507
Traceback (most recent call last):
1508
...
1509
TypeError: input block must be a 2 x 2 matrix over GF(16)
1510
"""
1511
if not isinstance(block, Matrix_dense) or \
1512
not (block.base_ring().order() == 16 and block.base_ring().is_field()):
1513
raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"
1514
if not (block.nrows() == block.ncols() == 2):
1515
raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"
1516
1517
MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)
1518
mat = MS([ [block[0][0], block[0][1]],
1519
[block[1][1], block[1][0]] ] )
1520
return mat
1521
1522
1523
### conversion functions to convert between different data formats
1524
1525
def GF_to_binary(self, G):
1526
r"""
1527
Return the binary representation of ``G``.
1528
If ``G`` is an element of the finite field `\GF{2^4}`, then
1529
obtain the binary representation of ``G``. If ``G`` is a list of
1530
elements belonging to `\GF{2^4}`, obtain the 4-bit
1531
representation of each element of the list, then concatenate the
1532
resulting 4-bit strings into a binary string. If ``G`` is a matrix
1533
with entries over `\GF{2^4}`, convert each matrix entry to its
1534
4-bit representation, then concatenate the 4-bit strings. The
1535
concatenation is performed starting from the top-left corner of the
1536
matrix, working across left to right, top to bottom. Each element of
1537
`\GF{2^4}` can be associated with a unique 4-bit string
1538
according to the following table:
1539
1540
.. MATH::
1541
1542
\begin{tabular}{ll|ll} \hline
1543
4-bit string & $\GF{2^4}$ & 4-bit string & $\GF{2^4}$ \\\hline
1544
0000 & $0$ & 1000 & $x^3$ \\
1545
0001 & $1$ & 1001 & $x^3 + 1$ \\
1546
0010 & $x$ & 1010 & $x^3 + x$ \\
1547
0011 & $x + 1$ & 1011 & $x^3 + x + 1$ \\
1548
0100 & $x^2$ & 1100 & $x^3 + x^2$ \\
1549
0101 & $x^2 + 1$ & 1101 & $x^3 + x^2 + 1$ \\
1550
0110 & $x^2 + x$ & 1110 & $x^3 + x^2 + x$ \\
1551
0111 & $x^2 + x + 1$ & 1111 & $x^3 + x^2 + x+ 1$ \\\hline
1552
\end{tabular}
1553
1554
INPUT:
1555
1556
- ``G`` -- an element of `\GF{2^4}`, a list of elements of
1557
`\GF{2^4}`, or a matrix over `\GF{2^4}`
1558
1559
OUTPUT:
1560
1561
- A binary string representation of ``G``.
1562
1563
EXAMPLES:
1564
1565
Obtain the binary representation of all elements of `\GF{2^4}`::
1566
1567
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1568
sage: maes = MiniAES()
1569
sage: K = FiniteField(16, "x")
1570
sage: S = Set(K); len(S) # GF(2^4) has this many elements
1571
16
1572
sage: [maes.GF_to_binary(S[i]) for i in xrange(len(S))]
1573
<BLANKLINE>
1574
[0000,
1575
0001,
1576
0010,
1577
0011,
1578
0100,
1579
0101,
1580
0110,
1581
0111,
1582
1000,
1583
1001,
1584
1010,
1585
1011,
1586
1100,
1587
1101,
1588
1110,
1589
1111]
1590
1591
The binary representation of a list of elements belonging to
1592
`\GF{2^4}`::
1593
1594
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1595
sage: maes = MiniAES()
1596
sage: K = FiniteField(16, "x")
1597
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")]
1598
sage: maes.GF_to_binary(G)
1599
01111100001010111111011000010111
1600
1601
The binary representation of a matrix over `\GF{2^4}`::
1602
1603
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1604
sage: maes = MiniAES()
1605
sage: K = FiniteField(16, "x")
1606
sage: MS = MatrixSpace(K, 2, 2)
1607
sage: G = MS([K("x^3 + x^2"), K("x + 1"), K("x^2 + x + 1"), K("x^3 + x^2 + x")]); G
1608
<BLANKLINE>
1609
[ x^3 + x^2 x + 1]
1610
[ x^2 + x + 1 x^3 + x^2 + x]
1611
sage: maes.GF_to_binary(G)
1612
1100001101111110
1613
sage: MS = MatrixSpace(K, 2, 4)
1614
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
1615
<BLANKLINE>
1616
[ x^2 + x + 1 x^3 + x^2 x x^3 + x + 1]
1617
[x^3 + x^2 + x + 1 x^2 + x 1 x^2 + x + 1]
1618
sage: maes.GF_to_binary(G)
1619
01111100001010111111011000010111
1620
1621
TESTS:
1622
1623
Input must be an element of `\GF{2^4}`::
1624
1625
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1626
sage: maes = MiniAES()
1627
sage: K = FiniteField(8, "x")
1628
sage: G = K.random_element()
1629
sage: maes.GF_to_binary(G)
1630
Traceback (most recent call last):
1631
...
1632
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1633
1634
A list of elements belonging to `\GF{2^4}`::
1635
1636
sage: maes.GF_to_binary([])
1637
Traceback (most recent call last):
1638
...
1639
ValueError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1640
sage: G = [K.random_element() for i in xrange(5)]
1641
sage: maes.GF_to_binary(G)
1642
Traceback (most recent call last):
1643
...
1644
KeyError:...
1645
1646
A matrix over `\GF{2^4}`::
1647
1648
sage: MS = MatrixSpace(FiniteField(7, "x"), 4, 5)
1649
sage: maes.GF_to_binary(MS.random_element())
1650
Traceback (most recent call last):
1651
...
1652
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1653
"""
1654
B = BinaryStrings()
1655
K = FiniteField(self._key_size, "x")
1656
# G is an element over GF(16)
1657
if G in K:
1658
return self._GF_to_bin[G]
1659
# G is a list of elements over GF(16)
1660
elif isinstance(G, list):
1661
if len(G) == 0:
1662
raise ValueError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"
1663
S = "".join([str(self._GF_to_bin[g]) for g in G])
1664
return B(S)
1665
# G is a matrix over GF(16)
1666
elif isinstance(G, Matrix_dense):
1667
if not (G.base_ring() is K):
1668
raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"
1669
S = "".join([str(self._GF_to_bin[G[i][j]]) for i in xrange(G.nrows()) for j in xrange(G.ncols())])
1670
return B(S)
1671
# the type of G doesn't match the supported types
1672
else:
1673
raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"
1674
1675
def GF_to_integer(self, G):
1676
r"""
1677
Return the integer representation of the finite field element ``G``.
1678
If ``G`` is an element of the finite field `\GF{2^4}`, then
1679
obtain the integer representation of ``G``. If ``G`` is a list of
1680
elements belonging to `\GF{2^4}`, obtain the integer
1681
representation of each element of the list, and return the result
1682
as a list of integers. If ``G`` is a matrix with entries over
1683
`\GF{2^4}`, convert each matrix entry to its integer representation,
1684
and return the result as a list of integers. The resulting list is
1685
obtained by starting from the top-left corner of the matrix, working
1686
across left to right, top to bottom. Each element of `\GF{2^4}` can
1687
be associated with a unique integer according to the following table:
1688
1689
.. MATH::
1690
1691
\begin{tabular}{ll|ll} \hline
1692
integer & $\GF{2^4}$ & integer & $\GF{2^4}$ \\\hline
1693
0 & $0$ & 8 & $x^3$ \\
1694
1 & $1$ & 9 & $x^3 + 1$ \\
1695
2 & $x$ & 10 & $x^3 + x$ \\
1696
3 & $x + 1$ & 11 & $x^3 + x + 1$ \\
1697
4 & $x^2$ & 12 & $x^3 + x^2$ \\
1698
5 & $x^2 + 1$ & 13 & $x^3 + x^2 + 1$ \\
1699
6 & $x^2 + x$ & 14 & $x^3 + x^2 + x$ \\
1700
7 & $x^2 + x + 1$ & 15 & $x^3 + x^2 + x+ 1$ \\\hline
1701
\end{tabular}
1702
1703
INPUT:
1704
1705
- ``G`` -- an element of `\GF{2^4}`, a list of elements belonging to
1706
`\GF{2^4}`, or a matrix over `\GF{2^4}`
1707
1708
OUTPUT:
1709
1710
- The integer representation of ``G``.
1711
1712
EXAMPLES:
1713
1714
Obtain the integer representation of all elements of `\GF{2^4}`::
1715
1716
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1717
sage: maes = MiniAES()
1718
sage: K = FiniteField(16, "x")
1719
sage: S = Set(K); len(S) # GF(2^4) has this many elements
1720
16
1721
sage: [maes.GF_to_integer(S[i]) for i in xrange(len(S))]
1722
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1723
1724
The integer representation of a list of elements belonging to
1725
`\GF{2^4}`::
1726
1727
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1728
sage: maes = MiniAES()
1729
sage: K = FiniteField(16, "x")
1730
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")]
1731
sage: maes.GF_to_integer(G)
1732
[7, 12, 2, 11, 15, 6, 1, 7]
1733
1734
The integer representation of a matrix over `\GF{2^4}`::
1735
1736
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1737
sage: maes = MiniAES()
1738
sage: K = FiniteField(16, "x")
1739
sage: MS = MatrixSpace(K, 2, 2)
1740
sage: G = MS([K("x^3 + x^2"), K("x + 1"), K("x^2 + x + 1"), K("x^3 + x^2 + x")]); G
1741
<BLANKLINE>
1742
[ x^3 + x^2 x + 1]
1743
[ x^2 + x + 1 x^3 + x^2 + x]
1744
sage: maes.GF_to_integer(G)
1745
[12, 3, 7, 14]
1746
sage: MS = MatrixSpace(K, 2, 4)
1747
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
1748
<BLANKLINE>
1749
[ x^2 + x + 1 x^3 + x^2 x x^3 + x + 1]
1750
[x^3 + x^2 + x + 1 x^2 + x 1 x^2 + x + 1]
1751
sage: maes.GF_to_integer(G)
1752
[7, 12, 2, 11, 15, 6, 1, 7]
1753
1754
TESTS:
1755
1756
Input must be an element of `\GF{2^4}`::
1757
1758
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1759
sage: maes = MiniAES()
1760
sage: K = FiniteField(7, "x")
1761
sage: G = K.random_element()
1762
sage: maes.GF_to_integer(G)
1763
Traceback (most recent call last):
1764
...
1765
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1766
1767
A list of elements belonging to `\GF{2^4}`::
1768
1769
sage: maes.GF_to_integer([])
1770
Traceback (most recent call last):
1771
...
1772
ValueError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1773
sage: G = [K.random_element() for i in xrange(5)]
1774
sage: maes.GF_to_integer(G)
1775
Traceback (most recent call last):
1776
...
1777
KeyError:...
1778
1779
A matrix over `\GF{2^4}`::
1780
1781
sage: MS = MatrixSpace(FiniteField(7, "x"), 4, 5)
1782
sage: maes.GF_to_integer(MS.random_element())
1783
Traceback (most recent call last):
1784
...
1785
TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)
1786
"""
1787
K = FiniteField(self._key_size, "x")
1788
# G is an element over GF(16)
1789
if G in K:
1790
return self._GF_to_int[G]
1791
# G is a list of elements over GF(16)
1792
elif isinstance(G, list):
1793
if len(G) == 0:
1794
raise ValueError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"
1795
return [self._GF_to_int[g] for g in G]
1796
# G is a matrix over GF(16)
1797
elif isinstance(G, Matrix_dense):
1798
if not (G.base_ring() is K):
1799
raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"
1800
return [self._GF_to_int[G[i][j]] for i in xrange(G.nrows()) for j in xrange(G.ncols())]
1801
# the type of G doesn't match the supported types
1802
else:
1803
raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"
1804
1805
def binary_to_GF(self, B):
1806
r"""
1807
Return a list of elements of `\GF{2^4}` that represents the
1808
binary string ``B``. The number of bits in ``B`` must be greater
1809
than zero and a multiple of 4. Each nibble (or 4-bit string) is
1810
uniquely associated with an element of `\GF{2^4}` as
1811
specified by the following table:
1812
1813
.. MATH::
1814
1815
\begin{tabular}{ll|ll} \hline
1816
4-bit string & $\GF{2^4}$ & 4-bit string & $\GF{2^4}$ \\\hline
1817
0000 & $0$ & 1000 & $x^3$ \\
1818
0001 & $1$ & 1001 & $x^3 + 1$ \\
1819
0010 & $x$ & 1010 & $x^3 + x$ \\
1820
0011 & $x + 1$ & 1011 & $x^3 + x + 1$ \\
1821
0100 & $x^2$ & 1100 & $x^3 + x^2$ \\
1822
0101 & $x^2 + 1$ & 1101 & $x^3 + x^2 + 1$ \\
1823
0110 & $x^2 + x$ & 1110 & $x^3 + x^2 + x$ \\
1824
0111 & $x^2 + x + 1$ & 1111 & $x^3 + x^2 + x+ 1$ \\\hline
1825
\end{tabular}
1826
1827
INPUT:
1828
1829
- ``B`` -- a binary string, where the number of bits is positive and
1830
a multiple of 4
1831
1832
OUTPUT:
1833
1834
- A list of elements of the finite field `\GF{2^4}` that
1835
represent the binary string ``B``.
1836
1837
EXAMPLES:
1838
1839
Obtain all the elements of the finite field `\GF{2^4}`::
1840
1841
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1842
sage: maes = MiniAES()
1843
sage: bin = BinaryStrings()
1844
sage: B = bin("0000000100100011010001010110011110001001101010111100110111101111")
1845
sage: maes.binary_to_GF(B)
1846
<BLANKLINE>
1847
[0,
1848
1,
1849
x,
1850
x + 1,
1851
x^2,
1852
x^2 + 1,
1853
x^2 + x,
1854
x^2 + x + 1,
1855
x^3,
1856
x^3 + 1,
1857
x^3 + x,
1858
x^3 + x + 1,
1859
x^3 + x^2,
1860
x^3 + x^2 + 1,
1861
x^3 + x^2 + x,
1862
x^3 + x^2 + x + 1]
1863
1864
TESTS:
1865
1866
The input ``B`` must be a non-empty binary string, where the number
1867
of bits is a multiple of 4::
1868
1869
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1870
sage: maes = MiniAES()
1871
sage: maes.binary_to_GF("")
1872
Traceback (most recent call last):
1873
...
1874
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
1875
sage: maes.binary_to_GF("101")
1876
Traceback (most recent call last):
1877
...
1878
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
1879
"""
1880
from sage.rings.finite_rings.integer_mod import Mod
1881
bin = BinaryStrings()
1882
b = bin(B)
1883
# an empty string
1884
if len(b) == 0:
1885
raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"
1886
# a string with number of bits that is a multiple of 4
1887
if Mod(len(b), 4).lift() == 0:
1888
M = len(b) / 4 # the number of nibbles
1889
return [self._bin_to_GF[b[i*4 : (i+1)*4]] for i in xrange(M)]
1890
else:
1891
raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"
1892
1893
def binary_to_integer(self, B):
1894
r"""
1895
Return a list of integers representing the binary string ``B``. The
1896
number of bits in ``B`` must be greater than zero and a multiple of
1897
4. Each nibble (or 4-bit string) is uniquely associated with an
1898
integer as specified by the following table:
1899
1900
.. MATH::
1901
1902
\begin{tabular}{ll|ll} \hline
1903
4-bit string & integer & 4-bit string & integer \\\hline
1904
0000 & 0 & 1000 & 8 \\
1905
0001 & 1 & 1001 & 9 \\
1906
0010 & 2 & 1010 & 10 \\
1907
0011 & 3 & 1011 & 11 \\
1908
0100 & 4 & 1100 & 12 \\
1909
0101 & 5 & 1101 & 13 \\
1910
0110 & 6 & 1110 & 14 \\
1911
0111 & 7 & 1111 & 15 \\\hline
1912
\end{tabular}
1913
1914
INPUT:
1915
1916
- ``B`` -- a binary string, where the number of bits is positive and
1917
a multiple of 4
1918
1919
OUTPUT:
1920
1921
- A list of integers that represent the binary string ``B``.
1922
1923
EXAMPLES:
1924
1925
Obtain the integer representation of every 4-bit string::
1926
1927
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1928
sage: maes = MiniAES()
1929
sage: bin = BinaryStrings()
1930
sage: B = bin("0000000100100011010001010110011110001001101010111100110111101111")
1931
sage: maes.binary_to_integer(B)
1932
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
1933
1934
TESTS:
1935
1936
The input ``B`` must be a non-empty binary string, where the number
1937
of bits is a multiple of 4::
1938
1939
sage: from sage.crypto.block_cipher.miniaes import MiniAES
1940
sage: maes = MiniAES()
1941
sage: maes.binary_to_integer("")
1942
Traceback (most recent call last):
1943
...
1944
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
1945
sage: maes.binary_to_integer("101")
1946
Traceback (most recent call last):
1947
...
1948
ValueError: the number of bits in the binary string B must be positive and a multiple of 4
1949
"""
1950
from sage.rings.finite_rings.integer_mod import Mod
1951
bin = BinaryStrings()
1952
b = bin(B)
1953
# an empty string
1954
if len(b) == 0:
1955
raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"
1956
# a string with number of bits that is a multiple of 4
1957
if Mod(len(b), 4).lift() == 0:
1958
M = len(b) / 4 # the number of nibbles
1959
return [self._bin_to_int[b[i*4 : (i+1)*4]] for i in xrange(M)]
1960
else:
1961
raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"
1962
1963
def integer_to_binary(self, N):
1964
r"""
1965
Return the binary representation of ``N``. If `N` is an integer such
1966
that `0 \leq N \leq 15`, return the binary representation of ``N``.
1967
If ``N`` is a list of integers each of which is `\geq 0` and
1968
`\leq 15`, then obtain the binary representation of each integer,
1969
and concatenate the individual binary representations into a single
1970
binary string. Each integer between 0 and 15, inclusive, can be
1971
associated with a unique 4-bit string according to the following
1972
table:
1973
1974
.. MATH::
1975
1976
\begin{tabular}{ll|ll} \hline
1977
4-bit string & integer & 4-bit string & integer \\\hline
1978
0000 & 0 & 1000 & 8 \\
1979
0001 & 1 & 1001 & 9 \\
1980
0010 & 2 & 1010 & 10 \\
1981
0011 & 3 & 1011 & 11 \\
1982
0100 & 4 & 1100 & 12 \\
1983
0101 & 5 & 1101 & 13 \\
1984
0110 & 6 & 1110 & 14 \\
1985
0111 & 7 & 1111 & 15 \\\hline
1986
\end{tabular}
1987
1988
INPUT:
1989
1990
- ``N`` -- a non-negative integer less than or equal to 15, or a list
1991
of such integers
1992
1993
OUTPUT:
1994
1995
- A binary string representing ``N``.
1996
1997
EXAMPLES:
1998
1999
The binary representations of all integers between 0 and
2000
15, inclusive::
2001
2002
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2003
sage: maes = MiniAES()
2004
sage: lst = [n for n in xrange(16)]; lst
2005
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2006
sage: maes.integer_to_binary(lst)
2007
0000000100100011010001010110011110001001101010111100110111101111
2008
2009
The binary representation of an integer between 0 and 15,
2010
inclusive::
2011
2012
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2013
sage: maes = MiniAES()
2014
sage: maes.integer_to_binary(3)
2015
0011
2016
sage: maes.integer_to_binary(5)
2017
0101
2018
sage: maes.integer_to_binary(7)
2019
0111
2020
2021
TESTS:
2022
2023
The input ``N`` can be an integer, but must be bounded such that
2024
`0 \leq N \leq 15`::
2025
2026
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2027
sage: maes = MiniAES()
2028
sage: maes.integer_to_binary(-1)
2029
Traceback (most recent call last):
2030
...
2031
KeyError:...
2032
sage: maes.integer_to_binary("1")
2033
Traceback (most recent call last):
2034
...
2035
TypeError: N must be an integer 0 <= N <= 15 or a list of such integers
2036
sage: maes.integer_to_binary("")
2037
Traceback (most recent call last):
2038
...
2039
TypeError: N must be an integer 0 <= N <= 15 or a list of such integers
2040
2041
The input ``N`` can be a list of integers, but each integer `n` of
2042
the list must be `0 \leq n \leq 15`::
2043
2044
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2045
sage: maes = MiniAES()
2046
sage: maes.integer_to_binary([])
2047
Traceback (most recent call last):
2048
...
2049
ValueError: N must be an integer 0 <= N <= 15 or a list of such integers
2050
sage: maes.integer_to_binary([""])
2051
Traceback (most recent call last):
2052
...
2053
KeyError:...
2054
sage: maes.integer_to_binary([0, 1, 2, 16])
2055
Traceback (most recent call last):
2056
...
2057
KeyError:...
2058
"""
2059
if isinstance(N, list):
2060
if len(N) == 0:
2061
raise ValueError, "N must be an integer 0 <= N <= 15 or a list of such integers"
2062
bin = BinaryStrings()
2063
# Here, we assume that each element of the list is an integer n
2064
# such that 0 <= n <= 15. An error will be raised if otherwise.
2065
b = "".join([str(self._int_to_bin[n]) for n in N])
2066
return bin(b)
2067
elif isinstance(N, Integer):
2068
# Here, we assume that N is an integer such that 0 <= n <= 15.
2069
# An error will be raised if otherwise.
2070
return self._int_to_bin[N]
2071
else:
2072
raise TypeError, "N must be an integer 0 <= N <= 15 or a list of such integers"
2073
2074
def integer_to_GF(self, N):
2075
r"""
2076
Return the finite field representation of ``N``. If `N` is an
2077
integer such that `0 \leq N \leq 15`, return the element of
2078
`\GF{2^4}` that represents ``N``. If ``N`` is a list of integers
2079
each of which is `\geq 0` and `\leq 15`, then obtain the element
2080
of `\GF{2^4}` that represents each such integer, and return a list
2081
of such finite field representations. Each integer between 0 and 15,
2082
inclusive, can be associated with a unique element of `\GF{2^4}`
2083
according to the following table:
2084
2085
.. MATH::
2086
2087
\begin{tabular}{ll|ll} \hline
2088
integer & $\GF{2^4}$ & integer & $\GF{2^4}$ \\\hline
2089
0 & $0$ & 8 & $x^3$ \\
2090
1 & $1$ & 9 & $x^3 + 1$ \\
2091
2 & $x$ & 10 & $x^3 + x$ \\
2092
3 & $x + 1$ & 11 & $x^3 + x + 1$ \\
2093
4 & $x^2$ & 12 & $x^3 + x^2$ \\
2094
5 & $x^2 + 1$ & 13 & $x^3 + x^2 + 1$ \\
2095
6 & $x^2 + x$ & 14 & $x^3 + x^2 + x$ \\
2096
7 & $x^2 + x + 1$ & 15 & $x^3 + x^2 + x+ 1$ \\\hline
2097
\end{tabular}
2098
2099
INPUT:
2100
2101
- ``N`` -- a non-negative integer less than or equal to 15, or a list
2102
of such integers
2103
2104
OUTPUT:
2105
2106
- Elements of the finite field `\GF{2^4}`.
2107
2108
EXAMPLES:
2109
2110
Obtain the element of `\GF{2^4}` representing an integer `n`, where
2111
`0 \leq n \leq 15`::
2112
2113
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2114
sage: maes = MiniAES()
2115
sage: maes.integer_to_GF(0)
2116
0
2117
sage: maes.integer_to_GF(2)
2118
x
2119
sage: maes.integer_to_GF(7)
2120
x^2 + x + 1
2121
2122
Obtain the finite field elements corresponding to all non-negative
2123
integers less than or equal to 15::
2124
2125
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2126
sage: maes = MiniAES()
2127
sage: lst = [n for n in xrange(16)]; lst
2128
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
2129
sage: maes.integer_to_GF(lst)
2130
<BLANKLINE>
2131
[0,
2132
1,
2133
x,
2134
x + 1,
2135
x^2,
2136
x^2 + 1,
2137
x^2 + x,
2138
x^2 + x + 1,
2139
x^3,
2140
x^3 + 1,
2141
x^3 + x,
2142
x^3 + x + 1,
2143
x^3 + x^2,
2144
x^3 + x^2 + 1,
2145
x^3 + x^2 + x,
2146
x^3 + x^2 + x + 1]
2147
2148
TESTS:
2149
2150
The input ``N`` can be an integer, but it must be such that
2151
`0 \leq N \leq 15`::
2152
2153
sage: from sage.crypto.block_cipher.miniaes import MiniAES
2154
sage: maes = MiniAES()
2155
sage: maes.integer_to_GF(-1)
2156
Traceback (most recent call last):
2157
...
2158
KeyError:...
2159
sage: maes.integer_to_GF(16)
2160
Traceback (most recent call last):
2161
...
2162
KeyError:...
2163
sage: maes.integer_to_GF("2")
2164
Traceback (most recent call last):
2165
...
2166
TypeError: N must be an integer 0 <= N <= 15 or a list of such integers
2167
2168
The input ``N`` can be a list of integers, but each integer `n` in
2169
the list must be bounded such that `0 \leq n \leq 15`::
2170
2171
sage: maes.integer_to_GF([])
2172
Traceback (most recent call last):
2173
...
2174
ValueError: N must be an integer 0 <= N <= 15 or a list of such integers
2175
sage: maes.integer_to_GF([""])
2176
Traceback (most recent call last):
2177
...
2178
KeyError:...
2179
sage: maes.integer_to_GF([0, 2, 3, "4"])
2180
Traceback (most recent call last):
2181
...
2182
KeyError:...
2183
sage: maes.integer_to_GF([0, 2, 3, 16])
2184
Traceback (most recent call last):
2185
...
2186
KeyError:...
2187
"""
2188
if isinstance(N, list):
2189
if len(N) == 0:
2190
raise ValueError, "N must be an integer 0 <= N <= 15 or a list of such integers"
2191
# Here, we assume that each element of the list is an integer n
2192
# such that 0 <= n <= 15. An error will be raised if otherwise.
2193
return [self._int_to_GF[n] for n in N]
2194
elif isinstance(N, Integer):
2195
# Here, we assume that N is an integer such that 0 <= n <= 15.
2196
# An error will be raised if otherwise.
2197
return self._int_to_GF[N]
2198
else:
2199
raise TypeError, "N must be an integer 0 <= N <= 15 or a list of such integers"
2200
2201