Path: blob/master/src/sage/crypto/block_cipher/miniaes.py
8817 views
r"""1Mini-AES23A simplified variant of the Advanced Encryption Standard (AES). Note that4Mini-AES is for educational purposes only. It is a small-scale version of5the AES designed to help beginners understand the basic structure of AES.67AUTHORS:89- Minh Van Nguyen (2009-05): initial version10"""1112###########################################################################13# Copyright (c) 2009 Minh Van Nguyen <[email protected]>14#15# This program is free software; you can redistribute it and/or modify16# it under the terms of the GNU General Public License as published by17# the Free Software Foundation; either version 2 of the License, or18# (at your option) any later version.19#20# This program is distributed in the hope that it will be useful,21# but WITHOUT ANY WARRANTY; without even the implied warranty of22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the23# GNU General Public License for more details.24#25# http://www.gnu.org/licenses/26###########################################################################2728from sage.matrix.matrix_dense import Matrix_dense29from sage.matrix.matrix_space import MatrixSpace30from sage.monoids.string_monoid import BinaryStrings31from sage.monoids.string_monoid_element import StringMonoidElement32from sage.rings.finite_rings.constructor import FiniteField33from sage.rings.integer import Integer34from sage.structure.sage_object import SageObject3536class MiniAES(SageObject):37r"""38This class implements the Mini Advanced Encryption Standard (Mini-AES)39described in [P02]_. Note that Phan's Mini-AES is for educational purposes40only and is not secure for practical purposes. Mini-AES is a version of41the AES with all parameters significantly reduced, but at the same time42preserving the structure of AES. The goal of Mini-AES is to allow a43beginner to understand the structure of AES, thus laying a foundation44for a thorough study of AES. Its goal is as a teaching tool and is45different from the :mod:`SR <sage.crypto.mq.sr>` small scale variants46of the AES. SR defines a family of parameterizable variants of the AES47suitable as a framework for comparing different cryptanalytic techniques48that can be brought to bear on the AES.4950EXAMPLES:5152Encrypt a plaintext::5354sage: from sage.crypto.block_cipher.miniaes import MiniAES55sage: maes = MiniAES()56sage: K = FiniteField(16, "x")57sage: MS = MatrixSpace(K, 2, 2)58sage: P = MS([K("x^3 + x"), K("x^2 + 1"), K("x^2 + x"), K("x^3 + x^2")]); P59<BLANKLINE>60[ x^3 + x x^2 + 1]61[ x^2 + x x^3 + x^2]62sage: key = MS([K("x^3 + x^2"), K("x^3 + x"), K("x^3 + x^2 + x"), K("x^2 + x + 1")]); key63<BLANKLINE>64[ x^3 + x^2 x^3 + x]65[x^3 + x^2 + x x^2 + x + 1]66sage: C = maes.encrypt(P, key); C67<BLANKLINE>68[ x x^2 + x]69[x^3 + x^2 + x x^3 + x]7071Decrypt the result::7273sage: plaintxt = maes.decrypt(C, key)74sage: plaintxt; P75<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]81sage: plaintxt == P82True8384We can also work directly with binary strings::8586sage: from sage.crypto.block_cipher.miniaes import MiniAES87sage: maes = MiniAES()88sage: bin = BinaryStrings()89sage: key = bin.encoding("KE"); key90010010110100010191sage: P = bin.encoding("Encrypt this secret message!"); P920100010101101110011000110111001001111001011100000111010000100000011101000110100001101001011100110010000001110011011001010110001101110010011001010111010000100000011011010110010101110011011100110110000101100111011001010010000193sage: C = maes(P, key, algorithm="encrypt"); C941000100010100110111100000111100001001100111011010100011101101101010100101110111110101100111001110010001110110010101010001010011111011001100101000100011101101101001000001100011000110000011100001110011010111100000000111000100195sage: plaintxt = maes(C, key, algorithm="decrypt")96sage: plaintxt == P97True9899Now we work with integers `n` such that `0 \leq n \leq 15`::100101sage: from sage.crypto.block_cipher.miniaes import MiniAES102sage: maes = MiniAES()103sage: P = [n for n in xrange(16)]; P104[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]105sage: key = [2, 3, 11, 0]; key106[2, 3, 11, 0]107sage: P = maes.integer_to_binary(P); P1080000000100100011010001010110011110001001101010111100110111101111109sage: key = maes.integer_to_binary(key); key1100010001110110000111sage: C = maes(P, key, algorithm="encrypt"); C1121100100000100011111001010101010101011011100111110001000011100001113sage: plaintxt = maes(C, key, algorithm="decrypt")114sage: plaintxt == P115True116117Generate some random plaintext and a random secret key. Encrypt the118plaintext using that secret key and decrypt the result. Then compare the119decrypted plaintext with the original plaintext::120121sage: from sage.crypto.block_cipher.miniaes import MiniAES122sage: maes = MiniAES()123sage: MS = MatrixSpace(FiniteField(16, "x"), 2, 2)124sage: P = MS.random_element()125sage: key = maes.random_key()126sage: C = maes.encrypt(P, key)127sage: plaintxt = maes.decrypt(C, key)128sage: plaintxt == P129True130131REFERENCES:132133.. [P02] R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a134testbed for cryptanalysis students. Cryptologia, 26(4):283--306, 2002.135"""136137def __init__(self):138r"""139A simplified variant of the Advanced Encryption Standard (AES).140141EXAMPLES::142143sage: from sage.crypto.block_cipher.miniaes import MiniAES144sage: maes = MiniAES(); maes145Mini-AES block cipher with 16-bit keys146sage: MS = MatrixSpace(FiniteField(16, "x"), 2, 2)147sage: P = MS.random_element()148sage: key = maes.random_key()149sage: C = maes.encrypt(P, key)150sage: plaintxt = maes.decrypt(C, key)151sage: plaintxt == P152True153"""154from sage.crypto.mq import SBox155self._key_size = 16 # the number of bits in a secret key156B = BinaryStrings()157K = FiniteField(self._key_size, "x")158# the S-box for encryption159self._sboxE = SBox(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7)160# the S-box for decryption161self._sboxD = SBox(14, 3, 4, 8, 1, 12, 10, 15, 7, 13, 9, 6, 11, 2, 0, 5)162# nibble to finite field element163self._bin_to_GF = { B("0000"): K("0"),164B("0001"): K("1"),165B("0010"): K("x"),166B("0011"): K("x + 1"),167B("0100"): K("x^2"),168B("0101"): K("x^2 + 1"),169B("0110"): K("x^2 + x"),170B("0111"): K("x^2 + x + 1"),171B("1000"): K("x^3"),172B("1001"): K("x^3 + 1"),173B("1010"): K("x^3 + x"),174B("1011"): K("x^3 + x + 1"),175B("1100"): K("x^3 + x^2"),176B("1101"): K("x^3 + x^2 + 1"),177B("1110"): K("x^3 + x^2 + x"),178B("1111"): K("x^3 + x^2 + x+ 1") }179# nibble to integer180self._bin_to_int = { B("0000"): Integer(0),181B("0001"): Integer(1),182B("0010"): Integer(2),183B("0011"): Integer(3),184B("0100"): Integer(4),185B("0101"): Integer(5),186B("0110"): Integer(6),187B("0111"): Integer(7),188B("1000"): Integer(8),189B("1001"): Integer(9),190B("1010"): Integer(10),191B("1011"): Integer(11),192B("1100"): Integer(12),193B("1101"): Integer(13),194B("1110"): Integer(14),195B("1111"): Integer(15) }196# finite field element to nibble197self._GF_to_bin = { K("0"): B("0000"),198K("1"): B("0001"),199K("x"): B("0010"),200K("x + 1"): B("0011"),201K("x^2"): B("0100"),202K("x^2 + 1"): B("0101"),203K("x^2 + x"): B("0110"),204K("x^2 + x + 1"): B("0111"),205K("x^3"): B("1000"),206K("x^3 + 1"): B("1001"),207K("x^3 + x"): B("1010"),208K("x^3 + x + 1"): B("1011"),209K("x^3 + x^2"): B("1100"),210K("x^3 + x^2 + 1"): B("1101"),211K("x^3 + x^2 + x"): B("1110"),212K("x^3 + x^2 + x+ 1"): B("1111") }213# finite field element to integer214self._GF_to_int = { K("0"): Integer(0),215K("1"): Integer(1),216K("x"): Integer(2),217K("x + 1"): Integer(3),218K("x^2"): Integer(4),219K("x^2 + 1"): Integer(5),220K("x^2 + x"): Integer(6),221K("x^2 + x + 1"): Integer(7),222K("x^3"): Integer(8),223K("x^3 + 1"): Integer(9),224K("x^3 + x"): Integer(10),225K("x^3 + x + 1"): Integer(11),226K("x^3 + x^2"): Integer(12),227K("x^3 + x^2 + 1"): Integer(13),228K("x^3 + x^2 + x"): Integer(14),229K("x^3 + x^2 + x+ 1"): Integer(15) }230# integer to nibble231self._int_to_bin = { Integer(0): B("0000"),232Integer(1): B("0001"),233Integer(2): B("0010"),234Integer(3): B("0011"),235Integer(4): B("0100"),236Integer(5): B("0101"),237Integer(6): B("0110"),238Integer(7): B("0111"),239Integer(8): B("1000"),240Integer(9): B("1001"),241Integer(10): B("1010"),242Integer(11): B("1011"),243Integer(12): B("1100"),244Integer(13): B("1101"),245Integer(14): B("1110"),246Integer(15): B("1111") }247# integer to finite field element248self._int_to_GF = { Integer(0): K("0"),249Integer(1): K("1"),250Integer(2): K("x"),251Integer(3): K("x + 1"),252Integer(4): K("x^2"),253Integer(5): K("x^2 + 1"),254Integer(6): K("x^2 + x"),255Integer(7): K("x^2 + x + 1"),256Integer(8): K("x^3"),257Integer(9): K("x^3 + 1"),258Integer(10): K("x^3 + x"),259Integer(11): K("x^3 + x + 1"),260Integer(12): K("x^3 + x^2"),261Integer(13): K("x^3 + x^2 + 1"),262Integer(14): K("x^3 + x^2 + x"),263Integer(15): K("x^3 + x^2 + x+ 1") }264265def __call__(self, B, key, algorithm="encrypt"):266r"""267Apply Mini-AES encryption or decryption on the binary string ``B``268using the key ``key``. The flag ``algorithm`` controls what action is269to be performed on ``B``.270271INPUT:272273- ``B`` -- a binary string, where the number of bits is positive and274a multiple of 16. The number of 16 bits is evenly divided into four275nibbles. Hence 16 bits can be conveniently represented as a276`2 \times 2` matrix block for encryption or decryption.277278- ``key`` -- a secret key; this must be a 16-bit binary string279280- ``algorithm`` -- (default: ``"encrypt"``) a string; a flag to signify281whether encryption or decryption is to be applied to the binary282string ``B``. The encryption flag is ``"encrypt"`` and the decryption283flag is ``"decrypt"``.284285OUTPUT:286287- The ciphertext (respectively plaintext) corresponding to the288binary string ``B``.289290EXAMPLES::291292sage: from sage.crypto.block_cipher.miniaes import MiniAES293sage: maes = MiniAES()294sage: bin = BinaryStrings()295sage: key = bin.encoding("KE"); key2960100101101000101297sage: P = bin.encoding("Encrypt this secret message!"); P29801000101011011100110001101110010011110010111000001110100001000000111010001101000011010010111001100100000011100110110010101100011011100100110010101110100001000000110110101100101011100110111001101100001011001110110010100100001299sage: C = maes(P, key, algorithm="encrypt"); C30010001000101001101111000001111000010011001110110101000111011011010101001011101111101011001110011100100011101100101010100010100111110110011001010001000111011011010010000011000110001100000111000011100110101111000000001110001001301sage: plaintxt = maes(C, key, algorithm="decrypt")302sage: plaintxt == P303True304305TESTS:306307The binary string ``B`` must be non-empty and the number of bits must308be a multiple of 16::309310sage: from sage.crypto.block_cipher.miniaes import MiniAES311sage: maes = MiniAES()312sage: maes("B", "key")313Traceback (most recent call last):314...315TypeError: input B must be a non-empty binary string with number of bits a multiple of 16316sage: bin = BinaryStrings()317sage: B = bin.encoding("A")318sage: maes(B, "key")319Traceback (most recent call last):320...321ValueError: the number of bits in the binary string B must be positive and a multiple of 16322323The secret key ``key`` must be a 16-bit binary string::324325sage: B = bin.encoding("ABCD")326sage: maes(B, "key")327Traceback (most recent call last):328...329TypeError: secret key must be a 16-bit binary string330sage: key = bin.encoding("K")331sage: maes(B, key)332Traceback (most recent call last):333...334ValueError: secret key must be a 16-bit binary string335336The value for ``algorithm`` must be either ``"encrypt"`` or337``"decrypt"``::338339sage: B = bin.encoding("ABCD")340sage: key = bin.encoding("KE")341sage: maes(B, key, algorithm="ABC")342Traceback (most recent call last):343...344ValueError: algorithm must be either 'encrypt' or 'decrypt'345sage: maes(B, key, algorithm="e")346Traceback (most recent call last):347...348ValueError: algorithm must be either 'encrypt' or 'decrypt'349sage: maes(B, key, algorithm="d")350Traceback (most recent call last):351...352ValueError: algorithm must be either 'encrypt' or 'decrypt'353"""354from sage.rings.finite_rings.integer_mod import Mod355if not isinstance(B, StringMonoidElement):356raise TypeError, "input B must be a non-empty binary string with number of bits a multiple of 16"357if (len(B) == 0) or (Mod(len(B), self._key_size).lift() != 0):358raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 16"359if not isinstance(key, StringMonoidElement):360raise TypeError, "secret key must be a 16-bit binary string"361if len(key) != self._key_size:362raise ValueError, "secret key must be a 16-bit binary string"363364N = len(B) / self._key_size # the number of 16-bit blocks365MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)366bin = BinaryStrings()367S = ""368if algorithm == "encrypt":369# encrypt each 16-bit block in succession370for i in xrange(N):371# here 16 is the number of bits per encryption block372block = B[i*16 : (i+1)*16]373matB = MS(self.binary_to_GF(block))374matK = MS(self.binary_to_GF(key))375e = self.encrypt(matB, matK)376e = self.GF_to_binary(e)377S = "".join([S, str(e)])378return bin(S)379elif algorithm == "decrypt":380# decrypt each 16-bit block in succession381for i in xrange(N):382# here 16 is the number of bits per encryption block383block = B[i*16 : (i+1)*16]384matB = MS(self.binary_to_GF(block))385matK = MS(self.binary_to_GF(key))386e = self.decrypt(matB, matK)387e = self.GF_to_binary(e)388S = "".join([S, str(e)])389return bin(S)390else:391raise ValueError, "algorithm must be either 'encrypt' or 'decrypt'"392393def __eq__(self, other):394r"""395Compare ``self`` with ``other``.396397Mini-AES objects are the same if they have the same key size and398the same S-boxes.399400EXAMPLES::401402sage: from sage.crypto.block_cipher.miniaes import MiniAES403sage: m = MiniAES()404sage: m == loads(dumps(m))405True406"""407return ( (self._key_size == other._key_size) and408(self._sboxE == other._sboxE) and409(self._sboxD == other._sboxD) )410411def __repr__(self):412r"""413Return the string representation of self.414415EXAMPLES::416417sage: from sage.crypto.block_cipher.miniaes import MiniAES418sage: m = MiniAES(); m419Mini-AES block cipher with 16-bit keys420"""421return "Mini-AES block cipher with 16-bit keys"422423def add_key(self, block, rkey):424r"""425Return the matrix addition of ``block`` and ``rkey``. Both ``block``426and ``rkey`` are `2 \times 2` matrices over the finite field427`\GF{2^4}`. This method just return the matrix addition of428these two matrices.429430INPUT:431432- ``block`` -- a `2 \times 2` matrix with entries over433`\GF{2^4}`434435- ``rkey`` -- a round key; a `2 \times 2` matrix with entries over436`\GF{2^4}`437438OUTPUT:439440- The matrix addition of ``block`` and ``rkey``.441442EXAMPLES:443444We can work with elements of `\GF{2^4}`::445446sage: from sage.crypto.block_cipher.miniaes import MiniAES447sage: maes = MiniAES()448sage: K = FiniteField(16, "x")449sage: MS = MatrixSpace(K, 2, 2)450sage: D = MS([ [K("x^3 + x^2 + x + 1"), K("x^3 + x")], [K("0"), K("x^3 + x^2")] ]); D451<BLANKLINE>452[x^3 + x^2 + x + 1 x^3 + x]453[ 0 x^3 + x^2]454sage: k = MS([ [K("x^2 + 1"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); k455<BLANKLINE>456[ x^2 + 1 x^3 + x^2 + x + 1]457[ x + 1 0]458sage: maes.add_key(D, k)459<BLANKLINE>460[ x^3 + x x^2 + 1]461[ x + 1 x^3 + x^2]462463Or work with binary strings::464465sage: bin = BinaryStrings()466sage: B = bin.encoding("We"); B4670101011101100101468sage: B = MS(maes.binary_to_GF(B)); B469<BLANKLINE>470[ x^2 + 1 x^2 + x + 1]471[ x^2 + x x^2 + 1]472sage: key = bin.encoding("KY"); key4730100101101011001474sage: key = MS(maes.binary_to_GF(key)); key475<BLANKLINE>476[ x^2 x^3 + x + 1]477[ x^2 + 1 x^3 + 1]478sage: maes.add_key(B, key)479<BLANKLINE>480[ 1 x^3 + x^2]481[ x + 1 x^3 + x^2]482483We can also work with integers `n` such that `0 \leq n \leq 15`::484485sage: N = [2, 3, 5, 7]; N486[2, 3, 5, 7]487sage: key = [9, 11, 13, 15]; key488[9, 11, 13, 15]489sage: N = MS(maes.integer_to_GF(N)); N490<BLANKLINE>491[ x x + 1]492[ x^2 + 1 x^2 + x + 1]493sage: key = MS(maes.integer_to_GF(key)); key494<BLANKLINE>495[ x^3 + 1 x^3 + x + 1]496[ x^3 + x^2 + 1 x^3 + x^2 + x + 1]497sage: maes.add_key(N, key)498<BLANKLINE>499[x^3 + x + 1 x^3]500[ x^3 x^3]501502TESTS:503504The input block and key must each be a matrix::505506sage: from sage.crypto.block_cipher.miniaes import MiniAES507sage: maes = MiniAES()508sage: K = FiniteField(16, "x")509sage: MSB = MatrixSpace(K, 2, 2)510sage: B = MSB([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])511sage: maes.add_key(B, "key")512Traceback (most recent call last):513...514TypeError: round key must be a 2 x 2 matrix over GF(16)515sage: maes.add_key("block", "key")516Traceback (most recent call last):517...518TypeError: input block must be a 2 x 2 matrix over GF(16)519520In addition, the dimensions of the input matrices must each be521`2 \times 2`::522523sage: MSB = MatrixSpace(K, 1, 2)524sage: B = MSB([ [K("x^3 + 1"), K("x^2 + x")] ])525sage: maes.add_key(B, "key")526Traceback (most recent call last):527...528TypeError: input block must be a 2 x 2 matrix over GF(16)529sage: MSB = MatrixSpace(K, 2, 2)530sage: B = MSB([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])531sage: MSK = MatrixSpace(K, 1, 2)532sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])533sage: maes.add_key(B, key)534Traceback (most recent call last):535...536TypeError: round key must be a 2 x 2 matrix over GF(16)537"""538if not isinstance(block, Matrix_dense) or \539not (block.base_ring().order() == 16 and block.base_ring().is_field()):540raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"541if not (block.nrows() == block.ncols() == 2):542raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"543544if not isinstance(rkey, Matrix_dense) or \545not (rkey.base_ring().order() == 16 and rkey.base_ring().is_field()):546raise TypeError, "round key must be a 2 x 2 matrix over GF(16)"547if not (rkey.nrows() == rkey.ncols() == 2):548raise TypeError, "round key must be a 2 x 2 matrix over GF(16)"549550return block + rkey551552def block_length(self):553r"""554Return the block length of Phan's Mini-AES block cipher. A key in555Phan's Mini-AES is a block of 16 bits. Each nibble of a key can be556considered as an element of the finite field `\GF{2^4}`.557Therefore the key consists of four elements from `\GF{2^4}`.558559OUTPUT:560561- The block (or key) length in number of bits.562563EXAMPLES::564565sage: from sage.crypto.block_cipher.miniaes import MiniAES566sage: maes = MiniAES()567sage: maes.block_length()56816569"""570return self._key_size571572def decrypt(self, C, key):573r"""574Use Phan's Mini-AES to decrypt the ciphertext ``C`` with the secret575key ``key``. Both ``C`` and ``key`` must be `2 \times 2` matrices over576the finite field `\GF{2^4}`. Let `\gamma` denote the577operation of nibble-sub, `\pi` denote shift-row, `\theta` denote578mix-column, and `\sigma_{K_i}` denote add-key with the round key579`K_i`. Then decryption `D` using Phan's Mini-AES is the function580composition581582.. MATH::583584D = \sigma_{K_0} \circ \gamma^{-1} \circ \pi \circ \theta \circ \sigma_{K_1} \circ \gamma^{-1} \circ \pi \circ \sigma_{K_2}585586where `\gamma^{-1}` is the nibble-sub operation that uses the S-box587for decryption, and the order of execution is from right to left.588589INPUT:590591- ``C`` -- a ciphertext block; must be a `2 \times 2` matrix over592the finite field `\GF{2^4}`593594- ``key`` -- a secret key for this Mini-AES block cipher; must be a595`2 \times 2` matrix over the finite field `\GF{2^4}`596597OUTPUT:598599- The plaintext corresponding to ``C``.600601EXAMPLES:602603We encrypt a plaintext, decrypt the ciphertext, then compare the604decrypted plaintext with the original plaintext. Here we work with605elements of `\GF{2^4}`::606607sage: from sage.crypto.block_cipher.miniaes import MiniAES608sage: maes = MiniAES()609sage: K = FiniteField(16, "x")610sage: MS = MatrixSpace(K, 2, 2)611sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ]); P612<BLANKLINE>613[ x^3 + 1 x^2 + x]614[x^3 + x^2 x + 1]615sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); key616<BLANKLINE>617[ x^3 + x^2 x^3 + x^2 + x + 1]618[ x + 1 0]619sage: C = maes.encrypt(P, key); C620<BLANKLINE>621[x^2 + x + 1 x^3 + x^2]622[ x x^2 + x]623sage: plaintxt = maes.decrypt(C, key)624sage: plaintxt; P625<BLANKLINE>626[ x^3 + 1 x^2 + x]627[x^3 + x^2 x + 1]628<BLANKLINE>629[ x^3 + 1 x^2 + x]630[x^3 + x^2 x + 1]631sage: plaintxt == P632True633634But we can also work with binary strings::635636sage: bin = BinaryStrings()637sage: P = bin.encoding("de"); P6380110010001100101639sage: P = MS(maes.binary_to_GF(P)); P640<BLANKLINE>641[x^2 + x x^2]642[x^2 + x x^2 + 1]643sage: key = bin.encoding("ke"); key6440110101101100101645sage: key = MS(maes.binary_to_GF(key)); key646<BLANKLINE>647[ x^2 + x x^3 + x + 1]648[ x^2 + x x^2 + 1]649sage: C = maes.encrypt(P, key)650sage: plaintxt = maes.decrypt(C, key)651sage: plaintxt == P652True653654Here we work with integers `n` such that `0 \leq n \leq 15`::655656sage: P = [3, 5, 7, 14]; P657[3, 5, 7, 14]658sage: key = [2, 6, 7, 8]; key659[2, 6, 7, 8]660sage: P = MS(maes.integer_to_GF(P)); P661<BLANKLINE>662[ x + 1 x^2 + 1]663[ x^2 + x + 1 x^3 + x^2 + x]664sage: key = MS(maes.integer_to_GF(key)); key665<BLANKLINE>666[ x x^2 + x]667[x^2 + x + 1 x^3]668sage: C = maes.encrypt(P, key)669sage: plaintxt = maes.decrypt(C, key)670sage: plaintxt == P671True672673TESTS:674675The input block must be a matrix::676677sage: from sage.crypto.block_cipher.miniaes import MiniAES678sage: maes = MiniAES()679sage: K = FiniteField(16, "x")680sage: MS = MatrixSpace(K, 2, 2)681sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])682sage: maes.decrypt("C", key)683Traceback (most recent call last):684...685TypeError: ciphertext block must be a 2 x 2 matrix over GF(16)686sage: C = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])687sage: maes.decrypt(C, "key")688Traceback (most recent call last):689...690TypeError: secret key must be a 2 x 2 matrix over GF(16)691692In addition, the dimensions of the input matrices must be693`2 \times 2`::694695sage: MS = MatrixSpace(K, 1, 2)696sage: C = MS([ [K("x^3 + 1"), K("x^2 + x")]])697sage: maes.decrypt(C, "key")698Traceback (most recent call last):699...700TypeError: ciphertext block must be a 2 x 2 matrix over GF(16)701sage: MSC = MatrixSpace(K, 2, 2)702sage: C = MSC([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])703sage: MSK = MatrixSpace(K, 1, 2)704sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])705sage: maes.decrypt(C, key)706Traceback (most recent call last):707...708TypeError: secret key must be a 2 x 2 matrix over GF(16)709"""710if not isinstance(C, Matrix_dense) or \711not (C.base_ring().order() == 16 and C.base_ring().is_field()):712raise TypeError, "ciphertext block must be a 2 x 2 matrix over GF(16)"713if not (C.nrows() == C.ncols() == 2):714raise TypeError, "ciphertext block must be a 2 x 2 matrix over GF(16)"715if not isinstance(key, Matrix_dense) or \716not (key.base_ring().order() == 16 and key.base_ring().is_field()):717raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"718if not (key.nrows() == key.ncols() == 2):719raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"720721# pre-compute the round keys722rkey0 = self.round_key(key, 0)723rkey1 = self.round_key(key, 1)724rkey2 = self.round_key(key, 2)725726# now proceed with decrypting the ciphertext727728# undo the result of round 2729plaintext = self.add_key(C, rkey2)730plaintext = self.shift_row(plaintext)731plaintext = self.nibble_sub(plaintext, algorithm="decrypt")732733# undo the result of round 1734plaintext = self.add_key(plaintext, rkey1)735plaintext = self.mix_column(plaintext)736plaintext = self.shift_row(plaintext)737plaintext = self.nibble_sub(plaintext, algorithm="decrypt")738739# undo the result of round 0740plaintext = self.add_key(plaintext, rkey0)741742return plaintext743744def encrypt(self, P, key):745r"""746Use Phan's Mini-AES to encrypt the plaintext ``P`` with the secret747key ``key``. Both ``P`` and ``key`` must be `2 \times 2` matrices748over the finite field `\GF{2^4}`. Let `\gamma` denote the749operation of nibble-sub, `\pi` denote shift-row, `\theta` denote750mix-column, and `\sigma_{K_i}` denote add-key with the round key751`K_i`. Then encryption `E` using Phan's Mini-AES is the function752composition753754.. MATH::755756E = \sigma_{K_2} \circ \pi \circ \gamma \circ \sigma_{K_1} \circ \theta \circ \pi \circ \gamma \circ \sigma_{K_0}757758where the order of execution is from right to left. Note that759`\gamma` is the nibble-sub operation that uses the S-box for760encryption.761762INPUT:763764- ``P`` -- a plaintext block; must be a `2 \times 2` matrix over765the finite field `\GF{2^4}`766767- ``key`` -- a secret key for this Mini-AES block cipher; must be a768`2 \times 2` matrix over the finite field `\GF{2^4}`769770OUTPUT:771772- The ciphertext corresponding to ``P``.773774EXAMPLES:775776Here we work with elements of `\GF{2^4}`::777778sage: from sage.crypto.block_cipher.miniaes import MiniAES779sage: maes = MiniAES()780sage: K = FiniteField(16, "x")781sage: MS = MatrixSpace(K, 2, 2)782sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ]); P783<BLANKLINE>784[ x^3 + 1 x^2 + x]785[x^3 + x^2 x + 1]786sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ]); key787<BLANKLINE>788[ x^3 + x^2 x^3 + x^2 + x + 1]789[ x + 1 0]790sage: maes.encrypt(P, key)791<BLANKLINE>792[x^2 + x + 1 x^3 + x^2]793[ x x^2 + x]794795But we can also work with binary strings::796797sage: bin = BinaryStrings()798sage: P = bin.encoding("de"); P7990110010001100101800sage: P = MS(maes.binary_to_GF(P)); P801<BLANKLINE>802[x^2 + x x^2]803[x^2 + x x^2 + 1]804sage: key = bin.encoding("ke"); key8050110101101100101806sage: key = MS(maes.binary_to_GF(key)); key807<BLANKLINE>808[ x^2 + x x^3 + x + 1]809[ x^2 + x x^2 + 1]810sage: C = maes.encrypt(P, key)811sage: plaintxt = maes.decrypt(C, key)812sage: plaintxt == P813True814815Now we work with integers `n` such that `0 \leq n \leq 15`::816817sage: P = [1, 5, 8, 12]; P818[1, 5, 8, 12]819sage: key = [5, 9, 15, 0]; key820[5, 9, 15, 0]821sage: P = MS(maes.integer_to_GF(P)); P822<BLANKLINE>823[ 1 x^2 + 1]824[ x^3 x^3 + x^2]825sage: key = MS(maes.integer_to_GF(key)); key826<BLANKLINE>827[ x^2 + 1 x^3 + 1]828[x^3 + x^2 + x + 1 0]829sage: C = maes.encrypt(P, key)830sage: plaintxt = maes.decrypt(C, key)831sage: plaintxt == P832True833834TESTS:835836The input block must be a matrix::837838sage: from sage.crypto.block_cipher.miniaes import MiniAES839sage: maes = MiniAES()840sage: K = FiniteField(16, "x")841sage: MS = MatrixSpace(K, 2, 2)842sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])843sage: maes.encrypt("P", key)844Traceback (most recent call last):845...846TypeError: plaintext block must be a 2 x 2 matrix over GF(16)847sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])848sage: maes.encrypt(P, "key")849Traceback (most recent call last):850...851TypeError: secret key must be a 2 x 2 matrix over GF(16)852853In addition, the dimensions of the input matrices must be854`2 \times 2`::855856sage: MS = MatrixSpace(K, 1, 2)857sage: P = MS([ [K("x^3 + 1"), K("x^2 + x")]])858sage: maes.encrypt(P, "key")859Traceback (most recent call last):860...861TypeError: plaintext block must be a 2 x 2 matrix over GF(16)862sage: MSP = MatrixSpace(K, 2, 2)863sage: P = MSP([ [K("x^3 + 1"), K("x^2 + x")], [K("x^3 + x^2"), K("x + 1")] ])864sage: MSK = MatrixSpace(K, 1, 2)865sage: key = MSK([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")]])866sage: maes.encrypt(P, key)867Traceback (most recent call last):868...869TypeError: secret key must be a 2 x 2 matrix over GF(16)870"""871if not isinstance(P, Matrix_dense) or \872not (P.base_ring().order() == 16 and P.base_ring().is_field()):873raise TypeError, "plaintext block must be a 2 x 2 matrix over GF(16)"874if not (P.nrows() == P.ncols() == 2):875raise TypeError, "plaintext block must be a 2 x 2 matrix over GF(16)"876if not isinstance(key, Matrix_dense) or \877not (key.base_ring().order() == 16 and key.base_ring().is_field()):878raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"879if not (key.nrows() == key.ncols() == 2):880raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"881882# pre-compute the round keys883rkey0 = self.round_key(key, 0)884rkey1 = self.round_key(key, 1)885rkey2 = self.round_key(key, 2)886887# now proceed with encrypting the plaintext888889# round 0890ciphertext = self.add_key(P, rkey0)891892# round 1893ciphertext = self.nibble_sub(ciphertext, algorithm="encrypt")894ciphertext = self.shift_row(ciphertext)895ciphertext = self.mix_column(ciphertext)896ciphertext = self.add_key(ciphertext, rkey1)897898# round 2899ciphertext = self.nibble_sub(ciphertext, algorithm="encrypt")900ciphertext = self.shift_row(ciphertext)901ciphertext = self.add_key(ciphertext, rkey2)902903return ciphertext904905def mix_column(self, block):906r"""907Return the matrix multiplication of ``block`` with a constant matrix.908The constant matrix is909910.. MATH::911912\begin{bmatrix}913x + 1 & x \\914x & x + 1915\end{bmatrix}916917If the input block is918919.. MATH::920921\begin{bmatrix}922c_0 & c_2 \\923c_1 & c_3924\end{bmatrix}925926then the output block is927928.. MATH::929930\begin{bmatrix}931d_0 & d_2 \\932d_1 & d_3933\end{bmatrix}934=935\begin{bmatrix}936x + 1 & x \\937x & x + 1938\end{bmatrix}939\begin{bmatrix}940c_0 & c_2 \\941c_1 & c_3942\end{bmatrix}943944INPUT:945946- ``block`` -- a `2 \times 2` matrix with entries over947`\GF{2^4}`948949OUTPUT:950951- A `2 \times 2` matrix resulting from multiplying the above constant952matrix with the input matrix ``block``.953954EXAMPLES:955956Here we work with elements of `\GF{2^4}`::957958sage: from sage.crypto.block_cipher.miniaes import MiniAES959sage: maes = MiniAES()960sage: K = FiniteField(16, "x")961sage: MS = MatrixSpace(K, 2, 2)962sage: mat = MS([ [K("x^2 + x + 1"), K("x^3 + x^2 + 1")], [K("x^3"), K("x")] ])963sage: maes.mix_column(mat)964<BLANKLINE>965[ x^3 + x 0]966[ x^2 + 1 x^3 + x^2 + x + 1]967968Multiplying by the identity matrix should leave the constant matrix969unchanged::970971sage: eye = MS([ [K("1"), K("0")], [K("0"), K("1")] ])972sage: maes.mix_column(eye)973<BLANKLINE>974[x + 1 x]975[ x x + 1]976977We can also work with binary strings::978979sage: bin = BinaryStrings()980sage: B = bin.encoding("rT"); B9810111001001010100982sage: B = MS(maes.binary_to_GF(B)); B983<BLANKLINE>984[x^2 + x + 1 x]985[ x^2 + 1 x^2]986sage: maes.mix_column(B)987<BLANKLINE>988[ x + 1 x^3 + x^2 + x]989[ 1 x^3]990991We can also work with integers `n` such that `0 \leq n \leq 15`::992993sage: P = [10, 5, 2, 7]; P994[10, 5, 2, 7]995sage: P = MS(maes.integer_to_GF(P)); P996<BLANKLINE>997[ x^3 + x x^2 + 1]998[ x x^2 + x + 1]999sage: maes.mix_column(P)1000<BLANKLINE>1001[x^3 + 1 1]1002[ 1 x + 1]10031004TESTS:10051006The input block must be a matrix::10071008sage: from sage.crypto.block_cipher.miniaes import MiniAES1009sage: maes = MiniAES()1010sage: maes.mix_column("mat")1011Traceback (most recent call last):1012...1013TypeError: input block must be a 2 x 2 matrix over GF(16)10141015In addition, the dimensions of the input matrix must be `2 \times 2`::10161017sage: K = FiniteField(16, "x")1018sage: MS = MatrixSpace(K, 1, 2)1019sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])1020sage: maes.mix_column(mat)1021Traceback (most recent call last):1022...1023TypeError: input block must be a 2 x 2 matrix over GF(16)1024"""1025if not isinstance(block, Matrix_dense) or \1026not (block.base_ring().order() == 16 and block.base_ring().is_field()):1027raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"1028if not (block.nrows() == block.ncols() == 2):1029raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"10301031K = FiniteField(self._key_size, "x")1032MS = MatrixSpace(K, 2, 2)1033M = MS( [ [K("x + 1"), K("x")],1034[K("x"), K("x + 1")] ] )1035return M * block10361037def nibble_sub(self, block, algorithm="encrypt"):1038r"""1039Substitute a nibble (or a block of 4 bits) using the following S-box:10401041.. MATH::10421043\begin{tabular}{ll|ll} \hline1044Input & Output & Input & Output \\\hline10450000 & 1110 & 1000 & 0011 \\10460001 & 0100 & 1001 & 1010 \\10470010 & 1101 & 1010 & 0110 \\10480011 & 0001 & 1011 & 1100 \\10490100 & 0010 & 1100 & 0101 \\10500101 & 1111 & 1101 & 1001 \\10510110 & 1011 & 1110 & 0000 \\10520111 & 1000 & 1111 & 0111 \\\hline1053\end{tabular}10541055The values in the above S-box are taken from the first row of the first1056S-box of the Data Encryption Standard (DES). Each nibble can be1057thought of as an element of the finite field `\GF{2^4}` of 161058elements. Thus in terms of `\GF{2^4}`, the S-box can also be1059specified as:10601061.. MATH::10621063\begin{tabular}{ll} \hline1064Input & Output \\\hline1065$0$ & $x^3 + x^2 + x$ \\1066$1$ & $x^2$ \\1067$x$ & $x^3 + x^2 + 1$ \\1068$x + 1$ & $1$ \\1069$x^2$ & $x$ \\1070$x^2 + 1$ & $x^3 + x^2 + x + 1$ \\1071$x^2 + x$ & $x^3 + x + 1$ \\1072$x^2 + x + 1$ & $x^3$ \\1073$x^3$ & $x + 1$ \\1074$x^3 + 1$ & $x^3 + x$ \\1075$x^3 + x$ & $x^2 + x$ \\1076$x^3 + x + 1$ & $x^3 + x^2$ \\1077$x^3 + x^2$ & $x^2 + 1$ \\1078$x^3 + x^2 + 1$ & $x^3 + 1$ \\1079$x^3 + x^2 + x$ & $0$ \\1080$x^3 + x^2 + x + 1$ & $x^2 + x + 1$ \\\hline1081\end{tabular}10821083Note that the above S-box is used for encryption. The S-box for1084decryption is obtained from the above S-box by reversing the role of1085the Input and Output columns. Thus the previous Input column for1086encryption now becomes the Output column for decryption, and the1087previous Output column for encryption is now the Input column for1088decryption. The S-box used for decryption can be specified as:10891090.. MATH::10911092\begin{tabular}{ll} \hline1093Input & Output \\\hline1094$0$ & $x^3 + x^2 + x$ \\1095$1$ & $x + 1$ \\1096$x$ & $x^2$ \\1097$x + 1$ & $x^3$ \\1098$x^2$ & $1$ \\1099$x^2 + 1$ & $x^3 + x^2$ \\1100$x^2 + x$ & $x^3 + x$ \\1101$x^2 + x + 1$ & $x^3 + x^2 + x + 1$ \\1102$x^3$ & $x^2 + x + 1$ \\1103$x^3 + 1$ & $x^3 + x^2 + 1$ \\1104$x^3 + x$ & $x^3 + 1$ \\1105$x^3 + x + 1$ & $x^2 + x$ \\1106$x^3 + x^2$ & $x^3 + x + 1$ \\1107$x^3 + x^2 + 1$ & $x$ \\1108$x^3 + x^2 + x$ & $0$ \\1109$x^3 + x^2 + x + 1$ & $x^2 + 1$ \\\hline1110\end{tabular}11111112INPUT:11131114- ``block`` -- a `2 \times 2` matrix with entries over1115`\GF{2^4}`11161117- ``algorithm`` -- (default: ``"encrypt"``) a string; a flag to signify1118whether this nibble-sub operation is used for encryption or1119decryption. The encryption flag is ``"encrypt"`` and the decryption1120flag is ``"decrypt"``.11211122OUTPUT:11231124- A `2 \times 2` matrix resulting from applying an S-box on1125entries of the `2 \times 2` matrix ``block``.11261127EXAMPLES:11281129Here we work with elements of the finite field `\GF{2^4}`::11301131sage: from sage.crypto.block_cipher.miniaes import MiniAES1132sage: maes = MiniAES()1133sage: K = FiniteField(16, "x")1134sage: MS = MatrixSpace(K, 2, 2)1135sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])1136sage: maes.nibble_sub(mat, algorithm="encrypt")1137<BLANKLINE>1138[ x^2 + x + 1 x^3 + x^2 + x]1139[ x^3 x^2 + x]11401141But we can also work with binary strings::11421143sage: bin = BinaryStrings()1144sage: B = bin.encoding("bi"); B114501100010011010011146sage: B = MS(maes.binary_to_GF(B)); B1147<BLANKLINE>1148[x^2 + x x]1149[x^2 + x x^3 + 1]1150sage: maes.nibble_sub(B, algorithm="encrypt")1151<BLANKLINE>1152[ x^3 + x + 1 x^3 + x^2 + 1]1153[ x^3 + x + 1 x^3 + x]1154sage: maes.nibble_sub(B, algorithm="decrypt")1155<BLANKLINE>1156[ x^3 + x x^2]1157[ x^3 + x x^3 + x^2 + 1]11581159Here we work with integers `n` such that `0 \leq n \leq 15`::11601161sage: P = [2, 6, 8, 14]; P1162[2, 6, 8, 14]1163sage: P = MS(maes.integer_to_GF(P)); P1164<BLANKLINE>1165[ x x^2 + x]1166[ x^3 x^3 + x^2 + x]1167sage: maes.nibble_sub(P, algorithm="encrypt")1168<BLANKLINE>1169[x^3 + x^2 + 1 x^3 + x + 1]1170[ x + 1 0]1171sage: maes.nibble_sub(P, algorithm="decrypt")1172<BLANKLINE>1173[ x^2 x^3 + x]1174[x^2 + x + 1 0]11751176TESTS:11771178The input block must be a matrix::11791180sage: from sage.crypto.block_cipher.miniaes import MiniAES1181sage: maes = MiniAES()1182sage: maes.nibble_sub("mat")1183Traceback (most recent call last):1184...1185TypeError: input block must be a 2 x 2 matrix over GF(16)11861187In addition, the dimensions of the input matrix must be `2 \times 2`::11881189sage: K = FiniteField(16, "x")1190sage: MS = MatrixSpace(K, 1, 2)1191sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])1192sage: maes.nibble_sub(mat)1193Traceback (most recent call last):1194...1195TypeError: input block must be a 2 x 2 matrix over GF(16)11961197The value for the option ``algorithm`` must be either the string1198``"encrypt"`` or ``"decrypt"``::11991200sage: K = FiniteField(16, "x")1201sage: MS = MatrixSpace(K, 2, 2)1202sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])1203sage: maes.nibble_sub(mat, algorithm="abc")1204Traceback (most recent call last):1205...1206ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'1207sage: maes.nibble_sub(mat, algorithm="e")1208Traceback (most recent call last):1209...1210ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'1211sage: maes.nibble_sub(mat, algorithm="d")1212Traceback (most recent call last):1213...1214ValueError: the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'1215"""1216if not isinstance(block, Matrix_dense) or \1217not (block.base_ring().order() == 16 and block.base_ring().is_field()):1218raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"1219if not (block.nrows() == block.ncols() == 2):1220raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"12211222MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)1223# get the integer representation of each GF(2^4) element1224# in the input matrix block1225lst = [self._GF_to_int[block[i][j]] for i in xrange(block.nrows()) for j in xrange(block.ncols())]1226if algorithm == "encrypt":1227# Now run each resulting integer through the S-box for1228# encryption. Then convert the result output by the S-box1229# to an element of GF(2^4).1230return MS([self._int_to_GF[self._sboxE[e]] for e in lst])1231elif algorithm == "decrypt":1232# Now run each resulting integer through the S-box for1233# decryption. Then convert the result output by the S-box1234# to an element of GF(2^4).1235return MS([self._int_to_GF[self._sboxD[e]] for e in lst])1236else:1237raise ValueError, "the algorithm for nibble-sub must be either 'encrypt' or 'decrypt'"12381239def random_key(self):1240r"""1241A random key within the key space of this Mini-AES block cipher. Like1242the AES, Phan's Mini-AES is a symmetric-key block cipher. A Mini-AES1243key is a block of 16 bits, or a `2 \times 2` matrix with entries over1244the finite field `\GF{2^4}`. Thus the number of possible keys is1245`2^{16} = 16^4`.12461247OUTPUT:12481249- A `2 \times 2` matrix over the finite field `\GF{2^4}`, used1250as a secret key for this Mini-AES block cipher.12511252EXAMPLES:12531254Each nibble of a key is an element of the finite field1255`\GF{2^4}`::12561257sage: K = FiniteField(16, "x")1258sage: from sage.crypto.block_cipher.miniaes import MiniAES1259sage: maes = MiniAES()1260sage: key = maes.random_key()1261sage: [key[i][j] in K for i in xrange(key.nrows()) for j in xrange(key.ncols())]1262[True, True, True, True]12631264Generate a random key, then perform encryption and decryption using1265that key::12661267sage: from sage.crypto.block_cipher.miniaes import MiniAES1268sage: maes = MiniAES()1269sage: K = FiniteField(16, "x")1270sage: MS = MatrixSpace(K, 2, 2)1271sage: key = maes.random_key()1272sage: P = MS.random_element()1273sage: C = maes.encrypt(P, key)1274sage: plaintxt = maes.decrypt(C, key)1275sage: plaintxt == P1276True1277"""1278MS = MatrixSpace(FiniteField(16, "x"), 2, 2)1279return MS.random_element()12801281def round_key(self, key, n):1282r"""1283Return the round key for round ``n``. Phan's Mini-AES is defined to1284have two rounds. The round key `K_0` is generated and used prior to1285the first round, with round keys `K_1` and `K_2` being used in rounds12861 and 2 respectively. In total, there are three round keys, each1287generated from the secret key ``key``.12881289INPUT:12901291- ``key`` -- the secret key12921293- ``n`` -- non-negative integer; the round number12941295OUTPUT:12961297- The `n`-th round key.12981299EXAMPLES:13001301Obtaining the round keys from the secret key::13021303sage: from sage.crypto.block_cipher.miniaes import MiniAES1304sage: maes = MiniAES()1305sage: K = FiniteField(16, "x")1306sage: MS = MatrixSpace(K, 2, 2)1307sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])1308sage: maes.round_key(key, 0)1309<BLANKLINE>1310[ x^3 + x^2 x^3 + x^2 + x + 1]1311[ x + 1 0]1312sage: key1313<BLANKLINE>1314[ x^3 + x^2 x^3 + x^2 + x + 1]1315[ x + 1 0]1316sage: maes.round_key(key, 1)1317<BLANKLINE>1318[ x + 1 x^3 + x^2 + x + 1]1319[ 0 x^3 + x^2 + x + 1]1320sage: maes.round_key(key, 2)1321<BLANKLINE>1322[x^2 + x x^3 + 1]1323[x^2 + x x^2 + x]13241325TESTS:13261327Only two rounds are defined for this AES variant::13281329sage: from sage.crypto.block_cipher.miniaes import MiniAES1330sage: maes = MiniAES()1331sage: K = FiniteField(16, "x")1332sage: MS = MatrixSpace(K, 2, 2)1333sage: key = MS([ [K("x^3 + x^2"), K("x^3 + x^2 + x + 1")], [K("x + 1"), K("0")] ])1334sage: maes.round_key(key, -1)1335Traceback (most recent call last):1336...1337ValueError: Mini-AES only defines two rounds1338sage: maes.round_key(key, 3)1339Traceback (most recent call last):1340...1341ValueError: Mini-AES only defines two rounds13421343The input key must be a matrix::13441345sage: maes.round_key("key", 0)1346Traceback (most recent call last):1347...1348TypeError: secret key must be a 2 x 2 matrix over GF(16)13491350In addition, the dimensions of the key matrix must be `2 \times 2`::13511352sage: K = FiniteField(16, "x")1353sage: MS = MatrixSpace(K, 1, 2)1354sage: key = MS([[K("x^3 + x^2 + x + 1"), K("0")]])1355sage: maes.round_key(key, 2)1356Traceback (most recent call last):1357...1358TypeError: secret key must be a 2 x 2 matrix over GF(16)1359"""1360if not isinstance(key, Matrix_dense) or \1361not (key.base_ring().order() == 16 and key.base_ring().is_field()):1362raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"1363if not (key.nrows() == key.ncols() == 2):1364raise TypeError, "secret key must be a 2 x 2 matrix over GF(16)"13651366K = FiniteField(self._key_size, "x")1367MS = MatrixSpace(K, 2, 2)1368# round 01369if n == 0:1370return key1371# round 11372if n == 1:1373round_constant_1 = K("1")1374w4 = key[0][0] + self._sboxE[key[1][1]] + round_constant_11375w5 = key[1][0] + w41376w6 = key[0][1] + w51377w7 = key[1][1] + w61378return MS([ [w4, w6], [w5, w7] ])1379# round 21380if n == 2:1381round_constant_2 = K("x")1382key1 = self.round_key(key, 1)1383w8 = key1[0][0] + self._sboxE[key1[1][1]] + round_constant_21384w9 = key1[1][0] + w81385w10 = key1[0][1] + w91386w11 = key1[1][1] + w101387return MS([ [w8, w10], [w9, w11] ])1388# unsupported round number1389if (n < 0) or (n > 2):1390raise ValueError, "Mini-AES only defines two rounds"13911392def sbox(self):1393r"""1394Return the S-box of Mini-AES.13951396EXAMPLES::13971398sage: from sage.crypto.block_cipher.miniaes import MiniAES1399sage: maes = MiniAES()1400sage: maes.sbox()1401(14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7)1402"""1403return self._sboxE14041405def shift_row(self, block):1406r"""1407Rotate each row of ``block`` to the left by different nibble1408amounts. The first or zero-th row is left unchanged, while the1409second or row one is rotated left by one nibble. This has the effect1410of only interchanging the nibbles in the second row. Let1411`b_0, b_1, b_2, b_3` be four nibbles arranged as the following1412`2 \times 2` matrix14131414.. MATH::14151416\begin{bmatrix}1417b_0 & b_2 \\1418b_1 & b_31419\end{bmatrix}14201421Then the operation of shift-row is the mapping14221423.. MATH::14241425\begin{bmatrix}1426b_0 & b_2 \\1427b_1 & b_31428\end{bmatrix}1429\longmapsto1430\begin{bmatrix}1431b_0 & b_2 \\1432b_3 & b_11433\end{bmatrix}14341435INPUT:14361437- ``block`` -- a `2 \times 2` matrix with entries over1438`\GF{2^4}`14391440OUTPUT:14411442- A `2 \times 2` matrix resulting from applying shift-row on ``block``.14431444EXAMPLES:14451446Here we work with elements of the finite field `\GF{2^4}`::14471448sage: from sage.crypto.block_cipher.miniaes import MiniAES1449sage: maes = MiniAES()1450sage: K = FiniteField(16, "x")1451sage: MS = MatrixSpace(K, 2, 2)1452sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")], [K("x^2 + x + 1"), K("x^3 + x")]])1453sage: maes.shift_row(mat)1454<BLANKLINE>1455[x^3 + x^2 + x + 1 0]1456[ x^3 + x x^2 + x + 1]1457sage: mat1458<BLANKLINE>1459[x^3 + x^2 + x + 1 0]1460[ x^2 + x + 1 x^3 + x]14611462But we can also work with binary strings::14631464sage: bin = BinaryStrings()1465sage: B = bin.encoding("Qt"); B146601010001011101001467sage: B = MS(maes.binary_to_GF(B)); B1468<BLANKLINE>1469[ x^2 + 1 1]1470[x^2 + x + 1 x^2]1471sage: maes.shift_row(B)1472<BLANKLINE>1473[ x^2 + 1 1]1474[ x^2 x^2 + x + 1]14751476Here we work with integers `n` such that `0 \leq n \leq 15`::14771478sage: P = [3, 6, 9, 12]; P1479[3, 6, 9, 12]1480sage: P = MS(maes.integer_to_GF(P)); P1481<BLANKLINE>1482[ x + 1 x^2 + x]1483[ x^3 + 1 x^3 + x^2]1484sage: maes.shift_row(P)1485<BLANKLINE>1486[ x + 1 x^2 + x]1487[x^3 + x^2 x^3 + 1]14881489TESTS:14901491The input block must be a matrix::14921493sage: from sage.crypto.block_cipher.miniaes import MiniAES1494sage: maes = MiniAES()1495sage: maes.shift_row("block")1496Traceback (most recent call last):1497...1498TypeError: input block must be a 2 x 2 matrix over GF(16)14991500In addition, the dimensions of the input matrix must be `2 \times 2`::15011502sage: K = FiniteField(16, "x")1503sage: MS = MatrixSpace(K, 1, 2)1504sage: mat = MS([[K("x^3 + x^2 + x + 1"), K("0")]])1505sage: maes.shift_row(mat)1506Traceback (most recent call last):1507...1508TypeError: input block must be a 2 x 2 matrix over GF(16)1509"""1510if not isinstance(block, Matrix_dense) or \1511not (block.base_ring().order() == 16 and block.base_ring().is_field()):1512raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"1513if not (block.nrows() == block.ncols() == 2):1514raise TypeError, "input block must be a 2 x 2 matrix over GF(16)"15151516MS = MatrixSpace(FiniteField(self._key_size, "x"), 2, 2)1517mat = MS([ [block[0][0], block[0][1]],1518[block[1][1], block[1][0]] ] )1519return mat152015211522### conversion functions to convert between different data formats15231524def GF_to_binary(self, G):1525r"""1526Return the binary representation of ``G``.1527If ``G`` is an element of the finite field `\GF{2^4}`, then1528obtain the binary representation of ``G``. If ``G`` is a list of1529elements belonging to `\GF{2^4}`, obtain the 4-bit1530representation of each element of the list, then concatenate the1531resulting 4-bit strings into a binary string. If ``G`` is a matrix1532with entries over `\GF{2^4}`, convert each matrix entry to its15334-bit representation, then concatenate the 4-bit strings. The1534concatenation is performed starting from the top-left corner of the1535matrix, working across left to right, top to bottom. Each element of1536`\GF{2^4}` can be associated with a unique 4-bit string1537according to the following table:15381539.. MATH::15401541\begin{tabular}{ll|ll} \hline15424-bit string & $\GF{2^4}$ & 4-bit string & $\GF{2^4}$ \\\hline15430000 & $0$ & 1000 & $x^3$ \\15440001 & $1$ & 1001 & $x^3 + 1$ \\15450010 & $x$ & 1010 & $x^3 + x$ \\15460011 & $x + 1$ & 1011 & $x^3 + x + 1$ \\15470100 & $x^2$ & 1100 & $x^3 + x^2$ \\15480101 & $x^2 + 1$ & 1101 & $x^3 + x^2 + 1$ \\15490110 & $x^2 + x$ & 1110 & $x^3 + x^2 + x$ \\15500111 & $x^2 + x + 1$ & 1111 & $x^3 + x^2 + x+ 1$ \\\hline1551\end{tabular}15521553INPUT:15541555- ``G`` -- an element of `\GF{2^4}`, a list of elements of1556`\GF{2^4}`, or a matrix over `\GF{2^4}`15571558OUTPUT:15591560- A binary string representation of ``G``.15611562EXAMPLES:15631564Obtain the binary representation of all elements of `\GF{2^4}`::15651566sage: from sage.crypto.block_cipher.miniaes import MiniAES1567sage: maes = MiniAES()1568sage: K = FiniteField(16, "x")1569sage: S = Set(K); len(S) # GF(2^4) has this many elements1570161571sage: [maes.GF_to_binary(S[i]) for i in xrange(len(S))]1572<BLANKLINE>1573[0000,15740001,15750010,15760011,15770100,15780101,15790110,15800111,15811000,15821001,15831010,15841011,15851100,15861101,15871110,15881111]15891590The binary representation of a list of elements belonging to1591`\GF{2^4}`::15921593sage: from sage.crypto.block_cipher.miniaes import MiniAES1594sage: maes = MiniAES()1595sage: K = FiniteField(16, "x")1596sage: 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")]1597sage: maes.GF_to_binary(G)15980111110000101011111101100001011115991600The binary representation of a matrix over `\GF{2^4}`::16011602sage: from sage.crypto.block_cipher.miniaes import MiniAES1603sage: maes = MiniAES()1604sage: K = FiniteField(16, "x")1605sage: MS = MatrixSpace(K, 2, 2)1606sage: G = MS([K("x^3 + x^2"), K("x + 1"), K("x^2 + x + 1"), K("x^3 + x^2 + x")]); G1607<BLANKLINE>1608[ x^3 + x^2 x + 1]1609[ x^2 + x + 1 x^3 + x^2 + x]1610sage: maes.GF_to_binary(G)161111000011011111101612sage: MS = MatrixSpace(K, 2, 4)1613sage: 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")]); G1614<BLANKLINE>1615[ x^2 + x + 1 x^3 + x^2 x x^3 + x + 1]1616[x^3 + x^2 + x + 1 x^2 + x 1 x^2 + x + 1]1617sage: maes.GF_to_binary(G)16180111110000101011111101100001011116191620TESTS:16211622Input must be an element of `\GF{2^4}`::16231624sage: from sage.crypto.block_cipher.miniaes import MiniAES1625sage: maes = MiniAES()1626sage: K = FiniteField(8, "x")1627sage: G = K.random_element()1628sage: maes.GF_to_binary(G)1629Traceback (most recent call last):1630...1631TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)16321633A list of elements belonging to `\GF{2^4}`::16341635sage: maes.GF_to_binary([])1636Traceback (most recent call last):1637...1638ValueError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)1639sage: G = [K.random_element() for i in xrange(5)]1640sage: maes.GF_to_binary(G)1641Traceback (most recent call last):1642...1643KeyError:...16441645A matrix over `\GF{2^4}`::16461647sage: MS = MatrixSpace(FiniteField(7, "x"), 4, 5)1648sage: maes.GF_to_binary(MS.random_element())1649Traceback (most recent call last):1650...1651TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)1652"""1653B = BinaryStrings()1654K = FiniteField(self._key_size, "x")1655# G is an element over GF(16)1656if G in K:1657return self._GF_to_bin[G]1658# G is a list of elements over GF(16)1659elif isinstance(G, list):1660if len(G) == 0:1661raise ValueError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"1662S = "".join([str(self._GF_to_bin[g]) for g in G])1663return B(S)1664# G is a matrix over GF(16)1665elif isinstance(G, Matrix_dense):1666if not (G.base_ring() is K):1667raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"1668S = "".join([str(self._GF_to_bin[G[i][j]]) for i in xrange(G.nrows()) for j in xrange(G.ncols())])1669return B(S)1670# the type of G doesn't match the supported types1671else:1672raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"16731674def GF_to_integer(self, G):1675r"""1676Return the integer representation of the finite field element ``G``.1677If ``G`` is an element of the finite field `\GF{2^4}`, then1678obtain the integer representation of ``G``. If ``G`` is a list of1679elements belonging to `\GF{2^4}`, obtain the integer1680representation of each element of the list, and return the result1681as a list of integers. If ``G`` is a matrix with entries over1682`\GF{2^4}`, convert each matrix entry to its integer representation,1683and return the result as a list of integers. The resulting list is1684obtained by starting from the top-left corner of the matrix, working1685across left to right, top to bottom. Each element of `\GF{2^4}` can1686be associated with a unique integer according to the following table:16871688.. MATH::16891690\begin{tabular}{ll|ll} \hline1691integer & $\GF{2^4}$ & integer & $\GF{2^4}$ \\\hline16920 & $0$ & 8 & $x^3$ \\16931 & $1$ & 9 & $x^3 + 1$ \\16942 & $x$ & 10 & $x^3 + x$ \\16953 & $x + 1$ & 11 & $x^3 + x + 1$ \\16964 & $x^2$ & 12 & $x^3 + x^2$ \\16975 & $x^2 + 1$ & 13 & $x^3 + x^2 + 1$ \\16986 & $x^2 + x$ & 14 & $x^3 + x^2 + x$ \\16997 & $x^2 + x + 1$ & 15 & $x^3 + x^2 + x+ 1$ \\\hline1700\end{tabular}17011702INPUT:17031704- ``G`` -- an element of `\GF{2^4}`, a list of elements belonging to1705`\GF{2^4}`, or a matrix over `\GF{2^4}`17061707OUTPUT:17081709- The integer representation of ``G``.17101711EXAMPLES:17121713Obtain the integer representation of all elements of `\GF{2^4}`::17141715sage: from sage.crypto.block_cipher.miniaes import MiniAES1716sage: maes = MiniAES()1717sage: K = FiniteField(16, "x")1718sage: S = Set(K); len(S) # GF(2^4) has this many elements1719161720sage: [maes.GF_to_integer(S[i]) for i in xrange(len(S))]1721[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]17221723The integer representation of a list of elements belonging to1724`\GF{2^4}`::17251726sage: from sage.crypto.block_cipher.miniaes import MiniAES1727sage: maes = MiniAES()1728sage: K = FiniteField(16, "x")1729sage: 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")]1730sage: maes.GF_to_integer(G)1731[7, 12, 2, 11, 15, 6, 1, 7]17321733The integer representation of a matrix over `\GF{2^4}`::17341735sage: from sage.crypto.block_cipher.miniaes import MiniAES1736sage: maes = MiniAES()1737sage: K = FiniteField(16, "x")1738sage: MS = MatrixSpace(K, 2, 2)1739sage: G = MS([K("x^3 + x^2"), K("x + 1"), K("x^2 + x + 1"), K("x^3 + x^2 + x")]); G1740<BLANKLINE>1741[ x^3 + x^2 x + 1]1742[ x^2 + x + 1 x^3 + x^2 + x]1743sage: maes.GF_to_integer(G)1744[12, 3, 7, 14]1745sage: MS = MatrixSpace(K, 2, 4)1746sage: 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")]); G1747<BLANKLINE>1748[ x^2 + x + 1 x^3 + x^2 x x^3 + x + 1]1749[x^3 + x^2 + x + 1 x^2 + x 1 x^2 + x + 1]1750sage: maes.GF_to_integer(G)1751[7, 12, 2, 11, 15, 6, 1, 7]17521753TESTS:17541755Input must be an element of `\GF{2^4}`::17561757sage: from sage.crypto.block_cipher.miniaes import MiniAES1758sage: maes = MiniAES()1759sage: K = FiniteField(7, "x")1760sage: G = K.random_element()1761sage: maes.GF_to_integer(G)1762Traceback (most recent call last):1763...1764TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)17651766A list of elements belonging to `\GF{2^4}`::17671768sage: maes.GF_to_integer([])1769Traceback (most recent call last):1770...1771ValueError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)1772sage: G = [K.random_element() for i in xrange(5)]1773sage: maes.GF_to_integer(G)1774Traceback (most recent call last):1775...1776KeyError:...17771778A matrix over `\GF{2^4}`::17791780sage: MS = MatrixSpace(FiniteField(7, "x"), 4, 5)1781sage: maes.GF_to_integer(MS.random_element())1782Traceback (most recent call last):1783...1784TypeError: input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)1785"""1786K = FiniteField(self._key_size, "x")1787# G is an element over GF(16)1788if G in K:1789return self._GF_to_int[G]1790# G is a list of elements over GF(16)1791elif isinstance(G, list):1792if len(G) == 0:1793raise ValueError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"1794return [self._GF_to_int[g] for g in G]1795# G is a matrix over GF(16)1796elif isinstance(G, Matrix_dense):1797if not (G.base_ring() is K):1798raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"1799return [self._GF_to_int[G[i][j]] for i in xrange(G.nrows()) for j in xrange(G.ncols())]1800# the type of G doesn't match the supported types1801else:1802raise TypeError, "input G must be an element of GF(16), a list of elements of GF(16), or a matrix over GF(16)"18031804def binary_to_GF(self, B):1805r"""1806Return a list of elements of `\GF{2^4}` that represents the1807binary string ``B``. The number of bits in ``B`` must be greater1808than zero and a multiple of 4. Each nibble (or 4-bit string) is1809uniquely associated with an element of `\GF{2^4}` as1810specified by the following table:18111812.. MATH::18131814\begin{tabular}{ll|ll} \hline18154-bit string & $\GF{2^4}$ & 4-bit string & $\GF{2^4}$ \\\hline18160000 & $0$ & 1000 & $x^3$ \\18170001 & $1$ & 1001 & $x^3 + 1$ \\18180010 & $x$ & 1010 & $x^3 + x$ \\18190011 & $x + 1$ & 1011 & $x^3 + x + 1$ \\18200100 & $x^2$ & 1100 & $x^3 + x^2$ \\18210101 & $x^2 + 1$ & 1101 & $x^3 + x^2 + 1$ \\18220110 & $x^2 + x$ & 1110 & $x^3 + x^2 + x$ \\18230111 & $x^2 + x + 1$ & 1111 & $x^3 + x^2 + x+ 1$ \\\hline1824\end{tabular}18251826INPUT:18271828- ``B`` -- a binary string, where the number of bits is positive and1829a multiple of 418301831OUTPUT:18321833- A list of elements of the finite field `\GF{2^4}` that1834represent the binary string ``B``.18351836EXAMPLES:18371838Obtain all the elements of the finite field `\GF{2^4}`::18391840sage: from sage.crypto.block_cipher.miniaes import MiniAES1841sage: maes = MiniAES()1842sage: bin = BinaryStrings()1843sage: B = bin("0000000100100011010001010110011110001001101010111100110111101111")1844sage: maes.binary_to_GF(B)1845<BLANKLINE>1846[0,18471,1848x,1849x + 1,1850x^2,1851x^2 + 1,1852x^2 + x,1853x^2 + x + 1,1854x^3,1855x^3 + 1,1856x^3 + x,1857x^3 + x + 1,1858x^3 + x^2,1859x^3 + x^2 + 1,1860x^3 + x^2 + x,1861x^3 + x^2 + x + 1]18621863TESTS:18641865The input ``B`` must be a non-empty binary string, where the number1866of bits is a multiple of 4::18671868sage: from sage.crypto.block_cipher.miniaes import MiniAES1869sage: maes = MiniAES()1870sage: maes.binary_to_GF("")1871Traceback (most recent call last):1872...1873ValueError: the number of bits in the binary string B must be positive and a multiple of 41874sage: maes.binary_to_GF("101")1875Traceback (most recent call last):1876...1877ValueError: the number of bits in the binary string B must be positive and a multiple of 41878"""1879from sage.rings.finite_rings.integer_mod import Mod1880bin = BinaryStrings()1881b = bin(B)1882# an empty string1883if len(b) == 0:1884raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"1885# a string with number of bits that is a multiple of 41886if Mod(len(b), 4).lift() == 0:1887M = len(b) / 4 # the number of nibbles1888return [self._bin_to_GF[b[i*4 : (i+1)*4]] for i in xrange(M)]1889else:1890raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"18911892def binary_to_integer(self, B):1893r"""1894Return a list of integers representing the binary string ``B``. The1895number of bits in ``B`` must be greater than zero and a multiple of18964. Each nibble (or 4-bit string) is uniquely associated with an1897integer as specified by the following table:18981899.. MATH::19001901\begin{tabular}{ll|ll} \hline19024-bit string & integer & 4-bit string & integer \\\hline19030000 & 0 & 1000 & 8 \\19040001 & 1 & 1001 & 9 \\19050010 & 2 & 1010 & 10 \\19060011 & 3 & 1011 & 11 \\19070100 & 4 & 1100 & 12 \\19080101 & 5 & 1101 & 13 \\19090110 & 6 & 1110 & 14 \\19100111 & 7 & 1111 & 15 \\\hline1911\end{tabular}19121913INPUT:19141915- ``B`` -- a binary string, where the number of bits is positive and1916a multiple of 419171918OUTPUT:19191920- A list of integers that represent the binary string ``B``.19211922EXAMPLES:19231924Obtain the integer representation of every 4-bit string::19251926sage: from sage.crypto.block_cipher.miniaes import MiniAES1927sage: maes = MiniAES()1928sage: bin = BinaryStrings()1929sage: B = bin("0000000100100011010001010110011110001001101010111100110111101111")1930sage: maes.binary_to_integer(B)1931[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]19321933TESTS:19341935The input ``B`` must be a non-empty binary string, where the number1936of bits is a multiple of 4::19371938sage: from sage.crypto.block_cipher.miniaes import MiniAES1939sage: maes = MiniAES()1940sage: maes.binary_to_integer("")1941Traceback (most recent call last):1942...1943ValueError: the number of bits in the binary string B must be positive and a multiple of 41944sage: maes.binary_to_integer("101")1945Traceback (most recent call last):1946...1947ValueError: the number of bits in the binary string B must be positive and a multiple of 41948"""1949from sage.rings.finite_rings.integer_mod import Mod1950bin = BinaryStrings()1951b = bin(B)1952# an empty string1953if len(b) == 0:1954raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"1955# a string with number of bits that is a multiple of 41956if Mod(len(b), 4).lift() == 0:1957M = len(b) / 4 # the number of nibbles1958return [self._bin_to_int[b[i*4 : (i+1)*4]] for i in xrange(M)]1959else:1960raise ValueError, "the number of bits in the binary string B must be positive and a multiple of 4"19611962def integer_to_binary(self, N):1963r"""1964Return the binary representation of ``N``. If `N` is an integer such1965that `0 \leq N \leq 15`, return the binary representation of ``N``.1966If ``N`` is a list of integers each of which is `\geq 0` and1967`\leq 15`, then obtain the binary representation of each integer,1968and concatenate the individual binary representations into a single1969binary string. Each integer between 0 and 15, inclusive, can be1970associated with a unique 4-bit string according to the following1971table:19721973.. MATH::19741975\begin{tabular}{ll|ll} \hline19764-bit string & integer & 4-bit string & integer \\\hline19770000 & 0 & 1000 & 8 \\19780001 & 1 & 1001 & 9 \\19790010 & 2 & 1010 & 10 \\19800011 & 3 & 1011 & 11 \\19810100 & 4 & 1100 & 12 \\19820101 & 5 & 1101 & 13 \\19830110 & 6 & 1110 & 14 \\19840111 & 7 & 1111 & 15 \\\hline1985\end{tabular}19861987INPUT:19881989- ``N`` -- a non-negative integer less than or equal to 15, or a list1990of such integers19911992OUTPUT:19931994- A binary string representing ``N``.19951996EXAMPLES:19971998The binary representations of all integers between 0 and199915, inclusive::20002001sage: from sage.crypto.block_cipher.miniaes import MiniAES2002sage: maes = MiniAES()2003sage: lst = [n for n in xrange(16)]; lst2004[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]2005sage: maes.integer_to_binary(lst)2006000000010010001101000101011001111000100110101011110011011110111120072008The binary representation of an integer between 0 and 15,2009inclusive::20102011sage: from sage.crypto.block_cipher.miniaes import MiniAES2012sage: maes = MiniAES()2013sage: maes.integer_to_binary(3)201400112015sage: maes.integer_to_binary(5)201601012017sage: maes.integer_to_binary(7)2018011120192020TESTS:20212022The input ``N`` can be an integer, but must be bounded such that2023`0 \leq N \leq 15`::20242025sage: from sage.crypto.block_cipher.miniaes import MiniAES2026sage: maes = MiniAES()2027sage: maes.integer_to_binary(-1)2028Traceback (most recent call last):2029...2030KeyError:...2031sage: maes.integer_to_binary("1")2032Traceback (most recent call last):2033...2034TypeError: N must be an integer 0 <= N <= 15 or a list of such integers2035sage: maes.integer_to_binary("")2036Traceback (most recent call last):2037...2038TypeError: N must be an integer 0 <= N <= 15 or a list of such integers20392040The input ``N`` can be a list of integers, but each integer `n` of2041the list must be `0 \leq n \leq 15`::20422043sage: from sage.crypto.block_cipher.miniaes import MiniAES2044sage: maes = MiniAES()2045sage: maes.integer_to_binary([])2046Traceback (most recent call last):2047...2048ValueError: N must be an integer 0 <= N <= 15 or a list of such integers2049sage: maes.integer_to_binary([""])2050Traceback (most recent call last):2051...2052KeyError:...2053sage: maes.integer_to_binary([0, 1, 2, 16])2054Traceback (most recent call last):2055...2056KeyError:...2057"""2058if isinstance(N, list):2059if len(N) == 0:2060raise ValueError, "N must be an integer 0 <= N <= 15 or a list of such integers"2061bin = BinaryStrings()2062# Here, we assume that each element of the list is an integer n2063# such that 0 <= n <= 15. An error will be raised if otherwise.2064b = "".join([str(self._int_to_bin[n]) for n in N])2065return bin(b)2066elif isinstance(N, Integer):2067# Here, we assume that N is an integer such that 0 <= n <= 15.2068# An error will be raised if otherwise.2069return self._int_to_bin[N]2070else:2071raise TypeError, "N must be an integer 0 <= N <= 15 or a list of such integers"20722073def integer_to_GF(self, N):2074r"""2075Return the finite field representation of ``N``. If `N` is an2076integer such that `0 \leq N \leq 15`, return the element of2077`\GF{2^4}` that represents ``N``. If ``N`` is a list of integers2078each of which is `\geq 0` and `\leq 15`, then obtain the element2079of `\GF{2^4}` that represents each such integer, and return a list2080of such finite field representations. Each integer between 0 and 15,2081inclusive, can be associated with a unique element of `\GF{2^4}`2082according to the following table:20832084.. MATH::20852086\begin{tabular}{ll|ll} \hline2087integer & $\GF{2^4}$ & integer & $\GF{2^4}$ \\\hline20880 & $0$ & 8 & $x^3$ \\20891 & $1$ & 9 & $x^3 + 1$ \\20902 & $x$ & 10 & $x^3 + x$ \\20913 & $x + 1$ & 11 & $x^3 + x + 1$ \\20924 & $x^2$ & 12 & $x^3 + x^2$ \\20935 & $x^2 + 1$ & 13 & $x^3 + x^2 + 1$ \\20946 & $x^2 + x$ & 14 & $x^3 + x^2 + x$ \\20957 & $x^2 + x + 1$ & 15 & $x^3 + x^2 + x+ 1$ \\\hline2096\end{tabular}20972098INPUT:20992100- ``N`` -- a non-negative integer less than or equal to 15, or a list2101of such integers21022103OUTPUT:21042105- Elements of the finite field `\GF{2^4}`.21062107EXAMPLES:21082109Obtain the element of `\GF{2^4}` representing an integer `n`, where2110`0 \leq n \leq 15`::21112112sage: from sage.crypto.block_cipher.miniaes import MiniAES2113sage: maes = MiniAES()2114sage: maes.integer_to_GF(0)211502116sage: maes.integer_to_GF(2)2117x2118sage: maes.integer_to_GF(7)2119x^2 + x + 121202121Obtain the finite field elements corresponding to all non-negative2122integers less than or equal to 15::21232124sage: from sage.crypto.block_cipher.miniaes import MiniAES2125sage: maes = MiniAES()2126sage: lst = [n for n in xrange(16)]; lst2127[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]2128sage: maes.integer_to_GF(lst)2129<BLANKLINE>2130[0,21311,2132x,2133x + 1,2134x^2,2135x^2 + 1,2136x^2 + x,2137x^2 + x + 1,2138x^3,2139x^3 + 1,2140x^3 + x,2141x^3 + x + 1,2142x^3 + x^2,2143x^3 + x^2 + 1,2144x^3 + x^2 + x,2145x^3 + x^2 + x + 1]21462147TESTS:21482149The input ``N`` can be an integer, but it must be such that2150`0 \leq N \leq 15`::21512152sage: from sage.crypto.block_cipher.miniaes import MiniAES2153sage: maes = MiniAES()2154sage: maes.integer_to_GF(-1)2155Traceback (most recent call last):2156...2157KeyError:...2158sage: maes.integer_to_GF(16)2159Traceback (most recent call last):2160...2161KeyError:...2162sage: maes.integer_to_GF("2")2163Traceback (most recent call last):2164...2165TypeError: N must be an integer 0 <= N <= 15 or a list of such integers21662167The input ``N`` can be a list of integers, but each integer `n` in2168the list must be bounded such that `0 \leq n \leq 15`::21692170sage: maes.integer_to_GF([])2171Traceback (most recent call last):2172...2173ValueError: N must be an integer 0 <= N <= 15 or a list of such integers2174sage: maes.integer_to_GF([""])2175Traceback (most recent call last):2176...2177KeyError:...2178sage: maes.integer_to_GF([0, 2, 3, "4"])2179Traceback (most recent call last):2180...2181KeyError:...2182sage: maes.integer_to_GF([0, 2, 3, 16])2183Traceback (most recent call last):2184...2185KeyError:...2186"""2187if isinstance(N, list):2188if len(N) == 0:2189raise ValueError, "N must be an integer 0 <= N <= 15 or a list of such integers"2190# Here, we assume that each element of the list is an integer n2191# such that 0 <= n <= 15. An error will be raised if otherwise.2192return [self._int_to_GF[n] for n in N]2193elif isinstance(N, Integer):2194# Here, we assume that N is an integer such that 0 <= n <= 15.2195# An error will be raised if otherwise.2196return self._int_to_GF[N]2197else:2198raise TypeError, "N must be an integer 0 <= N <= 15 or a list of such integers"219922002201